Creation of Cybook 2416 (actually Gen4) repository

This commit is contained in:
mlt
2009-12-18 17:10:00 +00:00
committed by godzil
commit 76f20f4d40
13791 changed files with 6812321 additions and 0 deletions

357
drivers/rtc/Kconfig Normal file
View File

@@ -0,0 +1,357 @@
#
# RTC class/drivers configuration
#
menu "Real Time Clock"
config RTC_LIB
tristate
config RTC_CLASS
tristate "RTC class"
depends on EXPERIMENTAL
default n
select RTC_LIB
help
Generic RTC class support. If you say yes here, you will
be allowed to plug one or more RTCs to your system. You will
probably want to enable one or more of the interfaces below.
This driver can also be built as a module. If so, the module
will be called rtc-class.
config RTC_HCTOSYS
bool "Set system time from RTC on startup"
depends on RTC_CLASS = y
default y
help
If you say yes here, the system time will be set using
the value read from the specified RTC device. This is useful
in order to avoid unnecessary fsck runs.
config RTC_HCTOSYS_DEVICE
string "The RTC to read the time from"
depends on RTC_HCTOSYS = y
default "rtc0"
help
The RTC device that will be used as the source for
the system time, usually rtc0.
config RTC_DEBUG
bool "RTC debug support"
depends on RTC_CLASS = y
help
Say yes here to enable debugging support in the RTC framework
and individual RTC drivers.
comment "RTC interfaces"
depends on RTC_CLASS
config RTC_INTF_SYSFS
tristate "sysfs"
depends on RTC_CLASS && SYSFS
default RTC_CLASS
help
Say yes here if you want to use your RTCs using sysfs interfaces,
/sys/class/rtc/rtc0 through /sys/.../rtcN.
This driver can also be built as a module. If so, the module
will be called rtc-sysfs.
config RTC_INTF_PROC
tristate "proc"
depends on RTC_CLASS && PROC_FS
default RTC_CLASS
help
Say yes here if you want to use your first RTC through the proc
interface, /proc/driver/rtc. Other RTCs will not be available
through that API.
This driver can also be built as a module. If so, the module
will be called rtc-proc.
config RTC_INTF_DEV
tristate "dev"
depends on RTC_CLASS
default RTC_CLASS
help
Say yes here if you want to use your RTCs using the /dev
interfaces, which "udev" sets up as /dev/rtc0 through
/dev/rtcN. You may want to set up a symbolic link so one
of these can be accessed as /dev/rtc, which is a name
expected by "hwclock" and some other programs.
This driver can also be built as a module. If so, the module
will be called rtc-dev.
config RTC_INTF_DEV_UIE_EMUL
bool "RTC UIE emulation on dev interface"
depends on RTC_INTF_DEV
help
Provides an emulation for RTC_UIE if the underlaying rtc chip
driver does not expose RTC_UIE ioctls. Those requests generate
once-per-second update interrupts, used for synchronization.
comment "RTC drivers"
depends on RTC_CLASS
# this 'CMOS' RTC driver is arch dependent because <asm-generic/rtc.h>
# requires <asm/mc146818rtc.h> defining CMOS_READ/CMOS_WRITE, and a
# global rtc_lock ... it's not yet just another platform_device.
config RTC_DRV_CMOS
tristate "PC-style 'CMOS' real time clock"
depends on RTC_CLASS && (X86 || ALPHA || ARM26 || ARM \
|| M32R || ATARI || POWERPC)
help
Say "yes" here to get direct support for the real time clock
found in every PC or ACPI-based system, and some other boards.
Specifically the original MC146818, compatibles like those in
PC south bridges, the DS12887 or M48T86, some multifunction
or LPC bus chips, and so on.
Your system will need to define the platform device used by
this driver, otherwise it won't be accessible. This means
you can safely enable this driver if you don't know whether
or not your board has this kind of hardware.
This driver can also be built as a module. If so, the module
will be called rtc-cmos.
config RTC_DRV_X1205
tristate "Xicor/Intersil X1205"
depends on RTC_CLASS && I2C
help
If you say yes here you get support for the
Xicor/Intersil X1205 RTC chip.
This driver can also be built as a module. If so, the module
will be called rtc-x1205.
config RTC_DRV_DS1307
tristate "Dallas/Maxim DS1307 and similar I2C RTC chips"
depends on RTC_CLASS && I2C
help
If you say yes here you get support for various compatible RTC
chips (often with battery backup) connected with I2C. This driver
should handle DS1307, DS1337, DS1338, DS1339, DS1340, ST M41T00,
and probably other chips. In some cases the RTC must already
have been initialized (by manufacturing or a bootloader).
The first seven registers on these chips hold an RTC, and other
registers may add features such as NVRAM, a trickle charger for
the RTC/NVRAM backup power, and alarms. This driver may not
expose all those available chip features.
This driver can also be built as a module. If so, the module
will be called rtc-ds1307.
config RTC_DRV_DS1553
tristate "Dallas DS1553"
depends on RTC_CLASS
help
If you say yes here you get support for the
Dallas DS1553 timekeeping chip.
This driver can also be built as a module. If so, the module
will be called rtc-ds1553.
config RTC_DRV_ISL1208
tristate "Intersil 1208"
depends on RTC_CLASS && I2C
help
If you say yes here you get support for the
Intersil 1208 RTC chip.
This driver can also be built as a module. If so, the module
will be called rtc-isl1208.
config RTC_DRV_DS1672
tristate "Dallas/Maxim DS1672"
depends on RTC_CLASS && I2C
help
If you say yes here you get support for the
Dallas/Maxim DS1672 timekeeping chip.
This driver can also be built as a module. If so, the module
will be called rtc-ds1672.
config RTC_DRV_DS1742
tristate "Dallas DS1742/1743"
depends on RTC_CLASS
help
If you say yes here you get support for the
Dallas DS1742/1743 timekeeping chip.
This driver can also be built as a module. If so, the module
will be called rtc-ds1742.
config RTC_DRV_OMAP
tristate "TI OMAP1"
depends on RTC_CLASS && ( \
ARCH_OMAP15XX || ARCH_OMAP16XX || ARCH_OMAP730 )
help
Say "yes" here to support the real time clock on TI OMAP1 chips.
This driver can also be built as a module called rtc-omap.
config RTC_DRV_PCF8563
tristate "Philips PCF8563/Epson RTC8564"
depends on RTC_CLASS && I2C
help
If you say yes here you get support for the
Philips PCF8563 RTC chip. The Epson RTC8564
should work as well.
This driver can also be built as a module. If so, the module
will be called rtc-pcf8563.
config RTC_DRV_PCF8583
tristate "Philips PCF8583"
depends on RTC_CLASS && I2C && ARCH_RPC
help
If you say yes here you get support for the Philips PCF8583
RTC chip found on Acorn RiscPCs. This driver supports the
platform specific method of retrieving the current year from
the RTC's SRAM.
This driver can also be built as a module. If so, the module
will be called rtc-pcf8583.
config RTC_DRV_RS5C348
tristate "Ricoh RS5C348A/B"
depends on RTC_CLASS && SPI
help
If you say yes here you get support for the
Ricoh RS5C348A and RS5C348B RTC chips.
This driver can also be built as a module. If so, the module
will be called rtc-rs5c348.
config RTC_DRV_RS5C372
tristate "Ricoh RS5C372A/B"
depends on RTC_CLASS && I2C
help
If you say yes here you get support for the
Ricoh RS5C372A and RS5C372B RTC chips.
This driver can also be built as a module. If so, the module
will be called rtc-rs5c372.
config RTC_DRV_S3C
tristate "Samsung S3C series SoC RTC"
depends on RTC_CLASS && ARCH_S3C2410
help
RTC (Realtime Clock) driver for the clock inbuilt into the
Samsung S3C24XX series of SoCs. This can provide periodic
interrupt rates from 1Hz to 64Hz for user programs, and
wakeup from Alarm.
The driver currently supports the common features on all the
S3C24XX range, such as the S3C2410, S3C2412, S3C2413, S3C2440
and S3C2442.
This driver can also be build as a module. If so, the module
will be called rtc-s3c.
config RTC_DRV_M48T86
tristate "ST M48T86/Dallas DS12887"
depends on RTC_CLASS
help
If you say Y here you will get support for the
ST M48T86 and Dallas DS12887 RTC chips.
This driver can also be built as a module. If so, the module
will be called rtc-m48t86.
config RTC_DRV_EP93XX
tristate "Cirrus Logic EP93XX"
depends on RTC_CLASS && ARCH_EP93XX
help
If you say yes here you get support for the
RTC embedded in the Cirrus Logic EP93XX processors.
This driver can also be built as a module. If so, the module
will be called rtc-ep93xx.
config RTC_DRV_SA1100
tristate "SA11x0/PXA2xx"
depends on RTC_CLASS && (ARCH_SA1100 || ARCH_PXA)
help
If you say Y here you will get access to the real time clock
built into your SA11x0 or PXA2xx CPU.
To compile this driver as a module, choose M here: the
module will be called rtc-sa1100.
config RTC_DRV_SH
tristate "SuperH On-Chip RTC"
depends on RTC_CLASS && SUPERH
help
Say Y here to enable support for the on-chip RTC found in
most SuperH processors.
To compile this driver as a module, choose M here: the
module will be called rtc-sh.
config RTC_DRV_VR41XX
tristate "NEC VR41XX"
depends on RTC_CLASS && CPU_VR41XX
help
If you say Y here you will get access to the real time clock
built into your NEC VR41XX CPU.
To compile this driver as a module, choose M here: the
module will be called rtc-vr41xx.
config RTC_DRV_PL031
tristate "ARM AMBA PL031 RTC"
depends on RTC_CLASS && ARM_AMBA
help
If you say Y here you will get access to ARM AMBA
PrimeCell PL031 UART found on certain ARM SOCs.
To compile this driver as a module, choose M here: the
module will be called rtc-pl031.
config RTC_DRV_AT91RM9200
tristate "AT91RM9200"
depends on RTC_CLASS && ARCH_AT91RM9200
help
Driver for the Atmel AT91RM9200's internal RTC (Realtime Clock).
config RTC_DRV_TEST
tristate "Test driver/device"
depends on RTC_CLASS
help
If you say yes here you get support for the
RTC test driver. It's a software RTC which can be
used to test the RTC subsystem APIs. It gets
the time from the system clock.
You want this driver only if you are doing development
on the RTC subsystem. Please read the source code
for further details.
This driver can also be built as a module. If so, the module
will be called rtc-test.
config RTC_DRV_MAX6902
tristate "Maxim 6902"
depends on RTC_CLASS && SPI
help
If you say yes here you will get support for the
Maxim MAX6902 spi RTC chip.
This driver can also be built as a module. If so, the module
will be called rtc-max6902.
config RTC_DRV_V3020
tristate "EM Microelectronic V3020"
depends on RTC_CLASS
help
If you say yes here you will get support for the
EM Microelectronic v3020 RTC chip.
This driver can also be built as a module. If so, the module
will be called rtc-v3020.
endmenu

40
drivers/rtc/Makefile Normal file
View File

@@ -0,0 +1,40 @@
#
# Makefile for RTC class/drivers.
#
ifeq ($(CONFIG_RTC_DEBUG),y)
EXTRA_CFLAGS += -DDEBUG
endif
obj-$(CONFIG_RTC_LIB) += rtc-lib.o
obj-$(CONFIG_RTC_HCTOSYS) += hctosys.o
obj-$(CONFIG_RTC_CLASS) += rtc-core.o
rtc-core-y := class.o interface.o
obj-$(CONFIG_RTC_INTF_SYSFS) += rtc-sysfs.o
obj-$(CONFIG_RTC_INTF_PROC) += rtc-proc.o
obj-$(CONFIG_RTC_INTF_DEV) += rtc-dev.o
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
obj-$(CONFIG_RTC_DRV_X1205) += rtc-x1205.o
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_PCF8563) += rtc-pcf8563.o
obj-$(CONFIG_RTC_DRV_PCF8583) += rtc-pcf8583.o
obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
obj-$(CONFIG_RTC_DRV_PL031) += rtc-pl031.o
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o

156
drivers/rtc/class.c Normal file
View File

@@ -0,0 +1,156 @@
/*
* RTC subsystem, base class
*
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* class skeleton from drivers/hwmon/hwmon.c
*
* 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/module.h>
#include <linux/rtc.h>
#include <linux/kdev_t.h>
#include <linux/idr.h>
static DEFINE_IDR(rtc_idr);
static DEFINE_MUTEX(idr_lock);
struct class *rtc_class;
static void rtc_device_release(struct class_device *class_dev)
{
struct rtc_device *rtc = to_rtc_device(class_dev);
mutex_lock(&idr_lock);
idr_remove(&rtc_idr, rtc->id);
mutex_unlock(&idr_lock);
kfree(rtc);
}
/**
* rtc_device_register - register w/ RTC class
* @dev: the device to register
*
* rtc_device_unregister() must be called when the class device is no
* longer needed.
*
* Returns the pointer to the new struct class device.
*/
struct rtc_device *rtc_device_register(const char *name, struct device *dev,
const struct rtc_class_ops *ops,
struct module *owner)
{
struct rtc_device *rtc;
int id, err;
if (idr_pre_get(&rtc_idr, GFP_KERNEL) == 0) {
err = -ENOMEM;
goto exit;
}
mutex_lock(&idr_lock);
err = idr_get_new(&rtc_idr, NULL, &id);
mutex_unlock(&idr_lock);
if (err < 0)
goto exit;
id = id & MAX_ID_MASK;
rtc = kzalloc(sizeof(struct rtc_device), GFP_KERNEL);
if (rtc == NULL) {
err = -ENOMEM;
goto exit_idr;
}
rtc->id = id;
rtc->ops = ops;
rtc->owner = owner;
rtc->max_user_freq = 64;
rtc->class_dev.dev = dev;
rtc->class_dev.class = rtc_class;
rtc->class_dev.release = rtc_device_release;
mutex_init(&rtc->ops_lock);
spin_lock_init(&rtc->irq_lock);
spin_lock_init(&rtc->irq_task_lock);
strlcpy(rtc->name, name, RTC_DEVICE_NAME_SIZE);
snprintf(rtc->class_dev.class_id, BUS_ID_SIZE, "rtc%d", id);
err = class_device_register(&rtc->class_dev);
if (err)
goto exit_kfree;
dev_info(dev, "rtc core: registered %s as %s\n",
rtc->name, rtc->class_dev.class_id);
return rtc;
exit_kfree:
kfree(rtc);
exit_idr:
mutex_lock(&idr_lock);
idr_remove(&rtc_idr, id);
mutex_unlock(&idr_lock);
exit:
dev_err(dev, "rtc core: unable to register %s, err = %d\n",
name, err);
return ERR_PTR(err);
}
EXPORT_SYMBOL_GPL(rtc_device_register);
/**
* rtc_device_unregister - removes the previously registered RTC class device
*
* @rtc: the RTC class device to destroy
*/
void rtc_device_unregister(struct rtc_device *rtc)
{
if (class_device_get(&rtc->class_dev) != NULL) {
mutex_lock(&rtc->ops_lock);
/* remove innards of this RTC, then disable it, before
* letting any rtc_class_open() users access it again
*/
class_device_unregister(&rtc->class_dev);
rtc->ops = NULL;
mutex_unlock(&rtc->ops_lock);
class_device_put(&rtc->class_dev);
}
}
EXPORT_SYMBOL_GPL(rtc_device_unregister);
int rtc_interface_register(struct class_interface *intf)
{
intf->class = rtc_class;
return class_interface_register(intf);
}
EXPORT_SYMBOL_GPL(rtc_interface_register);
static int __init rtc_init(void)
{
rtc_class = class_create(THIS_MODULE, "rtc");
if (IS_ERR(rtc_class)) {
printk(KERN_ERR "%s: couldn't create class\n", __FILE__);
return PTR_ERR(rtc_class);
}
return 0;
}
static void __exit rtc_exit(void)
{
class_destroy(rtc_class);
}
subsys_initcall(rtc_init);
module_exit(rtc_exit);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("RTC class support");
MODULE_LICENSE("GPL");

69
drivers/rtc/hctosys.c Normal file
View File

@@ -0,0 +1,69 @@
/*
* RTC subsystem, initialize system time on startup
*
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* 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/rtc.h>
/* IMPORTANT: the RTC only stores whole seconds. It is arbitrary
* whether it stores the most close value or the value with partial
* seconds truncated. However, it is important that we use it to store
* the truncated value. This is because otherwise it is necessary,
* in an rtc sync function, to read both xtime.tv_sec and
* xtime.tv_nsec. On some processors (i.e. ARM), an atomic read
* of >32bits is not possible. So storing the most close value would
* slow down the sync API. So here we have the truncated value and
* the best guess is to add 0.5s.
*/
static int __init rtc_hctosys(void)
{
int err;
struct rtc_time tm;
struct class_device *class_dev = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);
if (class_dev == NULL) {
printk("%s: unable to open rtc device (%s)\n",
__FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
return -ENODEV;
}
err = rtc_read_time(class_dev, &tm);
if (err == 0) {
err = rtc_valid_tm(&tm);
if (err == 0) {
struct timespec tv;
tv.tv_nsec = NSEC_PER_SEC >> 1;
rtc_tm_to_time(&tm, &tv.tv_sec);
do_settimeofday(&tv);
dev_info(class_dev->dev,
"setting the system clock to "
"%d-%02d-%02d %02d:%02d:%02d (%u)\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec,
(unsigned int) tv.tv_sec);
}
else
dev_err(class_dev->dev,
"hctosys: invalid date/time\n");
}
else
dev_err(class_dev->dev,
"hctosys: unable to read the hardware clock\n");
rtc_class_close(class_dev);
return 0;
}
late_initcall(rtc_hctosys);

276
drivers/rtc/interface.c Normal file
View File

@@ -0,0 +1,276 @@
/*
* RTC subsystem, interface functions
*
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* based on arch/arm/common/rtctime.c
*
* 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/rtc.h>
int rtc_read_time(struct class_device *class_dev, struct rtc_time *tm)
{
int err;
struct rtc_device *rtc = to_rtc_device(class_dev);
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return -EBUSY;
if (!rtc->ops)
err = -ENODEV;
else if (!rtc->ops->read_time)
err = -EINVAL;
else {
memset(tm, 0, sizeof(struct rtc_time));
err = rtc->ops->read_time(class_dev->dev, tm);
}
mutex_unlock(&rtc->ops_lock);
return err;
}
EXPORT_SYMBOL_GPL(rtc_read_time);
int rtc_set_time(struct class_device *class_dev, struct rtc_time *tm)
{
int err;
struct rtc_device *rtc = to_rtc_device(class_dev);
err = rtc_valid_tm(tm);
if (err != 0)
return err;
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return -EBUSY;
if (!rtc->ops)
err = -ENODEV;
else if (!rtc->ops->set_time)
err = -EINVAL;
else
err = rtc->ops->set_time(class_dev->dev, tm);
mutex_unlock(&rtc->ops_lock);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_time);
int rtc_set_mmss(struct class_device *class_dev, unsigned long secs)
{
int err;
struct rtc_device *rtc = to_rtc_device(class_dev);
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return -EBUSY;
if (!rtc->ops)
err = -ENODEV;
else if (rtc->ops->set_mmss)
err = rtc->ops->set_mmss(class_dev->dev, secs);
else if (rtc->ops->read_time && rtc->ops->set_time) {
struct rtc_time new, old;
err = rtc->ops->read_time(class_dev->dev, &old);
if (err == 0) {
rtc_time_to_tm(secs, &new);
/*
* avoid writing when we're going to change the day of
* the month. We will retry in the next minute. This
* basically means that if the RTC must not drift
* by more than 1 minute in 11 minutes.
*/
if (!((old.tm_hour == 23 && old.tm_min == 59) ||
(new.tm_hour == 23 && new.tm_min == 59)))
err = rtc->ops->set_time(class_dev->dev, &new);
}
}
else
err = -EINVAL;
mutex_unlock(&rtc->ops_lock);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_mmss);
int rtc_read_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
{
int err;
struct rtc_device *rtc = to_rtc_device(class_dev);
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return -EBUSY;
if (rtc->ops == NULL)
err = -ENODEV;
else if (!rtc->ops->read_alarm)
err = -EINVAL;
else {
memset(alarm, 0, sizeof(struct rtc_wkalrm));
err = rtc->ops->read_alarm(class_dev->dev, alarm);
}
mutex_unlock(&rtc->ops_lock);
return err;
}
EXPORT_SYMBOL_GPL(rtc_read_alarm);
int rtc_set_alarm(struct class_device *class_dev, struct rtc_wkalrm *alarm)
{
int err;
struct rtc_device *rtc = to_rtc_device(class_dev);
err = mutex_lock_interruptible(&rtc->ops_lock);
if (err)
return -EBUSY;
if (!rtc->ops)
err = -ENODEV;
else if (!rtc->ops->set_alarm)
err = -EINVAL;
else
err = rtc->ops->set_alarm(class_dev->dev, alarm);
mutex_unlock(&rtc->ops_lock);
return err;
}
EXPORT_SYMBOL_GPL(rtc_set_alarm);
/**
* rtc_update_irq - report RTC periodic, alarm, and/or update irqs
* @class_dev: the rtc's class device
* @num: how many irqs are being reported (usually one)
* @events: mask of RTC_IRQF with one or more of RTC_PF, RTC_AF, RTC_UF
* Context: in_interrupt(), irqs blocked
*/
void rtc_update_irq(struct class_device *class_dev,
unsigned long num, unsigned long events)
{
struct rtc_device *rtc = to_rtc_device(class_dev);
spin_lock(&rtc->irq_lock);
rtc->irq_data = (rtc->irq_data + (num << 8)) | events;
spin_unlock(&rtc->irq_lock);
spin_lock(&rtc->irq_task_lock);
if (rtc->irq_task)
rtc->irq_task->func(rtc->irq_task->private_data);
spin_unlock(&rtc->irq_task_lock);
wake_up_interruptible(&rtc->irq_queue);
kill_fasync(&rtc->async_queue, SIGIO, POLL_IN);
}
EXPORT_SYMBOL_GPL(rtc_update_irq);
struct class_device *rtc_class_open(char *name)
{
struct class_device *class_dev = NULL,
*class_dev_tmp;
down(&rtc_class->sem);
list_for_each_entry(class_dev_tmp, &rtc_class->children, node) {
if (strncmp(class_dev_tmp->class_id, name, BUS_ID_SIZE) == 0) {
class_dev = class_device_get(class_dev_tmp);
break;
}
}
if (class_dev) {
if (!try_module_get(to_rtc_device(class_dev)->owner))
class_dev = NULL;
}
up(&rtc_class->sem);
return class_dev;
}
EXPORT_SYMBOL_GPL(rtc_class_open);
void rtc_class_close(struct class_device *class_dev)
{
module_put(to_rtc_device(class_dev)->owner);
class_device_put(class_dev);
}
EXPORT_SYMBOL_GPL(rtc_class_close);
int rtc_irq_register(struct class_device *class_dev, struct rtc_task *task)
{
int retval = -EBUSY;
struct rtc_device *rtc = to_rtc_device(class_dev);
if (task == NULL || task->func == NULL)
return -EINVAL;
spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == NULL) {
rtc->irq_task = task;
retval = 0;
}
spin_unlock_irq(&rtc->irq_task_lock);
return retval;
}
EXPORT_SYMBOL_GPL(rtc_irq_register);
void rtc_irq_unregister(struct class_device *class_dev, struct rtc_task *task)
{
struct rtc_device *rtc = to_rtc_device(class_dev);
spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task == task)
rtc->irq_task = NULL;
spin_unlock_irq(&rtc->irq_task_lock);
}
EXPORT_SYMBOL_GPL(rtc_irq_unregister);
int rtc_irq_set_state(struct class_device *class_dev, struct rtc_task *task, int enabled)
{
int err = 0;
unsigned long flags;
struct rtc_device *rtc = to_rtc_device(class_dev);
if (rtc->ops->irq_set_state == NULL)
return -ENXIO;
spin_lock_irqsave(&rtc->irq_task_lock, flags);
if (rtc->irq_task != task)
err = -ENXIO;
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
if (err == 0)
err = rtc->ops->irq_set_state(class_dev->dev, enabled);
return err;
}
EXPORT_SYMBOL_GPL(rtc_irq_set_state);
int rtc_irq_set_freq(struct class_device *class_dev, struct rtc_task *task, int freq)
{
int err = 0;
unsigned long flags;
struct rtc_device *rtc = to_rtc_device(class_dev);
if (rtc->ops->irq_set_freq == NULL)
return -ENXIO;
spin_lock_irqsave(&rtc->irq_task_lock, flags);
if (rtc->irq_task != task)
err = -ENXIO;
spin_unlock_irqrestore(&rtc->irq_task_lock, flags);
if (err == 0) {
err = rtc->ops->irq_set_freq(class_dev->dev, freq);
if (err == 0)
rtc->irq_freq = freq;
}
return err;
}
EXPORT_SYMBOL_GPL(rtc_irq_set_freq);

View File

@@ -0,0 +1,439 @@
/*
* Real Time Clock interface for Linux on Atmel AT91RM9200
*
* Copyright (C) 2002 Rick Bronson
*
* Converted to RTC class model by Andrew Victor
*
* Ported to Linux 2.6 by Steven Scholz
* Based on s3c2410-rtc.c Simtec Electronics
*
* Based on sa1100-rtc.c by Nils Faerber
* Based on rtc.c by Paul Gortmaker
*
* 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/platform_device.h>
#include <linux/time.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/interrupt.h>
#include <linux/ioctl.h>
#include <linux/completion.h>
#include <asm/uaccess.h>
#include <asm/rtc.h>
#include <asm/mach/time.h>
#include <asm/arch/at91_rtc.h>
#define AT91_RTC_FREQ 1
#define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */
static DECLARE_COMPLETION(at91_rtc_updated);
static unsigned int at91_alarm_year = AT91_RTC_EPOCH;
/*
* Decode time/date into rtc_time structure
*/
static void at91_rtc_decodetime(unsigned int timereg, unsigned int calreg,
struct rtc_time *tm)
{
unsigned int time, date;
/* must read twice in case it changes */
do {
time = at91_sys_read(timereg);
date = at91_sys_read(calreg);
} while ((time != at91_sys_read(timereg)) ||
(date != at91_sys_read(calreg)));
tm->tm_sec = BCD2BIN((time & AT91_RTC_SEC) >> 0);
tm->tm_min = BCD2BIN((time & AT91_RTC_MIN) >> 8);
tm->tm_hour = BCD2BIN((time & AT91_RTC_HOUR) >> 16);
/*
* The Calendar Alarm register does not have a field for
* the year - so these will return an invalid value. When an
* alarm is set, at91_alarm_year wille store the current year.
*/
tm->tm_year = BCD2BIN(date & AT91_RTC_CENT) * 100; /* century */
tm->tm_year += BCD2BIN((date & AT91_RTC_YEAR) >> 8); /* year */
tm->tm_wday = BCD2BIN((date & AT91_RTC_DAY) >> 21) - 1; /* day of the week [0-6], Sunday=0 */
tm->tm_mon = BCD2BIN((date & AT91_RTC_MONTH) >> 16) - 1;
tm->tm_mday = BCD2BIN((date & AT91_RTC_DATE) >> 24);
}
/*
* Read current time and date in RTC
*/
static int at91_rtc_readtime(struct device *dev, struct rtc_time *tm)
{
at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, tm);
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
tm->tm_year = tm->tm_year - 1900;
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return 0;
}
/*
* Set current time and date in RTC
*/
static int at91_rtc_settime(struct device *dev, struct rtc_time *tm)
{
unsigned long cr;
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
/* Stop Time/Calendar from counting */
cr = at91_sys_read(AT91_RTC_CR);
at91_sys_write(AT91_RTC_CR, cr | AT91_RTC_UPDCAL | AT91_RTC_UPDTIM);
at91_sys_write(AT91_RTC_IER, AT91_RTC_ACKUPD);
wait_for_completion(&at91_rtc_updated); /* wait for ACKUPD interrupt */
at91_sys_write(AT91_RTC_IDR, AT91_RTC_ACKUPD);
at91_sys_write(AT91_RTC_TIMR,
BIN2BCD(tm->tm_sec) << 0
| BIN2BCD(tm->tm_min) << 8
| BIN2BCD(tm->tm_hour) << 16);
at91_sys_write(AT91_RTC_CALR,
BIN2BCD((tm->tm_year + 1900) / 100) /* century */
| BIN2BCD(tm->tm_year % 100) << 8 /* year */
| BIN2BCD(tm->tm_mon + 1) << 16 /* tm_mon starts at zero */
| BIN2BCD(tm->tm_wday + 1) << 21 /* day of the week [0-6], Sunday=0 */
| BIN2BCD(tm->tm_mday) << 24);
/* Restart Time/Calendar */
cr = at91_sys_read(AT91_RTC_CR);
at91_sys_write(AT91_RTC_CR, cr & ~(AT91_RTC_UPDCAL | AT91_RTC_UPDTIM));
return 0;
}
/*
* Read alarm time and date in RTC
*/
static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_time *tm = &alrm->time;
at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm);
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
tm->tm_year = at91_alarm_year - 1900;
alrm->enabled = (at91_sys_read(AT91_RTC_IMR) & AT91_RTC_ALARM)
? 1 : 0;
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return 0;
}
/*
* Set alarm time and date in RTC
*/
static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_time tm;
at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm);
at91_alarm_year = tm.tm_year;
tm.tm_hour = alrm->time.tm_hour;
tm.tm_min = alrm->time.tm_min;
tm.tm_sec = alrm->time.tm_sec;
at91_sys_write(AT91_RTC_IDR, AT91_RTC_ALARM);
at91_sys_write(AT91_RTC_TIMALR,
BIN2BCD(tm.tm_sec) << 0
| BIN2BCD(tm.tm_min) << 8
| BIN2BCD(tm.tm_hour) << 16
| AT91_RTC_HOUREN | AT91_RTC_MINEN | AT91_RTC_SECEN);
at91_sys_write(AT91_RTC_CALALR,
BIN2BCD(tm.tm_mon + 1) << 16 /* tm_mon starts at zero */
| BIN2BCD(tm.tm_mday) << 24
| AT91_RTC_DATEEN | AT91_RTC_MTHEN);
if (alrm->enabled)
at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM);
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec);
return 0;
}
/*
* Handle commands from user-space
*/
static int at91_rtc_ioctl(struct device *dev, unsigned int cmd,
unsigned long arg)
{
int ret = 0;
pr_debug("%s(): cmd=%08x, arg=%08lx.\n", __FUNCTION__, cmd, arg);
switch (cmd) {
case RTC_AIE_OFF: /* alarm off */
at91_sys_write(AT91_RTC_IDR, AT91_RTC_ALARM);
break;
case RTC_AIE_ON: /* alarm on */
at91_sys_write(AT91_RTC_IER, AT91_RTC_ALARM);
break;
case RTC_UIE_OFF: /* update off */
case RTC_PIE_OFF: /* periodic off */
at91_sys_write(AT91_RTC_IDR, AT91_RTC_SECEV);
break;
case RTC_UIE_ON: /* update on */
case RTC_PIE_ON: /* periodic on */
at91_sys_write(AT91_RTC_IER, AT91_RTC_SECEV);
break;
case RTC_IRQP_READ: /* read periodic alarm frequency */
ret = put_user(AT91_RTC_FREQ, (unsigned long *) arg);
break;
case RTC_IRQP_SET: /* set periodic alarm frequency */
if (arg != AT91_RTC_FREQ)
ret = -EINVAL;
break;
default:
ret = -ENOIOCTLCMD;
break;
}
return ret;
}
/*
* Provide additional RTC information in /proc/driver/rtc
*/
static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
{
unsigned long imr = at91_sys_read(AT91_RTC_IMR);
seq_printf(seq, "update_IRQ\t: %s\n",
(imr & AT91_RTC_ACKUPD) ? "yes" : "no");
seq_printf(seq, "periodic_IRQ\t: %s\n",
(imr & AT91_RTC_SECEV) ? "yes" : "no");
seq_printf(seq, "periodic_freq\t: %ld\n",
(unsigned long) AT91_RTC_FREQ);
return 0;
}
/*
* IRQ handler for the RTC
*/
static irqreturn_t at91_rtc_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct rtc_device *rtc = platform_get_drvdata(pdev);
unsigned int rtsr;
unsigned long events = 0;
rtsr = at91_sys_read(AT91_RTC_SR) & at91_sys_read(AT91_RTC_IMR);
if (rtsr) { /* this interrupt is shared! Is it ours? */
if (rtsr & AT91_RTC_ALARM)
events |= (RTC_AF | RTC_IRQF);
if (rtsr & AT91_RTC_SECEV)
events |= (RTC_UF | RTC_IRQF);
if (rtsr & AT91_RTC_ACKUPD)
complete(&at91_rtc_updated);
at91_sys_write(AT91_RTC_SCCR, rtsr); /* clear status reg */
rtc_update_irq(&rtc->class_dev, 1, events);
pr_debug("%s(): num=%ld, events=0x%02lx\n", __FUNCTION__,
events >> 8, events & 0x000000FF);
return IRQ_HANDLED;
}
return IRQ_NONE; /* not handled */
}
static const struct rtc_class_ops at91_rtc_ops = {
.ioctl = at91_rtc_ioctl,
.read_time = at91_rtc_readtime,
.set_time = at91_rtc_settime,
.read_alarm = at91_rtc_readalarm,
.set_alarm = at91_rtc_setalarm,
.proc = at91_rtc_proc,
};
/*
* Initialize and install RTC driver
*/
static int __init at91_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
int ret;
at91_sys_write(AT91_RTC_CR, 0);
at91_sys_write(AT91_RTC_MR, 0); /* 24 hour mode */
/* Disable all interrupts */
at91_sys_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM |
AT91_RTC_SECEV | AT91_RTC_TIMEV |
AT91_RTC_CALEV);
ret = request_irq(AT91_ID_SYS, at91_rtc_interrupt,
IRQF_DISABLED | IRQF_SHARED,
"at91_rtc", pdev);
if (ret) {
printk(KERN_ERR "at91_rtc: IRQ %d already in use.\n",
AT91_ID_SYS);
return ret;
}
/* cpu init code should really have flagged this device as
* being wake-capable; if it didn't, do that here.
*/
if (!device_can_wakeup(&pdev->dev))
device_init_wakeup(&pdev->dev, 1);
rtc = rtc_device_register(pdev->name, &pdev->dev,
&at91_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
free_irq(AT91_ID_SYS, pdev);
return PTR_ERR(rtc);
}
platform_set_drvdata(pdev, rtc);
printk(KERN_INFO "AT91 Real Time Clock driver.\n");
return 0;
}
/*
* Disable and remove the RTC driver
*/
static int __exit at91_rtc_remove(struct platform_device *pdev)
{
struct rtc_device *rtc = platform_get_drvdata(pdev);
/* Disable all interrupts */
at91_sys_write(AT91_RTC_IDR, AT91_RTC_ACKUPD | AT91_RTC_ALARM |
AT91_RTC_SECEV | AT91_RTC_TIMEV |
AT91_RTC_CALEV);
free_irq(AT91_ID_SYS, pdev);
rtc_device_unregister(rtc);
platform_set_drvdata(pdev, NULL);
return 0;
}
#ifdef CONFIG_PM
/* AT91RM9200 RTC Power management control */
static struct timespec at91_rtc_delta;
static u32 at91_rtc_imr;
static int at91_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct rtc_time tm;
struct timespec time;
time.tv_nsec = 0;
/* calculate time delta for suspend */
at91_rtc_readtime(&pdev->dev, &tm);
rtc_tm_to_time(&tm, &time.tv_sec);
save_time_delta(&at91_rtc_delta, &time);
/* this IRQ is shared with DBGU and other hardware which isn't
* necessarily doing PM like we are...
*/
at91_rtc_imr = at91_sys_read(AT91_RTC_IMR)
& (AT91_RTC_ALARM|AT91_RTC_SECEV);
if (at91_rtc_imr) {
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(AT91_ID_SYS);
else
at91_sys_write(AT91_RTC_IDR, at91_rtc_imr);
}
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
1900 + tm.tm_year, tm.tm_mon, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
return 0;
}
static int at91_rtc_resume(struct platform_device *pdev)
{
struct rtc_time tm;
struct timespec time;
time.tv_nsec = 0;
at91_rtc_readtime(&pdev->dev, &tm);
rtc_tm_to_time(&tm, &time.tv_sec);
restore_time_delta(&at91_rtc_delta, &time);
if (at91_rtc_imr) {
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(AT91_ID_SYS);
else
at91_sys_write(AT91_RTC_IER, at91_rtc_imr);
}
pr_debug("%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __FUNCTION__,
1900 + tm.tm_year, tm.tm_mon, tm.tm_mday,
tm.tm_hour, tm.tm_min, tm.tm_sec);
return 0;
}
#else
#define at91_rtc_suspend NULL
#define at91_rtc_resume NULL
#endif
static struct platform_driver at91_rtc_driver = {
.remove = __exit_p(at91_rtc_remove),
.suspend = at91_rtc_suspend,
.resume = at91_rtc_resume,
.driver = {
.name = "at91_rtc",
.owner = THIS_MODULE,
},
};
static int __init at91_rtc_init(void)
{
return platform_driver_probe(&at91_rtc_driver, at91_rtc_probe);
}
static void __exit at91_rtc_exit(void)
{
platform_driver_unregister(&at91_rtc_driver);
}
module_init(at91_rtc_init);
module_exit(at91_rtc_exit);
MODULE_AUTHOR("Rick Bronson");
MODULE_DESCRIPTION("RTC driver for Atmel AT91RM9200");
MODULE_LICENSE("GPL");

741
drivers/rtc/rtc-cmos.c Normal file
View File

@@ -0,0 +1,741 @@
/*
* RTC class driver for "CMOS RTC": PCs, ACPI, etc
*
* Copyright (C) 1996 Paul Gortmaker (drivers/char/rtc.c)
* Copyright (C) 2006 David Brownell (convert to new framework)
*
* 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.
*/
/*
* The original "cmos clock" chip was an MC146818 chip, now obsolete.
* That defined the register interface now provided by all PCs, some
* non-PC systems, and incorporated into ACPI. Modern PC chipsets
* integrate an MC146818 clone in their southbridge, and boards use
* that instead of discrete clones like the DS12887 or M48T86. There
* are also clones that connect using the LPC bus.
*
* That register API is also used directly by various other drivers
* (notably for integrated NVRAM), infrastructure (x86 has code to
* bypass the RTC framework, directly reading the RTC during boot
* and updating minutes/seconds for systems using NTP synch) and
* utilities (like userspace 'hwclock', if no /dev node exists).
*
* So **ALL** calls to CMOS_READ and CMOS_WRITE must be done with
* interrupts disabled, holding the global rtc_lock, to exclude those
* other drivers and utilities on correctly configured systems.
*/
#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/platform_device.h>
#include <linux/mod_devicetable.h>
/* this is for "generic access to PC-style RTC" using CMOS_READ/CMOS_WRITE */
#include <asm-generic/rtc.h>
struct cmos_rtc {
struct rtc_device *rtc;
struct device *dev;
int irq;
struct resource *iomem;
u8 suspend_ctrl;
/* newer hardware extends the original register set */
u8 day_alrm;
u8 mon_alrm;
u8 century;
};
/* both platform and pnp busses use negative numbers for invalid irqs */
#define is_valid_irq(n) ((n) >= 0)
static const char driver_name[] = "rtc_cmos";
/* The RTC_INTR register may have e.g. RTC_PF set even if RTC_PIE is clear;
* always mask it against the irq enable bits in RTC_CONTROL. Bit values
* are the same: PF==PIE, AF=AIE, UF=UIE; so RTC_IRQMASK works with both.
*/
#define RTC_IRQMASK (RTC_PF | RTC_AF | RTC_UF)
static inline int is_intr(u8 rtc_intr)
{
if (!(rtc_intr & RTC_IRQF))
return 0;
return rtc_intr & RTC_IRQMASK;
}
/*----------------------------------------------------------------*/
static int cmos_read_time(struct device *dev, struct rtc_time *t)
{
/* REVISIT: if the clock has a "century" register, use
* that instead of the heuristic in get_rtc_time().
* That'll make Y3K compatility (year > 2070) easy!
*/
get_rtc_time(t);
return 0;
}
static int cmos_set_time(struct device *dev, struct rtc_time *t)
{
/* REVISIT: set the "century" register if available
*
* NOTE: this ignores the issue whereby updating the seconds
* takes effect exactly 500ms after we write the register.
* (Also queueing and other delays before we get this far.)
*/
return set_rtc_time(t);
}
static int cmos_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char rtc_control;
if (!is_valid_irq(cmos->irq))
return -EIO;
/* Basic alarms only support hour, minute, and seconds fields.
* Some also support day and month, for alarms up to a year in
* the future.
*/
t->time.tm_mday = -1;
t->time.tm_mon = -1;
spin_lock_irq(&rtc_lock);
t->time.tm_sec = CMOS_READ(RTC_SECONDS_ALARM);
t->time.tm_min = CMOS_READ(RTC_MINUTES_ALARM);
t->time.tm_hour = CMOS_READ(RTC_HOURS_ALARM);
if (cmos->day_alrm) {
t->time.tm_mday = CMOS_READ(cmos->day_alrm);
if (!t->time.tm_mday)
t->time.tm_mday = -1;
if (cmos->mon_alrm) {
t->time.tm_mon = CMOS_READ(cmos->mon_alrm);
if (!t->time.tm_mon)
t->time.tm_mon = -1;
}
}
rtc_control = CMOS_READ(RTC_CONTROL);
spin_unlock_irq(&rtc_lock);
/* REVISIT this assumes PC style usage: always BCD */
if (((unsigned)t->time.tm_sec) < 0x60)
t->time.tm_sec = BCD2BIN(t->time.tm_sec);
else
t->time.tm_sec = -1;
if (((unsigned)t->time.tm_min) < 0x60)
t->time.tm_min = BCD2BIN(t->time.tm_min);
else
t->time.tm_min = -1;
if (((unsigned)t->time.tm_hour) < 0x24)
t->time.tm_hour = BCD2BIN(t->time.tm_hour);
else
t->time.tm_hour = -1;
if (cmos->day_alrm) {
if (((unsigned)t->time.tm_mday) <= 0x31)
t->time.tm_mday = BCD2BIN(t->time.tm_mday);
else
t->time.tm_mday = -1;
if (cmos->mon_alrm) {
if (((unsigned)t->time.tm_mon) <= 0x12)
t->time.tm_mon = BCD2BIN(t->time.tm_mon) - 1;
else
t->time.tm_mon = -1;
}
}
t->time.tm_year = -1;
t->enabled = !!(rtc_control & RTC_AIE);
t->pending = 0;
return 0;
}
static int cmos_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char mon, mday, hrs, min, sec;
unsigned char rtc_control, rtc_intr;
if (!is_valid_irq(cmos->irq))
return -EIO;
/* REVISIT this assumes PC style usage: always BCD */
/* Writing 0xff means "don't care" or "match all". */
mon = t->time.tm_mon;
mon = (mon < 12) ? BIN2BCD(mon) : 0xff;
mon++;
mday = t->time.tm_mday;
mday = (mday >= 1 && mday <= 31) ? BIN2BCD(mday) : 0xff;
hrs = t->time.tm_hour;
hrs = (hrs < 24) ? BIN2BCD(hrs) : 0xff;
min = t->time.tm_min;
min = (min < 60) ? BIN2BCD(min) : 0xff;
sec = t->time.tm_sec;
sec = (sec < 60) ? BIN2BCD(sec) : 0xff;
spin_lock_irq(&rtc_lock);
/* next rtc irq must not be from previous alarm setting */
rtc_control = CMOS_READ(RTC_CONTROL);
rtc_control &= ~RTC_AIE;
CMOS_WRITE(rtc_control, RTC_CONTROL);
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr);
/* update alarm */
CMOS_WRITE(hrs, RTC_HOURS_ALARM);
CMOS_WRITE(min, RTC_MINUTES_ALARM);
CMOS_WRITE(sec, RTC_SECONDS_ALARM);
/* the system may support an "enhanced" alarm */
if (cmos->day_alrm) {
CMOS_WRITE(mday, cmos->day_alrm);
if (cmos->mon_alrm)
CMOS_WRITE(mon, cmos->mon_alrm);
}
if (t->enabled) {
rtc_control |= RTC_AIE;
CMOS_WRITE(rtc_control, RTC_CONTROL);
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr);
}
spin_unlock_irq(&rtc_lock);
return 0;
}
static int cmos_set_freq(struct device *dev, int freq)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
int f;
unsigned long flags;
if (!is_valid_irq(cmos->irq))
return -ENXIO;
/* 0 = no irqs; 1 = 2^15 Hz ... 15 = 2^0 Hz */
f = ffs(freq);
if (f != 0) {
if (f-- > 16 || freq != (1 << f))
return -EINVAL;
f = 16 - f;
}
spin_lock_irqsave(&rtc_lock, flags);
CMOS_WRITE(RTC_REF_CLCK_32KHZ | f, RTC_FREQ_SELECT);
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE)
static int
cmos_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char rtc_control, rtc_intr;
unsigned long flags;
switch (cmd) {
case RTC_AIE_OFF:
case RTC_AIE_ON:
case RTC_UIE_OFF:
case RTC_UIE_ON:
case RTC_PIE_OFF:
case RTC_PIE_ON:
if (!is_valid_irq(cmos->irq))
return -EINVAL;
break;
default:
return -ENOIOCTLCMD;
}
spin_lock_irqsave(&rtc_lock, flags);
rtc_control = CMOS_READ(RTC_CONTROL);
switch (cmd) {
case RTC_AIE_OFF: /* alarm off */
rtc_control &= ~RTC_AIE;
break;
case RTC_AIE_ON: /* alarm on */
rtc_control |= RTC_AIE;
break;
case RTC_UIE_OFF: /* update off */
rtc_control &= ~RTC_UIE;
break;
case RTC_UIE_ON: /* update on */
rtc_control |= RTC_UIE;
break;
case RTC_PIE_OFF: /* periodic off */
rtc_control &= ~RTC_PIE;
break;
case RTC_PIE_ON: /* periodic on */
rtc_control |= RTC_PIE;
break;
}
CMOS_WRITE(rtc_control, RTC_CONTROL);
rtc_intr = CMOS_READ(RTC_INTR_FLAGS);
rtc_intr &= (rtc_control & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(rtc_intr))
rtc_update_irq(&cmos->rtc->class_dev, 1, rtc_intr);
spin_unlock_irqrestore(&rtc_lock, flags);
return 0;
}
#else
#define cmos_rtc_ioctl NULL
#endif
#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
static int cmos_procfs(struct device *dev, struct seq_file *seq)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char rtc_control, valid;
spin_lock_irq(&rtc_lock);
rtc_control = CMOS_READ(RTC_CONTROL);
valid = CMOS_READ(RTC_VALID);
spin_unlock_irq(&rtc_lock);
/* NOTE: at least ICH6 reports battery status using a different
* (non-RTC) bit; and SQWE is ignored on many current systems.
*/
return seq_printf(seq,
"periodic_IRQ\t: %s\n"
"update_IRQ\t: %s\n"
// "square_wave\t: %s\n"
// "BCD\t\t: %s\n"
"DST_enable\t: %s\n"
"periodic_freq\t: %d\n"
"batt_status\t: %s\n",
(rtc_control & RTC_PIE) ? "yes" : "no",
(rtc_control & RTC_UIE) ? "yes" : "no",
// (rtc_control & RTC_SQWE) ? "yes" : "no",
// (rtc_control & RTC_DM_BINARY) ? "no" : "yes",
(rtc_control & RTC_DST_EN) ? "yes" : "no",
cmos->rtc->irq_freq,
(valid & RTC_VRT) ? "okay" : "dead");
}
#else
#define cmos_procfs NULL
#endif
static const struct rtc_class_ops cmos_rtc_ops = {
.ioctl = cmos_rtc_ioctl,
.read_time = cmos_read_time,
.set_time = cmos_set_time,
.read_alarm = cmos_read_alarm,
.set_alarm = cmos_set_alarm,
.proc = cmos_procfs,
.irq_set_freq = cmos_set_freq,
};
/*----------------------------------------------------------------*/
static struct cmos_rtc cmos_rtc;
static irqreturn_t cmos_interrupt(int irq, void *p)
{
u8 irqstat;
spin_lock(&rtc_lock);
irqstat = CMOS_READ(RTC_INTR_FLAGS);
irqstat &= (CMOS_READ(RTC_CONTROL) & RTC_IRQMASK) | RTC_IRQF;
spin_unlock(&rtc_lock);
if (is_intr(irqstat)) {
rtc_update_irq(p, 1, irqstat);
return IRQ_HANDLED;
} else
return IRQ_NONE;
}
#ifdef CONFIG_PNPACPI
#define is_pnpacpi() 1
#define INITSECTION
#else
#define is_pnpacpi() 0
#define INITSECTION __init
#endif
static int INITSECTION
cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
{
struct cmos_rtc_board_info *info = dev->platform_data;
int retval = 0;
unsigned char rtc_control;
/* there can be only one ... */
if (cmos_rtc.dev)
return -EBUSY;
if (!ports)
return -ENODEV;
cmos_rtc.irq = rtc_irq;
cmos_rtc.iomem = ports;
/* For ACPI systems the info comes from the FADT. On others,
* board specific setup provides it as appropriate.
*/
if (info) {
cmos_rtc.day_alrm = info->rtc_day_alarm;
cmos_rtc.mon_alrm = info->rtc_mon_alarm;
cmos_rtc.century = info->rtc_century;
}
cmos_rtc.rtc = rtc_device_register(driver_name, dev,
&cmos_rtc_ops, THIS_MODULE);
if (IS_ERR(cmos_rtc.rtc))
return PTR_ERR(cmos_rtc.rtc);
cmos_rtc.dev = dev;
dev_set_drvdata(dev, &cmos_rtc);
/* platform and pnp busses handle resources incompatibly.
*
* REVISIT for non-x86 systems we may need to handle io memory
* resources: ioremap them, and request_mem_region().
*/
if (is_pnpacpi()) {
retval = request_resource(&ioport_resource, ports);
if (retval < 0) {
dev_dbg(dev, "i/o registers already in use\n");
goto cleanup0;
}
}
rename_region(ports, cmos_rtc.rtc->class_dev.class_id);
spin_lock_irq(&rtc_lock);
/* force periodic irq to CMOS reset default of 1024Hz;
*
* REVISIT it's been reported that at least one x86_64 ALI mobo
* doesn't use 32KHz here ... for portability we might need to
* do something about other clock frequencies.
*/
CMOS_WRITE(RTC_REF_CLCK_32KHZ | 0x06, RTC_FREQ_SELECT);
cmos_rtc.rtc->irq_freq = 1024;
/* disable irqs.
*
* NOTE after changing RTC_xIE bits we always read INTR_FLAGS;
* allegedly some older rtcs need that to handle irqs properly
*/
rtc_control = CMOS_READ(RTC_CONTROL);
rtc_control &= ~(RTC_PIE | RTC_AIE | RTC_UIE);
CMOS_WRITE(rtc_control, RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS);
spin_unlock_irq(&rtc_lock);
/* FIXME teach the alarm code how to handle binary mode;
* <asm-generic/rtc.h> doesn't know 12-hour mode either.
*/
if (!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY))) {
dev_dbg(dev, "only 24-hr BCD mode supported\n");
retval = -ENXIO;
goto cleanup1;
}
if (is_valid_irq(rtc_irq))
retval = request_irq(rtc_irq, cmos_interrupt, IRQF_DISABLED,
cmos_rtc.rtc->class_dev.class_id,
&cmos_rtc.rtc->class_dev);
if (retval < 0) {
dev_dbg(dev, "IRQ %d is already in use\n", rtc_irq);
goto cleanup1;
}
/* REVISIT optionally make 50 or 114 bytes NVRAM available,
* like rtc-ds1553, rtc-ds1742 ... this will often include
* registers for century, and day/month alarm.
*/
pr_info("%s: alarms up to one %s%s\n",
cmos_rtc.rtc->class_dev.class_id,
is_valid_irq(rtc_irq)
? (cmos_rtc.mon_alrm
? "year"
: (cmos_rtc.day_alrm
? "month" : "day"))
: "no",
cmos_rtc.century ? ", y3k" : ""
);
return 0;
cleanup1:
rename_region(ports, NULL);
cleanup0:
rtc_device_unregister(cmos_rtc.rtc);
return retval;
}
static void cmos_do_shutdown(void)
{
unsigned char rtc_control;
spin_lock_irq(&rtc_lock);
rtc_control = CMOS_READ(RTC_CONTROL);
rtc_control &= ~(RTC_PIE|RTC_AIE|RTC_UIE);
CMOS_WRITE(rtc_control, RTC_CONTROL);
CMOS_READ(RTC_INTR_FLAGS);
spin_unlock_irq(&rtc_lock);
}
static void __exit cmos_do_remove(struct device *dev)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
cmos_do_shutdown();
if (is_pnpacpi())
release_resource(cmos->iomem);
rename_region(cmos->iomem, NULL);
if (is_valid_irq(cmos->irq))
free_irq(cmos->irq, &cmos_rtc.rtc->class_dev);
rtc_device_unregister(cmos_rtc.rtc);
cmos_rtc.dev = NULL;
dev_set_drvdata(dev, NULL);
}
#ifdef CONFIG_PM
static int cmos_suspend(struct device *dev, pm_message_t mesg)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
int do_wake = device_may_wakeup(dev);
unsigned char tmp;
/* only the alarm might be a wakeup event source */
spin_lock_irq(&rtc_lock);
cmos->suspend_ctrl = tmp = CMOS_READ(RTC_CONTROL);
if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
unsigned char irqstat;
if (do_wake)
tmp &= ~(RTC_PIE|RTC_UIE);
else
tmp &= ~(RTC_PIE|RTC_AIE|RTC_UIE);
CMOS_WRITE(tmp, RTC_CONTROL);
irqstat = CMOS_READ(RTC_INTR_FLAGS);
irqstat &= (tmp & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(irqstat))
rtc_update_irq(&cmos->rtc->class_dev, 1, irqstat);
}
spin_unlock_irq(&rtc_lock);
/* ACPI HOOK: enable ACPI_EVENT_RTC when (tmp & RTC_AIE)
* ... it'd be best if we could do that under rtc_lock.
*/
pr_debug("%s: suspend%s, ctrl %02x\n",
cmos_rtc.rtc->class_dev.class_id,
(tmp & RTC_AIE) ? ", alarm may wake" : "",
tmp);
return 0;
}
static int cmos_resume(struct device *dev)
{
struct cmos_rtc *cmos = dev_get_drvdata(dev);
unsigned char tmp = cmos->suspend_ctrl;
/* REVISIT: a mechanism to resync the system clock (jiffies)
* on resume should be portable between platforms ...
*/
/* re-enable any irqs previously active */
if (tmp & (RTC_PIE|RTC_AIE|RTC_UIE)) {
/* ACPI HOOK: disable ACPI_EVENT_RTC when (tmp & RTC_AIE) */
spin_lock_irq(&rtc_lock);
CMOS_WRITE(tmp, RTC_CONTROL);
tmp = CMOS_READ(RTC_INTR_FLAGS);
tmp &= (cmos->suspend_ctrl & RTC_IRQMASK) | RTC_IRQF;
if (is_intr(tmp))
rtc_update_irq(&cmos->rtc->class_dev, 1, tmp);
spin_unlock_irq(&rtc_lock);
}
pr_debug("%s: resume, ctrl %02x\n",
cmos_rtc.rtc->class_dev.class_id,
cmos->suspend_ctrl);
return 0;
}
#else
#define cmos_suspend NULL
#define cmos_resume NULL
#endif
/*----------------------------------------------------------------*/
/* The "CMOS" RTC normally lives on the platform_bus. On ACPI systems,
* the device node will always be created as a PNPACPI device.
*/
#ifdef CONFIG_PNPACPI
#include <linux/pnp.h>
static int __devinit
cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
{
/* REVISIT paranoia argues for a shutdown notifier, since PNP
* drivers can't provide shutdown() methods to disable IRQs.
* Or better yet, fix PNP to allow those methods...
*/
return cmos_do_probe(&pnp->dev,
&pnp->res.port_resource[0],
pnp->res.irq_resource[0].start);
}
static void __exit cmos_pnp_remove(struct pnp_dev *pnp)
{
cmos_do_remove(&pnp->dev);
}
#ifdef CONFIG_PM
static int cmos_pnp_suspend(struct pnp_dev *pnp, pm_message_t mesg)
{
return cmos_suspend(&pnp->dev, mesg);
}
static int cmos_pnp_resume(struct pnp_dev *pnp)
{
return cmos_resume(&pnp->dev);
}
#else
#define cmos_pnp_suspend NULL
#define cmos_pnp_resume NULL
#endif
static const struct pnp_device_id rtc_ids[] = {
{ .id = "PNP0b00", },
{ .id = "PNP0b01", },
{ .id = "PNP0b02", },
{ },
};
MODULE_DEVICE_TABLE(pnp, rtc_ids);
static struct pnp_driver cmos_pnp_driver = {
.name = (char *) driver_name,
.id_table = rtc_ids,
.probe = cmos_pnp_probe,
.remove = __exit_p(cmos_pnp_remove),
/* flag ensures resume() gets called, and stops syslog spam */
.flags = PNP_DRIVER_RES_DO_NOT_CHANGE,
.suspend = cmos_pnp_suspend,
.resume = cmos_pnp_resume,
};
static int __init cmos_init(void)
{
return pnp_register_driver(&cmos_pnp_driver);
}
module_init(cmos_init);
static void __exit cmos_exit(void)
{
pnp_unregister_driver(&cmos_pnp_driver);
}
module_exit(cmos_exit);
#else /* no PNPACPI */
/*----------------------------------------------------------------*/
/* Platform setup should have set up an RTC device, when PNPACPI is
* unavailable ... this could happen even on (older) PCs.
*/
static int __init cmos_platform_probe(struct platform_device *pdev)
{
return cmos_do_probe(&pdev->dev,
platform_get_resource(pdev, IORESOURCE_IO, 0),
platform_get_irq(pdev, 0));
}
static int __exit cmos_platform_remove(struct platform_device *pdev)
{
cmos_do_remove(&pdev->dev);
return 0;
}
static void cmos_platform_shutdown(struct platform_device *pdev)
{
cmos_do_shutdown();
}
static struct platform_driver cmos_platform_driver = {
.remove = __exit_p(cmos_platform_remove),
.shutdown = cmos_platform_shutdown,
.driver = {
.name = (char *) driver_name,
.suspend = cmos_suspend,
.resume = cmos_resume,
}
};
static int __init cmos_init(void)
{
return platform_driver_probe(&cmos_platform_driver,
cmos_platform_probe);
}
module_init(cmos_init);
static void __exit cmos_exit(void)
{
platform_driver_unregister(&cmos_platform_driver);
}
module_exit(cmos_exit);
#endif /* !PNPACPI */
MODULE_AUTHOR("David Brownell");
MODULE_DESCRIPTION("Driver for PC-style 'CMOS' RTCs");
MODULE_LICENSE("GPL");

517
drivers/rtc/rtc-dev.c Normal file
View File

@@ -0,0 +1,517 @@
/*
* RTC subsystem, dev interface
*
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* based on arch/arm/common/rtctime.c
*
* 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/module.h>
#include <linux/rtc.h>
static struct class *rtc_dev_class;
static dev_t rtc_devt;
#define RTC_DEV_MAX 16 /* 16 RTCs should be enough for everyone... */
static int rtc_dev_open(struct inode *inode, struct file *file)
{
int err;
struct rtc_device *rtc = container_of(inode->i_cdev,
struct rtc_device, char_dev);
const struct rtc_class_ops *ops = rtc->ops;
/* We keep the lock as long as the device is in use
* and return immediately if busy
*/
if (!(mutex_trylock(&rtc->char_lock)))
return -EBUSY;
file->private_data = &rtc->class_dev;
err = ops->open ? ops->open(rtc->class_dev.dev) : 0;
if (err == 0) {
spin_lock_irq(&rtc->irq_lock);
rtc->irq_data = 0;
spin_unlock_irq(&rtc->irq_lock);
return 0;
}
/* something has gone wrong, release the lock */
mutex_unlock(&rtc->char_lock);
return err;
}
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
/*
* Routine to poll RTC seconds field for change as often as possible,
* after first RTC_UIE use timer to reduce polling
*/
static void rtc_uie_task(struct work_struct *work)
{
struct rtc_device *rtc =
container_of(work, struct rtc_device, uie_task);
struct rtc_time tm;
int num = 0;
int err;
err = rtc_read_time(&rtc->class_dev, &tm);
local_irq_disable();
spin_lock(&rtc->irq_lock);
if (rtc->stop_uie_polling || err) {
rtc->uie_task_active = 0;
} else if (rtc->oldsecs != tm.tm_sec) {
num = (tm.tm_sec + 60 - rtc->oldsecs) % 60;
rtc->oldsecs = tm.tm_sec;
rtc->uie_timer.expires = jiffies + HZ - (HZ/10);
rtc->uie_timer_active = 1;
rtc->uie_task_active = 0;
add_timer(&rtc->uie_timer);
} else if (schedule_work(&rtc->uie_task) == 0) {
rtc->uie_task_active = 0;
}
spin_unlock(&rtc->irq_lock);
if (num)
rtc_update_irq(&rtc->class_dev, num, RTC_UF | RTC_IRQF);
local_irq_enable();
}
static void rtc_uie_timer(unsigned long data)
{
struct rtc_device *rtc = (struct rtc_device *)data;
unsigned long flags;
spin_lock_irqsave(&rtc->irq_lock, flags);
rtc->uie_timer_active = 0;
rtc->uie_task_active = 1;
if ((schedule_work(&rtc->uie_task) == 0))
rtc->uie_task_active = 0;
spin_unlock_irqrestore(&rtc->irq_lock, flags);
}
static void clear_uie(struct rtc_device *rtc)
{
spin_lock_irq(&rtc->irq_lock);
if (rtc->irq_active) {
rtc->stop_uie_polling = 1;
if (rtc->uie_timer_active) {
spin_unlock_irq(&rtc->irq_lock);
del_timer_sync(&rtc->uie_timer);
spin_lock_irq(&rtc->irq_lock);
rtc->uie_timer_active = 0;
}
if (rtc->uie_task_active) {
spin_unlock_irq(&rtc->irq_lock);
flush_scheduled_work();
spin_lock_irq(&rtc->irq_lock);
}
rtc->irq_active = 0;
}
spin_unlock_irq(&rtc->irq_lock);
}
static int set_uie(struct rtc_device *rtc)
{
struct rtc_time tm;
int err;
err = rtc_read_time(&rtc->class_dev, &tm);
if (err)
return err;
spin_lock_irq(&rtc->irq_lock);
if (!rtc->irq_active) {
rtc->irq_active = 1;
rtc->stop_uie_polling = 0;
rtc->oldsecs = tm.tm_sec;
rtc->uie_task_active = 1;
if (schedule_work(&rtc->uie_task) == 0)
rtc->uie_task_active = 0;
}
rtc->irq_data = 0;
spin_unlock_irq(&rtc->irq_lock);
return 0;
}
#endif /* CONFIG_RTC_INTF_DEV_UIE_EMUL */
static ssize_t
rtc_dev_read(struct file *file, char __user *buf, size_t count, loff_t *ppos)
{
struct rtc_device *rtc = to_rtc_device(file->private_data);
DECLARE_WAITQUEUE(wait, current);
unsigned long data;
ssize_t ret;
if (count != sizeof(unsigned int) && count < sizeof(unsigned long))
return -EINVAL;
add_wait_queue(&rtc->irq_queue, &wait);
do {
__set_current_state(TASK_INTERRUPTIBLE);
spin_lock_irq(&rtc->irq_lock);
data = rtc->irq_data;
rtc->irq_data = 0;
spin_unlock_irq(&rtc->irq_lock);
if (data != 0) {
ret = 0;
break;
}
if (file->f_flags & O_NONBLOCK) {
ret = -EAGAIN;
break;
}
if (signal_pending(current)) {
ret = -ERESTARTSYS;
break;
}
schedule();
} while (1);
set_current_state(TASK_RUNNING);
remove_wait_queue(&rtc->irq_queue, &wait);
if (ret == 0) {
/* Check for any data updates */
if (rtc->ops->read_callback)
data = rtc->ops->read_callback(rtc->class_dev.dev,
data);
if (sizeof(int) != sizeof(long) &&
count == sizeof(unsigned int))
ret = put_user(data, (unsigned int __user *)buf) ?:
sizeof(unsigned int);
else
ret = put_user(data, (unsigned long __user *)buf) ?:
sizeof(unsigned long);
}
return ret;
}
static unsigned int rtc_dev_poll(struct file *file, poll_table *wait)
{
struct rtc_device *rtc = to_rtc_device(file->private_data);
unsigned long data;
poll_wait(file, &rtc->irq_queue, wait);
data = rtc->irq_data;
return (data != 0) ? (POLLIN | POLLRDNORM) : 0;
}
static int rtc_dev_ioctl(struct inode *inode, struct file *file,
unsigned int cmd, unsigned long arg)
{
int err = 0;
struct class_device *class_dev = file->private_data;
struct rtc_device *rtc = to_rtc_device(class_dev);
const struct rtc_class_ops *ops = rtc->ops;
struct rtc_time tm;
struct rtc_wkalrm alarm;
void __user *uarg = (void __user *) arg;
/* check that the calling task has appropriate permissions
* for certain ioctls. doing this check here is useful
* to avoid duplicate code in each driver.
*/
switch (cmd) {
case RTC_EPOCH_SET:
case RTC_SET_TIME:
if (!capable(CAP_SYS_TIME))
return -EACCES;
break;
case RTC_IRQP_SET:
if (arg > rtc->max_user_freq && !capable(CAP_SYS_RESOURCE))
return -EACCES;
break;
case RTC_PIE_ON:
if (!capable(CAP_SYS_RESOURCE))
return -EACCES;
break;
}
/* avoid conflicting IRQ users */
if (cmd == RTC_PIE_ON || cmd == RTC_PIE_OFF || cmd == RTC_IRQP_SET) {
spin_lock_irq(&rtc->irq_task_lock);
if (rtc->irq_task)
err = -EBUSY;
spin_unlock_irq(&rtc->irq_task_lock);
if (err < 0)
return err;
}
/* try the driver's ioctl interface */
if (ops->ioctl) {
err = ops->ioctl(class_dev->dev, cmd, arg);
if (err != -ENOIOCTLCMD)
return err;
}
/* if the driver does not provide the ioctl interface
* or if that particular ioctl was not implemented
* (-ENOIOCTLCMD), we will try to emulate here.
*/
switch (cmd) {
case RTC_ALM_READ:
err = rtc_read_alarm(class_dev, &alarm);
if (err < 0)
return err;
if (copy_to_user(uarg, &alarm.time, sizeof(tm)))
return -EFAULT;
break;
case RTC_ALM_SET:
if (copy_from_user(&alarm.time, uarg, sizeof(tm)))
return -EFAULT;
alarm.enabled = 0;
alarm.pending = 0;
alarm.time.tm_mday = -1;
alarm.time.tm_mon = -1;
alarm.time.tm_year = -1;
alarm.time.tm_wday = -1;
alarm.time.tm_yday = -1;
alarm.time.tm_isdst = -1;
err = rtc_set_alarm(class_dev, &alarm);
break;
case RTC_RD_TIME:
err = rtc_read_time(class_dev, &tm);
if (err < 0)
return err;
if (copy_to_user(uarg, &tm, sizeof(tm)))
return -EFAULT;
break;
case RTC_SET_TIME:
if (copy_from_user(&tm, uarg, sizeof(tm)))
return -EFAULT;
err = rtc_set_time(class_dev, &tm);
break;
case RTC_IRQP_READ:
if (ops->irq_set_freq)
err = put_user(rtc->irq_freq, (unsigned long __user *)uarg);
break;
case RTC_IRQP_SET:
if (ops->irq_set_freq)
err = rtc_irq_set_freq(class_dev, rtc->irq_task, arg);
break;
#if 0
case RTC_EPOCH_SET:
#ifndef rtc_epoch
/*
* There were no RTC clocks before 1900.
*/
if (arg < 1900) {
err = -EINVAL;
break;
}
rtc_epoch = arg;
err = 0;
#endif
break;
case RTC_EPOCH_READ:
err = put_user(rtc_epoch, (unsigned long __user *)uarg);
break;
#endif
case RTC_WKALM_SET:
if (copy_from_user(&alarm, uarg, sizeof(alarm)))
return -EFAULT;
err = rtc_set_alarm(class_dev, &alarm);
break;
case RTC_WKALM_RD:
err = rtc_read_alarm(class_dev, &alarm);
if (err < 0)
return err;
if (copy_to_user(uarg, &alarm, sizeof(alarm)))
return -EFAULT;
break;
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
case RTC_UIE_OFF:
clear_uie(rtc);
return 0;
case RTC_UIE_ON:
return set_uie(rtc);
#endif
default:
err = -ENOTTY;
break;
}
return err;
}
static int rtc_dev_release(struct inode *inode, struct file *file)
{
struct rtc_device *rtc = to_rtc_device(file->private_data);
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
clear_uie(rtc);
#endif
if (rtc->ops->release)
rtc->ops->release(rtc->class_dev.dev);
mutex_unlock(&rtc->char_lock);
return 0;
}
static int rtc_dev_fasync(int fd, struct file *file, int on)
{
struct rtc_device *rtc = to_rtc_device(file->private_data);
return fasync_helper(fd, file, on, &rtc->async_queue);
}
static const struct file_operations rtc_dev_fops = {
.owner = THIS_MODULE,
.llseek = no_llseek,
.read = rtc_dev_read,
.poll = rtc_dev_poll,
.ioctl = rtc_dev_ioctl,
.open = rtc_dev_open,
.release = rtc_dev_release,
.fasync = rtc_dev_fasync,
};
/* insertion/removal hooks */
static int rtc_dev_add_device(struct class_device *class_dev,
struct class_interface *class_intf)
{
int err = 0;
struct rtc_device *rtc = to_rtc_device(class_dev);
if (rtc->id >= RTC_DEV_MAX) {
dev_err(class_dev->dev, "too many RTCs\n");
return -EINVAL;
}
mutex_init(&rtc->char_lock);
spin_lock_init(&rtc->irq_lock);
init_waitqueue_head(&rtc->irq_queue);
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
INIT_WORK(&rtc->uie_task, rtc_uie_task);
setup_timer(&rtc->uie_timer, rtc_uie_timer, (unsigned long)rtc);
#endif
cdev_init(&rtc->char_dev, &rtc_dev_fops);
rtc->char_dev.owner = rtc->owner;
if (cdev_add(&rtc->char_dev, MKDEV(MAJOR(rtc_devt), rtc->id), 1)) {
dev_err(class_dev->dev,
"failed to add char device %d:%d\n",
MAJOR(rtc_devt), rtc->id);
return -ENODEV;
}
rtc->rtc_dev = class_device_create(rtc_dev_class, NULL,
MKDEV(MAJOR(rtc_devt), rtc->id),
class_dev->dev, "rtc%d", rtc->id);
if (IS_ERR(rtc->rtc_dev)) {
dev_err(class_dev->dev, "cannot create rtc_dev device\n");
err = PTR_ERR(rtc->rtc_dev);
goto err_cdev_del;
}
dev_dbg(class_dev->dev, "rtc intf: dev (%d:%d)\n",
MAJOR(rtc->rtc_dev->devt),
MINOR(rtc->rtc_dev->devt));
return 0;
err_cdev_del:
cdev_del(&rtc->char_dev);
return err;
}
static void rtc_dev_remove_device(struct class_device *class_dev,
struct class_interface *class_intf)
{
struct rtc_device *rtc = to_rtc_device(class_dev);
if (rtc->rtc_dev) {
dev_dbg(class_dev->dev, "removing char %d:%d\n",
MAJOR(rtc->rtc_dev->devt),
MINOR(rtc->rtc_dev->devt));
class_device_unregister(rtc->rtc_dev);
cdev_del(&rtc->char_dev);
}
}
/* interface registration */
static struct class_interface rtc_dev_interface = {
.add = &rtc_dev_add_device,
.remove = &rtc_dev_remove_device,
};
static int __init rtc_dev_init(void)
{
int err;
rtc_dev_class = class_create(THIS_MODULE, "rtc-dev");
if (IS_ERR(rtc_dev_class))
return PTR_ERR(rtc_dev_class);
err = alloc_chrdev_region(&rtc_devt, 0, RTC_DEV_MAX, "rtc");
if (err < 0) {
printk(KERN_ERR "%s: failed to allocate char dev region\n",
__FILE__);
goto err_destroy_class;
}
err = rtc_interface_register(&rtc_dev_interface);
if (err < 0) {
printk(KERN_ERR "%s: failed to register the interface\n",
__FILE__);
goto err_unregister_chrdev;
}
return 0;
err_unregister_chrdev:
unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
err_destroy_class:
class_destroy(rtc_dev_class);
return err;
}
static void __exit rtc_dev_exit(void)
{
class_interface_unregister(&rtc_dev_interface);
class_destroy(rtc_dev_class);
unregister_chrdev_region(rtc_devt, RTC_DEV_MAX);
}
subsys_initcall(rtc_dev_init);
module_exit(rtc_dev_exit);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("RTC class dev interface");
MODULE_LICENSE("GPL");

388
drivers/rtc/rtc-ds1307.c Normal file
View File

@@ -0,0 +1,388 @@
/*
* rtc-ds1307.c - RTC driver for some mostly-compatible I2C chips.
*
* Copyright (C) 2005 James Chapman (ds1337 core)
* Copyright (C) 2006 David Brownell
*
* 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/module.h>
#include <linux/init.h>
#include <linux/slab.h>
#include <linux/i2c.h>
#include <linux/string.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
/* We can't determine type by probing, but if we expect pre-Linux code
* to have set the chip up as a clock (turning on the oscillator and
* setting the date and time), Linux can ignore the non-clock features.
* That's a natural job for a factory or repair bench.
*
* If the I2C "force" mechanism is used, we assume the chip is a ds1337.
* (Much better would be board-specific tables of I2C devices, along with
* the platform_data drivers would use to sort such issues out.)
*/
enum ds_type {
unknown = 0,
ds_1307, /* or ds1338, ... */
ds_1337, /* or ds1339, ... */
ds_1340, /* or st m41t00, ... */
// rs5c372 too? different address...
};
static unsigned short normal_i2c[] = { 0x68, I2C_CLIENT_END };
I2C_CLIENT_INSMOD;
/* RTC registers don't differ much, except for the century flag */
#define DS1307_REG_SECS 0x00 /* 00-59 */
# define DS1307_BIT_CH 0x80
#define DS1307_REG_MIN 0x01 /* 00-59 */
#define DS1307_REG_HOUR 0x02 /* 00-23, or 1-12{am,pm} */
# define DS1340_BIT_CENTURY_EN 0x80 /* in REG_HOUR */
# define DS1340_BIT_CENTURY 0x40 /* in REG_HOUR */
#define DS1307_REG_WDAY 0x03 /* 01-07 */
#define DS1307_REG_MDAY 0x04 /* 01-31 */
#define DS1307_REG_MONTH 0x05 /* 01-12 */
# define DS1337_BIT_CENTURY 0x80 /* in REG_MONTH */
#define DS1307_REG_YEAR 0x06 /* 00-99 */
/* Other registers (control, status, alarms, trickle charge, NVRAM, etc)
* start at 7, and they differ a lot. Only control and status matter for RTC;
* be careful using them.
*/
#define DS1307_REG_CONTROL 0x07
# define DS1307_BIT_OUT 0x80
# define DS1307_BIT_SQWE 0x10
# define DS1307_BIT_RS1 0x02
# define DS1307_BIT_RS0 0x01
#define DS1337_REG_CONTROL 0x0e
# define DS1337_BIT_nEOSC 0x80
# define DS1337_BIT_RS2 0x10
# define DS1337_BIT_RS1 0x08
# define DS1337_BIT_INTCN 0x04
# define DS1337_BIT_A2IE 0x02
# define DS1337_BIT_A1IE 0x01
#define DS1337_REG_STATUS 0x0f
# define DS1337_BIT_OSF 0x80
# define DS1337_BIT_A2I 0x02
# define DS1337_BIT_A1I 0x01
#define DS1339_REG_TRICKLE 0x10
struct ds1307 {
u8 reg_addr;
u8 regs[8];
enum ds_type type;
struct i2c_msg msg[2];
struct i2c_client client;
struct rtc_device *rtc;
};
static int ds1307_get_time(struct device *dev, struct rtc_time *t)
{
struct ds1307 *ds1307 = dev_get_drvdata(dev);
int tmp;
/* read the RTC registers all at once */
ds1307->msg[1].flags = I2C_M_RD;
ds1307->msg[1].len = 7;
tmp = i2c_transfer(ds1307->client.adapter, ds1307->msg, 2);
if (tmp != 2) {
dev_err(dev, "%s error %d\n", "read", tmp);
return -EIO;
}
dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n",
"read",
ds1307->regs[0], ds1307->regs[1],
ds1307->regs[2], ds1307->regs[3],
ds1307->regs[4], ds1307->regs[5],
ds1307->regs[6]);
t->tm_sec = BCD2BIN(ds1307->regs[DS1307_REG_SECS] & 0x7f);
t->tm_min = BCD2BIN(ds1307->regs[DS1307_REG_MIN] & 0x7f);
tmp = ds1307->regs[DS1307_REG_HOUR] & 0x3f;
t->tm_hour = BCD2BIN(tmp);
t->tm_wday = BCD2BIN(ds1307->regs[DS1307_REG_WDAY] & 0x07) - 1;
t->tm_mday = BCD2BIN(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
tmp = ds1307->regs[DS1307_REG_MONTH] & 0x1f;
t->tm_mon = BCD2BIN(tmp) - 1;
/* assume 20YY not 19YY, and ignore DS1337_BIT_CENTURY */
t->tm_year = BCD2BIN(ds1307->regs[DS1307_REG_YEAR]) + 100;
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"read", t->tm_sec, t->tm_min,
t->tm_hour, t->tm_mday,
t->tm_mon, t->tm_year, t->tm_wday);
return 0;
}
static int ds1307_set_time(struct device *dev, struct rtc_time *t)
{
struct ds1307 *ds1307 = dev_get_drvdata(dev);
int result;
int tmp;
u8 *buf = ds1307->regs;
dev_dbg(dev, "%s secs=%d, mins=%d, "
"hours=%d, mday=%d, mon=%d, year=%d, wday=%d\n",
"write", t->tm_sec, t->tm_min,
t->tm_hour, t->tm_mday,
t->tm_mon, t->tm_year, t->tm_wday);
*buf++ = 0; /* first register addr */
buf[DS1307_REG_SECS] = BIN2BCD(t->tm_sec);
buf[DS1307_REG_MIN] = BIN2BCD(t->tm_min);
buf[DS1307_REG_HOUR] = BIN2BCD(t->tm_hour);
buf[DS1307_REG_WDAY] = BIN2BCD(t->tm_wday + 1);
buf[DS1307_REG_MDAY] = BIN2BCD(t->tm_mday);
buf[DS1307_REG_MONTH] = BIN2BCD(t->tm_mon + 1);
/* assume 20YY not 19YY */
tmp = t->tm_year - 100;
buf[DS1307_REG_YEAR] = BIN2BCD(tmp);
if (ds1307->type == ds_1337)
buf[DS1307_REG_MONTH] |= DS1337_BIT_CENTURY;
else if (ds1307->type == ds_1340)
buf[DS1307_REG_HOUR] |= DS1340_BIT_CENTURY_EN
| DS1340_BIT_CENTURY;
ds1307->msg[1].flags = 0;
ds1307->msg[1].len = 8;
dev_dbg(dev, "%s: %02x %02x %02x %02x %02x %02x %02x\n",
"write", buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6]);
result = i2c_transfer(ds1307->client.adapter, &ds1307->msg[1], 1);
if (result != 1) {
dev_err(dev, "%s error %d\n", "write", tmp);
return -EIO;
}
return 0;
}
static const struct rtc_class_ops ds13xx_rtc_ops = {
.read_time = ds1307_get_time,
.set_time = ds1307_set_time,
};
static struct i2c_driver ds1307_driver;
static int __devinit
ds1307_detect(struct i2c_adapter *adapter, int address, int kind)
{
struct ds1307 *ds1307;
int err = -ENODEV;
struct i2c_client *client;
int tmp;
if (!(ds1307 = kzalloc(sizeof(struct ds1307), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
client = &ds1307->client;
client->addr = address;
client->adapter = adapter;
client->driver = &ds1307_driver;
client->flags = 0;
i2c_set_clientdata(client, ds1307);
ds1307->msg[0].addr = client->addr;
ds1307->msg[0].flags = 0;
ds1307->msg[0].len = 1;
ds1307->msg[0].buf = &ds1307->reg_addr;
ds1307->msg[1].addr = client->addr;
ds1307->msg[1].flags = I2C_M_RD;
ds1307->msg[1].len = sizeof(ds1307->regs);
ds1307->msg[1].buf = ds1307->regs;
/* HACK: "force" implies "needs ds1337-style-oscillator setup" */
if (kind >= 0) {
ds1307->type = ds_1337;
ds1307->reg_addr = DS1337_REG_CONTROL;
ds1307->msg[1].len = 2;
tmp = i2c_transfer(client->adapter, ds1307->msg, 2);
if (tmp != 2) {
pr_debug("read error %d\n", tmp);
err = -EIO;
goto exit_free;
}
ds1307->reg_addr = 0;
ds1307->msg[1].len = sizeof(ds1307->regs);
/* oscillator is off; need to turn it on */
if ((ds1307->regs[0] & DS1337_BIT_nEOSC)
|| (ds1307->regs[1] & DS1337_BIT_OSF)) {
printk(KERN_ERR "no ds1337 oscillator code\n");
goto exit_free;
}
} else
ds1307->type = ds_1307;
read_rtc:
/* read RTC registers */
tmp = i2c_transfer(client->adapter, ds1307->msg, 2);
if (tmp != 2) {
pr_debug("read error %d\n", tmp);
err = -EIO;
goto exit_free;
}
/* minimal sanity checking; some chips (like DS1340) don't
* specify the extra bits as must-be-zero, but there are
* still a few values that are clearly out-of-range.
*/
tmp = ds1307->regs[DS1307_REG_SECS];
if (tmp & DS1307_BIT_CH) {
if (ds1307->type && ds1307->type != ds_1307) {
pr_debug("not a ds1307?\n");
goto exit_free;
}
ds1307->type = ds_1307;
/* this partial initialization should work for ds1307,
* ds1338, ds1340, st m41t00, and more.
*/
dev_warn(&client->dev, "oscillator started; SET TIME!\n");
i2c_smbus_write_byte_data(client, 0, 0);
goto read_rtc;
}
tmp = BCD2BIN(tmp & 0x7f);
if (tmp > 60)
goto exit_free;
tmp = BCD2BIN(ds1307->regs[DS1307_REG_MIN] & 0x7f);
if (tmp > 60)
goto exit_free;
tmp = BCD2BIN(ds1307->regs[DS1307_REG_MDAY] & 0x3f);
if (tmp == 0 || tmp > 31)
goto exit_free;
tmp = BCD2BIN(ds1307->regs[DS1307_REG_MONTH] & 0x1f);
if (tmp == 0 || tmp > 12)
goto exit_free;
/* force into in 24 hour mode (most chips) or
* disable century bit (ds1340)
*/
tmp = ds1307->regs[DS1307_REG_HOUR];
if (tmp & (1 << 6)) {
if (tmp & (1 << 5))
tmp = BCD2BIN(tmp & 0x1f) + 12;
else
tmp = BCD2BIN(tmp);
i2c_smbus_write_byte_data(client,
DS1307_REG_HOUR,
BIN2BCD(tmp));
}
/* FIXME chips like 1337 can generate alarm irqs too; those are
* worth exposing through the API (especially when the irq is
* wakeup-capable).
*/
switch (ds1307->type) {
case unknown:
strlcpy(client->name, "unknown", I2C_NAME_SIZE);
break;
case ds_1307:
strlcpy(client->name, "ds1307", I2C_NAME_SIZE);
break;
case ds_1337:
strlcpy(client->name, "ds1337", I2C_NAME_SIZE);
break;
case ds_1340:
strlcpy(client->name, "ds1340", I2C_NAME_SIZE);
break;
}
/* Tell the I2C layer a new client has arrived */
if ((err = i2c_attach_client(client)))
goto exit_free;
ds1307->rtc = rtc_device_register(client->name, &client->dev,
&ds13xx_rtc_ops, THIS_MODULE);
if (IS_ERR(ds1307->rtc)) {
err = PTR_ERR(ds1307->rtc);
dev_err(&client->dev,
"unable to register the class device\n");
goto exit_detach;
}
return 0;
exit_detach:
i2c_detach_client(client);
exit_free:
kfree(ds1307);
exit:
return err;
}
static int __devinit
ds1307_attach_adapter(struct i2c_adapter *adapter)
{
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C))
return 0;
return i2c_probe(adapter, &addr_data, ds1307_detect);
}
static int __devexit ds1307_detach_client(struct i2c_client *client)
{
int err;
struct ds1307 *ds1307 = i2c_get_clientdata(client);
rtc_device_unregister(ds1307->rtc);
if ((err = i2c_detach_client(client)))
return err;
kfree(ds1307);
return 0;
}
static struct i2c_driver ds1307_driver = {
.driver = {
.name = "ds1307",
.owner = THIS_MODULE,
},
.attach_adapter = ds1307_attach_adapter,
.detach_client = __devexit_p(ds1307_detach_client),
};
static int __init ds1307_init(void)
{
return i2c_add_driver(&ds1307_driver);
}
module_init(ds1307_init);
static void __exit ds1307_exit(void)
{
i2c_del_driver(&ds1307_driver);
}
module_exit(ds1307_exit);
MODULE_DESCRIPTION("RTC driver for DS1307 and similar chips");
MODULE_LICENSE("GPL");

418
drivers/rtc/rtc-ds1553.c Normal file
View File

@@ -0,0 +1,418 @@
/*
* 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>
#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>
#define DRV_VERSION "0.2"
#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 baseaddr;
unsigned long last_jiffies;
int irq;
unsigned int irqen;
int alrm_sec;
int alrm_min;
int alrm_hour;
int alrm_mday;
};
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;
century = BIN2BCD((tm->tm_year + 1900) / 100);
writeb(RTC_WRITE, pdata->ioaddr + RTC_CONTROL);
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);
/* 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);
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;
/* year is 1900 + tm->tm_year */
tm->tm_year = BCD2BIN(year) + BCD2BIN(century) * 100 - 1900;
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;
spin_lock_irqsave(&pdata->rtc->irq_lock, flags);
writeb(pdata->alrm_mday < 0 || (pdata->irqen & RTC_UF) ?
0x80 : BIN2BCD(pdata->alrm_mday),
ioaddr + RTC_DATE_ALARM);
writeb(pdata->alrm_hour < 0 || (pdata->irqen & RTC_UF) ?
0x80 : BIN2BCD(pdata->alrm_hour),
ioaddr + RTC_HOURS_ALARM);
writeb(pdata->alrm_min < 0 || (pdata->irqen & RTC_UF) ?
0x80 : BIN2BCD(pdata->alrm_min),
ioaddr + RTC_MINUTES_ALARM);
writeb(pdata->alrm_sec < 0 || (pdata->irqen & RTC_UF) ?
0x80 : BIN2BCD(pdata->alrm_sec),
ioaddr + RTC_SECONDS_ALARM);
writeb(pdata->irqen ? RTC_INTS_AE : 0, ioaddr + RTC_INTERRUPTS);
readb(ioaddr + RTC_FLAGS); /* clear interrupts */
spin_unlock_irqrestore(&pdata->rtc->irq_lock, flags);
}
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);
if (pdata->irq < 0)
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);
if (pdata->irq < 0)
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;
}
static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = dev_id;
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr;
unsigned long events = RTC_IRQF;
/* read and clear interrupt */
if (!(readb(ioaddr + RTC_FLAGS) & RTC_FLAGS_AF))
return IRQ_NONE;
if (readb(ioaddr + RTC_SECONDS_ALARM) & 0x80)
events |= RTC_UF;
else
events |= RTC_AF;
rtc_update_irq(&pdata->rtc->class_dev, 1, events);
return IRQ_HANDLED;
}
static void ds1553_rtc_release(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
if (pdata->irq >= 0) {
pdata->irqen = 0;
ds1553_rtc_update_alarm(pdata);
}
}
static int ds1553_rtc_ioctl(struct device *dev, unsigned int cmd,
unsigned long arg)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
if (pdata->irq < 0)
return -ENOIOCTLCMD; /* fall back into rtc-dev's emulation */
switch (cmd) {
case RTC_AIE_OFF:
pdata->irqen &= ~RTC_AF;
ds1553_rtc_update_alarm(pdata);
break;
case RTC_AIE_ON:
pdata->irqen |= RTC_AF;
ds1553_rtc_update_alarm(pdata);
break;
case RTC_UIE_OFF:
pdata->irqen &= ~RTC_UF;
ds1553_rtc_update_alarm(pdata);
break;
case RTC_UIE_ON:
pdata->irqen |= RTC_UF;
ds1553_rtc_update_alarm(pdata);
break;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static const struct rtc_class_ops ds1553_rtc_ops = {
.read_time = ds1553_rtc_read_time,
.set_time = ds1553_rtc_set_time,
.read_alarm = ds1553_rtc_read_alarm,
.set_alarm = ds1553_rtc_set_alarm,
.release = ds1553_rtc_release,
.ioctl = ds1553_rtc_ioctl,
};
static ssize_t ds1553_nvram_read(struct kobject *kobj, char *buf,
loff_t pos, size_t size)
{
struct platform_device *pdev =
to_platform_device(container_of(kobj, struct device, kobj));
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;
}
static ssize_t ds1553_nvram_write(struct kobject *kobj, char *buf,
loff_t pos, size_t size)
{
struct platform_device *pdev =
to_platform_device(container_of(kobj, struct device, kobj));
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",
.mode = S_IRUGO | S_IWUGO,
.owner = THIS_MODULE,
},
.size = RTC_OFFSET,
.read = ds1553_nvram_read,
.write = ds1553_nvram_write,
};
static int __devinit ds1553_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
unsigned int cen, sec;
struct rtc_plat_data *pdata = NULL;
void __iomem *ioaddr = NULL;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->irq = -1;
if (!request_mem_region(res->start, RTC_REG_SIZE, pdev->name)) {
ret = -EBUSY;
goto out;
}
pdata->baseaddr = res->start;
ioaddr = ioremap(pdata->baseaddr, RTC_REG_SIZE);
if (!ioaddr) {
ret = -ENOMEM;
goto out;
}
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");
if (pdata->irq >= 0) {
writeb(0, ioaddr + RTC_INTERRUPTS);
if (request_irq(pdata->irq, ds1553_rtc_interrupt,
IRQF_DISABLED | IRQF_SHARED,
pdev->name, pdev) < 0) {
dev_warn(&pdev->dev, "interrupt not available.\n");
pdata->irq = -1;
}
}
rtc = rtc_device_register(pdev->name, &pdev->dev,
&ds1553_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc);
goto out;
}
pdata->rtc = rtc;
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
if (ret)
goto out;
return 0;
out:
if (pdata->rtc)
rtc_device_unregister(pdata->rtc);
if (pdata->irq >= 0)
free_irq(pdata->irq, pdev);
if (ioaddr)
iounmap(ioaddr);
if (pdata->baseaddr)
release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
kfree(pdata);
return ret;
}
static int __devexit ds1553_rtc_remove(struct platform_device *pdev)
{
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
sysfs_remove_bin_file(&pdev->dev.kobj, &ds1553_nvram_attr);
rtc_device_unregister(pdata->rtc);
if (pdata->irq >= 0) {
writeb(0, pdata->ioaddr + RTC_INTERRUPTS);
free_irq(pdata->irq, pdev);
}
iounmap(pdata->ioaddr);
release_mem_region(pdata->baseaddr, RTC_REG_SIZE);
kfree(pdata);
return 0;
}
static struct platform_driver ds1553_rtc_driver = {
.probe = ds1553_rtc_probe,
.remove = __devexit_p(ds1553_rtc_remove),
.driver = {
.name = "ds1553",
.owner = THIS_MODULE,
},
};
static __init int ds1553_init(void)
{
return platform_driver_register(&ds1553_rtc_driver);
}
static __exit void ds1553_exit(void)
{
return platform_driver_unregister(&ds1553_rtc_driver);
}
module_init(ds1553_init);
module_exit(ds1553_exit);
MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
MODULE_DESCRIPTION("Dallas DS1553 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);

282
drivers/rtc/rtc-ds1672.c Normal file
View File

@@ -0,0 +1,282 @@
/*
* An rtc/i2c driver for the Dallas DS1672
* Copyright 2005-06 Tower Technologies
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* 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/module.h>
#include <linux/i2c.h>
#include <linux/rtc.h>
#define DRV_VERSION "0.3"
/* Addresses to scan: none. This chip cannot be detected. */
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
/* Insmod parameters */
I2C_CLIENT_INSMOD;
/* Registers */
#define DS1672_REG_CNT_BASE 0
#define DS1672_REG_CONTROL 4
#define DS1672_REG_TRICKLE 5
#define DS1672_REG_CONTROL_EOSC 0x80
/* Prototypes */
static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind);
/*
* In the routines that deal directly with the ds1672 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
* Epoch is initialized as 2000. Time is set to UTC.
*/
static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
unsigned long time;
unsigned char addr = DS1672_REG_CNT_BASE;
unsigned char buf[4];
struct i2c_msg msgs[] = {
{ client->addr, 0, 1, &addr }, /* setup read ptr */
{ client->addr, I2C_M_RD, 4, buf }, /* read date */
};
/* read date registers */
if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
dev_dbg(&client->dev,
"%s: raw read data - counters=%02x,%02x,%02x,%02x\n",
__FUNCTION__, buf[0], buf[1], buf[2], buf[3]);
time = (buf[3] << 24) | (buf[2] << 16) | (buf[1] << 8) | buf[0];
rtc_time_to_tm(time, tm);
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__, tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
return 0;
}
static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs)
{
int xfer;
unsigned char buf[6];
buf[0] = DS1672_REG_CNT_BASE;
buf[1] = secs & 0x000000FF;
buf[2] = (secs & 0x0000FF00) >> 8;
buf[3] = (secs & 0x00FF0000) >> 16;
buf[4] = (secs & 0xFF000000) >> 24;
buf[5] = 0; /* set control reg to enable counting */
xfer = i2c_master_send(client, buf, 6);
if (xfer != 6) {
dev_err(&client->dev, "%s: send: %d\n", __FUNCTION__, xfer);
return -EIO;
}
return 0;
}
static int ds1672_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
unsigned long secs;
dev_dbg(&client->dev,
"%s: secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
rtc_tm_to_time(tm, &secs);
return ds1672_set_mmss(client, secs);
}
static int ds1672_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return ds1672_get_datetime(to_i2c_client(dev), tm);
}
static int ds1672_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return ds1672_set_datetime(to_i2c_client(dev), tm);
}
static int ds1672_rtc_set_mmss(struct device *dev, unsigned long secs)
{
return ds1672_set_mmss(to_i2c_client(dev), secs);
}
static int ds1672_get_control(struct i2c_client *client, u8 *status)
{
unsigned char addr = DS1672_REG_CONTROL;
struct i2c_msg msgs[] = {
{ client->addr, 0, 1, &addr }, /* setup read ptr */
{ client->addr, I2C_M_RD, 1, status }, /* read control */
};
/* read control register */
if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
return 0;
}
/* following are the sysfs callback functions */
static ssize_t show_control(struct device *dev, struct device_attribute *attr, char *buf)
{
struct i2c_client *client = to_i2c_client(dev);
u8 control;
int err;
err = ds1672_get_control(client, &control);
if (err)
return err;
return sprintf(buf, "%s\n", (control & DS1672_REG_CONTROL_EOSC)
? "disabled" : "enabled");
}
static DEVICE_ATTR(control, S_IRUGO, show_control, NULL);
static const struct rtc_class_ops ds1672_rtc_ops = {
.read_time = ds1672_rtc_read_time,
.set_time = ds1672_rtc_set_time,
.set_mmss = ds1672_rtc_set_mmss,
};
static int ds1672_attach(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, ds1672_probe);
}
static int ds1672_detach(struct i2c_client *client)
{
int err;
struct rtc_device *rtc = i2c_get_clientdata(client);
if (rtc)
rtc_device_unregister(rtc);
if ((err = i2c_detach_client(client)))
return err;
kfree(client);
return 0;
}
static struct i2c_driver ds1672_driver = {
.driver = {
.name = "ds1672",
},
.id = I2C_DRIVERID_DS1672,
.attach_adapter = &ds1672_attach,
.detach_client = &ds1672_detach,
};
static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind)
{
int err = 0;
u8 control;
struct i2c_client *client;
struct rtc_device *rtc;
dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto exit;
}
if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
/* I2C client */
client->addr = address;
client->driver = &ds1672_driver;
client->adapter = adapter;
strlcpy(client->name, ds1672_driver.driver.name, I2C_NAME_SIZE);
/* Inform the i2c layer */
if ((err = i2c_attach_client(client)))
goto exit_kfree;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
rtc = rtc_device_register(ds1672_driver.driver.name, &client->dev,
&ds1672_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
err = PTR_ERR(rtc);
goto exit_detach;
}
i2c_set_clientdata(client, rtc);
/* read control register */
err = ds1672_get_control(client, &control);
if (err)
goto exit_devreg;
if (control & DS1672_REG_CONTROL_EOSC)
dev_warn(&client->dev, "Oscillator not enabled. "
"Set time to enable.\n");
/* Register sysfs hooks */
err = device_create_file(&client->dev, &dev_attr_control);
if (err)
goto exit_devreg;
return 0;
exit_devreg:
rtc_device_unregister(rtc);
exit_detach:
i2c_detach_client(client);
exit_kfree:
kfree(client);
exit:
return err;
}
static int __init ds1672_init(void)
{
return i2c_add_driver(&ds1672_driver);
}
static void __exit ds1672_exit(void)
{
i2c_del_driver(&ds1672_driver);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("Dallas/Maxim DS1672 timekeeper driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
module_init(ds1672_init);
module_exit(ds1672_exit);

274
drivers/rtc/rtc-ds1742.c Normal file
View File

@@ -0,0 +1,274 @@
/*
* An rtc driver for the Dallas DS1742
*
* 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.
*
* Copyright (C) 2006 Torsten Ertbjerg Rasmussen <tr@newtec.dk>
* - nvram size determined from resource
* - this ds1742 driver now supports ds1743.
*/
#include <linux/bcd.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/delay.h>
#include <linux/jiffies.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/io.h>
#define DRV_VERSION "0.3"
#define RTC_SIZE 8
#define RTC_CONTROL 0
#define RTC_CENTURY 0
#define RTC_SECONDS 1
#define RTC_MINUTES 2
#define RTC_HOURS 3
#define RTC_DAY 4
#define RTC_DATE 5
#define RTC_MONTH 6
#define RTC_YEAR 7
#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 Day register */
#define RTC_BATT_FLAG 0x80
struct rtc_plat_data {
struct rtc_device *rtc;
void __iomem *ioaddr_nvram;
void __iomem *ioaddr_rtc;
size_t size_nvram;
size_t size;
unsigned long baseaddr;
unsigned long last_jiffies;
};
static int ds1742_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_rtc;
u8 century;
century = BIN2BCD((tm->tm_year + 1900) / 100);
writeb(RTC_WRITE, ioaddr + RTC_CONTROL);
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);
/* 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 ds1742_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_rtc;
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);
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;
/* year is 1900 + tm->tm_year */
tm->tm_year = BCD2BIN(year) + BCD2BIN(century) * 100 - 1900;
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 const struct rtc_class_ops ds1742_rtc_ops = {
.read_time = ds1742_rtc_read_time,
.set_time = ds1742_rtc_set_time,
};
static ssize_t ds1742_nvram_read(struct kobject *kobj, char *buf,
loff_t pos, size_t size)
{
struct platform_device *pdev =
to_platform_device(container_of(kobj, struct device, kobj));
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr_nvram;
ssize_t count;
for (count = 0; size > 0 && pos < pdata->size_nvram; count++, size--)
*buf++ = readb(ioaddr + pos++);
return count;
}
static ssize_t ds1742_nvram_write(struct kobject *kobj, char *buf,
loff_t pos, size_t size)
{
struct platform_device *pdev =
to_platform_device(container_of(kobj, struct device, kobj));
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
void __iomem *ioaddr = pdata->ioaddr_nvram;
ssize_t count;
for (count = 0; size > 0 && pos < pdata->size_nvram; count++, size--)
writeb(*buf++, ioaddr + pos++);
return count;
}
static struct bin_attribute ds1742_nvram_attr = {
.attr = {
.name = "nvram",
.mode = S_IRUGO | S_IWUGO,
.owner = THIS_MODULE,
},
.read = ds1742_nvram_read,
.write = ds1742_nvram_write,
};
static int __devinit ds1742_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
unsigned int cen, sec;
struct rtc_plat_data *pdata = NULL;
void __iomem *ioaddr = NULL;
int ret = 0;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res)
return -ENODEV;
pdata = kzalloc(sizeof(*pdata), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
pdata->size = res->end - res->start + 1;
if (!request_mem_region(res->start, pdata->size, pdev->name)) {
ret = -EBUSY;
goto out;
}
pdata->baseaddr = res->start;
ioaddr = ioremap(pdata->baseaddr, pdata->size);
if (!ioaddr) {
ret = -ENOMEM;
goto out;
}
pdata->ioaddr_nvram = ioaddr;
pdata->size_nvram = pdata->size - RTC_SIZE;
pdata->ioaddr_rtc = ioaddr + pdata->size_nvram;
/* turn RTC on if it was not on */
ioaddr = pdata->ioaddr_rtc;
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_DAY) & RTC_BATT_FLAG))
dev_warn(&pdev->dev, "voltage-low detected.\n");
rtc = rtc_device_register(pdev->name, &pdev->dev,
&ds1742_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc);
goto out;
}
pdata->rtc = rtc;
pdata->last_jiffies = jiffies;
platform_set_drvdata(pdev, pdata);
ds1742_nvram_attr.size = max(ds1742_nvram_attr.size,
pdata->size_nvram);
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1742_nvram_attr);
if (ret)
goto out;
return 0;
out:
if (pdata->rtc)
rtc_device_unregister(pdata->rtc);
if (pdata->ioaddr_nvram)
iounmap(pdata->ioaddr_nvram);
if (pdata->baseaddr)
release_mem_region(pdata->baseaddr, pdata->size);
kfree(pdata);
return ret;
}
static int __devexit ds1742_rtc_remove(struct platform_device *pdev)
{
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
sysfs_remove_bin_file(&pdev->dev.kobj, &ds1742_nvram_attr);
rtc_device_unregister(pdata->rtc);
iounmap(pdata->ioaddr_nvram);
release_mem_region(pdata->baseaddr, pdata->size);
kfree(pdata);
return 0;
}
static struct platform_driver ds1742_rtc_driver = {
.probe = ds1742_rtc_probe,
.remove = __devexit_p(ds1742_rtc_remove),
.driver = {
.name = "ds1742",
.owner = THIS_MODULE,
},
};
static __init int ds1742_init(void)
{
return platform_driver_register(&ds1742_rtc_driver);
}
static __exit void ds1742_exit(void)
{
return platform_driver_unregister(&ds1742_rtc_driver);
}
module_init(ds1742_init);
module_exit(ds1742_exit);
MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
MODULE_DESCRIPTION("Dallas DS1742 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);

160
drivers/rtc/rtc-ep93xx.c Normal file
View File

@@ -0,0 +1,160 @@
/*
* A driver for the RTC embedded in the Cirrus Logic EP93XX processors
* Copyright (c) 2006 Tower Technologies
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* 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/module.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <asm/hardware.h>
#define EP93XX_RTC_REG(x) (EP93XX_RTC_BASE + (x))
#define EP93XX_RTC_DATA EP93XX_RTC_REG(0x0000)
#define EP93XX_RTC_LOAD EP93XX_RTC_REG(0x000C)
#define EP93XX_RTC_SWCOMP EP93XX_RTC_REG(0x0108)
#define DRV_VERSION "0.2"
static int ep93xx_get_swcomp(struct device *dev, unsigned short *preload,
unsigned short *delete)
{
unsigned short comp = __raw_readl(EP93XX_RTC_SWCOMP);
if (preload)
*preload = comp & 0xffff;
if (delete)
*delete = (comp >> 16) & 0x1f;
return 0;
}
static int ep93xx_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
unsigned long time = __raw_readl(EP93XX_RTC_DATA);
rtc_time_to_tm(time, tm);
return 0;
}
static int ep93xx_rtc_set_mmss(struct device *dev, unsigned long secs)
{
__raw_writel(secs + 1, EP93XX_RTC_LOAD);
return 0;
}
static int ep93xx_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
int err;
unsigned long secs;
err = rtc_tm_to_time(tm, &secs);
if (err != 0)
return err;
return ep93xx_rtc_set_mmss(dev, secs);
}
static int ep93xx_rtc_proc(struct device *dev, struct seq_file *seq)
{
unsigned short preload, delete;
ep93xx_get_swcomp(dev, &preload, &delete);
seq_printf(seq, "preload\t\t: %d\n", preload);
seq_printf(seq, "delete\t\t: %d\n", delete);
return 0;
}
static const struct rtc_class_ops ep93xx_rtc_ops = {
.read_time = ep93xx_rtc_read_time,
.set_time = ep93xx_rtc_set_time,
.set_mmss = ep93xx_rtc_set_mmss,
.proc = ep93xx_rtc_proc,
};
static ssize_t ep93xx_sysfs_show_comp_preload(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned short preload;
ep93xx_get_swcomp(dev, &preload, NULL);
return sprintf(buf, "%d\n", preload);
}
static DEVICE_ATTR(comp_preload, S_IRUGO, ep93xx_sysfs_show_comp_preload, NULL);
static ssize_t ep93xx_sysfs_show_comp_delete(struct device *dev,
struct device_attribute *attr, char *buf)
{
unsigned short delete;
ep93xx_get_swcomp(dev, NULL, &delete);
return sprintf(buf, "%d\n", delete);
}
static DEVICE_ATTR(comp_delete, S_IRUGO, ep93xx_sysfs_show_comp_delete, NULL);
static int __devinit ep93xx_rtc_probe(struct platform_device *dev)
{
struct rtc_device *rtc = rtc_device_register("ep93xx",
&dev->dev, &ep93xx_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
return PTR_ERR(rtc);
}
platform_set_drvdata(dev, rtc);
device_create_file(&dev->dev, &dev_attr_comp_preload);
device_create_file(&dev->dev, &dev_attr_comp_delete);
return 0;
}
static int __devexit ep93xx_rtc_remove(struct platform_device *dev)
{
struct rtc_device *rtc = platform_get_drvdata(dev);
if (rtc)
rtc_device_unregister(rtc);
platform_set_drvdata(dev, NULL);
return 0;
}
static struct platform_driver ep93xx_rtc_platform_driver = {
.driver = {
.name = "ep93xx-rtc",
.owner = THIS_MODULE,
},
.probe = ep93xx_rtc_probe,
.remove = __devexit_p(ep93xx_rtc_remove),
};
static int __init ep93xx_rtc_init(void)
{
return platform_driver_register(&ep93xx_rtc_platform_driver);
}
static void __exit ep93xx_rtc_exit(void)
{
platform_driver_unregister(&ep93xx_rtc_platform_driver);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("EP93XX RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
module_init(ep93xx_rtc_init);
module_exit(ep93xx_rtc_exit);

591
drivers/rtc/rtc-isl1208.c Normal file
View File

@@ -0,0 +1,591 @@
/*
* Intersil ISL1208 rtc class driver
*
* Copyright 2005,2006 Hebert Valerio Riedel <hvr@gnu.org>
*
* 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/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#define DRV_NAME "isl1208"
#define DRV_VERSION "0.2"
/* Register map */
/* rtc section */
#define ISL1208_REG_SC 0x00
#define ISL1208_REG_MN 0x01
#define ISL1208_REG_HR 0x02
#define ISL1208_REG_HR_MIL (1<<7) /* 24h/12h mode */
#define ISL1208_REG_HR_PM (1<<5) /* PM/AM bit in 12h mode */
#define ISL1208_REG_DT 0x03
#define ISL1208_REG_MO 0x04
#define ISL1208_REG_YR 0x05
#define ISL1208_REG_DW 0x06
#define ISL1208_RTC_SECTION_LEN 7
/* control/status section */
#define ISL1208_REG_SR 0x07
#define ISL1208_REG_SR_ARST (1<<7) /* auto reset */
#define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */
#define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */
#define ISL1208_REG_SR_ALM (1<<2) /* alarm */
#define ISL1208_REG_SR_BAT (1<<1) /* battery */
#define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */
#define ISL1208_REG_INT 0x08
#define ISL1208_REG_09 0x09 /* reserved */
#define ISL1208_REG_ATR 0x0a
#define ISL1208_REG_DTR 0x0b
/* alarm section */
#define ISL1208_REG_SCA 0x0c
#define ISL1208_REG_MNA 0x0d
#define ISL1208_REG_HRA 0x0e
#define ISL1208_REG_DTA 0x0f
#define ISL1208_REG_MOA 0x10
#define ISL1208_REG_DWA 0x11
#define ISL1208_ALARM_SECTION_LEN 6
/* user section */
#define ISL1208_REG_USR1 0x12
#define ISL1208_REG_USR2 0x13
#define ISL1208_USR_SECTION_LEN 2
/* i2c configuration */
#define ISL1208_I2C_ADDR 0xde
static unsigned short normal_i2c[] = {
ISL1208_I2C_ADDR>>1, I2C_CLIENT_END
};
I2C_CLIENT_INSMOD; /* defines addr_data */
static int isl1208_attach_adapter(struct i2c_adapter *adapter);
static int isl1208_detach_client(struct i2c_client *client);
static struct i2c_driver isl1208_driver = {
.driver = {
.name = DRV_NAME,
},
.id = I2C_DRIVERID_ISL1208,
.attach_adapter = &isl1208_attach_adapter,
.detach_client = &isl1208_detach_client,
};
/* block read */
static int
isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
unsigned len)
{
u8 reg_addr[1] = { reg };
struct i2c_msg msgs[2] = {
{ client->addr, client->flags, sizeof(reg_addr), reg_addr },
{ client->addr, client->flags | I2C_M_RD, len, buf }
};
int ret;
BUG_ON(len == 0);
BUG_ON(reg > ISL1208_REG_USR2);
BUG_ON(reg + len > ISL1208_REG_USR2 + 1);
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret > 0)
ret = 0;
return ret;
}
/* block write */
static int
isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],
unsigned len)
{
u8 i2c_buf[ISL1208_REG_USR2 + 2];
struct i2c_msg msgs[1] = {
{ client->addr, client->flags, len + 1, i2c_buf }
};
int ret;
BUG_ON(len == 0);
BUG_ON(reg > ISL1208_REG_USR2);
BUG_ON(reg + len > ISL1208_REG_USR2 + 1);
i2c_buf[0] = reg;
memcpy(&i2c_buf[1], &buf[0], len);
ret = i2c_transfer(client->adapter, msgs, 1);
if (ret > 0)
ret = 0;
return ret;
}
/* simple check to see wether we have a isl1208 */
static int isl1208_i2c_validate_client(struct i2c_client *client)
{
u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
u8 zero_mask[ISL1208_RTC_SECTION_LEN] = {
0x80, 0x80, 0x40, 0xc0, 0xe0, 0x00, 0xf8
};
int i;
int ret;
ret = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);
if (ret < 0)
return ret;
for (i = 0; i < ISL1208_RTC_SECTION_LEN; ++i) {
if (regs[i] & zero_mask[i]) /* check if bits are cleared */
return -ENODEV;
}
return 0;
}
static int isl1208_i2c_get_sr(struct i2c_client *client)
{
return i2c_smbus_read_byte_data(client, ISL1208_REG_SR) == -1 ? -EIO:0;
}
static int isl1208_i2c_get_atr(struct i2c_client *client)
{
int atr = i2c_smbus_read_byte_data(client, ISL1208_REG_ATR);
if (atr < 0)
return -EIO;
/* The 6bit value in the ATR register controls the load
* capacitance C_load * in steps of 0.25pF
*
* bit (1<<5) of the ATR register is inverted
*
* C_load(ATR=0x20) = 4.50pF
* C_load(ATR=0x00) = 12.50pF
* C_load(ATR=0x1f) = 20.25pF
*
*/
atr &= 0x3f; /* mask out lsb */
atr ^= 1<<5; /* invert 6th bit */
atr += 2*9; /* add offset of 4.5pF; unit[atr] = 0.25pF */
return atr;
}
static int isl1208_i2c_get_dtr(struct i2c_client *client)
{
int dtr = i2c_smbus_read_byte_data(client, ISL1208_REG_DTR);
if (dtr < 0)
return -EIO;
/* dtr encodes adjustments of {-60,-40,-20,0,20,40,60} ppm */
dtr = ((dtr & 0x3) * 20) * (dtr & (1<<2) ? -1 : 1);
return dtr;
}
static int isl1208_i2c_get_usr(struct i2c_client *client)
{
u8 buf[ISL1208_USR_SECTION_LEN] = { 0, };
int ret;
ret = isl1208_i2c_read_regs (client, ISL1208_REG_USR1, buf,
ISL1208_USR_SECTION_LEN);
if (ret < 0)
return ret;
return (buf[1] << 8) | buf[0];
}
static int isl1208_i2c_set_usr(struct i2c_client *client, u16 usr)
{
u8 buf[ISL1208_USR_SECTION_LEN];
buf[0] = usr & 0xff;
buf[1] = (usr >> 8) & 0xff;
return isl1208_i2c_set_regs (client, ISL1208_REG_USR1, buf,
ISL1208_USR_SECTION_LEN);
}
static int isl1208_rtc_proc(struct device *dev, struct seq_file *seq)
{
struct i2c_client *const client = to_i2c_client(dev);
int sr, dtr, atr, usr;
sr = isl1208_i2c_get_sr(client);
if (sr < 0) {
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
return sr;
}
seq_printf(seq, "status_reg\t:%s%s%s%s%s%s (0x%.2x)\n",
(sr & ISL1208_REG_SR_RTCF) ? " RTCF" : "",
(sr & ISL1208_REG_SR_BAT) ? " BAT" : "",
(sr & ISL1208_REG_SR_ALM) ? " ALM" : "",
(sr & ISL1208_REG_SR_WRTC) ? " WRTC" : "",
(sr & ISL1208_REG_SR_XTOSCB) ? " XTOSCB" : "",
(sr & ISL1208_REG_SR_ARST) ? " ARST" : "",
sr);
seq_printf(seq, "batt_status\t: %s\n",
(sr & ISL1208_REG_SR_RTCF) ? "bad" : "okay");
dtr = isl1208_i2c_get_dtr(client);
if (dtr >= 0 -1)
seq_printf(seq, "digital_trim\t: %d ppm\n", dtr);
atr = isl1208_i2c_get_atr(client);
if (atr >= 0)
seq_printf(seq, "analog_trim\t: %d.%.2d pF\n",
atr>>2, (atr&0x3)*25);
usr = isl1208_i2c_get_usr(client);
if (usr >= 0)
seq_printf(seq, "user_data\t: 0x%.4x\n", usr);
return 0;
}
static int isl1208_i2c_read_time(struct i2c_client *client,
struct rtc_time *tm)
{
int sr;
u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
sr = isl1208_i2c_get_sr(client);
if (sr < 0) {
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
return -EIO;
}
sr = isl1208_i2c_read_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);
if (sr < 0) {
dev_err(&client->dev, "%s: reading RTC section failed\n",
__func__);
return sr;
}
tm->tm_sec = BCD2BIN(regs[ISL1208_REG_SC]);
tm->tm_min = BCD2BIN(regs[ISL1208_REG_MN]);
{ /* HR field has a more complex interpretation */
const u8 _hr = regs[ISL1208_REG_HR];
if (_hr & ISL1208_REG_HR_MIL) /* 24h format */
tm->tm_hour = BCD2BIN(_hr & 0x3f);
else { // 12h format
tm->tm_hour = BCD2BIN(_hr & 0x1f);
if (_hr & ISL1208_REG_HR_PM) /* PM flag set */
tm->tm_hour += 12;
}
}
tm->tm_mday = BCD2BIN(regs[ISL1208_REG_DT]);
tm->tm_mon = BCD2BIN(regs[ISL1208_REG_MO]) - 1; /* rtc starts at 1 */
tm->tm_year = BCD2BIN(regs[ISL1208_REG_YR]) + 100;
tm->tm_wday = BCD2BIN(regs[ISL1208_REG_DW]);
return 0;
}
static int isl1208_i2c_read_alarm(struct i2c_client *client,
struct rtc_wkalrm *alarm)
{
struct rtc_time *const tm = &alarm->time;
u8 regs[ISL1208_ALARM_SECTION_LEN] = { 0, };
int sr;
sr = isl1208_i2c_get_sr(client);
if (sr < 0) {
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
return sr;
}
sr = isl1208_i2c_read_regs(client, ISL1208_REG_SCA, regs,
ISL1208_ALARM_SECTION_LEN);
if (sr < 0) {
dev_err(&client->dev, "%s: reading alarm section failed\n",
__func__);
return sr;
}
/* MSB of each alarm register is an enable bit */
tm->tm_sec = BCD2BIN(regs[ISL1208_REG_SCA-ISL1208_REG_SCA] & 0x7f);
tm->tm_min = BCD2BIN(regs[ISL1208_REG_MNA-ISL1208_REG_SCA] & 0x7f);
tm->tm_hour = BCD2BIN(regs[ISL1208_REG_HRA-ISL1208_REG_SCA] & 0x3f);
tm->tm_mday = BCD2BIN(regs[ISL1208_REG_DTA-ISL1208_REG_SCA] & 0x3f);
tm->tm_mon = BCD2BIN(regs[ISL1208_REG_MOA-ISL1208_REG_SCA] & 0x1f)-1;
tm->tm_wday = BCD2BIN(regs[ISL1208_REG_DWA-ISL1208_REG_SCA] & 0x03);
return 0;
}
static int isl1208_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return isl1208_i2c_read_time(to_i2c_client(dev), tm);
}
static int isl1208_i2c_set_time(struct i2c_client *client,
struct rtc_time const *tm)
{
int sr;
u8 regs[ISL1208_RTC_SECTION_LEN] = { 0, };
regs[ISL1208_REG_SC] = BIN2BCD(tm->tm_sec);
regs[ISL1208_REG_MN] = BIN2BCD(tm->tm_min);
regs[ISL1208_REG_HR] = BIN2BCD(tm->tm_hour) | ISL1208_REG_HR_MIL;
regs[ISL1208_REG_DT] = BIN2BCD(tm->tm_mday);
regs[ISL1208_REG_MO] = BIN2BCD(tm->tm_mon + 1);
regs[ISL1208_REG_YR] = BIN2BCD(tm->tm_year - 100);
regs[ISL1208_REG_DW] = BIN2BCD(tm->tm_wday & 7);
sr = isl1208_i2c_get_sr(client);
if (sr < 0) {
dev_err(&client->dev, "%s: reading SR failed\n", __func__);
return sr;
}
/* set WRTC */
sr = i2c_smbus_write_byte_data (client, ISL1208_REG_SR,
sr | ISL1208_REG_SR_WRTC);
if (sr < 0) {
dev_err(&client->dev, "%s: writing SR failed\n", __func__);
return sr;
}
/* write RTC registers */
sr = isl1208_i2c_set_regs(client, 0, regs, ISL1208_RTC_SECTION_LEN);
if (sr < 0) {
dev_err(&client->dev, "%s: writing RTC section failed\n",
__func__);
return sr;
}
/* clear WRTC again */
sr = i2c_smbus_write_byte_data (client, ISL1208_REG_SR,
sr & ~ISL1208_REG_SR_WRTC);
if (sr < 0) {
dev_err(&client->dev, "%s: writing SR failed\n", __func__);
return sr;
}
return 0;
}
static int isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return isl1208_i2c_set_time(to_i2c_client(dev), tm);
}
static int isl1208_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
return isl1208_i2c_read_alarm(to_i2c_client(dev), alarm);
}
static const struct rtc_class_ops isl1208_rtc_ops = {
.proc = isl1208_rtc_proc,
.read_time = isl1208_rtc_read_time,
.set_time = isl1208_rtc_set_time,
.read_alarm = isl1208_rtc_read_alarm,
//.set_alarm = isl1208_rtc_set_alarm,
};
/* sysfs interface */
static ssize_t isl1208_sysfs_show_atrim(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int atr;
atr = isl1208_i2c_get_atr(to_i2c_client(dev));
if (atr < 0)
return atr;
return sprintf(buf, "%d.%.2d pF\n", atr>>2, (atr&0x3)*25);
}
static DEVICE_ATTR(atrim, S_IRUGO, isl1208_sysfs_show_atrim, NULL);
static ssize_t isl1208_sysfs_show_dtrim(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int dtr;
dtr = isl1208_i2c_get_dtr(to_i2c_client(dev));
if (dtr < 0)
return dtr;
return sprintf(buf, "%d ppm\n", dtr);
}
static DEVICE_ATTR(dtrim, S_IRUGO, isl1208_sysfs_show_dtrim, NULL);
static ssize_t isl1208_sysfs_show_usr(struct device *dev,
struct device_attribute *attr,
char *buf)
{
int usr;
usr = isl1208_i2c_get_usr(to_i2c_client(dev));
if (usr < 0)
return usr;
return sprintf(buf, "0x%.4x\n", usr);
}
static ssize_t isl1208_sysfs_store_usr(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int usr = -1;
if (buf[0] == '0' && (buf[1] == 'x' || buf[1] == 'X')) {
if (sscanf(buf, "%x", &usr) != 1)
return -EINVAL;
} else {
if (sscanf(buf, "%d", &usr) != 1)
return -EINVAL;
}
if (usr < 0 || usr > 0xffff)
return -EINVAL;
return isl1208_i2c_set_usr(to_i2c_client(dev), usr) ? -EIO : count;
}
static DEVICE_ATTR(usr, S_IRUGO | S_IWUSR, isl1208_sysfs_show_usr,
isl1208_sysfs_store_usr);
static int
isl1208_probe(struct i2c_adapter *adapter, int addr, int kind)
{
int rc = 0;
struct i2c_client *new_client = NULL;
struct rtc_device *rtc = NULL;
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
rc = -ENODEV;
goto failout;
}
new_client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
if (new_client == NULL) {
rc = -ENOMEM;
goto failout;
}
new_client->addr = addr;
new_client->adapter = adapter;
new_client->driver = &isl1208_driver;
new_client->flags = 0;
strcpy(new_client->name, DRV_NAME);
if (kind < 0) {
rc = isl1208_i2c_validate_client(new_client);
if (rc < 0)
goto failout;
}
rc = i2c_attach_client(new_client);
if (rc < 0)
goto failout;
dev_info(&new_client->dev,
"chip found, driver version " DRV_VERSION "\n");
rtc = rtc_device_register(isl1208_driver.driver.name,
&new_client->dev,
&isl1208_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
rc = PTR_ERR(rtc);
goto failout_detach;
}
i2c_set_clientdata(new_client, rtc);
rc = isl1208_i2c_get_sr(new_client);
if (rc < 0) {
dev_err(&new_client->dev, "reading status failed\n");
goto failout_unregister;
}
if (rc & ISL1208_REG_SR_RTCF)
dev_warn(&new_client->dev, "rtc power failure detected, "
"please set clock.\n");
rc = device_create_file(&new_client->dev, &dev_attr_atrim);
if (rc < 0)
goto failout_unregister;
rc = device_create_file(&new_client->dev, &dev_attr_dtrim);
if (rc < 0)
goto failout_atrim;
rc = device_create_file(&new_client->dev, &dev_attr_usr);
if (rc < 0)
goto failout_dtrim;
return 0;
failout_dtrim:
device_remove_file(&new_client->dev, &dev_attr_dtrim);
failout_atrim:
device_remove_file(&new_client->dev, &dev_attr_atrim);
failout_unregister:
rtc_device_unregister(rtc);
failout_detach:
i2c_detach_client(new_client);
failout:
kfree(new_client);
return rc;
}
static int
isl1208_attach_adapter (struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, isl1208_probe);
}
static int
isl1208_detach_client(struct i2c_client *client)
{
int rc;
struct rtc_device *const rtc = i2c_get_clientdata(client);
if (rtc)
rtc_device_unregister(rtc); /* do we need to kfree? */
rc = i2c_detach_client(client);
if (rc)
return rc;
kfree(client);
return 0;
}
/* module management */
static int __init isl1208_init(void)
{
return i2c_add_driver(&isl1208_driver);
}
static void __exit isl1208_exit(void)
{
i2c_del_driver(&isl1208_driver);
}
MODULE_AUTHOR("Herbert Valerio Riedel <hvr@gnu.org>");
MODULE_DESCRIPTION("Intersil ISL1208 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
module_init(isl1208_init);
module_exit(isl1208_exit);

201
drivers/rtc/rtc-lib.c Normal file
View File

@@ -0,0 +1,201 @@
/*
* rtc and date/time utility functions
*
* Copyright (C) 2005-06 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* based on arch/arm/common/rtctime.c and other bits
*
* 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/module.h>
#include <linux/rtc.h>
static const unsigned char rtc_days_in_month[] = {
31, 28, 31, 30, 31, 30, 31, 31, 30, 31, 30, 31
};
static const unsigned short rtc_ydays[2][13] = {
/* Normal years */
{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },
/* Leap years */
{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};
#define LEAPS_THRU_END_OF(y) ((y)/4 - (y)/100 + (y)/400)
#define LEAP_YEAR(year) ((!(year % 4) && (year % 100)) || !(year % 400))
/*
* The number of days in the month.
*/
int rtc_month_days(unsigned int month, unsigned int year)
{
return rtc_days_in_month[month] + (LEAP_YEAR(year) && month == 1);
}
EXPORT_SYMBOL(rtc_month_days);
/*
* The number of days since January 1. (0 to 365)
*/
int rtc_year_days(unsigned int day, unsigned int month, unsigned int year)
{
return rtc_ydays[LEAP_YEAR(year)][month] + day-1;
}
EXPORT_SYMBOL(rtc_year_days);
/*
* Convert seconds since 01-01-1970 00:00:00 to Gregorian date.
*/
void rtc_time_to_tm(unsigned long time, struct rtc_time *tm)
{
register int days, month, year;
days = time / 86400;
time -= days * 86400;
/* day of the week, 1970-01-01 was a Thursday */
tm->tm_wday = (days + 4) % 7;
year = 1970 + days / 365;
days -= (year - 1970) * 365
+ LEAPS_THRU_END_OF(year - 1)
- LEAPS_THRU_END_OF(1970 - 1);
if (days < 0) {
year -= 1;
days += 365 + LEAP_YEAR(year);
}
tm->tm_year = year - 1900;
tm->tm_yday = days + 1;
for (month = 0; month < 11; month++) {
int newdays;
newdays = days - rtc_month_days(month, year);
if (newdays < 0)
break;
days = newdays;
}
tm->tm_mon = month;
tm->tm_mday = days + 1;
tm->tm_hour = time / 3600;
time -= tm->tm_hour * 3600;
tm->tm_min = time / 60;
tm->tm_sec = time - tm->tm_min * 60;
}
EXPORT_SYMBOL(rtc_time_to_tm);
/*
* Does the rtc_time represent a valid date/time?
*/
int rtc_valid_tm(struct rtc_time *tm)
{
if (tm->tm_year < 70
|| ((unsigned)tm->tm_mon) >= 12
|| tm->tm_mday < 1
|| tm->tm_mday > rtc_month_days(tm->tm_mon, tm->tm_year + 1900)
|| ((unsigned)tm->tm_hour) >= 24
|| ((unsigned)tm->tm_min) >= 60
|| ((unsigned)tm->tm_sec) >= 60)
return -EINVAL;
return 0;
}
EXPORT_SYMBOL(rtc_valid_tm);
/*
* Convert Gregorian date to seconds since 01-01-1970 00:00:00.
*/
int rtc_tm_to_time(struct rtc_time *tm, unsigned long *time)
{
*time = mktime(tm->tm_year + 1900, tm->tm_mon + 1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
return 0;
}
EXPORT_SYMBOL(rtc_tm_to_time);
/* Merge the valid (i.e. non-negative) fields of alarm into the current
* time. If the valid alarm fields are earlier than the equivalent
* fields in the time, carry one into the least significant invalid
* field, so that the alarm expiry is in the future. It assumes that the
* least significant invalid field is more significant than the most
* significant valid field, and that the seconds field is valid.
*
* This is used by alarms that take relative (rather than absolute)
* times, and/or have a simple binary second counter instead of
* day/hour/minute/sec registers.
*/
void rtc_merge_alarm(struct rtc_time *now, struct rtc_time *alarm)
{
int *alarmp = &alarm->tm_sec;
int *timep = &now->tm_sec;
int carry_into, i;
/* Ignore everything past the 6th element (tm_year). */
for (i = 5; i > 0; i--) {
if (alarmp[i] < 0)
alarmp[i] = timep[i];
else
break;
}
/* No carry needed if all fields are valid. */
if (i == 5)
return;
for (carry_into = i + 1; i >= 0; i--) {
if (alarmp[i] < timep[i])
break;
if (alarmp[i] > timep[i])
return;
}
switch (carry_into) {
case 1:
alarm->tm_min++;
if (alarm->tm_min < 60)
return;
alarm->tm_min = 0;
/* fall-through */
case 2:
alarm->tm_hour++;
if (alarm->tm_hour < 60)
return;
alarm->tm_hour = 0;
/* fall-through */
case 3:
alarm->tm_mday++;
if (alarm->tm_mday <= rtc_days_in_month[alarm->tm_mon])
return;
alarm->tm_mday = 1;
/* fall-through */
case 4:
alarm->tm_mon++;
if (alarm->tm_mon <= 12)
return;
alarm->tm_mon = 1;
/* fall-through */
case 5:
alarm->tm_year++;
}
}
EXPORT_SYMBOL(rtc_merge_alarm);
MODULE_LICENSE("GPL");

204
drivers/rtc/rtc-m48t86.c Normal file
View File

@@ -0,0 +1,204 @@
/*
* ST M48T86 / Dallas DS12887 RTC driver
* Copyright (c) 2006 Tower Technologies
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* 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.
*
* This drivers only supports the clock running in BCD and 24H mode.
* If it will be ever adapted to binary and 12H mode, care must be taken
* to not introduce bugs.
*/
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
#include <linux/m48t86.h>
#include <linux/bcd.h>
#define M48T86_REG_SEC 0x00
#define M48T86_REG_SECALRM 0x01
#define M48T86_REG_MIN 0x02
#define M48T86_REG_MINALRM 0x03
#define M48T86_REG_HOUR 0x04
#define M48T86_REG_HOURALRM 0x05
#define M48T86_REG_DOW 0x06 /* 1 = sunday */
#define M48T86_REG_DOM 0x07
#define M48T86_REG_MONTH 0x08 /* 1 - 12 */
#define M48T86_REG_YEAR 0x09 /* 0 - 99 */
#define M48T86_REG_A 0x0A
#define M48T86_REG_B 0x0B
#define M48T86_REG_C 0x0C
#define M48T86_REG_D 0x0D
#define M48T86_REG_B_H24 (1 << 1)
#define M48T86_REG_B_DM (1 << 2)
#define M48T86_REG_B_SET (1 << 7)
#define M48T86_REG_D_VRT (1 << 7)
#define DRV_VERSION "0.1"
static int m48t86_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
unsigned char reg;
struct platform_device *pdev = to_platform_device(dev);
struct m48t86_ops *ops = pdev->dev.platform_data;
reg = ops->readbyte(M48T86_REG_B);
if (reg & M48T86_REG_B_DM) {
/* data (binary) mode */
tm->tm_sec = ops->readbyte(M48T86_REG_SEC);
tm->tm_min = ops->readbyte(M48T86_REG_MIN);
tm->tm_hour = ops->readbyte(M48T86_REG_HOUR) & 0x3F;
tm->tm_mday = ops->readbyte(M48T86_REG_DOM);
/* tm_mon is 0-11 */
tm->tm_mon = ops->readbyte(M48T86_REG_MONTH) - 1;
tm->tm_year = ops->readbyte(M48T86_REG_YEAR) + 100;
tm->tm_wday = ops->readbyte(M48T86_REG_DOW);
} else {
/* bcd mode */
tm->tm_sec = BCD2BIN(ops->readbyte(M48T86_REG_SEC));
tm->tm_min = BCD2BIN(ops->readbyte(M48T86_REG_MIN));
tm->tm_hour = BCD2BIN(ops->readbyte(M48T86_REG_HOUR) & 0x3F);
tm->tm_mday = BCD2BIN(ops->readbyte(M48T86_REG_DOM));
/* tm_mon is 0-11 */
tm->tm_mon = BCD2BIN(ops->readbyte(M48T86_REG_MONTH)) - 1;
tm->tm_year = BCD2BIN(ops->readbyte(M48T86_REG_YEAR)) + 100;
tm->tm_wday = BCD2BIN(ops->readbyte(M48T86_REG_DOW));
}
/* correct the hour if the clock is in 12h mode */
if (!(reg & M48T86_REG_B_H24))
if (ops->readbyte(M48T86_REG_HOUR) & 0x80)
tm->tm_hour += 12;
return 0;
}
static int m48t86_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned char reg;
struct platform_device *pdev = to_platform_device(dev);
struct m48t86_ops *ops = pdev->dev.platform_data;
reg = ops->readbyte(M48T86_REG_B);
/* update flag and 24h mode */
reg |= M48T86_REG_B_SET | M48T86_REG_B_H24;
ops->writebyte(reg, M48T86_REG_B);
if (reg & M48T86_REG_B_DM) {
/* data (binary) mode */
ops->writebyte(tm->tm_sec, M48T86_REG_SEC);
ops->writebyte(tm->tm_min, M48T86_REG_MIN);
ops->writebyte(tm->tm_hour, M48T86_REG_HOUR);
ops->writebyte(tm->tm_mday, M48T86_REG_DOM);
ops->writebyte(tm->tm_mon + 1, M48T86_REG_MONTH);
ops->writebyte(tm->tm_year % 100, M48T86_REG_YEAR);
ops->writebyte(tm->tm_wday, M48T86_REG_DOW);
} else {
/* bcd mode */
ops->writebyte(BIN2BCD(tm->tm_sec), M48T86_REG_SEC);
ops->writebyte(BIN2BCD(tm->tm_min), M48T86_REG_MIN);
ops->writebyte(BIN2BCD(tm->tm_hour), M48T86_REG_HOUR);
ops->writebyte(BIN2BCD(tm->tm_mday), M48T86_REG_DOM);
ops->writebyte(BIN2BCD(tm->tm_mon + 1), M48T86_REG_MONTH);
ops->writebyte(BIN2BCD(tm->tm_year % 100), M48T86_REG_YEAR);
ops->writebyte(BIN2BCD(tm->tm_wday), M48T86_REG_DOW);
}
/* update ended */
reg &= ~M48T86_REG_B_SET;
ops->writebyte(reg, M48T86_REG_B);
return 0;
}
static int m48t86_rtc_proc(struct device *dev, struct seq_file *seq)
{
unsigned char reg;
struct platform_device *pdev = to_platform_device(dev);
struct m48t86_ops *ops = pdev->dev.platform_data;
reg = ops->readbyte(M48T86_REG_B);
seq_printf(seq, "mode\t\t: %s\n",
(reg & M48T86_REG_B_DM) ? "binary" : "bcd");
reg = ops->readbyte(M48T86_REG_D);
seq_printf(seq, "battery\t\t: %s\n",
(reg & M48T86_REG_D_VRT) ? "ok" : "exhausted");
return 0;
}
static const struct rtc_class_ops m48t86_rtc_ops = {
.read_time = m48t86_rtc_read_time,
.set_time = m48t86_rtc_set_time,
.proc = m48t86_rtc_proc,
};
static int __devinit m48t86_rtc_probe(struct platform_device *dev)
{
unsigned char reg;
struct m48t86_ops *ops = dev->dev.platform_data;
struct rtc_device *rtc = rtc_device_register("m48t86",
&dev->dev, &m48t86_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
platform_set_drvdata(dev, rtc);
/* read battery status */
reg = ops->readbyte(M48T86_REG_D);
dev_info(&dev->dev, "battery %s\n",
(reg & M48T86_REG_D_VRT) ? "ok" : "exhausted");
return 0;
}
static int __devexit m48t86_rtc_remove(struct platform_device *dev)
{
struct rtc_device *rtc = platform_get_drvdata(dev);
if (rtc)
rtc_device_unregister(rtc);
platform_set_drvdata(dev, NULL);
return 0;
}
static struct platform_driver m48t86_rtc_platform_driver = {
.driver = {
.name = "rtc-m48t86",
.owner = THIS_MODULE,
},
.probe = m48t86_rtc_probe,
.remove = __devexit_p(m48t86_rtc_remove),
};
static int __init m48t86_rtc_init(void)
{
return platform_driver_register(&m48t86_rtc_platform_driver);
}
static void __exit m48t86_rtc_exit(void)
{
platform_driver_unregister(&m48t86_rtc_platform_driver);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("M48T86 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
module_init(m48t86_rtc_init);
module_exit(m48t86_rtc_exit);

285
drivers/rtc/rtc-max6902.c Normal file
View File

@@ -0,0 +1,285 @@
/* drivers/rtc/rtc-max6902.c
*
* Copyright (C) 2006 8D Technologies inc.
* Copyright (C) 2004 Compulab Ltd.
*
* 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.
*
* Driver for MAX6902 spi RTC
*
* Changelog:
*
* 24-May-2006: Raphael Assenat <raph@8d.com>
* - Major rework
* Converted to rtc_device and uses the SPI layer.
*
* ??-???-2005: Someone at Compulab
* - Initial driver creation.
*/
#include <linux/module.h>
#include <linux/version.h>
#include <linux/kernel.h>
#include <linux/platform_device.h>
#include <linux/init.h>
#include <linux/rtc.h>
#include <linux/spi/spi.h>
#include <linux/bcd.h>
#include <linux/delay.h>
#define MAX6902_REG_SECONDS 0x01
#define MAX6902_REG_MINUTES 0x03
#define MAX6902_REG_HOURS 0x05
#define MAX6902_REG_DATE 0x07
#define MAX6902_REG_MONTH 0x09
#define MAX6902_REG_DAY 0x0B
#define MAX6902_REG_YEAR 0x0D
#define MAX6902_REG_CONTROL 0x0F
#define MAX6902_REG_CENTURY 0x13
#undef MAX6902_DEBUG
struct max6902 {
struct rtc_device *rtc;
u8 buf[9]; /* Burst read cmd + 8 registers */
u8 tx_buf[2];
u8 rx_buf[2];
};
static void max6902_set_reg(struct device *dev, unsigned char address,
unsigned char data)
{
struct spi_device *spi = to_spi_device(dev);
unsigned char buf[2];
/* MSB must be '0' to write */
buf[0] = address & 0x7f;
buf[1] = data;
spi_write(spi, buf, 2);
}
static int max6902_get_reg(struct device *dev, unsigned char address,
unsigned char *data)
{
struct spi_device *spi = to_spi_device(dev);
struct max6902 *chip = dev_get_drvdata(dev);
struct spi_message message;
struct spi_transfer xfer;
int status;
if (!data)
return -EINVAL;
/* Build our spi message */
spi_message_init(&message);
memset(&xfer, 0, sizeof(xfer));
xfer.len = 2;
/* Can tx_buf and rx_buf be equal? The doc in spi.h is not sure... */
xfer.tx_buf = chip->tx_buf;
xfer.rx_buf = chip->rx_buf;
/* Set MSB to indicate read */
chip->tx_buf[0] = address | 0x80;
spi_message_add_tail(&xfer, &message);
/* do the i/o */
status = spi_sync(spi, &message);
if (status == 0)
status = message.status;
else
return status;
*data = chip->rx_buf[1];
return status;
}
static int max6902_get_datetime(struct device *dev, struct rtc_time *dt)
{
unsigned char tmp;
int century;
int err;
struct spi_device *spi = to_spi_device(dev);
struct max6902 *chip = dev_get_drvdata(dev);
struct spi_message message;
struct spi_transfer xfer;
int status;
err = max6902_get_reg(dev, MAX6902_REG_CENTURY, &tmp);
if (err)
return err;
/* build the message */
spi_message_init(&message);
memset(&xfer, 0, sizeof(xfer));
xfer.len = 1 + 7; /* Burst read command + 7 registers */
xfer.tx_buf = chip->buf;
xfer.rx_buf = chip->buf;
chip->buf[0] = 0xbf; /* Burst read */
spi_message_add_tail(&xfer, &message);
/* do the i/o */
status = spi_sync(spi, &message);
if (status == 0)
status = message.status;
else
return status;
/* The chip sends data in this order:
* Seconds, Minutes, Hours, Date, Month, Day, Year */
dt->tm_sec = BCD2BIN(chip->buf[1]);
dt->tm_min = BCD2BIN(chip->buf[2]);
dt->tm_hour = BCD2BIN(chip->buf[3]);
dt->tm_mday = BCD2BIN(chip->buf[4]);
dt->tm_mon = BCD2BIN(chip->buf[5]) - 1;
dt->tm_wday = BCD2BIN(chip->buf[6]);
dt->tm_year = BCD2BIN(chip->buf[7]);
century = BCD2BIN(tmp) * 100;
dt->tm_year += century;
dt->tm_year -= 1900;
#ifdef MAX6902_DEBUG
printk("\n%s : Read RTC values\n",__FUNCTION__);
printk("tm_hour: %i\n",dt->tm_hour);
printk("tm_min : %i\n",dt->tm_min);
printk("tm_sec : %i\n",dt->tm_sec);
printk("tm_year: %i\n",dt->tm_year);
printk("tm_mon : %i\n",dt->tm_mon);
printk("tm_mday: %i\n",dt->tm_mday);
printk("tm_wday: %i\n",dt->tm_wday);
#endif
return 0;
}
static int max6902_set_datetime(struct device *dev, struct rtc_time *dt)
{
dt->tm_year = dt->tm_year+1900;
#ifdef MAX6902_DEBUG
printk("\n%s : Setting RTC values\n",__FUNCTION__);
printk("tm_sec : %i\n",dt->tm_sec);
printk("tm_min : %i\n",dt->tm_min);
printk("tm_hour: %i\n",dt->tm_hour);
printk("tm_mday: %i\n",dt->tm_mday);
printk("tm_wday: %i\n",dt->tm_wday);
printk("tm_year: %i\n",dt->tm_year);
#endif
/* Remove write protection */
max6902_set_reg(dev, 0xF, 0);
max6902_set_reg(dev, 0x01, BIN2BCD(dt->tm_sec));
max6902_set_reg(dev, 0x03, BIN2BCD(dt->tm_min));
max6902_set_reg(dev, 0x05, BIN2BCD(dt->tm_hour));
max6902_set_reg(dev, 0x07, BIN2BCD(dt->tm_mday));
max6902_set_reg(dev, 0x09, BIN2BCD(dt->tm_mon+1));
max6902_set_reg(dev, 0x0B, BIN2BCD(dt->tm_wday));
max6902_set_reg(dev, 0x0D, BIN2BCD(dt->tm_year%100));
max6902_set_reg(dev, 0x13, BIN2BCD(dt->tm_year/100));
/* Compulab used a delay here. However, the datasheet
* does not mention a delay being required anywhere... */
/* delay(2000); */
/* Write protect */
max6902_set_reg(dev, 0xF, 0x80);
return 0;
}
static int max6902_read_time(struct device *dev, struct rtc_time *tm)
{
return max6902_get_datetime(dev, tm);
}
static int max6902_set_time(struct device *dev, struct rtc_time *tm)
{
return max6902_set_datetime(dev, tm);
}
static const struct rtc_class_ops max6902_rtc_ops = {
.read_time = max6902_read_time,
.set_time = max6902_set_time,
};
static int __devinit max6902_probe(struct spi_device *spi)
{
struct rtc_device *rtc;
unsigned char tmp;
struct max6902 *chip;
int res;
rtc = rtc_device_register("max6902",
&spi->dev, &max6902_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
spi->mode = SPI_MODE_3;
spi->bits_per_word = 8;
spi_setup(spi);
chip = kzalloc(sizeof *chip, GFP_KERNEL);
if (!chip) {
rtc_device_unregister(rtc);
return -ENOMEM;
}
chip->rtc = rtc;
dev_set_drvdata(&spi->dev, chip);
res = max6902_get_reg(&spi->dev, MAX6902_REG_SECONDS, &tmp);
if (res) {
rtc_device_unregister(rtc);
return res;
}
return 0;
}
static int __devexit max6902_remove(struct spi_device *spi)
{
struct max6902 *chip = platform_get_drvdata(spi);
struct rtc_device *rtc = chip->rtc;
if (rtc)
rtc_device_unregister(rtc);
kfree(chip);
return 0;
}
static struct spi_driver max6902_driver = {
.driver = {
.name = "max6902",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = max6902_probe,
.remove = __devexit_p(max6902_remove),
};
static __init int max6902_init(void)
{
printk("max6902 spi driver\n");
return spi_register_driver(&max6902_driver);
}
module_init(max6902_init);
static __exit void max6902_exit(void)
{
spi_unregister_driver(&max6902_driver);
}
module_exit(max6902_exit);
MODULE_DESCRIPTION ("max6902 spi RTC driver");
MODULE_AUTHOR ("Raphael Assenat");
MODULE_LICENSE ("GPL");

571
drivers/rtc/rtc-omap.c Normal file
View File

@@ -0,0 +1,571 @@
/*
* TI OMAP1 Real Time Clock interface for Linux
*
* Copyright (C) 2003 MontaVista Software, Inc.
* Author: George G. Davis <gdavis@mvista.com> or <source@mvista.com>
*
* Copyright (C) 2006 David Brownell (new RTC framework)
*
* 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/kernel.h>
#include <linux/init.h>
#include <linux/module.h>
#include <linux/ioport.h>
#include <linux/delay.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/platform_device.h>
#include <asm/io.h>
#include <asm/mach/time.h>
/* The OMAP1 RTC is a year/month/day/hours/minutes/seconds BCD clock
* with century-range alarm matching, driven by the 32kHz clock.
*
* The main user-visible ways it differs from PC RTCs are by omitting
* "don't care" alarm fields and sub-second periodic IRQs, and having
* an autoadjust mechanism to calibrate to the true oscillator rate.
*
* Board-specific wiring options include using split power mode with
* RTC_OFF_NOFF used as the reset signal (so the RTC won't be reset),
* and wiring RTC_WAKE_INT (so the RTC alarm can wake the system from
* low power modes). See the BOARD-SPECIFIC CUSTOMIZATION comment.
*/
#define OMAP_RTC_BASE 0xfffb4800
/* RTC registers */
#define OMAP_RTC_SECONDS_REG 0x00
#define OMAP_RTC_MINUTES_REG 0x04
#define OMAP_RTC_HOURS_REG 0x08
#define OMAP_RTC_DAYS_REG 0x0C
#define OMAP_RTC_MONTHS_REG 0x10
#define OMAP_RTC_YEARS_REG 0x14
#define OMAP_RTC_WEEKS_REG 0x18
#define OMAP_RTC_ALARM_SECONDS_REG 0x20
#define OMAP_RTC_ALARM_MINUTES_REG 0x24
#define OMAP_RTC_ALARM_HOURS_REG 0x28
#define OMAP_RTC_ALARM_DAYS_REG 0x2c
#define OMAP_RTC_ALARM_MONTHS_REG 0x30
#define OMAP_RTC_ALARM_YEARS_REG 0x34
#define OMAP_RTC_CTRL_REG 0x40
#define OMAP_RTC_STATUS_REG 0x44
#define OMAP_RTC_INTERRUPTS_REG 0x48
#define OMAP_RTC_COMP_LSB_REG 0x4c
#define OMAP_RTC_COMP_MSB_REG 0x50
#define OMAP_RTC_OSC_REG 0x54
/* OMAP_RTC_CTRL_REG bit fields: */
#define OMAP_RTC_CTRL_SPLIT (1<<7)
#define OMAP_RTC_CTRL_DISABLE (1<<6)
#define OMAP_RTC_CTRL_SET_32_COUNTER (1<<5)
#define OMAP_RTC_CTRL_TEST (1<<4)
#define OMAP_RTC_CTRL_MODE_12_24 (1<<3)
#define OMAP_RTC_CTRL_AUTO_COMP (1<<2)
#define OMAP_RTC_CTRL_ROUND_30S (1<<1)
#define OMAP_RTC_CTRL_STOP (1<<0)
/* OMAP_RTC_STATUS_REG bit fields: */
#define OMAP_RTC_STATUS_POWER_UP (1<<7)
#define OMAP_RTC_STATUS_ALARM (1<<6)
#define OMAP_RTC_STATUS_1D_EVENT (1<<5)
#define OMAP_RTC_STATUS_1H_EVENT (1<<4)
#define OMAP_RTC_STATUS_1M_EVENT (1<<3)
#define OMAP_RTC_STATUS_1S_EVENT (1<<2)
#define OMAP_RTC_STATUS_RUN (1<<1)
#define OMAP_RTC_STATUS_BUSY (1<<0)
/* OMAP_RTC_INTERRUPTS_REG bit fields: */
#define OMAP_RTC_INTERRUPTS_IT_ALARM (1<<3)
#define OMAP_RTC_INTERRUPTS_IT_TIMER (1<<2)
#define rtc_read(addr) omap_readb(OMAP_RTC_BASE + (addr))
#define rtc_write(val, addr) omap_writeb(val, OMAP_RTC_BASE + (addr))
/* platform_bus isn't hotpluggable, so for static linkage it'd be safe
* to get rid of probe() and remove() code ... too bad the driver struct
* remembers probe(), that's about 25% of the runtime footprint!!
*/
#ifndef MODULE
#undef __devexit
#undef __devexit_p
#define __devexit __exit
#define __devexit_p __exit_p
#endif
/* we rely on the rtc framework to handle locking (rtc->ops_lock),
* so the only other requirement is that register accesses which
* require BUSY to be clear are made with IRQs locally disabled
*/
static void rtc_wait_not_busy(void)
{
int count = 0;
u8 status;
/* BUSY may stay active for 1/32768 second (~30 usec) */
for (count = 0; count < 50; count++) {
status = rtc_read(OMAP_RTC_STATUS_REG);
if ((status & (u8)OMAP_RTC_STATUS_BUSY) == 0)
break;
udelay(1);
}
/* now we have ~15 usec to read/write various registers */
}
static irqreturn_t rtc_irq(int irq, void *class_dev)
{
unsigned long events = 0;
u8 irq_data;
irq_data = rtc_read(OMAP_RTC_STATUS_REG);
/* alarm irq? */
if (irq_data & OMAP_RTC_STATUS_ALARM) {
rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);
events |= RTC_IRQF | RTC_AF;
}
/* 1/sec periodic/update irq? */
if (irq_data & OMAP_RTC_STATUS_1S_EVENT)
events |= RTC_IRQF | RTC_UF;
rtc_update_irq(class_dev, 1, events);
return IRQ_HANDLED;
}
#ifdef CONFIG_RTC_INTF_DEV
static int
omap_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
u8 reg;
switch (cmd) {
case RTC_AIE_OFF:
case RTC_AIE_ON:
case RTC_UIE_OFF:
case RTC_UIE_ON:
break;
default:
return -ENOIOCTLCMD;
}
local_irq_disable();
rtc_wait_not_busy();
reg = rtc_read(OMAP_RTC_INTERRUPTS_REG);
switch (cmd) {
/* AIE = Alarm Interrupt Enable */
case RTC_AIE_OFF:
reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM;
break;
case RTC_AIE_ON:
reg |= OMAP_RTC_INTERRUPTS_IT_ALARM;
break;
/* UIE = Update Interrupt Enable (1/second) */
case RTC_UIE_OFF:
reg &= ~OMAP_RTC_INTERRUPTS_IT_TIMER;
break;
case RTC_UIE_ON:
reg |= OMAP_RTC_INTERRUPTS_IT_TIMER;
break;
}
rtc_wait_not_busy();
rtc_write(reg, OMAP_RTC_INTERRUPTS_REG);
local_irq_enable();
return 0;
}
#else
#define omap_rtc_ioctl NULL
#endif
/* this hardware doesn't support "don't care" alarm fields */
static int tm2bcd(struct rtc_time *tm)
{
if (rtc_valid_tm(tm) != 0)
return -EINVAL;
tm->tm_sec = BIN2BCD(tm->tm_sec);
tm->tm_min = BIN2BCD(tm->tm_min);
tm->tm_hour = BIN2BCD(tm->tm_hour);
tm->tm_mday = BIN2BCD(tm->tm_mday);
tm->tm_mon = BIN2BCD(tm->tm_mon + 1);
/* epoch == 1900 */
if (tm->tm_year < 100 || tm->tm_year > 199)
return -EINVAL;
tm->tm_year = BIN2BCD(tm->tm_year - 100);
return 0;
}
static void bcd2tm(struct rtc_time *tm)
{
tm->tm_sec = BCD2BIN(tm->tm_sec);
tm->tm_min = BCD2BIN(tm->tm_min);
tm->tm_hour = BCD2BIN(tm->tm_hour);
tm->tm_mday = BCD2BIN(tm->tm_mday);
tm->tm_mon = BCD2BIN(tm->tm_mon) - 1;
/* epoch == 1900 */
tm->tm_year = BCD2BIN(tm->tm_year) + 100;
}
static int omap_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
/* we don't report wday/yday/isdst ... */
local_irq_disable();
rtc_wait_not_busy();
tm->tm_sec = rtc_read(OMAP_RTC_SECONDS_REG);
tm->tm_min = rtc_read(OMAP_RTC_MINUTES_REG);
tm->tm_hour = rtc_read(OMAP_RTC_HOURS_REG);
tm->tm_mday = rtc_read(OMAP_RTC_DAYS_REG);
tm->tm_mon = rtc_read(OMAP_RTC_MONTHS_REG);
tm->tm_year = rtc_read(OMAP_RTC_YEARS_REG);
local_irq_enable();
bcd2tm(tm);
return 0;
}
static int omap_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
if (tm2bcd(tm) < 0)
return -EINVAL;
local_irq_disable();
rtc_wait_not_busy();
rtc_write(tm->tm_year, OMAP_RTC_YEARS_REG);
rtc_write(tm->tm_mon, OMAP_RTC_MONTHS_REG);
rtc_write(tm->tm_mday, OMAP_RTC_DAYS_REG);
rtc_write(tm->tm_hour, OMAP_RTC_HOURS_REG);
rtc_write(tm->tm_min, OMAP_RTC_MINUTES_REG);
rtc_write(tm->tm_sec, OMAP_RTC_SECONDS_REG);
local_irq_enable();
return 0;
}
static int omap_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
local_irq_disable();
rtc_wait_not_busy();
alm->time.tm_sec = rtc_read(OMAP_RTC_ALARM_SECONDS_REG);
alm->time.tm_min = rtc_read(OMAP_RTC_ALARM_MINUTES_REG);
alm->time.tm_hour = rtc_read(OMAP_RTC_ALARM_HOURS_REG);
alm->time.tm_mday = rtc_read(OMAP_RTC_ALARM_DAYS_REG);
alm->time.tm_mon = rtc_read(OMAP_RTC_ALARM_MONTHS_REG);
alm->time.tm_year = rtc_read(OMAP_RTC_ALARM_YEARS_REG);
local_irq_enable();
bcd2tm(&alm->time);
alm->enabled = !!(rtc_read(OMAP_RTC_INTERRUPTS_REG)
& OMAP_RTC_INTERRUPTS_IT_ALARM);
return 0;
}
static int omap_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
{
u8 reg;
/* Much userspace code uses RTC_ALM_SET, thus "don't care" for
* day/month/year specifies alarms up to 24 hours in the future.
* So we need to handle that ... but let's ignore the "don't care"
* values for hours/minutes/seconds.
*/
if (alm->time.tm_mday <= 0
&& alm->time.tm_mon < 0
&& alm->time.tm_year < 0) {
struct rtc_time tm;
unsigned long now, then;
omap_rtc_read_time(dev, &tm);
rtc_tm_to_time(&tm, &now);
alm->time.tm_mday = tm.tm_mday;
alm->time.tm_mon = tm.tm_mon;
alm->time.tm_year = tm.tm_year;
rtc_tm_to_time(&alm->time, &then);
/* sometimes the alarm wraps into tomorrow */
if (then < now) {
rtc_time_to_tm(now + 24 * 60 * 60, &tm);
alm->time.tm_mday = tm.tm_mday;
alm->time.tm_mon = tm.tm_mon;
alm->time.tm_year = tm.tm_year;
}
}
if (tm2bcd(&alm->time) < 0)
return -EINVAL;
local_irq_disable();
rtc_wait_not_busy();
rtc_write(alm->time.tm_year, OMAP_RTC_ALARM_YEARS_REG);
rtc_write(alm->time.tm_mon, OMAP_RTC_ALARM_MONTHS_REG);
rtc_write(alm->time.tm_mday, OMAP_RTC_ALARM_DAYS_REG);
rtc_write(alm->time.tm_hour, OMAP_RTC_ALARM_HOURS_REG);
rtc_write(alm->time.tm_min, OMAP_RTC_ALARM_MINUTES_REG);
rtc_write(alm->time.tm_sec, OMAP_RTC_ALARM_SECONDS_REG);
reg = rtc_read(OMAP_RTC_INTERRUPTS_REG);
if (alm->enabled)
reg |= OMAP_RTC_INTERRUPTS_IT_ALARM;
else
reg &= ~OMAP_RTC_INTERRUPTS_IT_ALARM;
rtc_write(reg, OMAP_RTC_INTERRUPTS_REG);
local_irq_enable();
return 0;
}
static struct rtc_class_ops omap_rtc_ops = {
.ioctl = omap_rtc_ioctl,
.read_time = omap_rtc_read_time,
.set_time = omap_rtc_set_time,
.read_alarm = omap_rtc_read_alarm,
.set_alarm = omap_rtc_set_alarm,
};
static int omap_rtc_alarm;
static int omap_rtc_timer;
static int __devinit omap_rtc_probe(struct platform_device *pdev)
{
struct resource *res, *mem;
struct rtc_device *rtc;
u8 reg, new_ctrl;
omap_rtc_timer = platform_get_irq(pdev, 0);
if (omap_rtc_timer <= 0) {
pr_debug("%s: no update irq?\n", pdev->name);
return -ENOENT;
}
omap_rtc_alarm = platform_get_irq(pdev, 1);
if (omap_rtc_alarm <= 0) {
pr_debug("%s: no alarm irq?\n", pdev->name);
return -ENOENT;
}
/* NOTE: using static mapping for RTC registers */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res && res->start != OMAP_RTC_BASE) {
pr_debug("%s: RTC registers at %08x, expected %08x\n",
pdev->name, (unsigned) res->start, OMAP_RTC_BASE);
return -ENOENT;
}
if (res)
mem = request_mem_region(res->start,
res->end - res->start + 1,
pdev->name);
else
mem = NULL;
if (!mem) {
pr_debug("%s: RTC registers at %08x are not free\n",
pdev->name, OMAP_RTC_BASE);
return -EBUSY;
}
rtc = rtc_device_register(pdev->name, &pdev->dev,
&omap_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
pr_debug("%s: can't register RTC device, err %ld\n",
pdev->name, PTR_ERR(rtc));
goto fail;
}
platform_set_drvdata(pdev, rtc);
class_set_devdata(&rtc->class_dev, mem);
/* clear pending irqs, and set 1/second periodic,
* which we'll use instead of update irqs
*/
rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
/* clear old status */
reg = rtc_read(OMAP_RTC_STATUS_REG);
if (reg & (u8) OMAP_RTC_STATUS_POWER_UP) {
pr_info("%s: RTC power up reset detected\n",
pdev->name);
rtc_write(OMAP_RTC_STATUS_POWER_UP, OMAP_RTC_STATUS_REG);
}
if (reg & (u8) OMAP_RTC_STATUS_ALARM)
rtc_write(OMAP_RTC_STATUS_ALARM, OMAP_RTC_STATUS_REG);
/* handle periodic and alarm irqs */
if (request_irq(omap_rtc_timer, rtc_irq, IRQF_DISABLED,
rtc->class_dev.class_id, &rtc->class_dev)) {
pr_debug("%s: RTC timer interrupt IRQ%d already claimed\n",
pdev->name, omap_rtc_timer);
goto fail0;
}
if (request_irq(omap_rtc_alarm, rtc_irq, IRQF_DISABLED,
rtc->class_dev.class_id, &rtc->class_dev)) {
pr_debug("%s: RTC alarm interrupt IRQ%d already claimed\n",
pdev->name, omap_rtc_alarm);
goto fail1;
}
/* On boards with split power, RTC_ON_NOFF won't reset the RTC */
reg = rtc_read(OMAP_RTC_CTRL_REG);
if (reg & (u8) OMAP_RTC_CTRL_STOP)
pr_info("%s: already running\n", pdev->name);
/* force to 24 hour mode */
new_ctrl = reg & ~(OMAP_RTC_CTRL_SPLIT|OMAP_RTC_CTRL_AUTO_COMP);
new_ctrl |= OMAP_RTC_CTRL_STOP;
/* BOARD-SPECIFIC CUSTOMIZATION CAN GO HERE:
*
* - Boards wired so that RTC_WAKE_INT does something, and muxed
* right (W13_1610_RTC_WAKE_INT is the default after chip reset),
* should initialize the device wakeup flag appropriately.
*
* - Boards wired so RTC_ON_nOFF is used as the reset signal,
* rather than nPWRON_RESET, should forcibly enable split
* power mode. (Some chip errata report that RTC_CTRL_SPLIT
* is write-only, and always reads as zero...)
*/
device_init_wakeup(&pdev->dev, 0);
if (new_ctrl & (u8) OMAP_RTC_CTRL_SPLIT)
pr_info("%s: split power mode\n", pdev->name);
if (reg != new_ctrl)
rtc_write(new_ctrl, OMAP_RTC_CTRL_REG);
return 0;
fail1:
free_irq(omap_rtc_timer, NULL);
fail0:
rtc_device_unregister(rtc);
fail:
release_resource(mem);
return -EIO;
}
static int __devexit omap_rtc_remove(struct platform_device *pdev)
{
struct rtc_device *rtc = platform_get_drvdata(pdev);;
device_init_wakeup(&pdev->dev, 0);
/* leave rtc running, but disable irqs */
rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
free_irq(omap_rtc_timer, rtc);
free_irq(omap_rtc_alarm, rtc);
release_resource(class_get_devdata(&rtc->class_dev));
rtc_device_unregister(rtc);
return 0;
}
#ifdef CONFIG_PM
static struct timespec rtc_delta;
static u8 irqstat;
static int omap_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct rtc_time rtc_tm;
struct timespec time;
time.tv_nsec = 0;
omap_rtc_read_time(NULL, &rtc_tm);
rtc_tm_to_time(&rtc_tm, &time.tv_sec);
save_time_delta(&rtc_delta, &time);
irqstat = rtc_read(OMAP_RTC_INTERRUPTS_REG);
/* FIXME the RTC alarm is not currently acting as a wakeup event
* source, and in fact this enable() call is just saving a flag
* that's never used...
*/
if (device_may_wakeup(&pdev->dev))
enable_irq_wake(omap_rtc_alarm);
else
rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
return 0;
}
static int omap_rtc_resume(struct platform_device *pdev)
{
struct rtc_time rtc_tm;
struct timespec time;
time.tv_nsec = 0;
omap_rtc_read_time(NULL, &rtc_tm);
rtc_tm_to_time(&rtc_tm, &time.tv_sec);
restore_time_delta(&rtc_delta, &time);
if (device_may_wakeup(&pdev->dev))
disable_irq_wake(omap_rtc_alarm);
else
rtc_write(irqstat, OMAP_RTC_INTERRUPTS_REG);
return 0;
}
#else
#define omap_rtc_suspend NULL
#define omap_rtc_resume NULL
#endif
static void omap_rtc_shutdown(struct platform_device *pdev)
{
rtc_write(0, OMAP_RTC_INTERRUPTS_REG);
}
MODULE_ALIAS("omap_rtc");
static struct platform_driver omap_rtc_driver = {
.probe = omap_rtc_probe,
.remove = __devexit_p(omap_rtc_remove),
.suspend = omap_rtc_suspend,
.resume = omap_rtc_resume,
.shutdown = omap_rtc_shutdown,
.driver = {
.name = "omap_rtc",
.owner = THIS_MODULE,
},
};
static int __init rtc_init(void)
{
return platform_driver_register(&omap_rtc_driver);
}
module_init(rtc_init);
static void __exit rtc_exit(void)
{
platform_driver_unregister(&omap_rtc_driver);
}
module_exit(rtc_exit);
MODULE_AUTHOR("George G. Davis (and others)");
MODULE_LICENSE("GPL");

370
drivers/rtc/rtc-pcf8563.c Normal file
View File

@@ -0,0 +1,370 @@
/*
* An I2C driver for the Philips PCF8563 RTC
* Copyright 2005-06 Tower Technologies
*
* Author: Alessandro Zummo <a.zummo@towertech.it>
* Maintainers: http://www.nslu2-linux.org/
*
* based on the other drivers in this same directory.
*
* http://www.semiconductors.philips.com/acrobat/datasheets/PCF8563-04.pdf
*
* 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/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#define DRV_VERSION "0.4.2"
/* Addresses to scan: none
* This chip cannot be reliably autodetected. An empty eeprom
* located at 0x51 will pass the validation routine due to
* the way the registers are implemented.
*/
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
/* Module parameters */
I2C_CLIENT_INSMOD;
#define PCF8563_REG_ST1 0x00 /* status */
#define PCF8563_REG_ST2 0x01
#define PCF8563_REG_SC 0x02 /* datetime */
#define PCF8563_REG_MN 0x03
#define PCF8563_REG_HR 0x04
#define PCF8563_REG_DM 0x05
#define PCF8563_REG_DW 0x06
#define PCF8563_REG_MO 0x07
#define PCF8563_REG_YR 0x08
#define PCF8563_REG_AMN 0x09 /* alarm */
#define PCF8563_REG_AHR 0x0A
#define PCF8563_REG_ADM 0x0B
#define PCF8563_REG_ADW 0x0C
#define PCF8563_REG_CLKO 0x0D /* clock out */
#define PCF8563_REG_TMRC 0x0E /* timer control */
#define PCF8563_REG_TMR 0x0F /* timer */
#define PCF8563_SC_LV 0x80 /* low voltage */
#define PCF8563_MO_C 0x80 /* century */
struct pcf8563 {
struct i2c_client client;
/*
* The meaning of MO_C bit varies by the chip type.
* From PCF8563 datasheet: this bit is toggled when the years
* register overflows from 99 to 00
* 0 indicates the century is 20xx
* 1 indicates the century is 19xx
* From RTC8564 datasheet: this bit indicates change of
* century. When the year digit data overflows from 99 to 00,
* this bit is set. By presetting it to 0 while still in the
* 20th century, it will be set in year 2000, ...
* There seems no reliable way to know how the system use this
* bit. So let's do it heuristically, assuming we are live in
* 1970...2069.
*/
int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */
};
static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind);
static int pcf8563_detach(struct i2c_client *client);
/*
* In the routines that deal directly with the pcf8563 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch.
*/
static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct pcf8563 *pcf8563 = container_of(client, struct pcf8563, client);
unsigned char buf[13] = { PCF8563_REG_ST1 };
struct i2c_msg msgs[] = {
{ client->addr, 0, 1, buf }, /* setup read ptr */
{ client->addr, I2C_M_RD, 13, buf }, /* read status + date */
};
/* read registers */
if ((i2c_transfer(client->adapter, msgs, 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
if (buf[PCF8563_REG_SC] & PCF8563_SC_LV)
dev_info(&client->dev,
"low voltage detected, date/time is not reliable.\n");
dev_dbg(&client->dev,
"%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, "
"mday=%02x, wday=%02x, mon=%02x, year=%02x\n",
__FUNCTION__,
buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7],
buf[8]);
tm->tm_sec = BCD2BIN(buf[PCF8563_REG_SC] & 0x7F);
tm->tm_min = BCD2BIN(buf[PCF8563_REG_MN] & 0x7F);
tm->tm_hour = BCD2BIN(buf[PCF8563_REG_HR] & 0x3F); /* rtc hr 0-23 */
tm->tm_mday = BCD2BIN(buf[PCF8563_REG_DM] & 0x3F);
tm->tm_wday = buf[PCF8563_REG_DW] & 0x07;
tm->tm_mon = BCD2BIN(buf[PCF8563_REG_MO] & 0x1F) - 1; /* rtc mn 1-12 */
tm->tm_year = BCD2BIN(buf[PCF8563_REG_YR]);
if (tm->tm_year < 70)
tm->tm_year += 100; /* assume we are in 1970...2069 */
/* detect the polarity heuristically. see note above. */
pcf8563->c_polarity = (buf[PCF8563_REG_MO] & PCF8563_MO_C) ?
(tm->tm_year >= 100) : (tm->tm_year < 100);
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
/* the clock can give out invalid datetime, but we cannot return
* -EINVAL otherwise hwclock will refuse to set the time on bootup.
*/
if (rtc_valid_tm(tm) < 0)
dev_err(&client->dev, "retrieved date/time is not valid.\n");
return 0;
}
static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct pcf8563 *pcf8563 = container_of(client, struct pcf8563, client);
int i, err;
unsigned char buf[9];
dev_dbg(&client->dev, "%s: secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
/* hours, minutes and seconds */
buf[PCF8563_REG_SC] = BIN2BCD(tm->tm_sec);
buf[PCF8563_REG_MN] = BIN2BCD(tm->tm_min);
buf[PCF8563_REG_HR] = BIN2BCD(tm->tm_hour);
buf[PCF8563_REG_DM] = BIN2BCD(tm->tm_mday);
/* month, 1 - 12 */
buf[PCF8563_REG_MO] = BIN2BCD(tm->tm_mon + 1);
/* year and century */
buf[PCF8563_REG_YR] = BIN2BCD(tm->tm_year % 100);
if (pcf8563->c_polarity ? (tm->tm_year >= 100) : (tm->tm_year < 100))
buf[PCF8563_REG_MO] |= PCF8563_MO_C;
buf[PCF8563_REG_DW] = tm->tm_wday & 0x07;
/* write register's data */
for (i = 0; i < 7; i++) {
unsigned char data[2] = { PCF8563_REG_SC + i,
buf[PCF8563_REG_SC + i] };
err = i2c_master_send(client, data, sizeof(data));
if (err != sizeof(data)) {
dev_err(&client->dev,
"%s: err=%d addr=%02x, data=%02x\n",
__FUNCTION__, err, data[0], data[1]);
return -EIO;
}
};
return 0;
}
struct pcf8563_limit
{
unsigned char reg;
unsigned char mask;
unsigned char min;
unsigned char max;
};
static int pcf8563_validate_client(struct i2c_client *client)
{
int i;
static const struct pcf8563_limit pattern[] = {
/* register, mask, min, max */
{ PCF8563_REG_SC, 0x7F, 0, 59 },
{ PCF8563_REG_MN, 0x7F, 0, 59 },
{ PCF8563_REG_HR, 0x3F, 0, 23 },
{ PCF8563_REG_DM, 0x3F, 0, 31 },
{ PCF8563_REG_MO, 0x1F, 0, 12 },
};
/* check limits (only registers with bcd values) */
for (i = 0; i < ARRAY_SIZE(pattern); i++) {
int xfer;
unsigned char value;
unsigned char buf = pattern[i].reg;
struct i2c_msg msgs[] = {
{ client->addr, 0, 1, &buf },
{ client->addr, I2C_M_RD, 1, &buf },
};
xfer = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
if (xfer != ARRAY_SIZE(msgs)) {
dev_err(&client->dev,
"%s: could not read register 0x%02X\n",
__FUNCTION__, pattern[i].reg);
return -EIO;
}
value = BCD2BIN(buf & pattern[i].mask);
if (value > pattern[i].max ||
value < pattern[i].min) {
dev_dbg(&client->dev,
"%s: pattern=%d, reg=%x, mask=0x%02x, min=%d, "
"max=%d, value=%d, raw=0x%02X\n",
__FUNCTION__, i, pattern[i].reg, pattern[i].mask,
pattern[i].min, pattern[i].max,
value, buf);
return -ENODEV;
}
}
return 0;
}
static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_get_datetime(to_i2c_client(dev), tm);
}
static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return pcf8563_set_datetime(to_i2c_client(dev), tm);
}
static const struct rtc_class_ops pcf8563_rtc_ops = {
.read_time = pcf8563_rtc_read_time,
.set_time = pcf8563_rtc_set_time,
};
static int pcf8563_attach(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, pcf8563_probe);
}
static struct i2c_driver pcf8563_driver = {
.driver = {
.name = "pcf8563",
},
.id = I2C_DRIVERID_PCF8563,
.attach_adapter = &pcf8563_attach,
.detach_client = &pcf8563_detach,
};
static int pcf8563_probe(struct i2c_adapter *adapter, int address, int kind)
{
struct pcf8563 *pcf8563;
struct i2c_client *client;
struct rtc_device *rtc;
int err = 0;
dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto exit;
}
if (!(pcf8563 = kzalloc(sizeof(struct pcf8563), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
client = &pcf8563->client;
client->addr = address;
client->driver = &pcf8563_driver;
client->adapter = adapter;
strlcpy(client->name, pcf8563_driver.driver.name, I2C_NAME_SIZE);
/* Verify the chip is really an PCF8563 */
if (kind < 0) {
if (pcf8563_validate_client(client) < 0) {
err = -ENODEV;
goto exit_kfree;
}
}
/* Inform the i2c layer */
if ((err = i2c_attach_client(client)))
goto exit_kfree;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
rtc = rtc_device_register(pcf8563_driver.driver.name, &client->dev,
&pcf8563_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
err = PTR_ERR(rtc);
goto exit_detach;
}
i2c_set_clientdata(client, rtc);
return 0;
exit_detach:
i2c_detach_client(client);
exit_kfree:
kfree(pcf8563);
exit:
return err;
}
static int pcf8563_detach(struct i2c_client *client)
{
struct pcf8563 *pcf8563 = container_of(client, struct pcf8563, client);
int err;
struct rtc_device *rtc = i2c_get_clientdata(client);
if (rtc)
rtc_device_unregister(rtc);
if ((err = i2c_detach_client(client)))
return err;
kfree(pcf8563);
return 0;
}
static int __init pcf8563_init(void)
{
return i2c_add_driver(&pcf8563_driver);
}
static void __exit pcf8563_exit(void)
{
i2c_del_driver(&pcf8563_driver);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
module_init(pcf8563_init);
module_exit(pcf8563_exit);

395
drivers/rtc/rtc-pcf8583.c Normal file
View File

@@ -0,0 +1,395 @@
/*
* drivers/rtc/rtc-pcf8583.c
*
* Copyright (C) 2000 Russell King
*
* 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.
*
* Driver for PCF8583 RTC & RAM chip
*
* Converted to the generic RTC susbsystem by G. Liakhovetski (2006)
*/
#include <linux/module.h>
#include <linux/i2c.h>
#include <linux/slab.h>
#include <linux/string.h>
#include <linux/mc146818rtc.h>
#include <linux/init.h>
#include <linux/errno.h>
#include <linux/bcd.h>
struct rtc_mem {
unsigned int loc;
unsigned int nr;
unsigned char *data;
};
struct pcf8583 {
struct i2c_client client;
struct rtc_device *rtc;
unsigned char ctrl;
};
#define CTRL_STOP 0x80
#define CTRL_HOLD 0x40
#define CTRL_32KHZ 0x00
#define CTRL_MASK 0x08
#define CTRL_ALARMEN 0x04
#define CTRL_ALARM 0x02
#define CTRL_TIMER 0x01
static unsigned short normal_i2c[] = { 0x50, I2C_CLIENT_END };
/* Module parameters */
I2C_CLIENT_INSMOD;
static struct i2c_driver pcf8583_driver;
#define get_ctrl(x) ((struct pcf8583 *)i2c_get_clientdata(x))->ctrl
#define set_ctrl(x, v) get_ctrl(x) = v
#define CMOS_YEAR (64 + 128)
#define CMOS_CHECKSUM (63)
static int pcf8583_get_datetime(struct i2c_client *client, struct rtc_time *dt)
{
unsigned char buf[8], addr[1] = { 1 };
struct i2c_msg msgs[2] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = addr,
}, {
.addr = client->addr,
.flags = I2C_M_RD,
.len = 6,
.buf = buf,
}
};
int ret;
memset(buf, 0, sizeof(buf));
ret = i2c_transfer(client->adapter, msgs, 2);
if (ret == 2) {
dt->tm_year = buf[4] >> 6;
dt->tm_wday = buf[5] >> 5;
buf[4] &= 0x3f;
buf[5] &= 0x1f;
dt->tm_sec = BCD2BIN(buf[1]);
dt->tm_min = BCD2BIN(buf[2]);
dt->tm_hour = BCD2BIN(buf[3]);
dt->tm_mday = BCD2BIN(buf[4]);
dt->tm_mon = BCD2BIN(buf[5]) - 1;
}
return ret == 2 ? 0 : -EIO;
}
static int pcf8583_set_datetime(struct i2c_client *client, struct rtc_time *dt, int datetoo)
{
unsigned char buf[8];
int ret, len = 6;
buf[0] = 0;
buf[1] = get_ctrl(client) | 0x80;
buf[2] = 0;
buf[3] = BIN2BCD(dt->tm_sec);
buf[4] = BIN2BCD(dt->tm_min);
buf[5] = BIN2BCD(dt->tm_hour);
if (datetoo) {
len = 8;
buf[6] = BIN2BCD(dt->tm_mday) | (dt->tm_year << 6);
buf[7] = BIN2BCD(dt->tm_mon + 1) | (dt->tm_wday << 5);
}
ret = i2c_master_send(client, (char *)buf, len);
if (ret != len)
return -EIO;
buf[1] = get_ctrl(client);
ret = i2c_master_send(client, (char *)buf, 2);
return ret == 2 ? 0 : -EIO;
}
static int pcf8583_get_ctrl(struct i2c_client *client, unsigned char *ctrl)
{
*ctrl = get_ctrl(client);
return 0;
}
static int pcf8583_set_ctrl(struct i2c_client *client, unsigned char *ctrl)
{
unsigned char buf[2];
buf[0] = 0;
buf[1] = *ctrl;
set_ctrl(client, *ctrl);
return i2c_master_send(client, (char *)buf, 2);
}
static int pcf8583_read_mem(struct i2c_client *client, struct rtc_mem *mem)
{
unsigned char addr[1];
struct i2c_msg msgs[2] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = addr,
}, {
.addr = client->addr,
.flags = I2C_M_RD,
.len = mem->nr,
.buf = mem->data,
}
};
if (mem->loc < 8)
return -EINVAL;
addr[0] = mem->loc;
return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;
}
static int pcf8583_write_mem(struct i2c_client *client, struct rtc_mem *mem)
{
unsigned char addr[1];
struct i2c_msg msgs[2] = {
{
.addr = client->addr,
.flags = 0,
.len = 1,
.buf = addr,
}, {
.addr = client->addr,
.flags = I2C_M_NOSTART,
.len = mem->nr,
.buf = mem->data,
}
};
if (mem->loc < 8)
return -EINVAL;
addr[0] = mem->loc;
return i2c_transfer(client->adapter, msgs, 2) == 2 ? 0 : -EIO;
}
static int pcf8583_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned char ctrl, year[2];
struct rtc_mem mem = { CMOS_YEAR, sizeof(year), year };
int real_year, year_offset, err;
/*
* Ensure that the RTC is running.
*/
pcf8583_get_ctrl(client, &ctrl);
if (ctrl & (CTRL_STOP | CTRL_HOLD)) {
unsigned char new_ctrl = ctrl & ~(CTRL_STOP | CTRL_HOLD);
printk(KERN_WARNING "RTC: resetting control %02x -> %02x\n",
ctrl, new_ctrl);
if ((err = pcf8583_set_ctrl(client, &new_ctrl)) < 0)
return err;
}
if (pcf8583_get_datetime(client, tm) ||
pcf8583_read_mem(client, &mem))
return -EIO;
real_year = year[0];
/*
* The RTC year holds the LSB two bits of the current
* year, which should reflect the LSB two bits of the
* CMOS copy of the year. Any difference indicates
* that we have to correct the CMOS version.
*/
year_offset = tm->tm_year - (real_year & 3);
if (year_offset < 0)
/*
* RTC year wrapped. Adjust it appropriately.
*/
year_offset += 4;
tm->tm_year = (real_year + year_offset + year[1] * 100) - 1900;
return 0;
}
static int pcf8583_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct i2c_client *client = to_i2c_client(dev);
unsigned char year[2], chk;
struct rtc_mem cmos_year = { CMOS_YEAR, sizeof(year), year };
struct rtc_mem cmos_check = { CMOS_CHECKSUM, 1, &chk };
unsigned int proper_year = tm->tm_year + 1900;
int ret;
/*
* The RTC's own 2-bit year must reflect the least
* significant two bits of the CMOS year.
*/
ret = pcf8583_set_datetime(client, tm, 1);
if (ret)
return ret;
ret = pcf8583_read_mem(client, &cmos_check);
if (ret)
return ret;
ret = pcf8583_read_mem(client, &cmos_year);
if (ret)
return ret;
chk -= year[1] + year[0];
year[1] = proper_year / 100;
year[0] = proper_year % 100;
chk += year[1] + year[0];
ret = pcf8583_write_mem(client, &cmos_year);
if (ret)
return ret;
ret = pcf8583_write_mem(client, &cmos_check);
return ret;
}
static const struct rtc_class_ops pcf8583_rtc_ops = {
.read_time = pcf8583_rtc_read_time,
.set_time = pcf8583_rtc_set_time,
};
static int pcf8583_probe(struct i2c_adapter *adap, int addr, int kind);
static int pcf8583_attach(struct i2c_adapter *adap)
{
return i2c_probe(adap, &addr_data, pcf8583_probe);
}
static int pcf8583_detach(struct i2c_client *client)
{
int err;
struct pcf8583 *pcf = i2c_get_clientdata(client);
struct rtc_device *rtc = pcf->rtc;
if (rtc)
rtc_device_unregister(rtc);
if ((err = i2c_detach_client(client)))
return err;
kfree(pcf);
return 0;
}
static struct i2c_driver pcf8583_driver = {
.driver = {
.name = "pcf8583",
},
.id = I2C_DRIVERID_PCF8583,
.attach_adapter = pcf8583_attach,
.detach_client = pcf8583_detach,
};
static int pcf8583_probe(struct i2c_adapter *adap, int addr, int kind)
{
struct pcf8583 *pcf;
struct i2c_client *client;
struct rtc_device *rtc;
unsigned char buf[1], ad[1] = { 0 };
int err;
struct i2c_msg msgs[2] = {
{
.addr = addr,
.flags = 0,
.len = 1,
.buf = ad,
}, {
.addr = addr,
.flags = I2C_M_RD,
.len = 1,
.buf = buf,
}
};
pcf = kzalloc(sizeof(*pcf), GFP_KERNEL);
if (!pcf)
return -ENOMEM;
client = &pcf->client;
client->addr = addr;
client->adapter = adap;
client->driver = &pcf8583_driver;
strlcpy(client->name, pcf8583_driver.driver.name, I2C_NAME_SIZE);
if (i2c_transfer(client->adapter, msgs, 2) != 2) {
err = -EIO;
goto exit_kfree;
}
err = i2c_attach_client(client);
if (err)
goto exit_kfree;
rtc = rtc_device_register(pcf8583_driver.driver.name, &client->dev,
&pcf8583_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
err = PTR_ERR(rtc);
goto exit_detach;
}
pcf->rtc = rtc;
i2c_set_clientdata(client, pcf);
set_ctrl(client, buf[0]);
return 0;
exit_detach:
i2c_detach_client(client);
exit_kfree:
kfree(pcf);
return err;
}
static __init int pcf8583_init(void)
{
return i2c_add_driver(&pcf8583_driver);
}
static __exit void pcf8583_exit(void)
{
i2c_del_driver(&pcf8583_driver);
}
module_init(pcf8583_init);
module_exit(pcf8583_exit);
MODULE_AUTHOR("Russell King");
MODULE_DESCRIPTION("PCF8583 I2C RTC driver");
MODULE_LICENSE("GPL");

233
drivers/rtc/rtc-pl031.c Normal file
View File

@@ -0,0 +1,233 @@
/*
* drivers/rtc/rtc-pl031.c
*
* Real Time Clock interface for ARM AMBA PrimeCell 031 RTC
*
* Author: Deepak Saxena <dsaxena@plexity.net>
*
* Copyright 2006 (c) MontaVista Software, Inc.
*
* 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/platform_device.h>
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/pm.h>
#include <linux/amba/bus.h>
#include <asm/io.h>
#include <asm/bitops.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/rtc.h>
/*
* Register definitions
*/
#define RTC_DR 0x00 /* Data read register */
#define RTC_MR 0x04 /* Match register */
#define RTC_LR 0x08 /* Data load register */
#define RTC_CR 0x0c /* Control register */
#define RTC_IMSC 0x10 /* Interrupt mask and set register */
#define RTC_RIS 0x14 /* Raw interrupt status register */
#define RTC_MIS 0x18 /* Masked interrupt status register */
#define RTC_ICR 0x1c /* Interrupt clear register */
struct pl031_local {
struct rtc_device *rtc;
void __iomem *base;
};
static irqreturn_t pl031_interrupt(int irq, void *dev_id)
{
struct rtc_device *rtc = dev_id;
rtc_update_irq(&rtc->class_dev, 1, RTC_AF);
return IRQ_HANDLED;
}
static int pl031_open(struct device *dev)
{
/*
* We request IRQ in pl031_probe, so nothing to do here...
*/
return 0;
}
static void pl031_release(struct device *dev)
{
}
static int pl031_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
switch (cmd) {
case RTC_AIE_OFF:
__raw_writel(1, ldata->base + RTC_MIS);
return 0;
case RTC_AIE_ON:
__raw_writel(0, ldata->base + RTC_MIS);
return 0;
}
return -ENOIOCTLCMD;
}
static int pl031_read_time(struct device *dev, struct rtc_time *tm)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
rtc_time_to_tm(__raw_readl(ldata->base + RTC_DR), tm);
return 0;
}
static int pl031_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned long time;
struct pl031_local *ldata = dev_get_drvdata(dev);
rtc_tm_to_time(tm, &time);
__raw_writel(time, ldata->base + RTC_LR);
return 0;
}
static int pl031_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
rtc_time_to_tm(__raw_readl(ldata->base + RTC_MR), &alarm->time);
alarm->pending = __raw_readl(ldata->base + RTC_RIS);
alarm->enabled = __raw_readl(ldata->base + RTC_IMSC);
return 0;
}
static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
{
struct pl031_local *ldata = dev_get_drvdata(dev);
unsigned long time;
rtc_tm_to_time(&alarm->time, &time);
__raw_writel(time, ldata->base + RTC_MR);
__raw_writel(!alarm->enabled, ldata->base + RTC_MIS);
return 0;
}
static const struct rtc_class_ops pl031_ops = {
.open = pl031_open,
.release = pl031_release,
.ioctl = pl031_ioctl,
.read_time = pl031_read_time,
.set_time = pl031_set_time,
.read_alarm = pl031_read_alarm,
.set_alarm = pl031_set_alarm,
};
static int pl031_remove(struct amba_device *adev)
{
struct pl031_local *ldata = dev_get_drvdata(&adev->dev);
if (ldata) {
dev_set_drvdata(&adev->dev, NULL);
free_irq(adev->irq[0], ldata->rtc);
rtc_device_unregister(ldata->rtc);
iounmap(ldata->base);
kfree(ldata);
}
return 0;
}
static int pl031_probe(struct amba_device *adev, void *id)
{
int ret;
struct pl031_local *ldata;
ldata = kmalloc(sizeof(struct pl031_local), GFP_KERNEL);
if (!ldata) {
ret = -ENOMEM;
goto out;
}
dev_set_drvdata(&adev->dev, ldata);
ldata->base = ioremap(adev->res.start,
adev->res.end - adev->res.start + 1);
if (!ldata->base) {
ret = -ENOMEM;
goto out_no_remap;
}
if (request_irq(adev->irq[0], pl031_interrupt, IRQF_DISABLED,
"rtc-pl031", ldata->rtc)) {
ret = -EIO;
goto out_no_irq;
}
ldata->rtc = rtc_device_register("pl031", &adev->dev, &pl031_ops,
THIS_MODULE);
if (IS_ERR(ldata->rtc)) {
ret = PTR_ERR(ldata->rtc);
goto out_no_rtc;
}
return 0;
out_no_rtc:
free_irq(adev->irq[0], ldata->rtc);
out_no_irq:
iounmap(ldata->base);
out_no_remap:
dev_set_drvdata(&adev->dev, NULL);
kfree(ldata);
out:
return ret;
}
static struct amba_id pl031_ids[] __initdata = {
{
.id = 0x00041031,
.mask = 0x000fffff, },
{0, 0},
};
static struct amba_driver pl031_driver = {
.drv = {
.name = "rtc-pl031",
},
.id_table = pl031_ids,
.probe = pl031_probe,
.remove = pl031_remove,
};
static int __init pl031_init(void)
{
return amba_driver_register(&pl031_driver);
}
static void __exit pl031_exit(void)
{
amba_driver_unregister(&pl031_driver);
}
module_init(pl031_init);
module_exit(pl031_exit);
MODULE_AUTHOR("Deepak Saxena <dsaxena@plexity.net");
MODULE_DESCRIPTION("ARM AMBA PL031 RTC Driver");
MODULE_LICENSE("GPL");

164
drivers/rtc/rtc-proc.c Normal file
View File

@@ -0,0 +1,164 @@
/*
* RTC subsystem, proc interface
*
* Copyright (C) 2005-06 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* based on arch/arm/common/rtctime.c
*
* 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/module.h>
#include <linux/rtc.h>
#include <linux/proc_fs.h>
#include <linux/seq_file.h>
static struct class_device *rtc_dev = NULL;
static DEFINE_MUTEX(rtc_lock);
static int rtc_proc_show(struct seq_file *seq, void *offset)
{
int err;
struct class_device *class_dev = seq->private;
const struct rtc_class_ops *ops = to_rtc_device(class_dev)->ops;
struct rtc_wkalrm alrm;
struct rtc_time tm;
err = rtc_read_time(class_dev, &tm);
if (err == 0) {
seq_printf(seq,
"rtc_time\t: %02d:%02d:%02d\n"
"rtc_date\t: %04d-%02d-%02d\n",
tm.tm_hour, tm.tm_min, tm.tm_sec,
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
}
err = rtc_read_alarm(class_dev, &alrm);
if (err == 0) {
seq_printf(seq, "alrm_time\t: ");
if ((unsigned int)alrm.time.tm_hour <= 24)
seq_printf(seq, "%02d:", alrm.time.tm_hour);
else
seq_printf(seq, "**:");
if ((unsigned int)alrm.time.tm_min <= 59)
seq_printf(seq, "%02d:", alrm.time.tm_min);
else
seq_printf(seq, "**:");
if ((unsigned int)alrm.time.tm_sec <= 59)
seq_printf(seq, "%02d\n", alrm.time.tm_sec);
else
seq_printf(seq, "**\n");
seq_printf(seq, "alrm_date\t: ");
if ((unsigned int)alrm.time.tm_year <= 200)
seq_printf(seq, "%04d-", alrm.time.tm_year + 1900);
else
seq_printf(seq, "****-");
if ((unsigned int)alrm.time.tm_mon <= 11)
seq_printf(seq, "%02d-", alrm.time.tm_mon + 1);
else
seq_printf(seq, "**-");
if (alrm.time.tm_mday && (unsigned int)alrm.time.tm_mday <= 31)
seq_printf(seq, "%02d\n", alrm.time.tm_mday);
else
seq_printf(seq, "**\n");
seq_printf(seq, "alarm_IRQ\t: %s\n",
alrm.enabled ? "yes" : "no");
seq_printf(seq, "alrm_pending\t: %s\n",
alrm.pending ? "yes" : "no");
}
seq_printf(seq, "24hr\t\t: yes\n");
if (ops->proc)
ops->proc(class_dev->dev, seq);
return 0;
}
static int rtc_proc_open(struct inode *inode, struct file *file)
{
struct class_device *class_dev = PDE(inode)->data;
if (!try_module_get(THIS_MODULE))
return -ENODEV;
return single_open(file, rtc_proc_show, class_dev);
}
static int rtc_proc_release(struct inode *inode, struct file *file)
{
int res = single_release(inode, file);
module_put(THIS_MODULE);
return res;
}
static const struct file_operations rtc_proc_fops = {
.open = rtc_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = rtc_proc_release,
};
static int rtc_proc_add_device(struct class_device *class_dev,
struct class_interface *class_intf)
{
mutex_lock(&rtc_lock);
if (rtc_dev == NULL) {
struct proc_dir_entry *ent;
rtc_dev = class_dev;
ent = create_proc_entry("driver/rtc", 0, NULL);
if (ent) {
struct rtc_device *rtc = to_rtc_device(class_dev);
ent->proc_fops = &rtc_proc_fops;
ent->owner = rtc->owner;
ent->data = class_dev;
dev_dbg(class_dev->dev, "rtc intf: proc\n");
}
else
rtc_dev = NULL;
}
mutex_unlock(&rtc_lock);
return 0;
}
static void rtc_proc_remove_device(struct class_device *class_dev,
struct class_interface *class_intf)
{
mutex_lock(&rtc_lock);
if (rtc_dev == class_dev) {
remove_proc_entry("driver/rtc", NULL);
rtc_dev = NULL;
}
mutex_unlock(&rtc_lock);
}
static struct class_interface rtc_proc_interface = {
.add = &rtc_proc_add_device,
.remove = &rtc_proc_remove_device,
};
static int __init rtc_proc_init(void)
{
return rtc_interface_register(&rtc_proc_interface);
}
static void __exit rtc_proc_exit(void)
{
class_interface_unregister(&rtc_proc_interface);
}
subsys_initcall(rtc_proc_init);
module_exit(rtc_proc_exit);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("RTC class proc interface");
MODULE_LICENSE("GPL");

253
drivers/rtc/rtc-rs5c348.c Normal file
View File

@@ -0,0 +1,253 @@
/*
* A SPI driver for the Ricoh RS5C348 RTC
*
* 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.
*
* The board specific init code should provide characteristics of this
* device:
* Mode 1 (High-Active, Shift-Then-Sample), High Avtive CS
*/
#include <linux/bcd.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/errno.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/string.h>
#include <linux/rtc.h>
#include <linux/workqueue.h>
#include <linux/spi/spi.h>
#define DRV_VERSION "0.2"
#define RS5C348_REG_SECS 0
#define RS5C348_REG_MINS 1
#define RS5C348_REG_HOURS 2
#define RS5C348_REG_WDAY 3
#define RS5C348_REG_DAY 4
#define RS5C348_REG_MONTH 5
#define RS5C348_REG_YEAR 6
#define RS5C348_REG_CTL1 14
#define RS5C348_REG_CTL2 15
#define RS5C348_SECS_MASK 0x7f
#define RS5C348_MINS_MASK 0x7f
#define RS5C348_HOURS_MASK 0x3f
#define RS5C348_WDAY_MASK 0x03
#define RS5C348_DAY_MASK 0x3f
#define RS5C348_MONTH_MASK 0x1f
#define RS5C348_BIT_PM 0x20 /* REG_HOURS */
#define RS5C348_BIT_Y2K 0x80 /* REG_MONTH */
#define RS5C348_BIT_24H 0x20 /* REG_CTL1 */
#define RS5C348_BIT_XSTP 0x10 /* REG_CTL2 */
#define RS5C348_BIT_VDET 0x40 /* REG_CTL2 */
#define RS5C348_CMD_W(addr) (((addr) << 4) | 0x08) /* single write */
#define RS5C348_CMD_R(addr) (((addr) << 4) | 0x0c) /* single read */
#define RS5C348_CMD_MW(addr) (((addr) << 4) | 0x00) /* burst write */
#define RS5C348_CMD_MR(addr) (((addr) << 4) | 0x04) /* burst read */
struct rs5c348_plat_data {
struct rtc_device *rtc;
int rtc_24h;
};
static int
rs5c348_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct spi_device *spi = to_spi_device(dev);
struct rs5c348_plat_data *pdata = spi->dev.platform_data;
u8 txbuf[5+7], *txp;
int ret;
/* Transfer 5 bytes before writing SEC. This gives 31us for carry. */
txp = txbuf;
txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
txbuf[1] = 0; /* dummy */
txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
txbuf[3] = 0; /* dummy */
txbuf[4] = RS5C348_CMD_MW(RS5C348_REG_SECS); /* cmd, sec, ... */
txp = &txbuf[5];
txp[RS5C348_REG_SECS] = BIN2BCD(tm->tm_sec);
txp[RS5C348_REG_MINS] = BIN2BCD(tm->tm_min);
if (pdata->rtc_24h) {
txp[RS5C348_REG_HOURS] = BIN2BCD(tm->tm_hour);
} else {
/* hour 0 is AM12, noon is PM12 */
txp[RS5C348_REG_HOURS] = BIN2BCD((tm->tm_hour + 11) % 12 + 1) |
(tm->tm_hour >= 12 ? RS5C348_BIT_PM : 0);
}
txp[RS5C348_REG_WDAY] = BIN2BCD(tm->tm_wday);
txp[RS5C348_REG_DAY] = BIN2BCD(tm->tm_mday);
txp[RS5C348_REG_MONTH] = BIN2BCD(tm->tm_mon + 1) |
(tm->tm_year >= 100 ? RS5C348_BIT_Y2K : 0);
txp[RS5C348_REG_YEAR] = BIN2BCD(tm->tm_year % 100);
/* write in one transfer to avoid data inconsistency */
ret = spi_write_then_read(spi, txbuf, sizeof(txbuf), NULL, 0);
udelay(62); /* Tcsr 62us */
return ret;
}
static int
rs5c348_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct spi_device *spi = to_spi_device(dev);
struct rs5c348_plat_data *pdata = spi->dev.platform_data;
u8 txbuf[5], rxbuf[7];
int ret;
/* Transfer 5 byte befores reading SEC. This gives 31us for carry. */
txbuf[0] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
txbuf[1] = 0; /* dummy */
txbuf[2] = RS5C348_CMD_R(RS5C348_REG_CTL2); /* cmd, ctl2 */
txbuf[3] = 0; /* dummy */
txbuf[4] = RS5C348_CMD_MR(RS5C348_REG_SECS); /* cmd, sec, ... */
/* read in one transfer to avoid data inconsistency */
ret = spi_write_then_read(spi, txbuf, sizeof(txbuf),
rxbuf, sizeof(rxbuf));
udelay(62); /* Tcsr 62us */
if (ret < 0)
return ret;
tm->tm_sec = BCD2BIN(rxbuf[RS5C348_REG_SECS] & RS5C348_SECS_MASK);
tm->tm_min = BCD2BIN(rxbuf[RS5C348_REG_MINS] & RS5C348_MINS_MASK);
tm->tm_hour = BCD2BIN(rxbuf[RS5C348_REG_HOURS] & RS5C348_HOURS_MASK);
if (!pdata->rtc_24h) {
tm->tm_hour %= 12;
if (rxbuf[RS5C348_REG_HOURS] & RS5C348_BIT_PM)
tm->tm_hour += 12;
}
tm->tm_wday = BCD2BIN(rxbuf[RS5C348_REG_WDAY] & RS5C348_WDAY_MASK);
tm->tm_mday = BCD2BIN(rxbuf[RS5C348_REG_DAY] & RS5C348_DAY_MASK);
tm->tm_mon =
BCD2BIN(rxbuf[RS5C348_REG_MONTH] & RS5C348_MONTH_MASK) - 1;
/* year is 1900 + tm->tm_year */
tm->tm_year = BCD2BIN(rxbuf[RS5C348_REG_YEAR]) +
((rxbuf[RS5C348_REG_MONTH] & RS5C348_BIT_Y2K) ? 100 : 0);
if (rtc_valid_tm(tm) < 0) {
dev_err(&spi->dev, "retrieved date/time is not valid.\n");
rtc_time_to_tm(0, tm);
}
return 0;
}
static const struct rtc_class_ops rs5c348_rtc_ops = {
.read_time = rs5c348_rtc_read_time,
.set_time = rs5c348_rtc_set_time,
};
static struct spi_driver rs5c348_driver;
static int __devinit rs5c348_probe(struct spi_device *spi)
{
int ret;
struct rtc_device *rtc;
struct rs5c348_plat_data *pdata;
pdata = kzalloc(sizeof(struct rs5c348_plat_data), GFP_KERNEL);
if (!pdata)
return -ENOMEM;
spi->dev.platform_data = pdata;
/* Check D7 of SECOND register */
ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_SECS));
if (ret < 0 || (ret & 0x80)) {
dev_err(&spi->dev, "not found.\n");
goto kfree_exit;
}
dev_info(&spi->dev, "chip found, driver version " DRV_VERSION "\n");
dev_info(&spi->dev, "spiclk %u KHz.\n",
(spi->max_speed_hz + 500) / 1000);
/* turn RTC on if it was not on */
ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL2));
if (ret < 0)
goto kfree_exit;
if (ret & (RS5C348_BIT_XSTP | RS5C348_BIT_VDET)) {
u8 buf[2];
struct rtc_time tm;
if (ret & RS5C348_BIT_VDET)
dev_warn(&spi->dev, "voltage-low detected.\n");
if (ret & RS5C348_BIT_XSTP)
dev_warn(&spi->dev, "oscillator-stop detected.\n");
rtc_time_to_tm(0, &tm); /* 1970/1/1 */
ret = rs5c348_rtc_set_time(&spi->dev, &tm);
if (ret < 0)
goto kfree_exit;
buf[0] = RS5C348_CMD_W(RS5C348_REG_CTL2);
buf[1] = 0;
ret = spi_write_then_read(spi, buf, sizeof(buf), NULL, 0);
if (ret < 0)
goto kfree_exit;
}
ret = spi_w8r8(spi, RS5C348_CMD_R(RS5C348_REG_CTL1));
if (ret < 0)
goto kfree_exit;
if (ret & RS5C348_BIT_24H)
pdata->rtc_24h = 1;
rtc = rtc_device_register(rs5c348_driver.driver.name, &spi->dev,
&rs5c348_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc);
goto kfree_exit;
}
pdata->rtc = rtc;
return 0;
kfree_exit:
kfree(pdata);
return ret;
}
static int __devexit rs5c348_remove(struct spi_device *spi)
{
struct rs5c348_plat_data *pdata = spi->dev.platform_data;
struct rtc_device *rtc = pdata->rtc;
if (rtc)
rtc_device_unregister(rtc);
kfree(pdata);
return 0;
}
static struct spi_driver rs5c348_driver = {
.driver = {
.name = "rs5c348",
.bus = &spi_bus_type,
.owner = THIS_MODULE,
},
.probe = rs5c348_probe,
.remove = __devexit_p(rs5c348_remove),
};
static __init int rs5c348_init(void)
{
return spi_register_driver(&rs5c348_driver);
}
static __exit void rs5c348_exit(void)
{
spi_unregister_driver(&rs5c348_driver);
}
module_init(rs5c348_init);
module_exit(rs5c348_exit);
MODULE_AUTHOR("Atsushi Nemoto <anemo@mba.ocn.ne.jp>");
MODULE_DESCRIPTION("Ricoh RS5C348 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);

701
drivers/rtc/rtc-rs5c372.c Normal file
View File

@@ -0,0 +1,701 @@
/*
* An I2C driver for Ricoh RS5C372 and RV5C38[67] RTCs
*
* Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net>
* Copyright (C) 2006 Tower Technologies
*
* 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/i2c.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#define DRV_VERSION "0.4"
/* Addresses to scan */
static unsigned short normal_i2c[] = { /* 0x32,*/ I2C_CLIENT_END };
/* Insmod parameters */
I2C_CLIENT_INSMOD;
/*
* Ricoh has a family of I2C based RTCs, which differ only slightly from
* each other. Differences center on pinout (e.g. how many interrupts,
* output clock, etc) and how the control registers are used. The '372
* is significant only because that's the one this driver first supported.
*/
#define RS5C372_REG_SECS 0
#define RS5C372_REG_MINS 1
#define RS5C372_REG_HOURS 2
#define RS5C372_REG_WDAY 3
#define RS5C372_REG_DAY 4
#define RS5C372_REG_MONTH 5
#define RS5C372_REG_YEAR 6
#define RS5C372_REG_TRIM 7
# define RS5C372_TRIM_XSL 0x80
# define RS5C372_TRIM_MASK 0x7F
#define RS5C_REG_ALARM_A_MIN 8 /* or ALARM_W */
#define RS5C_REG_ALARM_A_HOURS 9
#define RS5C_REG_ALARM_A_WDAY 10
#define RS5C_REG_ALARM_B_MIN 11 /* or ALARM_D */
#define RS5C_REG_ALARM_B_HOURS 12
#define RS5C_REG_ALARM_B_WDAY 13 /* (ALARM_B only) */
#define RS5C_REG_CTRL1 14
# define RS5C_CTRL1_AALE (1 << 7) /* or WALE */
# define RS5C_CTRL1_BALE (1 << 6) /* or DALE */
# define RV5C387_CTRL1_24 (1 << 5)
# define RS5C372A_CTRL1_SL1 (1 << 5)
# define RS5C_CTRL1_CT_MASK (7 << 0)
# define RS5C_CTRL1_CT0 (0 << 0) /* no periodic irq */
# define RS5C_CTRL1_CT4 (4 << 0) /* 1 Hz level irq */
#define RS5C_REG_CTRL2 15
# define RS5C372_CTRL2_24 (1 << 5)
# define RS5C_CTRL2_XSTP (1 << 4)
# define RS5C_CTRL2_CTFG (1 << 2)
# define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */
# define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */
/* to read (style 1) or write registers starting at R */
#define RS5C_ADDR(R) (((R) << 4) | 0)
enum rtc_type {
rtc_undef = 0,
rtc_rs5c372a,
rtc_rs5c372b,
rtc_rv5c386,
rtc_rv5c387a,
};
/* REVISIT: this assumes that:
* - we're in the 21st century, so it's safe to ignore the century
* bit for rv5c38[67] (REG_MONTH bit 7);
* - we should use ALARM_A not ALARM_B (may be wrong on some boards)
*/
struct rs5c372 {
struct i2c_client *client;
struct rtc_device *rtc;
enum rtc_type type;
unsigned time24:1;
unsigned has_irq:1;
char buf[17];
char *regs;
/* on conversion to a "new style" i2c driver, this vanishes */
struct i2c_client dev;
};
static int rs5c_get_regs(struct rs5c372 *rs5c)
{
struct i2c_client *client = rs5c->client;
struct i2c_msg msgs[] = {
{ client->addr, I2C_M_RD, sizeof rs5c->buf, rs5c->buf },
};
/* This implements the third reading method from the datasheet, using
* an internal address that's reset after each transaction (by STOP)
* to 0x0f ... so we read extra registers, and skip the first one.
*
* The first method doesn't work with the iop3xx adapter driver, on at
* least 80219 chips; this works around that bug.
*/
if ((i2c_transfer(client->adapter, msgs, 1)) != 1) {
pr_debug("%s: can't read registers\n", rs5c->rtc->name);
return -EIO;
}
dev_dbg(&client->dev,
"%02x %02x %02x (%02x) %02x %02x %02x (%02x), "
"%02x %02x %02x, %02x %02x %02x; %02x %02x\n",
rs5c->regs[0], rs5c->regs[1], rs5c->regs[2], rs5c->regs[3],
rs5c->regs[4], rs5c->regs[5], rs5c->regs[6], rs5c->regs[7],
rs5c->regs[8], rs5c->regs[9], rs5c->regs[10], rs5c->regs[11],
rs5c->regs[12], rs5c->regs[13], rs5c->regs[14], rs5c->regs[15]);
return 0;
}
static unsigned rs5c_reg2hr(struct rs5c372 *rs5c, unsigned reg)
{
unsigned hour;
if (rs5c->time24)
return BCD2BIN(reg & 0x3f);
hour = BCD2BIN(reg & 0x1f);
if (hour == 12)
hour = 0;
if (reg & 0x20)
hour += 12;
return hour;
}
static unsigned rs5c_hr2reg(struct rs5c372 *rs5c, unsigned hour)
{
if (rs5c->time24)
return BIN2BCD(hour);
if (hour > 12)
return 0x20 | BIN2BCD(hour - 12);
if (hour == 12)
return 0x20 | BIN2BCD(12);
if (hour == 0)
return BIN2BCD(12);
return BIN2BCD(hour);
}
static int rs5c372_get_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct rs5c372 *rs5c = i2c_get_clientdata(client);
int status = rs5c_get_regs(rs5c);
if (status < 0)
return status;
tm->tm_sec = BCD2BIN(rs5c->regs[RS5C372_REG_SECS] & 0x7f);
tm->tm_min = BCD2BIN(rs5c->regs[RS5C372_REG_MINS] & 0x7f);
tm->tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C372_REG_HOURS]);
tm->tm_wday = BCD2BIN(rs5c->regs[RS5C372_REG_WDAY] & 0x07);
tm->tm_mday = BCD2BIN(rs5c->regs[RS5C372_REG_DAY] & 0x3f);
/* tm->tm_mon is zero-based */
tm->tm_mon = BCD2BIN(rs5c->regs[RS5C372_REG_MONTH] & 0x1f) - 1;
/* year is 1900 + tm->tm_year */
tm->tm_year = BCD2BIN(rs5c->regs[RS5C372_REG_YEAR]) + 100;
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
/* rtc might need initialization */
return rtc_valid_tm(tm);
}
static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{
struct rs5c372 *rs5c = i2c_get_clientdata(client);
unsigned char buf[8];
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
buf[0] = RS5C_ADDR(RS5C372_REG_SECS);
buf[1] = BIN2BCD(tm->tm_sec);
buf[2] = BIN2BCD(tm->tm_min);
buf[3] = rs5c_hr2reg(rs5c, tm->tm_hour);
buf[4] = BIN2BCD(tm->tm_wday);
buf[5] = BIN2BCD(tm->tm_mday);
buf[6] = BIN2BCD(tm->tm_mon + 1);
buf[7] = BIN2BCD(tm->tm_year - 100);
if ((i2c_master_send(client, buf, 8)) != 8) {
dev_err(&client->dev, "%s: write error\n", __FUNCTION__);
return -EIO;
}
return 0;
}
#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
#define NEED_TRIM
#endif
#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
#define NEED_TRIM
#endif
#ifdef NEED_TRIM
static int rs5c372_get_trim(struct i2c_client *client, int *osc, int *trim)
{
struct rs5c372 *rs5c372 = i2c_get_clientdata(client);
u8 tmp = rs5c372->regs[RS5C372_REG_TRIM];
if (osc)
*osc = (tmp & RS5C372_TRIM_XSL) ? 32000 : 32768;
if (trim) {
dev_dbg(&client->dev, "%s: raw trim=%x\n", __FUNCTION__, tmp);
tmp &= RS5C372_TRIM_MASK;
if (tmp & 0x3e) {
int t = tmp & 0x3f;
if (tmp & 0x40)
t = (~t | (s8)0xc0) + 1;
else
t = t - 1;
tmp = t * 2;
} else
tmp = 0;
*trim = tmp;
}
return 0;
}
#endif
static int rs5c372_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return rs5c372_get_datetime(to_i2c_client(dev), tm);
}
static int rs5c372_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return rs5c372_set_datetime(to_i2c_client(dev), tm);
}
#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE)
static int
rs5c_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
unsigned char buf[2];
int status;
buf[1] = rs5c->regs[RS5C_REG_CTRL1];
switch (cmd) {
case RTC_UIE_OFF:
case RTC_UIE_ON:
/* some 327a modes use a different IRQ pin for 1Hz irqs */
if (rs5c->type == rtc_rs5c372a
&& (buf[1] & RS5C372A_CTRL1_SL1))
return -ENOIOCTLCMD;
case RTC_AIE_OFF:
case RTC_AIE_ON:
/* these irq management calls only make sense for chips
* which are wired up to an IRQ.
*/
if (!rs5c->has_irq)
return -ENOIOCTLCMD;
break;
default:
return -ENOIOCTLCMD;
}
status = rs5c_get_regs(rs5c);
if (status < 0)
return status;
buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);
switch (cmd) {
case RTC_AIE_OFF: /* alarm off */
buf[1] &= ~RS5C_CTRL1_AALE;
break;
case RTC_AIE_ON: /* alarm on */
buf[1] |= RS5C_CTRL1_AALE;
break;
case RTC_UIE_OFF: /* update off */
buf[1] &= ~RS5C_CTRL1_CT_MASK;
break;
case RTC_UIE_ON: /* update on */
buf[1] &= ~RS5C_CTRL1_CT_MASK;
buf[1] |= RS5C_CTRL1_CT4;
break;
}
if ((i2c_master_send(client, buf, 2)) != 2) {
printk(KERN_WARNING "%s: can't update alarm\n",
rs5c->rtc->name);
status = -EIO;
} else
rs5c->regs[RS5C_REG_CTRL1] = buf[1];
return status;
}
#else
#define rs5c_rtc_ioctl NULL
#endif
/* NOTE: Since RTC_WKALM_{RD,SET} were originally defined for EFI,
* which only exposes a polled programming interface; and since
* these calls map directly to those EFI requests; we don't demand
* we have an IRQ for this chip when we go through this API.
*
* The older x86_pc derived RTC_ALM_{READ,SET} calls require irqs
* though, managed through RTC_AIE_{ON,OFF} requests.
*/
static int rs5c_read_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
int status;
status = rs5c_get_regs(rs5c);
if (status < 0)
return status;
/* report alarm time */
t->time.tm_sec = 0;
t->time.tm_min = BCD2BIN(rs5c->regs[RS5C_REG_ALARM_A_MIN] & 0x7f);
t->time.tm_hour = rs5c_reg2hr(rs5c, rs5c->regs[RS5C_REG_ALARM_A_HOURS]);
t->time.tm_mday = -1;
t->time.tm_mon = -1;
t->time.tm_year = -1;
t->time.tm_wday = -1;
t->time.tm_yday = -1;
t->time.tm_isdst = -1;
/* ... and status */
t->enabled = !!(rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE);
t->pending = !!(rs5c->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_AAFG);
return 0;
}
static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t)
{
struct i2c_client *client = to_i2c_client(dev);
struct rs5c372 *rs5c = i2c_get_clientdata(client);
int status;
unsigned char buf[4];
/* only handle up to 24 hours in the future, like RTC_ALM_SET */
if (t->time.tm_mday != -1
|| t->time.tm_mon != -1
|| t->time.tm_year != -1)
return -EINVAL;
/* REVISIT: round up tm_sec */
/* if needed, disable irq (clears pending status) */
status = rs5c_get_regs(rs5c);
if (status < 0)
return status;
if (rs5c->regs[RS5C_REG_CTRL1] & RS5C_CTRL1_AALE) {
buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);
buf[1] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE;
if (i2c_master_send(client, buf, 2) != 2) {
pr_debug("%s: can't disable alarm\n", rs5c->rtc->name);
return -EIO;
}
rs5c->regs[RS5C_REG_CTRL1] = buf[1];
}
/* set alarm */
buf[0] = RS5C_ADDR(RS5C_REG_ALARM_A_MIN);
buf[1] = BIN2BCD(t->time.tm_min);
buf[2] = rs5c_hr2reg(rs5c, t->time.tm_hour);
buf[3] = 0x7f; /* any/all days */
if ((i2c_master_send(client, buf, 4)) != 4) {
pr_debug("%s: can't set alarm time\n", rs5c->rtc->name);
return -EIO;
}
/* ... and maybe enable its irq */
if (t->enabled) {
buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);
buf[1] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE;
if ((i2c_master_send(client, buf, 2)) != 2)
printk(KERN_WARNING "%s: can't enable alarm\n",
rs5c->rtc->name);
rs5c->regs[RS5C_REG_CTRL1] = buf[1];
}
return 0;
}
#if defined(CONFIG_RTC_INTF_PROC) || defined(CONFIG_RTC_INTF_PROC_MODULE)
static int rs5c372_rtc_proc(struct device *dev, struct seq_file *seq)
{
int err, osc, trim;
err = rs5c372_get_trim(to_i2c_client(dev), &osc, &trim);
if (err == 0) {
seq_printf(seq, "crystal\t\t: %d.%03d KHz\n",
osc / 1000, osc % 1000);
seq_printf(seq, "trim\t\t: %d\n", trim);
}
return 0;
}
#else
#define rs5c372_rtc_proc NULL
#endif
static const struct rtc_class_ops rs5c372_rtc_ops = {
.proc = rs5c372_rtc_proc,
.ioctl = rs5c_rtc_ioctl,
.read_time = rs5c372_rtc_read_time,
.set_time = rs5c372_rtc_set_time,
.read_alarm = rs5c_read_alarm,
.set_alarm = rs5c_set_alarm,
};
#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
static ssize_t rs5c372_sysfs_show_trim(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err, trim;
err = rs5c372_get_trim(to_i2c_client(dev), NULL, &trim);
if (err)
return err;
return sprintf(buf, "%d\n", trim);
}
static DEVICE_ATTR(trim, S_IRUGO, rs5c372_sysfs_show_trim, NULL);
static ssize_t rs5c372_sysfs_show_osc(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err, osc;
err = rs5c372_get_trim(to_i2c_client(dev), &osc, NULL);
if (err)
return err;
return sprintf(buf, "%d.%03d KHz\n", osc / 1000, osc % 1000);
}
static DEVICE_ATTR(osc, S_IRUGO, rs5c372_sysfs_show_osc, NULL);
static int rs5c_sysfs_register(struct device *dev)
{
int err;
err = device_create_file(dev, &dev_attr_trim);
if (err)
return err;
err = device_create_file(dev, &dev_attr_osc);
if (err)
device_remove_file(dev, &dev_attr_trim);
return err;
}
#else
static int rs5c_sysfs_register(struct device *dev)
{
return 0;
}
#endif /* SYSFS */
static struct i2c_driver rs5c372_driver;
static int rs5c372_probe(struct i2c_adapter *adapter, int address, int kind)
{
int err = 0;
struct i2c_client *client;
struct rs5c372 *rs5c372;
struct rtc_time tm;
dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto exit;
}
if (!(rs5c372 = kzalloc(sizeof(struct rs5c372), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
/* we read registers 0x0f then 0x00-0x0f; skip the first one */
rs5c372->regs=&rs5c372->buf[1];
/* On conversion to a "new style" i2c driver, we'll be handed
* the i2c_client (we won't create it)
*/
client = &rs5c372->dev;
rs5c372->client = client;
/* I2C client */
client->addr = address;
client->driver = &rs5c372_driver;
client->adapter = adapter;
strlcpy(client->name, rs5c372_driver.driver.name, I2C_NAME_SIZE);
i2c_set_clientdata(client, rs5c372);
/* Inform the i2c layer */
if ((err = i2c_attach_client(client)))
goto exit_kfree;
err = rs5c_get_regs(rs5c372);
if (err < 0)
goto exit_detach;
/* For "new style" drivers, irq is in i2c_client and chip type
* info comes from i2c_client.dev.platform_data. Meanwhile:
*
* STICK BOARD-SPECIFIC SETUP CODE RIGHT HERE
*/
if (rs5c372->type == rtc_undef) {
rs5c372->type = rtc_rs5c372b;
dev_warn(&client->dev, "assuming rs5c372b\n");
}
/* clock may be set for am/pm or 24 hr time */
switch (rs5c372->type) {
case rtc_rs5c372a:
case rtc_rs5c372b:
/* alarm uses ALARM_A; and nINTRA on 372a, nINTR on 372b.
* so does periodic irq, except some 327a modes.
*/
if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C372_CTRL2_24)
rs5c372->time24 = 1;
break;
case rtc_rv5c386:
case rtc_rv5c387a:
if (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24)
rs5c372->time24 = 1;
/* alarm uses ALARM_W; and nINTRB for alarm and periodic
* irq, on both 386 and 387
*/
break;
default:
dev_err(&client->dev, "unknown RTC type\n");
goto exit_detach;
}
/* if the oscillator lost power and no other software (like
* the bootloader) set it up, do it here.
*/
if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP) {
unsigned char buf[3];
rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP;
buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);
buf[1] = rs5c372->regs[RS5C_REG_CTRL1];
buf[2] = rs5c372->regs[RS5C_REG_CTRL2];
/* use 24hr mode */
switch (rs5c372->type) {
case rtc_rs5c372a:
case rtc_rs5c372b:
buf[2] |= RS5C372_CTRL2_24;
rs5c372->time24 = 1;
break;
case rtc_rv5c386:
case rtc_rv5c387a:
buf[1] |= RV5C387_CTRL1_24;
rs5c372->time24 = 1;
break;
default:
/* impossible */
break;
}
if ((i2c_master_send(client, buf, 3)) != 3) {
dev_err(&client->dev, "setup error\n");
goto exit_detach;
}
rs5c372->regs[RS5C_REG_CTRL1] = buf[1];
rs5c372->regs[RS5C_REG_CTRL2] = buf[2];
}
if (rs5c372_get_datetime(client, &tm) < 0)
dev_warn(&client->dev, "clock needs to be set\n");
dev_info(&client->dev, "%s found, %s, driver version " DRV_VERSION "\n",
({ char *s; switch (rs5c372->type) {
case rtc_rs5c372a: s = "rs5c372a"; break;
case rtc_rs5c372b: s = "rs5c372b"; break;
case rtc_rv5c386: s = "rv5c386"; break;
case rtc_rv5c387a: s = "rv5c387a"; break;
default: s = "chip"; break;
}; s;}),
rs5c372->time24 ? "24hr" : "am/pm"
);
/* FIXME when client->irq exists, use it to register alarm irq */
rs5c372->rtc = rtc_device_register(rs5c372_driver.driver.name,
&client->dev, &rs5c372_rtc_ops, THIS_MODULE);
if (IS_ERR(rs5c372->rtc)) {
err = PTR_ERR(rs5c372->rtc);
goto exit_detach;
}
err = rs5c_sysfs_register(&client->dev);
if (err)
goto exit_devreg;
return 0;
exit_devreg:
rtc_device_unregister(rs5c372->rtc);
exit_detach:
i2c_detach_client(client);
exit_kfree:
kfree(rs5c372);
exit:
return err;
}
static int rs5c372_attach(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, rs5c372_probe);
}
static int rs5c372_detach(struct i2c_client *client)
{
int err;
struct rs5c372 *rs5c372 = i2c_get_clientdata(client);
if (rs5c372->rtc)
rtc_device_unregister(rs5c372->rtc);
/* REVISIT properly destroy the sysfs files ... */
if ((err = i2c_detach_client(client)))
return err;
kfree(rs5c372);
return 0;
}
static struct i2c_driver rs5c372_driver = {
.driver = {
.name = "rtc-rs5c372",
},
.attach_adapter = &rs5c372_attach,
.detach_client = &rs5c372_detach,
};
static __init int rs5c372_init(void)
{
return i2c_add_driver(&rs5c372_driver);
}
static __exit void rs5c372_exit(void)
{
i2c_del_driver(&rs5c372_driver);
}
module_init(rs5c372_init);
module_exit(rs5c372_exit);
MODULE_AUTHOR(
"Pavel Mironchik <pmironchik@optifacio.net>, "
"Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);

769
drivers/rtc/rtc-s3c.c Normal file
View File

@@ -0,0 +1,769 @@
/* drivers/rtc/rtc-s3c.c
*
* Copyright (c) 2004,2006 Simtec Electronics
* Ben Dooks, <ben@simtec.co.uk>
* http://armlinux.simtec.co.uk/
*
* 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.
*
* S3C2410/S3C2440/S3C24XX Internal RTC Driver
*/
#include <linux/module.h>
#include <linux/fs.h>
#include <linux/string.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/interrupt.h>
#include <linux/rtc.h>
#include <linux/bcd.h>
#include <linux/clk.h>
#include <asm/hardware.h>
#include <asm/uaccess.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/rtc.h>
#include <asm/mach/time.h>
#include <asm/arch/regs-rtc.h>
/* I have yet to find an S3C implementation with more than one
* of these rtc blocks in */
static struct resource *s3c_rtc_mem;
static void __iomem *s3c_rtc_base;
static int s3c_rtc_alarmno = NO_IRQ;
static int s3c_rtc_tickno = NO_IRQ;
static int s3c_rtc_freq = 1;
static DEFINE_SPINLOCK(s3c_rtc_pie_lock);
static unsigned int tick_count;
/* IRQ Handlers */
/* Qisda, ShiYong Lin, 2009/07/22, Implement function for RTC tick service {*/
static unsigned long uiResolution = 0;
extern void rtc_tick_keypad_message_to_ap (void);
/* Qisda, ShiYong Lin, 2009/07/22, Implement function for RTC tick service }*/
static irqreturn_t s3c_rtc_alarmirq(int irq, void *id)
{
struct rtc_device *rdev = id;
rtc_update_irq(&rdev->class_dev, 1, RTC_AF | RTC_IRQF);
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
writeb(S3C_INTP_ALM, s3c_rtc_base + S3C_INTP);
#endif
return IRQ_HANDLED;
}
static irqreturn_t s3c_rtc_tickirq(int irq, void *id)
{
struct rtc_device *rdev = id;
rtc_tick_keypad_message_to_ap();
rtc_update_irq(&rdev->class_dev, tick_count++, RTC_PF | RTC_IRQF);
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C6410)
writeb(S3C_INTP_TIC, s3c_rtc_base + S3C_INTP);
#endif
return IRQ_HANDLED;
}
/* Update control registers */
static void s3c_rtc_setaie(int to)
{
unsigned int tmp;
pr_debug("%s: aie=%d\n", __FUNCTION__, to);
tmp = readb(s3c_rtc_base + S3C2410_RTCALM) & ~S3C2410_RTCALM_ALMEN;
if (to)
tmp |= S3C2410_RTCALM_ALMEN;
writeb(tmp, s3c_rtc_base + S3C2410_RTCALM);
/* Qisda, ShiYong Lin, 2009/07/18, RTC debug message{*/
#ifdef RTC_DEBUG
tmp = readb(s3c_rtc_base + S3C2410_RTCALM);
printk("s3c_rtc_setaie after read S3C2410_RTCALM = 0x%x\n", tmp);
tmp = readb(s3c_rtc_base + S3C2410_TICNT);
printk("s3c_rtc_setaie after read S3C2410_TICNT = 0x%x\n", tmp);
tmp = readw(s3c_rtc_base + S3C2410_RTCCON);
printk("s3c_rtc_setaie after read S3C2410_RTCCON = 0x%x\n", tmp);
tmp = readl(s3c_rtc_base + 0x90);
printk("s3c_rtc_setaie after read S3C2410_TICKCNT = 0x%x\n", tmp);
#endif
/* Qisda, ShiYong Lin, 2009/07/18, RTC debug message}*/
#if !defined(CONFIG_CPU_S3C6400) && !defined(CONFIG_CPU_S3C6410)
if (readb(s3c_rtc_base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN)
#else
if (1)
#endif
enable_irq_wake(s3c_rtc_alarmno);
else
disable_irq_wake(s3c_rtc_alarmno);
}
static void s3c_rtc_setpie(int to)
{
unsigned int tmp;
pr_debug("%s: pie=%d\n", __FUNCTION__, to);
spin_lock_irq(&s3c_rtc_pie_lock);
#if !defined(CONFIG_CPU_S3C6400) && !defined(CONFIG_CPU_S3C6410)
tmp = readb(s3c_rtc_base + S3C2410_TICNT) & ~S3C2410_TICNT_ENABLE;
if (to)
tmp |= S3C2410_TICNT_ENABLE;
writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
#else
tmp = readw(s3c_rtc_base + S3C2410_RTCCON) & ~S3C_RTCCON_TICEN;
if (to)
tmp |= S3C_RTCCON_TICEN;
writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
#endif
spin_unlock_irq(&s3c_rtc_pie_lock);
}
static void s3c_rtc_setfreq(int freq)
{
unsigned int tmp;
spin_lock_irq(&s3c_rtc_pie_lock);
#if !defined(CONFIG_CPU_S3C6400) && !defined(CONFIG_CPU_S3C6410)
#if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
#if 0
writew(readw(s3c_rtc_base + S3C2410_RTCCON) & (~(1<<8)),s3c_rtc_base + S3C2410_RTCCON);
#else
writew((readw(s3c_rtc_base + S3C2410_RTCCON) & (~(1<<8)))| uiResolution,s3c_rtc_base + S3C2410_RTCCON);
#endif
#else
tmp = readb(s3c_rtc_base + S3C2410_TICNT) & S3C2410_TICNT_ENABLE;
#endif
s3c_rtc_freq = freq;
tmp |= (128 / freq)-1;
writeb(tmp, s3c_rtc_base + S3C2410_TICNT);
#else
tmp = readw(s3c_rtc_base + S3C2410_RTCCON) & (S3C_RTCCON_TICEN | S3C2410_RTCCON_RTCEN );
writew(tmp, s3c_rtc_base + S3C2410_RTCCON);
s3c_rtc_freq = freq;
tmp = (32768 / freq)-1;
#if defined(CONFIG_CPU_S3C6410)
writel(tmp, s3c_rtc_base + S3C2410_TICNT);
#elif defined(CONFIG_CPU_S3C6400)
writew(tmp, s3c_rtc_base + S3C2410_TICNT);
#endif
#endif
spin_unlock_irq(&s3c_rtc_pie_lock);
}
/* Time read/write */
static int s3c_rtc_gettime(struct device *dev, struct rtc_time *rtc_tm)
{
unsigned int have_retried = 0;
void __iomem *base = s3c_rtc_base;
retry_get_time:
rtc_tm->tm_min = readb(base + S3C2410_RTCMIN);
rtc_tm->tm_hour = readb(base + S3C2410_RTCHOUR);
rtc_tm->tm_mday = readb(base + S3C2410_RTCDATE);
rtc_tm->tm_mon = readb(base + S3C2410_RTCMON);
rtc_tm->tm_year = readb(base + S3C2410_RTCYEAR);
rtc_tm->tm_sec = readb(base + S3C2410_RTCSEC);
/* the only way to work out wether the system was mid-update
* when we read it is to check the second counter, and if it
* is zero, then we re-try the entire read
*/
if (rtc_tm->tm_sec == 0 && !have_retried) {
have_retried = 1;
goto retry_get_time;
}
pr_debug("read time %02x.%02x.%02x %02x:%02x:%02x\n",
rtc_tm->tm_year, rtc_tm->tm_mon, rtc_tm->tm_mday,
rtc_tm->tm_hour, rtc_tm->tm_min, rtc_tm->tm_sec);
BCD_TO_BIN(rtc_tm->tm_sec);
BCD_TO_BIN(rtc_tm->tm_min);
BCD_TO_BIN(rtc_tm->tm_hour);
BCD_TO_BIN(rtc_tm->tm_mday);
BCD_TO_BIN(rtc_tm->tm_mon);
BCD_TO_BIN(rtc_tm->tm_year);
rtc_tm->tm_year += 100;
rtc_tm->tm_mon -= 1;
return 0;
}
static int s3c_rtc_settime(struct device *dev, struct rtc_time *tm)
{
void __iomem *base = s3c_rtc_base;
int year = tm->tm_year - 100;
pr_debug("set time %02d.%02d.%02d %02d/%02d/%02d\n",
tm->tm_year, tm->tm_mon, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
/* we get around y2k by simply not supporting it */
if (year < 0 || year >= 100) {
dev_err(dev, "rtc only supports 100 years\n");
return -EINVAL;
}
writeb(BIN2BCD(tm->tm_sec), base + S3C2410_RTCSEC);
writeb(BIN2BCD(tm->tm_min), base + S3C2410_RTCMIN);
writeb(BIN2BCD(tm->tm_hour), base + S3C2410_RTCHOUR);
writeb(BIN2BCD(tm->tm_mday), base + S3C2410_RTCDATE);
writeb(BIN2BCD(tm->tm_mon + 1), base + S3C2410_RTCMON);
writeb(BIN2BCD(year), base + S3C2410_RTCYEAR);
return 0;
}
static int s3c_rtc_getalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_time *alm_tm = &alrm->time;
void __iomem *base = s3c_rtc_base;
unsigned int alm_en;
alm_tm->tm_sec = readb(base + S3C2410_ALMSEC);
alm_tm->tm_min = readb(base + S3C2410_ALMMIN);
alm_tm->tm_hour = readb(base + S3C2410_ALMHOUR);
alm_tm->tm_mon = readb(base + S3C2410_ALMMON);
alm_tm->tm_mday = readb(base + S3C2410_ALMDATE);
alm_tm->tm_year = readb(base + S3C2410_ALMYEAR);
alm_en = readb(base + S3C2410_RTCALM);
alrm->enabled = (alm_en & S3C2410_RTCALM_ALMEN) ? 1 : 0;
pr_debug("read alarm %02x %02x.%02x.%02x %02x/%02x/%02x\n",
alm_en,
alm_tm->tm_year, alm_tm->tm_mon, alm_tm->tm_mday,
alm_tm->tm_hour, alm_tm->tm_min, alm_tm->tm_sec);
/* decode the alarm enable field */
if (alm_en & S3C2410_RTCALM_SECEN)
BCD_TO_BIN(alm_tm->tm_sec);
else
alm_tm->tm_sec = 0xff;
if (alm_en & S3C2410_RTCALM_MINEN)
BCD_TO_BIN(alm_tm->tm_min);
else
alm_tm->tm_min = 0xff;
if (alm_en & S3C2410_RTCALM_HOUREN)
BCD_TO_BIN(alm_tm->tm_hour);
else
alm_tm->tm_hour = 0xff;
if (alm_en & S3C2410_RTCALM_DAYEN)
BCD_TO_BIN(alm_tm->tm_mday);
else
alm_tm->tm_mday = 0xff;
if (alm_en & S3C2410_RTCALM_MONEN) {
BCD_TO_BIN(alm_tm->tm_mon);
alm_tm->tm_mon -= 1;
} else {
alm_tm->tm_mon = 0xff;
}
if (alm_en & S3C2410_RTCALM_YEAREN)
BCD_TO_BIN(alm_tm->tm_year);
else
alm_tm->tm_year = 0xffff;
return 0;
}
static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct rtc_time *tm = &alrm->time;
void __iomem *base = s3c_rtc_base;
unsigned int alrm_en;
pr_debug("s3c_rtc_setalarm: %d, %02x/%02x/%02x %02x.%02x.%02x\n",
alrm->enabled,
tm->tm_mday & 0xff, tm->tm_mon & 0xff, tm->tm_year & 0xff,
tm->tm_hour & 0xff, tm->tm_min & 0xff, tm->tm_sec);
alrm_en = readb(base + S3C2410_RTCALM) & S3C2410_RTCALM_ALMEN;
writeb(0x00, base + S3C2410_RTCALM);
if (tm->tm_sec < 60 && tm->tm_sec >= 0) {
alrm_en |= S3C2410_RTCALM_SECEN;
writeb(BIN2BCD(tm->tm_sec), base + S3C2410_ALMSEC);
}
if (tm->tm_min < 60 && tm->tm_min >= 0) {
alrm_en |= S3C2410_RTCALM_MINEN;
writeb(BIN2BCD(tm->tm_min), base + S3C2410_ALMMIN);
}
if (tm->tm_hour < 24 && tm->tm_hour >= 0) {
alrm_en |= S3C2410_RTCALM_HOUREN;
writeb(BIN2BCD(tm->tm_hour), base + S3C2410_ALMHOUR);
}
pr_debug("setting S3C2410_RTCALM to %08x\n", alrm_en);
writeb(alrm_en, base + S3C2410_RTCALM);
if (0) {
alrm_en = readb(base + S3C2410_RTCALM);
alrm_en &= ~S3C2410_RTCALM_ALMEN;
writeb(alrm_en, base + S3C2410_RTCALM);
disable_irq_wake(s3c_rtc_alarmno);
}
/*
#if !defined(CONFIG_CPU_S3C6400) && !defined(CONFIG_CPU_S3C6410)
if (alrm->enabled)
#else
if (1)
#endif
enable_irq_wake(s3c_rtc_alarmno);
else
disable_irq_wake(s3c_rtc_alarmno);
*/
return 0;
}
static int s3c_rtc_ioctl(struct device *dev,
unsigned int cmd, unsigned long arg)
{
unsigned int ret = -ENOIOCTLCMD;
switch (cmd) {
case RTC_AIE_OFF:
case RTC_AIE_ON:
s3c_rtc_setaie((cmd == RTC_AIE_ON) ? 1 : 0);
ret = 0;
break;
case RTC_PIE_OFF:
case RTC_PIE_ON:
tick_count = 0;
s3c_rtc_setpie((cmd == RTC_PIE_ON) ? 1 : 0);
ret = 0;
break;
case RTC_IRQP_READ:
ret = put_user(s3c_rtc_freq, (unsigned long __user *)arg);
break;
case RTC_IRQP_SET:
/* check for power of 2 */
printk("\nRTC_IRQP_SET arg = %d\n",(int)arg );
if ((arg & (arg-1)) != 0 || arg < 1) {
ret = -EINVAL;
goto exit;
}
s3c_rtc_setfreq(arg);
ret = 0;
break;
case RTC_UIE_ON:
case RTC_UIE_OFF:
ret = -EINVAL;
break;
/* Qisda, ShiYong Lin, 2009/07/22, Implement function for RTC tick service {*/
case RTC_SET_RESOLUTION:
printk("RTC_SET_RESOLUTIONk = %d\n", (int)arg);
if( arg > 14 || arg < 0)
{
printk("RTC_SET_RESOLUTION, Error\n");
ret = -EINVAL;
}
else
{
uiResolution = (unsigned long) arg;
uiResolution = uiResolution << 5;
printk("RTC_SET_RESOLUTION, uiResolution = %d\n", (int)uiResolution);
ret = 0;
}
break;
case RTC_GET_RESOLUTION:
printk("RTC_GET_RESOLUTION = %d\n", (int)uiResolution);
ret = put_user(uiResolution, (unsigned long __user *)arg);
break;
/* Qisda, ShiYong Lin, 2009/07/22, Implement function for RTC tick service }*/
}
exit:
return ret;
}
static int s3c_rtc_proc(struct device *dev, struct seq_file *seq)
{
unsigned int ticnt = readb(s3c_rtc_base + S3C2410_TICNT);
seq_printf(seq, "periodic_IRQ\t: %s\n",
(ticnt & S3C2410_TICNT_ENABLE) ? "yes" : "no" );
seq_printf(seq, "periodic_freq\t: %d\n", s3c_rtc_freq);
return 0;
}
static int s3c_rtc_open(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
int ret;
ret = request_irq(s3c_rtc_alarmno, s3c_rtc_alarmirq,
IRQF_DISABLED, "s3c2410-rtc alarm", rtc_dev);
if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_alarmno, ret);
return ret;
}
ret = request_irq(s3c_rtc_tickno, s3c_rtc_tickirq,
IRQF_DISABLED, "s3c2410-rtc tick", rtc_dev);
if (ret) {
dev_err(dev, "IRQ%d error %d\n", s3c_rtc_tickno, ret);
goto tick_err;
}
return ret;
tick_err:
free_irq(s3c_rtc_alarmno, rtc_dev);
return ret;
}
static void s3c_rtc_release(struct device *dev)
{
struct platform_device *pdev = to_platform_device(dev);
struct rtc_device *rtc_dev = platform_get_drvdata(pdev);
/* do not clear AIE here, it may be needed for wake */
s3c_rtc_setpie(0);
free_irq(s3c_rtc_alarmno, rtc_dev);
free_irq(s3c_rtc_tickno, rtc_dev);
}
static const struct rtc_class_ops s3c_rtcops = {
.open = s3c_rtc_open,
.release = s3c_rtc_release,
.ioctl = s3c_rtc_ioctl,
.read_time = s3c_rtc_gettime,
.set_time = s3c_rtc_settime,
.read_alarm = s3c_rtc_getalarm,
.set_alarm = s3c_rtc_setalarm,
.proc = s3c_rtc_proc,
};
static void s3c_rtc_enable(struct platform_device *pdev, int en)
{
void __iomem *base = s3c_rtc_base;
unsigned int tmp;
if (s3c_rtc_base == NULL)
return;
if (!en) {
#if !defined(CONFIG_CPU_S3C6400) && !defined(CONFIG_CPU_S3C6410)
tmp = readb(base + S3C2410_RTCCON);
writeb(tmp & ~S3C2410_RTCCON_RTCEN, base + S3C2410_RTCCON);
tmp = readb(base + S3C2410_TICNT);
writeb(tmp & ~S3C2410_TICNT_ENABLE, base + S3C2410_TICNT);
#else
tmp = readw(base + S3C2410_RTCCON);
writew(tmp & ~ (S3C2410_RTCCON_RTCEN | S3C_RTCCON_TICEN), base + S3C2410_RTCCON);
#endif
} else {
/* re-enable the device, and check it is ok */
#if !defined(CONFIG_CPU_S3C6400) && !defined(CONFIG_CPU_S3C6410)
if ((readb(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
tmp = readb(base + S3C2410_RTCCON);
writeb(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);
}
if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");
tmp = readb(base + S3C2410_RTCCON);
writeb(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);
}
if ((readb(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
tmp = readb(base + S3C2410_RTCCON);
writeb(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);
}
#else
if ((readw(base+S3C2410_RTCCON) & S3C2410_RTCCON_RTCEN) == 0){
dev_info(&pdev->dev, "rtc disabled, re-enabling\n");
tmp = readw(base + S3C2410_RTCCON);
writew(tmp|S3C2410_RTCCON_RTCEN, base+S3C2410_RTCCON);
}
if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CNTSEL)){
dev_info(&pdev->dev, "removing RTCCON_CNTSEL\n");
tmp = readw(base + S3C2410_RTCCON);
writew(tmp& ~S3C2410_RTCCON_CNTSEL, base+S3C2410_RTCCON);
}
if ((readw(base + S3C2410_RTCCON) & S3C2410_RTCCON_CLKRST)){
dev_info(&pdev->dev, "removing RTCCON_CLKRST\n");
tmp = readw(base + S3C2410_RTCCON);
writew(tmp & ~S3C2410_RTCCON_CLKRST, base+S3C2410_RTCCON);
}
#endif
}
}
static int s3c_rtc_remove(struct platform_device *dev)
{
struct rtc_device *rtc = platform_get_drvdata(dev);
platform_set_drvdata(dev, NULL);
rtc_device_unregister(rtc);
s3c_rtc_setpie(0);
s3c_rtc_setaie(0);
iounmap(s3c_rtc_base);
release_resource(s3c_rtc_mem);
kfree(s3c_rtc_mem);
return 0;
}
static int s3c_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
struct resource *res;
int ret;
/* Qisda, ShiYong Lin, 2009/07/18, Set system time when boot{*/
struct rtc_time rtc_tm;
/* Qisda, ShiYong Lin, 2009/07/18, Set system time when boot}*/
pr_debug("%s: probe=%p\n", __FUNCTION__, pdev);
/* find the IRQs */
s3c_rtc_tickno = platform_get_irq(pdev, 1);
if (s3c_rtc_tickno < 0) {
dev_err(&pdev->dev, "no irq for rtc tick\n");
return -ENOENT;
}
s3c_rtc_alarmno = platform_get_irq(pdev, 0);
if (s3c_rtc_alarmno < 0) {
dev_err(&pdev->dev, "no irq for alarm\n");
return -ENOENT;
}
pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
s3c_rtc_tickno, s3c_rtc_alarmno);
/* get the memory region */
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (res == NULL) {
dev_err(&pdev->dev, "failed to get memory region resource\n");
return -ENOENT;
}
printk("res->start : %x res->end : %x",res->start,res->end);
s3c_rtc_mem = request_mem_region(res->start,
res->end-res->start+1,
pdev->name);
if (s3c_rtc_mem == NULL) {
dev_err(&pdev->dev, "failed to reserve memory region\n");
ret = -ENOENT;
goto err_nores;
}
s3c_rtc_base = ioremap(res->start, res->end - res->start + 1);
if (s3c_rtc_base == NULL) {
dev_err(&pdev->dev, "failed ioremap()\n");
ret = -EINVAL;
goto err_nomap;
}
/* check to see if everything is setup correctly */
s3c_rtc_enable(pdev, 1);
pr_debug("s3c2410_rtc: RTCCON=%02x\n",
readb(s3c_rtc_base + S3C2410_RTCCON));
s3c_rtc_setfreq(s3c_rtc_freq);
/* register RTC and exit */
rtc = rtc_device_register("s3c", &pdev->dev, &s3c_rtcops,
THIS_MODULE);
if (IS_ERR(rtc)) {
dev_err(&pdev->dev, "cannot attach rtc\n");
ret = PTR_ERR(rtc);
goto err_nortc;
}
#if !defined(CONFIG_CPU_S3C6400) && !defined(CONFIG_CPU_S3C6410)
rtc->max_user_freq = 128;
#else
rtc->max_user_freq = 32768;
#endif
/* Qisda, ShiYong Lin, 2009/08/18, No need to set up RTC when boot {*/
/*
rtc_tm.tm_mday = 2;
rtc_tm.tm_mon = 7;
rtc_tm.tm_year = 9;
rtc_tm.tm_hour = 17;
rtc_tm.tm_min = 35;
rtc_tm.tm_sec = 5;
writeb(BIN2BCD(rtc_tm.tm_sec), s3c_rtc_base + S3C2410_RTCSEC);
writeb(BIN2BCD(rtc_tm.tm_min), s3c_rtc_base + S3C2410_RTCMIN);
writeb(BIN2BCD(rtc_tm.tm_hour), s3c_rtc_base + S3C2410_RTCHOUR);
writeb(BIN2BCD(rtc_tm.tm_mday), s3c_rtc_base + S3C2410_RTCDATE);
writeb(BIN2BCD(rtc_tm.tm_mon + 1), s3c_rtc_base + S3C2410_RTCMON);
writeb(BIN2BCD(rtc_tm.tm_year), s3c_rtc_base + S3C2410_RTCYEAR);
*/
/* } Qisda, ShiYong Lin, 2009/08/18, No need to set up RTC when boot */
platform_set_drvdata(pdev, rtc);
return 0;
err_nortc:
s3c_rtc_enable(pdev, 0);
iounmap(s3c_rtc_base);
err_nomap:
release_resource(s3c_rtc_mem);
err_nores:
return ret;
}
#ifdef CONFIG_PM
/* RTC Power management control */
static struct timespec s3c_rtc_delta;
static int ticnt_save;
static int s3c_rtc_suspend(struct platform_device *pdev, pm_message_t state)
{
struct rtc_time tm;
struct timespec time;
time.tv_nsec = 0;
/* save TICNT for anyone using periodic interrupts */
ticnt_save = readb(s3c_rtc_base + S3C2410_TICNT);
/* calculate time delta for suspend */
s3c_rtc_gettime(&pdev->dev, &tm);
rtc_tm_to_time(&tm, &time.tv_sec);
save_time_delta(&s3c_rtc_delta, &time);
s3c_rtc_enable(pdev, 0);
return 0;
}
static int s3c_rtc_resume(struct platform_device *pdev)
{
struct rtc_time tm;
struct timespec time;
time.tv_nsec = 0;
s3c_rtc_enable(pdev, 1);
s3c_rtc_gettime(&pdev->dev, &tm);
rtc_tm_to_time(&tm, &time.tv_sec);
restore_time_delta(&s3c_rtc_delta, &time);
writeb(ticnt_save, s3c_rtc_base + S3C2410_TICNT);
return 0;
}
#else
#define s3c_rtc_suspend NULL
#define s3c_rtc_resume NULL
#endif
static struct platform_driver s3c2410_rtcdrv = {
.probe = s3c_rtc_probe,
.remove = s3c_rtc_remove,
.suspend = s3c_rtc_suspend,
.resume = s3c_rtc_resume,
.driver = {
.name = "s3c2410-rtc",
.owner = THIS_MODULE,
},
};
static char __initdata banner[] = "S3C24XX RTC, (c) 2004,2006 Simtec Electronics\n";
static int __init s3c_rtc_init(void)
{
printk(banner);
return platform_driver_register(&s3c2410_rtcdrv);
}
static void __exit s3c_rtc_exit(void)
{
platform_driver_unregister(&s3c2410_rtcdrv);
}
module_init(s3c_rtc_init);
module_exit(s3c_rtc_exit);
MODULE_DESCRIPTION("Samsung S3C RTC Driver");
MODULE_AUTHOR("Ben Dooks <ben@simtec.co.uk>");
MODULE_LICENSE("GPL");

378
drivers/rtc/rtc-sa1100.c Normal file
View File

@@ -0,0 +1,378 @@
/*
* Real Time Clock interface for StrongARM SA1x00 and XScale PXA2xx
*
* Copyright (c) 2000 Nils Faerber
*
* Based on rtc.c by Paul Gortmaker
*
* Original Driver by Nils Faerber <nils@kernelconcepts.de>
*
* Modifications from:
* CIH <cih@coventive.com>
* Nicolas Pitre <nico@cam.org>
* Andrew Christian <andrew.christian@hp.com>
*
* Converted to the RTC subsystem and Driver Model
* by Richard Purdie <rpurdie@rpsys.net>
*
* 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/platform_device.h>
#include <linux/module.h>
#include <linux/rtc.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/interrupt.h>
#include <linux/string.h>
#include <linux/pm.h>
#include <asm/bitops.h>
#include <asm/hardware.h>
#include <asm/irq.h>
#include <asm/rtc.h>
#ifdef CONFIG_ARCH_PXA
#include <asm/arch/pxa-regs.h>
#endif
#define TIMER_FREQ CLOCK_TICK_RATE
#define RTC_DEF_DIVIDER 32768 - 1
#define RTC_DEF_TRIM 0
static unsigned long rtc_freq = 1024;
static struct rtc_time rtc_alarm;
static DEFINE_SPINLOCK(sa1100_rtc_lock);
static int rtc_update_alarm(struct rtc_time *alrm)
{
struct rtc_time alarm_tm, now_tm;
unsigned long now, time;
int ret;
do {
now = RCNR;
rtc_time_to_tm(now, &now_tm);
rtc_next_alarm_time(&alarm_tm, &now_tm, alrm);
ret = rtc_tm_to_time(&alarm_tm, &time);
if (ret != 0)
break;
RTSR = RTSR & (RTSR_HZE|RTSR_ALE|RTSR_AL);
RTAR = time;
} while (now != RCNR);
return ret;
}
static irqreturn_t sa1100_rtc_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = to_platform_device(dev_id);
struct rtc_device *rtc = platform_get_drvdata(pdev);
unsigned int rtsr;
unsigned long events = 0;
spin_lock(&sa1100_rtc_lock);
rtsr = RTSR;
/* clear interrupt sources */
RTSR = 0;
RTSR = (RTSR_AL | RTSR_HZ) & (rtsr >> 2);
/* clear alarm interrupt if it has occurred */
if (rtsr & RTSR_AL)
rtsr &= ~RTSR_ALE;
RTSR = rtsr & (RTSR_ALE | RTSR_HZE);
/* update irq data & counter */
if (rtsr & RTSR_AL)
events |= RTC_AF | RTC_IRQF;
if (rtsr & RTSR_HZ)
events |= RTC_UF | RTC_IRQF;
rtc_update_irq(&rtc->class_dev, 1, events);
if (rtsr & RTSR_AL && rtc_periodic_alarm(&rtc_alarm))
rtc_update_alarm(&rtc_alarm);
spin_unlock(&sa1100_rtc_lock);
return IRQ_HANDLED;
}
static int rtc_timer1_count;
static irqreturn_t timer1_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = to_platform_device(dev_id);
struct rtc_device *rtc = platform_get_drvdata(pdev);
/*
* If we match for the first time, rtc_timer1_count will be 1.
* Otherwise, we wrapped around (very unlikely but
* still possible) so compute the amount of missed periods.
* The match reg is updated only when the data is actually retrieved
* to avoid unnecessary interrupts.
*/
OSSR = OSSR_M1; /* clear match on timer1 */
rtc_update_irq(&rtc->class_dev, rtc_timer1_count, RTC_PF | RTC_IRQF);
if (rtc_timer1_count == 1)
rtc_timer1_count = (rtc_freq * ((1<<30)/(TIMER_FREQ>>2)));
return IRQ_HANDLED;
}
static int sa1100_rtc_read_callback(struct device *dev, int data)
{
if (data & RTC_PF) {
/* interpolate missed periods and set match for the next */
unsigned long period = TIMER_FREQ/rtc_freq;
unsigned long oscr = OSCR;
unsigned long osmr1 = OSMR1;
unsigned long missed = (oscr - osmr1)/period;
data += missed << 8;
OSSR = OSSR_M1; /* clear match on timer 1 */
OSMR1 = osmr1 + (missed + 1)*period;
/* Ensure we didn't miss another match in the mean time.
* Here we compare (match - OSCR) 8 instead of 0 --
* see comment in pxa_timer_interrupt() for explanation.
*/
while( (signed long)((osmr1 = OSMR1) - OSCR) <= 8 ) {
data += 0x100;
OSSR = OSSR_M1; /* clear match on timer 1 */
OSMR1 = osmr1 + period;
}
}
return data;
}
static int sa1100_rtc_open(struct device *dev)
{
int ret;
ret = request_irq(IRQ_RTC1Hz, sa1100_rtc_interrupt, IRQF_DISABLED,
"rtc 1Hz", dev);
if (ret) {
dev_err(dev, "IRQ %d already in use.\n", IRQ_RTC1Hz);
goto fail_ui;
}
ret = request_irq(IRQ_RTCAlrm, sa1100_rtc_interrupt, IRQF_DISABLED,
"rtc Alrm", dev);
if (ret) {
dev_err(dev, "IRQ %d already in use.\n", IRQ_RTCAlrm);
goto fail_ai;
}
ret = request_irq(IRQ_OST1, timer1_interrupt, IRQF_DISABLED,
"rtc timer", dev);
if (ret) {
dev_err(dev, "IRQ %d already in use.\n", IRQ_OST1);
goto fail_pi;
}
return 0;
fail_pi:
free_irq(IRQ_RTCAlrm, dev);
fail_ai:
free_irq(IRQ_RTC1Hz, dev);
fail_ui:
return ret;
}
static void sa1100_rtc_release(struct device *dev)
{
spin_lock_irq(&sa1100_rtc_lock);
RTSR = 0;
OIER &= ~OIER_E1;
OSSR = OSSR_M1;
spin_unlock_irq(&sa1100_rtc_lock);
free_irq(IRQ_OST1, dev);
free_irq(IRQ_RTCAlrm, dev);
free_irq(IRQ_RTC1Hz, dev);
}
static int sa1100_rtc_ioctl(struct device *dev, unsigned int cmd,
unsigned long arg)
{
switch(cmd) {
case RTC_AIE_OFF:
spin_lock_irq(&sa1100_rtc_lock);
RTSR &= ~RTSR_ALE;
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_AIE_ON:
spin_lock_irq(&sa1100_rtc_lock);
RTSR |= RTSR_ALE;
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_UIE_OFF:
spin_lock_irq(&sa1100_rtc_lock);
RTSR &= ~RTSR_HZE;
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_UIE_ON:
spin_lock_irq(&sa1100_rtc_lock);
RTSR |= RTSR_HZE;
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_PIE_OFF:
spin_lock_irq(&sa1100_rtc_lock);
OIER &= ~OIER_E1;
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_PIE_ON:
spin_lock_irq(&sa1100_rtc_lock);
OSMR1 = TIMER_FREQ/rtc_freq + OSCR;
OIER |= OIER_E1;
rtc_timer1_count = 1;
spin_unlock_irq(&sa1100_rtc_lock);
return 0;
case RTC_IRQP_READ:
return put_user(rtc_freq, (unsigned long *)arg);
case RTC_IRQP_SET:
if (arg < 1 || arg > TIMER_FREQ)
return -EINVAL;
rtc_freq = arg;
return 0;
}
return -ENOIOCTLCMD;
}
static int sa1100_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
rtc_time_to_tm(RCNR, tm);
return 0;
}
static int sa1100_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
unsigned long time;
int ret;
ret = rtc_tm_to_time(tm, &time);
if (ret == 0)
RCNR = time;
return ret;
}
static int sa1100_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
u32 rtsr;
memcpy(&alrm->time, &rtc_alarm, sizeof(struct rtc_time));
rtsr = RTSR;
alrm->enabled = (rtsr & RTSR_ALE) ? 1 : 0;
alrm->pending = (rtsr & RTSR_AL) ? 1 : 0;
return 0;
}
static int sa1100_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
int ret;
spin_lock_irq(&sa1100_rtc_lock);
ret = rtc_update_alarm(&alrm->time);
if (ret == 0) {
if (alrm->enabled)
RTSR |= RTSR_ALE;
else
RTSR &= ~RTSR_ALE;
}
spin_unlock_irq(&sa1100_rtc_lock);
return ret;
}
static int sa1100_rtc_proc(struct device *dev, struct seq_file *seq)
{
seq_printf(seq, "trim/divider\t: 0x%08x\n", (u32) RTTR);
seq_printf(seq, "update_IRQ\t: %s\n",
(RTSR & RTSR_HZE) ? "yes" : "no");
seq_printf(seq, "periodic_IRQ\t: %s\n",
(OIER & OIER_E1) ? "yes" : "no");
seq_printf(seq, "periodic_freq\t: %ld\n", rtc_freq);
return 0;
}
static const struct rtc_class_ops sa1100_rtc_ops = {
.open = sa1100_rtc_open,
.read_callback = sa1100_rtc_read_callback,
.release = sa1100_rtc_release,
.ioctl = sa1100_rtc_ioctl,
.read_time = sa1100_rtc_read_time,
.set_time = sa1100_rtc_set_time,
.read_alarm = sa1100_rtc_read_alarm,
.set_alarm = sa1100_rtc_set_alarm,
.proc = sa1100_rtc_proc,
};
static int sa1100_rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
/*
* According to the manual we should be able to let RTTR be zero
* and then a default diviser for a 32.768KHz clock is used.
* Apparently this doesn't work, at least for my SA1110 rev 5.
* If the clock divider is uninitialized then reset it to the
* default value to get the 1Hz clock.
*/
if (RTTR == 0) {
RTTR = RTC_DEF_DIVIDER + (RTC_DEF_TRIM << 16);
dev_warn(&pdev->dev, "warning: initializing default clock divider/trim value\n");
/* The current RTC value probably doesn't make sense either */
RCNR = 0;
}
rtc = rtc_device_register(pdev->name, &pdev->dev, &sa1100_rtc_ops,
THIS_MODULE);
if (IS_ERR(rtc))
return PTR_ERR(rtc);
platform_set_drvdata(pdev, rtc);
return 0;
}
static int sa1100_rtc_remove(struct platform_device *pdev)
{
struct rtc_device *rtc = platform_get_drvdata(pdev);
if (rtc)
rtc_device_unregister(rtc);
return 0;
}
static struct platform_driver sa1100_rtc_driver = {
.probe = sa1100_rtc_probe,
.remove = sa1100_rtc_remove,
.driver = {
.name = "sa1100-rtc",
},
};
static int __init sa1100_rtc_init(void)
{
return platform_driver_register(&sa1100_rtc_driver);
}
static void __exit sa1100_rtc_exit(void)
{
platform_driver_unregister(&sa1100_rtc_driver);
}
module_init(sa1100_rtc_init);
module_exit(sa1100_rtc_exit);
MODULE_AUTHOR("Richard Purdie <rpurdie@rpsys.net>");
MODULE_DESCRIPTION("SA11x0/PXA2xx Realtime Clock Driver (RTC)");
MODULE_LICENSE("GPL");

646
drivers/rtc/rtc-sh.c Normal file
View File

@@ -0,0 +1,646 @@
/*
* SuperH On-Chip RTC Support
*
* Copyright (C) 2006 Paul Mundt
* Copyright (C) 2006 Jamie Lenehan
*
* Based on the old arch/sh/kernel/cpu/rtc.c by:
*
* Copyright (C) 2000 Philipp Rumpf <prumpf@tux.org>
* Copyright (C) 1999 Tetsuya Okada & Niibe Yutaka
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/seq_file.h>
#include <linux/interrupt.h>
#include <linux/spinlock.h>
#include <linux/io.h>
#define DRV_NAME "sh-rtc"
#define DRV_VERSION "0.1.2"
#ifdef CONFIG_CPU_SH3
#define rtc_reg_size sizeof(u16)
#define RTC_BIT_INVERTED 0 /* No bug on SH7708, SH7709A */
#elif defined(CONFIG_CPU_SH4)
#define rtc_reg_size sizeof(u32)
#define RTC_BIT_INVERTED 0x40 /* bug on SH7750, SH7750S */
#endif
#define RTC_REG(r) ((r) * rtc_reg_size)
#define R64CNT RTC_REG(0)
#define RSECCNT RTC_REG(1) /* RTC sec */
#define RMINCNT RTC_REG(2) /* RTC min */
#define RHRCNT RTC_REG(3) /* RTC hour */
#define RWKCNT RTC_REG(4) /* RTC week */
#define RDAYCNT RTC_REG(5) /* RTC day */
#define RMONCNT RTC_REG(6) /* RTC month */
#define RYRCNT RTC_REG(7) /* RTC year */
#define RSECAR RTC_REG(8) /* ALARM sec */
#define RMINAR RTC_REG(9) /* ALARM min */
#define RHRAR RTC_REG(10) /* ALARM hour */
#define RWKAR RTC_REG(11) /* ALARM week */
#define RDAYAR RTC_REG(12) /* ALARM day */
#define RMONAR RTC_REG(13) /* ALARM month */
#define RCR1 RTC_REG(14) /* Control */
#define RCR2 RTC_REG(15) /* Control */
/* ALARM Bits - or with BCD encoded value */
#define AR_ENB 0x80 /* Enable for alarm cmp */
/* RCR1 Bits */
#define RCR1_CF 0x80 /* Carry Flag */
#define RCR1_CIE 0x10 /* Carry Interrupt Enable */
#define RCR1_AIE 0x08 /* Alarm Interrupt Enable */
#define RCR1_AF 0x01 /* Alarm Flag */
/* RCR2 Bits */
#define RCR2_PEF 0x80 /* PEriodic interrupt Flag */
#define RCR2_PESMASK 0x70 /* Periodic interrupt Set */
#define RCR2_RTCEN 0x08 /* ENable RTC */
#define RCR2_ADJ 0x04 /* ADJustment (30-second) */
#define RCR2_RESET 0x02 /* Reset bit */
#define RCR2_START 0x01 /* Start bit */
struct sh_rtc {
void __iomem *regbase;
unsigned long regsize;
struct resource *res;
unsigned int alarm_irq, periodic_irq, carry_irq;
struct rtc_device *rtc_dev;
spinlock_t lock;
int rearm_aie;
};
static irqreturn_t sh_rtc_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = to_platform_device(dev_id);
struct sh_rtc *rtc = platform_get_drvdata(pdev);
unsigned int tmp, events = 0;
spin_lock(&rtc->lock);
tmp = readb(rtc->regbase + RCR1);
tmp &= ~RCR1_CF;
if (rtc->rearm_aie) {
if (tmp & RCR1_AF)
tmp &= ~RCR1_AF; /* try to clear AF again */
else {
tmp |= RCR1_AIE; /* AF has cleared, rearm IRQ */
rtc->rearm_aie = 0;
}
}
writeb(tmp, rtc->regbase + RCR1);
rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events);
spin_unlock(&rtc->lock);
return IRQ_HANDLED;
}
static irqreturn_t sh_rtc_alarm(int irq, void *dev_id)
{
struct platform_device *pdev = to_platform_device(dev_id);
struct sh_rtc *rtc = platform_get_drvdata(pdev);
unsigned int tmp, events = 0;
spin_lock(&rtc->lock);
tmp = readb(rtc->regbase + RCR1);
/*
* If AF is set then the alarm has triggered. If we clear AF while
* the alarm time still matches the RTC time then AF will
* immediately be set again, and if AIE is enabled then the alarm
* interrupt will immediately be retrigger. So we clear AIE here
* and use rtc->rearm_aie so that the carry interrupt will keep
* trying to clear AF and once it stays cleared it'll re-enable
* AIE.
*/
if (tmp & RCR1_AF) {
events |= RTC_AF | RTC_IRQF;
tmp &= ~(RCR1_AF|RCR1_AIE);
writeb(tmp, rtc->regbase + RCR1);
rtc->rearm_aie = 1;
rtc_update_irq(&rtc->rtc_dev->class_dev, 1, events);
}
spin_unlock(&rtc->lock);
return IRQ_HANDLED;
}
static irqreturn_t sh_rtc_periodic(int irq, void *dev_id)
{
struct platform_device *pdev = to_platform_device(dev_id);
struct sh_rtc *rtc = platform_get_drvdata(pdev);
spin_lock(&rtc->lock);
rtc_update_irq(&rtc->rtc_dev->class_dev, 1, RTC_PF | RTC_IRQF);
spin_unlock(&rtc->lock);
return IRQ_HANDLED;
}
static inline void sh_rtc_setpie(struct device *dev, unsigned int enable)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
unsigned int tmp;
spin_lock_irq(&rtc->lock);
tmp = readb(rtc->regbase + RCR2);
if (enable) {
tmp &= ~RCR2_PESMASK;
tmp |= RCR2_PEF | (2 << 4);
} else
tmp &= ~(RCR2_PESMASK | RCR2_PEF);
writeb(tmp, rtc->regbase + RCR2);
spin_unlock_irq(&rtc->lock);
}
static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
unsigned int tmp;
spin_lock_irq(&rtc->lock);
tmp = readb(rtc->regbase + RCR1);
if (!enable) {
tmp &= ~RCR1_AIE;
rtc->rearm_aie = 0;
} else if (rtc->rearm_aie == 0)
tmp |= RCR1_AIE;
writeb(tmp, rtc->regbase + RCR1);
spin_unlock_irq(&rtc->lock);
}
static int sh_rtc_open(struct device *dev)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
unsigned int tmp;
int ret;
tmp = readb(rtc->regbase + RCR1);
tmp &= ~RCR1_CF;
tmp |= RCR1_CIE;
writeb(tmp, rtc->regbase + RCR1);
ret = request_irq(rtc->periodic_irq, sh_rtc_periodic, IRQF_DISABLED,
"sh-rtc period", dev);
if (unlikely(ret)) {
dev_err(dev, "request period IRQ failed with %d, IRQ %d\n",
ret, rtc->periodic_irq);
return ret;
}
ret = request_irq(rtc->carry_irq, sh_rtc_interrupt, IRQF_DISABLED,
"sh-rtc carry", dev);
if (unlikely(ret)) {
dev_err(dev, "request carry IRQ failed with %d, IRQ %d\n",
ret, rtc->carry_irq);
free_irq(rtc->periodic_irq, dev);
goto err_bad_carry;
}
ret = request_irq(rtc->alarm_irq, sh_rtc_alarm, IRQF_DISABLED,
"sh-rtc alarm", dev);
if (unlikely(ret)) {
dev_err(dev, "request alarm IRQ failed with %d, IRQ %d\n",
ret, rtc->alarm_irq);
goto err_bad_alarm;
}
return 0;
err_bad_alarm:
free_irq(rtc->carry_irq, dev);
err_bad_carry:
free_irq(rtc->periodic_irq, dev);
return ret;
}
static void sh_rtc_release(struct device *dev)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
sh_rtc_setpie(dev, 0);
sh_rtc_setaie(dev, 0);
free_irq(rtc->periodic_irq, dev);
free_irq(rtc->carry_irq, dev);
free_irq(rtc->alarm_irq, dev);
}
static int sh_rtc_proc(struct device *dev, struct seq_file *seq)
{
struct sh_rtc *rtc = dev_get_drvdata(dev);
unsigned int tmp;
tmp = readb(rtc->regbase + RCR1);
seq_printf(seq, "carry_IRQ\t: %s\n",
(tmp & RCR1_CIE) ? "yes" : "no");
tmp = readb(rtc->regbase + RCR2);
seq_printf(seq, "periodic_IRQ\t: %s\n",
(tmp & RCR2_PEF) ? "yes" : "no");
return 0;
}
static int sh_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
unsigned int ret = -ENOIOCTLCMD;
switch (cmd) {
case RTC_PIE_OFF:
case RTC_PIE_ON:
sh_rtc_setpie(dev, cmd == RTC_PIE_ON);
ret = 0;
break;
case RTC_AIE_OFF:
case RTC_AIE_ON:
sh_rtc_setaie(dev, cmd == RTC_AIE_ON);
ret = 0;
break;
}
return ret;
}
static int sh_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_rtc *rtc = platform_get_drvdata(pdev);
unsigned int sec128, sec2, yr, yr100, cf_bit;
do {
unsigned int tmp;
spin_lock_irq(&rtc->lock);
tmp = readb(rtc->regbase + RCR1);
tmp &= ~RCR1_CF; /* Clear CF-bit */
tmp |= RCR1_CIE;
writeb(tmp, rtc->regbase + RCR1);
sec128 = readb(rtc->regbase + R64CNT);
tm->tm_sec = BCD2BIN(readb(rtc->regbase + RSECCNT));
tm->tm_min = BCD2BIN(readb(rtc->regbase + RMINCNT));
tm->tm_hour = BCD2BIN(readb(rtc->regbase + RHRCNT));
tm->tm_wday = BCD2BIN(readb(rtc->regbase + RWKCNT));
tm->tm_mday = BCD2BIN(readb(rtc->regbase + RDAYCNT));
tm->tm_mon = BCD2BIN(readb(rtc->regbase + RMONCNT)) - 1;
#if defined(CONFIG_CPU_SH4)
yr = readw(rtc->regbase + RYRCNT);
yr100 = BCD2BIN(yr >> 8);
yr &= 0xff;
#else
yr = readb(rtc->regbase + RYRCNT);
yr100 = BCD2BIN((yr == 0x99) ? 0x19 : 0x20);
#endif
tm->tm_year = (yr100 * 100 + BCD2BIN(yr)) - 1900;
sec2 = readb(rtc->regbase + R64CNT);
cf_bit = readb(rtc->regbase + RCR1) & RCR1_CF;
spin_unlock_irq(&rtc->lock);
} while (cf_bit != 0 || ((sec128 ^ sec2) & RTC_BIT_INVERTED) != 0);
#if RTC_BIT_INVERTED != 0
if ((sec128 & RTC_BIT_INVERTED))
tm->tm_sec--;
#endif
dev_dbg(&dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon + 1, tm->tm_year, tm->tm_wday);
if (rtc_valid_tm(tm) < 0)
dev_err(dev, "invalid date\n");
return 0;
}
static int sh_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_rtc *rtc = platform_get_drvdata(pdev);
unsigned int tmp;
int year;
spin_lock_irq(&rtc->lock);
/* Reset pre-scaler & stop RTC */
tmp = readb(rtc->regbase + RCR2);
tmp |= RCR2_RESET;
writeb(tmp, rtc->regbase + RCR2);
writeb(BIN2BCD(tm->tm_sec), rtc->regbase + RSECCNT);
writeb(BIN2BCD(tm->tm_min), rtc->regbase + RMINCNT);
writeb(BIN2BCD(tm->tm_hour), rtc->regbase + RHRCNT);
writeb(BIN2BCD(tm->tm_wday), rtc->regbase + RWKCNT);
writeb(BIN2BCD(tm->tm_mday), rtc->regbase + RDAYCNT);
writeb(BIN2BCD(tm->tm_mon + 1), rtc->regbase + RMONCNT);
#ifdef CONFIG_CPU_SH3
year = tm->tm_year % 100;
writeb(BIN2BCD(year), rtc->regbase + RYRCNT);
#else
year = (BIN2BCD((tm->tm_year + 1900) / 100) << 8) |
BIN2BCD(tm->tm_year % 100);
writew(year, rtc->regbase + RYRCNT);
#endif
/* Start RTC */
tmp = readb(rtc->regbase + RCR2);
tmp &= ~RCR2_RESET;
tmp |= RCR2_RTCEN | RCR2_START;
writeb(tmp, rtc->regbase + RCR2);
spin_unlock_irq(&rtc->lock);
return 0;
}
static inline int sh_rtc_read_alarm_value(struct sh_rtc *rtc, int reg_off)
{
unsigned int byte;
int value = 0xff; /* return 0xff for ignored values */
byte = readb(rtc->regbase + reg_off);
if (byte & AR_ENB) {
byte &= ~AR_ENB; /* strip the enable bit */
value = BCD2BIN(byte);
}
return value;
}
static int sh_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_rtc *rtc = platform_get_drvdata(pdev);
struct rtc_time* tm = &wkalrm->time;
spin_lock_irq(&rtc->lock);
tm->tm_sec = sh_rtc_read_alarm_value(rtc, RSECAR);
tm->tm_min = sh_rtc_read_alarm_value(rtc, RMINAR);
tm->tm_hour = sh_rtc_read_alarm_value(rtc, RHRAR);
tm->tm_wday = sh_rtc_read_alarm_value(rtc, RWKAR);
tm->tm_mday = sh_rtc_read_alarm_value(rtc, RDAYAR);
tm->tm_mon = sh_rtc_read_alarm_value(rtc, RMONAR);
if (tm->tm_mon > 0)
tm->tm_mon -= 1; /* RTC is 1-12, tm_mon is 0-11 */
tm->tm_year = 0xffff;
wkalrm->enabled = (readb(rtc->regbase + RCR1) & RCR1_AIE) ? 1 : 0;
spin_unlock_irq(&rtc->lock);
return 0;
}
static inline void sh_rtc_write_alarm_value(struct sh_rtc *rtc,
int value, int reg_off)
{
/* < 0 for a value that is ignored */
if (value < 0)
writeb(0, rtc->regbase + reg_off);
else
writeb(BIN2BCD(value) | AR_ENB, rtc->regbase + reg_off);
}
static int sh_rtc_check_alarm(struct rtc_time* tm)
{
/*
* The original rtc says anything > 0xc0 is "don't care" or "match
* all" - most users use 0xff but rtc-dev uses -1 for the same thing.
* The original rtc doesn't support years - some things use -1 and
* some 0xffff. We use -1 to make out tests easier.
*/
if (tm->tm_year == 0xffff)
tm->tm_year = -1;
if (tm->tm_mon >= 0xff)
tm->tm_mon = -1;
if (tm->tm_mday >= 0xff)
tm->tm_mday = -1;
if (tm->tm_wday >= 0xff)
tm->tm_wday = -1;
if (tm->tm_hour >= 0xff)
tm->tm_hour = -1;
if (tm->tm_min >= 0xff)
tm->tm_min = -1;
if (tm->tm_sec >= 0xff)
tm->tm_sec = -1;
if (tm->tm_year > 9999 ||
tm->tm_mon >= 12 ||
tm->tm_mday == 0 || tm->tm_mday >= 32 ||
tm->tm_wday >= 7 ||
tm->tm_hour >= 24 ||
tm->tm_min >= 60 ||
tm->tm_sec >= 60)
return -EINVAL;
return 0;
}
static int sh_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
struct platform_device *pdev = to_platform_device(dev);
struct sh_rtc *rtc = platform_get_drvdata(pdev);
unsigned int rcr1;
struct rtc_time *tm = &wkalrm->time;
int mon, err;
err = sh_rtc_check_alarm(tm);
if (unlikely(err < 0))
return err;
spin_lock_irq(&rtc->lock);
/* disable alarm interrupt and clear the alarm flag */
rcr1 = readb(rtc->regbase + RCR1);
rcr1 &= ~(RCR1_AF|RCR1_AIE);
writeb(rcr1, rtc->regbase + RCR1);
rtc->rearm_aie = 0;
/* set alarm time */
sh_rtc_write_alarm_value(rtc, tm->tm_sec, RSECAR);
sh_rtc_write_alarm_value(rtc, tm->tm_min, RMINAR);
sh_rtc_write_alarm_value(rtc, tm->tm_hour, RHRAR);
sh_rtc_write_alarm_value(rtc, tm->tm_wday, RWKAR);
sh_rtc_write_alarm_value(rtc, tm->tm_mday, RDAYAR);
mon = tm->tm_mon;
if (mon >= 0)
mon += 1;
sh_rtc_write_alarm_value(rtc, mon, RMONAR);
if (wkalrm->enabled) {
rcr1 |= RCR1_AIE;
writeb(rcr1, rtc->regbase + RCR1);
}
spin_unlock_irq(&rtc->lock);
return 0;
}
static struct rtc_class_ops sh_rtc_ops = {
.open = sh_rtc_open,
.release = sh_rtc_release,
.ioctl = sh_rtc_ioctl,
.read_time = sh_rtc_read_time,
.set_time = sh_rtc_set_time,
.read_alarm = sh_rtc_read_alarm,
.set_alarm = sh_rtc_set_alarm,
.proc = sh_rtc_proc,
};
static int __devinit sh_rtc_probe(struct platform_device *pdev)
{
struct sh_rtc *rtc;
struct resource *res;
int ret = -ENOENT;
rtc = kzalloc(sizeof(struct sh_rtc), GFP_KERNEL);
if (unlikely(!rtc))
return -ENOMEM;
spin_lock_init(&rtc->lock);
rtc->periodic_irq = platform_get_irq(pdev, 0);
if (unlikely(rtc->periodic_irq < 0)) {
dev_err(&pdev->dev, "No IRQ for period\n");
goto err_badres;
}
rtc->carry_irq = platform_get_irq(pdev, 1);
if (unlikely(rtc->carry_irq < 0)) {
dev_err(&pdev->dev, "No IRQ for carry\n");
goto err_badres;
}
rtc->alarm_irq = platform_get_irq(pdev, 2);
if (unlikely(rtc->alarm_irq < 0)) {
dev_err(&pdev->dev, "No IRQ for alarm\n");
goto err_badres;
}
res = platform_get_resource(pdev, IORESOURCE_IO, 0);
if (unlikely(res == NULL)) {
dev_err(&pdev->dev, "No IO resource\n");
goto err_badres;
}
rtc->regsize = res->end - res->start + 1;
rtc->res = request_mem_region(res->start, rtc->regsize, pdev->name);
if (unlikely(!rtc->res)) {
ret = -EBUSY;
goto err_badres;
}
rtc->regbase = (void __iomem *)rtc->res->start;
if (unlikely(!rtc->regbase)) {
ret = -EINVAL;
goto err_badmap;
}
rtc->rtc_dev = rtc_device_register("sh", &pdev->dev,
&sh_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
ret = PTR_ERR(rtc->rtc_dev);
goto err_badmap;
}
platform_set_drvdata(pdev, rtc);
return 0;
err_badmap:
release_resource(rtc->res);
err_badres:
kfree(rtc);
return ret;
}
static int __devexit sh_rtc_remove(struct platform_device *pdev)
{
struct sh_rtc *rtc = platform_get_drvdata(pdev);
if (likely(rtc->rtc_dev))
rtc_device_unregister(rtc->rtc_dev);
sh_rtc_setpie(&pdev->dev, 0);
sh_rtc_setaie(&pdev->dev, 0);
release_resource(rtc->res);
platform_set_drvdata(pdev, NULL);
kfree(rtc);
return 0;
}
static struct platform_driver sh_rtc_platform_driver = {
.driver = {
.name = DRV_NAME,
.owner = THIS_MODULE,
},
.probe = sh_rtc_probe,
.remove = __devexit_p(sh_rtc_remove),
};
static int __init sh_rtc_init(void)
{
return platform_driver_register(&sh_rtc_platform_driver);
}
static void __exit sh_rtc_exit(void)
{
platform_driver_unregister(&sh_rtc_platform_driver);
}
module_init(sh_rtc_init);
module_exit(sh_rtc_exit);
MODULE_DESCRIPTION("SuperH on-chip RTC driver");
MODULE_VERSION(DRV_VERSION);
MODULE_AUTHOR("Paul Mundt <lethal@linux-sh.org>, Jamie Lenehan <lenehan@twibble.org>");
MODULE_LICENSE("GPL");

223
drivers/rtc/rtc-sysfs.c Normal file
View File

@@ -0,0 +1,223 @@
/*
* RTC subsystem, sysfs interface
*
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* 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/module.h>
#include <linux/rtc.h>
/* device attributes */
static ssize_t rtc_sysfs_show_name(struct class_device *dev, char *buf)
{
return sprintf(buf, "%s\n", to_rtc_device(dev)->name);
}
static CLASS_DEVICE_ATTR(name, S_IRUGO, rtc_sysfs_show_name, NULL);
static ssize_t rtc_sysfs_show_date(struct class_device *dev, char *buf)
{
ssize_t retval;
struct rtc_time tm;
retval = rtc_read_time(dev, &tm);
if (retval == 0) {
retval = sprintf(buf, "%04d-%02d-%02d\n",
tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday);
}
return retval;
}
static CLASS_DEVICE_ATTR(date, S_IRUGO, rtc_sysfs_show_date, NULL);
static ssize_t rtc_sysfs_show_time(struct class_device *dev, char *buf)
{
ssize_t retval;
struct rtc_time tm;
retval = rtc_read_time(dev, &tm);
if (retval == 0) {
retval = sprintf(buf, "%02d:%02d:%02d\n",
tm.tm_hour, tm.tm_min, tm.tm_sec);
}
return retval;
}
static CLASS_DEVICE_ATTR(time, S_IRUGO, rtc_sysfs_show_time, NULL);
static ssize_t rtc_sysfs_show_since_epoch(struct class_device *dev, char *buf)
{
ssize_t retval;
struct rtc_time tm;
retval = rtc_read_time(dev, &tm);
if (retval == 0) {
unsigned long time;
rtc_tm_to_time(&tm, &time);
retval = sprintf(buf, "%lu\n", time);
}
return retval;
}
static CLASS_DEVICE_ATTR(since_epoch, S_IRUGO, rtc_sysfs_show_since_epoch, NULL);
static struct attribute *rtc_attrs[] = {
&class_device_attr_name.attr,
&class_device_attr_date.attr,
&class_device_attr_time.attr,
&class_device_attr_since_epoch.attr,
NULL,
};
static struct attribute_group rtc_attr_group = {
.attrs = rtc_attrs,
};
static ssize_t
rtc_sysfs_show_wakealarm(struct class_device *dev, char *buf)
{
ssize_t retval;
unsigned long alarm;
struct rtc_wkalrm alm;
/* Don't show disabled alarms; but the RTC could leave the
* alarm enabled after it's already triggered. Alarms are
* conceptually one-shot, even though some common hardware
* (PCs) doesn't actually work that way.
*
* REVISIT maybe we should require RTC implementations to
* disable the RTC alarm after it triggers, for uniformity.
*/
retval = rtc_read_alarm(dev, &alm);
if (retval == 0 && alm.enabled) {
rtc_tm_to_time(&alm.time, &alarm);
retval = sprintf(buf, "%lu\n", alarm);
}
return retval;
}
static ssize_t
rtc_sysfs_set_wakealarm(struct class_device *dev, const char *buf, size_t n)
{
ssize_t retval;
unsigned long now, alarm;
struct rtc_wkalrm alm;
/* Only request alarms that trigger in the future. Disable them
* by writing another time, e.g. 0 meaning Jan 1 1970 UTC.
*/
retval = rtc_read_time(dev, &alm.time);
if (retval < 0)
return retval;
rtc_tm_to_time(&alm.time, &now);
alarm = simple_strtoul(buf, NULL, 0);
if (alarm > now) {
/* Avoid accidentally clobbering active alarms; we can't
* entirely prevent that here, without even the minimal
* locking from the /dev/rtcN api.
*/
retval = rtc_read_alarm(dev, &alm);
if (retval < 0)
return retval;
if (alm.enabled)
return -EBUSY;
alm.enabled = 1;
} else {
alm.enabled = 0;
/* Provide a valid future alarm time. Linux isn't EFI,
* this time won't be ignored when disabling the alarm.
*/
alarm = now + 300;
}
rtc_time_to_tm(alarm, &alm.time);
retval = rtc_set_alarm(dev, &alm);
return (retval < 0) ? retval : n;
}
static const CLASS_DEVICE_ATTR(wakealarm, S_IRUGO | S_IWUSR,
rtc_sysfs_show_wakealarm, rtc_sysfs_set_wakealarm);
/* The reason to trigger an alarm with no process watching it (via sysfs)
* is its side effect: waking from a system state like suspend-to-RAM or
* suspend-to-disk. So: no attribute unless that side effect is possible.
* (Userspace may disable that mechanism later.)
*/
static inline int rtc_does_wakealarm(struct class_device *class_dev)
{
struct rtc_device *rtc;
if (!device_can_wakeup(class_dev->dev))
return 0;
rtc = to_rtc_device(class_dev);
return rtc->ops->set_alarm != NULL;
}
static int rtc_sysfs_add_device(struct class_device *class_dev,
struct class_interface *class_intf)
{
int err;
dev_dbg(class_dev->dev, "rtc intf: sysfs\n");
err = sysfs_create_group(&class_dev->kobj, &rtc_attr_group);
if (err)
dev_err(class_dev->dev, "failed to create %s\n",
"sysfs attributes");
else if (rtc_does_wakealarm(class_dev)) {
/* not all RTCs support both alarms and wakeup */
err = class_device_create_file(class_dev,
&class_device_attr_wakealarm);
if (err) {
dev_err(class_dev->dev, "failed to create %s\n",
"alarm attribute");
sysfs_remove_group(&class_dev->kobj, &rtc_attr_group);
}
}
return err;
}
static void rtc_sysfs_remove_device(struct class_device *class_dev,
struct class_interface *class_intf)
{
if (rtc_does_wakealarm(class_dev))
class_device_remove_file(class_dev,
&class_device_attr_wakealarm);
sysfs_remove_group(&class_dev->kobj, &rtc_attr_group);
}
/* interface registration */
static struct class_interface rtc_sysfs_interface = {
.add = &rtc_sysfs_add_device,
.remove = &rtc_sysfs_remove_device,
};
static int __init rtc_sysfs_init(void)
{
return rtc_interface_register(&rtc_sysfs_interface);
}
static void __exit rtc_sysfs_exit(void)
{
class_interface_unregister(&rtc_sysfs_interface);
}
subsys_initcall(rtc_sysfs_init);
module_exit(rtc_sysfs_exit);
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("RTC class sysfs interface");
MODULE_LICENSE("GPL");

210
drivers/rtc/rtc-test.c Normal file
View File

@@ -0,0 +1,210 @@
/*
* An RTC test device/driver
* Copyright (C) 2005 Tower Technologies
* Author: Alessandro Zummo <a.zummo@towertech.it>
*
* 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/module.h>
#include <linux/err.h>
#include <linux/rtc.h>
#include <linux/platform_device.h>
static struct platform_device *test0 = NULL, *test1 = NULL;
static int test_rtc_read_alarm(struct device *dev,
struct rtc_wkalrm *alrm)
{
return 0;
}
static int test_rtc_set_alarm(struct device *dev,
struct rtc_wkalrm *alrm)
{
return 0;
}
static int test_rtc_read_time(struct device *dev,
struct rtc_time *tm)
{
rtc_time_to_tm(get_seconds(), tm);
return 0;
}
static int test_rtc_set_time(struct device *dev,
struct rtc_time *tm)
{
return 0;
}
static int test_rtc_set_mmss(struct device *dev, unsigned long secs)
{
return 0;
}
static int test_rtc_proc(struct device *dev, struct seq_file *seq)
{
struct platform_device *plat_dev = to_platform_device(dev);
seq_printf(seq, "test\t\t: yes\n");
seq_printf(seq, "id\t\t: %d\n", plat_dev->id);
return 0;
}
static int test_rtc_ioctl(struct device *dev, unsigned int cmd,
unsigned long arg)
{
/* We do support interrupts, they're generated
* using the sysfs interface.
*/
switch (cmd) {
case RTC_PIE_ON:
case RTC_PIE_OFF:
case RTC_UIE_ON:
case RTC_UIE_OFF:
case RTC_AIE_ON:
case RTC_AIE_OFF:
return 0;
default:
return -ENOIOCTLCMD;
}
}
static const struct rtc_class_ops test_rtc_ops = {
.proc = test_rtc_proc,
.read_time = test_rtc_read_time,
.set_time = test_rtc_set_time,
.read_alarm = test_rtc_read_alarm,
.set_alarm = test_rtc_set_alarm,
.set_mmss = test_rtc_set_mmss,
.ioctl = test_rtc_ioctl,
};
static ssize_t test_irq_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
return sprintf(buf, "%d\n", 42);
}
static ssize_t test_irq_store(struct device *dev,
struct device_attribute *attr,
const char *buf, size_t count)
{
int retval;
struct platform_device *plat_dev = to_platform_device(dev);
struct rtc_device *rtc = platform_get_drvdata(plat_dev);
retval = count;
local_irq_disable();
if (strncmp(buf, "tick", 4) == 0)
rtc_update_irq(&rtc->class_dev, 1, RTC_PF | RTC_IRQF);
else if (strncmp(buf, "alarm", 5) == 0)
rtc_update_irq(&rtc->class_dev, 1, RTC_AF | RTC_IRQF);
else if (strncmp(buf, "update", 6) == 0)
rtc_update_irq(&rtc->class_dev, 1, RTC_UF | RTC_IRQF);
else
retval = -EINVAL;
local_irq_enable();
return retval;
}
static DEVICE_ATTR(irq, S_IRUGO | S_IWUSR, test_irq_show, test_irq_store);
static int test_probe(struct platform_device *plat_dev)
{
int err;
struct rtc_device *rtc = rtc_device_register("test", &plat_dev->dev,
&test_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
err = PTR_ERR(rtc);
return err;
}
err = device_create_file(&plat_dev->dev, &dev_attr_irq);
if (err)
goto err;
platform_set_drvdata(plat_dev, rtc);
return 0;
err:
rtc_device_unregister(rtc);
return err;
}
static int __devexit test_remove(struct platform_device *plat_dev)
{
struct rtc_device *rtc = platform_get_drvdata(plat_dev);
rtc_device_unregister(rtc);
device_remove_file(&plat_dev->dev, &dev_attr_irq);
return 0;
}
static struct platform_driver test_drv = {
.probe = test_probe,
.remove = __devexit_p(test_remove),
.driver = {
.name = "rtc-test",
.owner = THIS_MODULE,
},
};
static int __init test_init(void)
{
int err;
if ((err = platform_driver_register(&test_drv)))
return err;
if ((test0 = platform_device_alloc("rtc-test", 0)) == NULL) {
err = -ENOMEM;
goto exit_driver_unregister;
}
if ((test1 = platform_device_alloc("rtc-test", 1)) == NULL) {
err = -ENOMEM;
goto exit_free_test0;
}
if ((err = platform_device_add(test0)))
goto exit_free_test1;
if ((err = platform_device_add(test1)))
goto exit_device_unregister;
return 0;
exit_device_unregister:
platform_device_unregister(test0);
exit_free_test1:
platform_device_put(test1);
exit_free_test0:
platform_device_put(test0);
exit_driver_unregister:
platform_driver_unregister(&test_drv);
return err;
}
static void __exit test_exit(void)
{
platform_device_unregister(test0);
platform_device_unregister(test1);
platform_driver_unregister(&test_drv);
}
MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("RTC test driver/device");
MODULE_LICENSE("GPL");
module_init(test_init);
module_exit(test_exit);

261
drivers/rtc/rtc-v3020.c Normal file
View File

@@ -0,0 +1,261 @@
/* drivers/rtc/rtc-v3020.c
*
* Copyright (C) 2006 8D Technologies inc.
* Copyright (C) 2004 Compulab Ltd.
*
* 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.
*
* Driver for the V3020 RTC
*
* Changelog:
*
* 10-May-2006: Raphael Assenat <raph@8d.com>
* - Converted to platform driver
* - Use the generic rtc class
*
* ??-???-2004: Someone at Compulab
* - Initial driver creation.
*
*/
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/init.h>
#include <linux/rtc.h>
#include <linux/types.h>
#include <linux/bcd.h>
#include <linux/rtc-v3020.h>
#include <asm/io.h>
#undef DEBUG
struct v3020 {
void __iomem *ioaddress;
int leftshift;
struct rtc_device *rtc;
};
static void v3020_set_reg(struct v3020 *chip, unsigned char address,
unsigned char data)
{
int i;
unsigned char tmp;
tmp = address;
for (i = 0; i < 4; i++) {
writel((tmp & 1) << chip->leftshift, chip->ioaddress);
tmp >>= 1;
}
/* Commands dont have data */
if (!V3020_IS_COMMAND(address)) {
for (i = 0; i < 8; i++) {
writel((data & 1) << chip->leftshift, chip->ioaddress);
data >>= 1;
}
}
}
static unsigned char v3020_get_reg(struct v3020 *chip, unsigned char address)
{
unsigned int data=0;
int i;
for (i = 0; i < 4; i++) {
writel((address & 1) << chip->leftshift, chip->ioaddress);
address >>= 1;
}
for (i = 0; i < 8; i++) {
data >>= 1;
if (readl(chip->ioaddress) & (1 << chip->leftshift))
data |= 0x80;
}
return data;
}
static int v3020_read_time(struct device *dev, struct rtc_time *dt)
{
struct v3020 *chip = dev_get_drvdata(dev);
int tmp;
/* Copy the current time to ram... */
v3020_set_reg(chip, V3020_CMD_CLOCK2RAM, 0);
/* ...and then read constant values. */
tmp = v3020_get_reg(chip, V3020_SECONDS);
dt->tm_sec = BCD2BIN(tmp);
tmp = v3020_get_reg(chip, V3020_MINUTES);
dt->tm_min = BCD2BIN(tmp);
tmp = v3020_get_reg(chip, V3020_HOURS);
dt->tm_hour = BCD2BIN(tmp);
tmp = v3020_get_reg(chip, V3020_MONTH_DAY);
dt->tm_mday = BCD2BIN(tmp);
tmp = v3020_get_reg(chip, V3020_MONTH);
dt->tm_mon = BCD2BIN(tmp);
tmp = v3020_get_reg(chip, V3020_WEEK_DAY);
dt->tm_wday = BCD2BIN(tmp);
tmp = v3020_get_reg(chip, V3020_YEAR);
dt->tm_year = BCD2BIN(tmp)+100;
#ifdef DEBUG
printk("\n%s : Read RTC values\n",__FUNCTION__);
printk("tm_hour: %i\n",dt->tm_hour);
printk("tm_min : %i\n",dt->tm_min);
printk("tm_sec : %i\n",dt->tm_sec);
printk("tm_year: %i\n",dt->tm_year);
printk("tm_mon : %i\n",dt->tm_mon);
printk("tm_mday: %i\n",dt->tm_mday);
printk("tm_wday: %i\n",dt->tm_wday);
#endif
return 0;
}
static int v3020_set_time(struct device *dev, struct rtc_time *dt)
{
struct v3020 *chip = dev_get_drvdata(dev);
#ifdef DEBUG
printk("\n%s : Setting RTC values\n",__FUNCTION__);
printk("tm_sec : %i\n",dt->tm_sec);
printk("tm_min : %i\n",dt->tm_min);
printk("tm_hour: %i\n",dt->tm_hour);
printk("tm_mday: %i\n",dt->tm_mday);
printk("tm_wday: %i\n",dt->tm_wday);
printk("tm_year: %i\n",dt->tm_year);
#endif
/* Write all the values to ram... */
v3020_set_reg(chip, V3020_SECONDS, BIN2BCD(dt->tm_sec));
v3020_set_reg(chip, V3020_MINUTES, BIN2BCD(dt->tm_min));
v3020_set_reg(chip, V3020_HOURS, BIN2BCD(dt->tm_hour));
v3020_set_reg(chip, V3020_MONTH_DAY, BIN2BCD(dt->tm_mday));
v3020_set_reg(chip, V3020_MONTH, BIN2BCD(dt->tm_mon));
v3020_set_reg(chip, V3020_WEEK_DAY, BIN2BCD(dt->tm_wday));
v3020_set_reg(chip, V3020_YEAR, BIN2BCD(dt->tm_year % 100));
/* ...and set the clock. */
v3020_set_reg(chip, V3020_CMD_RAM2CLOCK, 0);
/* Compulab used this delay here. I dont know why,
* the datasheet does not specify a delay. */
/*mdelay(5);*/
return 0;
}
static const struct rtc_class_ops v3020_rtc_ops = {
.read_time = v3020_read_time,
.set_time = v3020_set_time,
};
static int rtc_probe(struct platform_device *pdev)
{
struct v3020_platform_data *pdata = pdev->dev.platform_data;
struct v3020 *chip;
struct rtc_device *rtc;
int retval = -EBUSY;
int i;
int temp;
if (pdev->num_resources != 1)
return -EBUSY;
if (pdev->resource[0].flags != IORESOURCE_MEM)
return -EBUSY;
chip = kzalloc(sizeof *chip, GFP_KERNEL);
if (!chip)
return -ENOMEM;
chip->leftshift = pdata->leftshift;
chip->ioaddress = ioremap(pdev->resource[0].start, 1);
if (chip->ioaddress == NULL)
goto err_chip;
/* Make sure the v3020 expects a communication cycle
* by reading 8 times */
for (i = 0; i < 8; i++)
temp = readl(chip->ioaddress);
/* Test chip by doing a write/read sequence
* to the chip ram */
v3020_set_reg(chip, V3020_SECONDS, 0x33);
if(v3020_get_reg(chip, V3020_SECONDS) != 0x33) {
retval = -ENODEV;
goto err_io;
}
/* Make sure frequency measurment mode, test modes, and lock
* are all disabled */
v3020_set_reg(chip, V3020_STATUS_0, 0x0);
dev_info(&pdev->dev, "Chip available at physical address 0x%llx,"
"data connected to D%d\n",
(unsigned long long)pdev->resource[0].start,
chip->leftshift);
platform_set_drvdata(pdev, chip);
rtc = rtc_device_register("v3020",
&pdev->dev, &v3020_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
retval = PTR_ERR(rtc);
goto err_io;
}
chip->rtc = rtc;
return 0;
err_io:
iounmap(chip->ioaddress);
err_chip:
kfree(chip);
return retval;
}
static int rtc_remove(struct platform_device *dev)
{
struct v3020 *chip = platform_get_drvdata(dev);
struct rtc_device *rtc = chip->rtc;
if (rtc)
rtc_device_unregister(rtc);
iounmap(chip->ioaddress);
kfree(chip);
return 0;
}
static struct platform_driver rtc_device_driver = {
.probe = rtc_probe,
.remove = rtc_remove,
.driver = {
.name = "v3020",
.owner = THIS_MODULE,
},
};
static __init int v3020_init(void)
{
return platform_driver_register(&rtc_device_driver);
}
static __exit void v3020_exit(void)
{
platform_driver_unregister(&rtc_device_driver);
}
module_init(v3020_init);
module_exit(v3020_exit);
MODULE_DESCRIPTION("V3020 RTC");
MODULE_AUTHOR("Raphael Assenat");
MODULE_LICENSE("GPL");

463
drivers/rtc/rtc-vr41xx.c Normal file
View File

@@ -0,0 +1,463 @@
/*
* Driver for NEC VR4100 series Real Time Clock unit.
*
* Copyright (C) 2003-2006 Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>
*
* 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.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA
*/
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/irq.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/rtc.h>
#include <linux/spinlock.h>
#include <linux/types.h>
#include <asm/div64.h>
#include <asm/io.h>
#include <asm/uaccess.h>
#include <asm/vr41xx/irq.h>
MODULE_AUTHOR("Yoichi Yuasa <yoichi_yuasa@tripeaks.co.jp>");
MODULE_DESCRIPTION("NEC VR4100 series RTC driver");
MODULE_LICENSE("GPL");
#define RTC1_TYPE1_START 0x0b0000c0UL
#define RTC1_TYPE1_END 0x0b0000dfUL
#define RTC2_TYPE1_START 0x0b0001c0UL
#define RTC2_TYPE1_END 0x0b0001dfUL
#define RTC1_TYPE2_START 0x0f000100UL
#define RTC1_TYPE2_END 0x0f00011fUL
#define RTC2_TYPE2_START 0x0f000120UL
#define RTC2_TYPE2_END 0x0f00013fUL
#define RTC1_SIZE 0x20
#define RTC2_SIZE 0x20
/* RTC 1 registers */
#define ETIMELREG 0x00
#define ETIMEMREG 0x02
#define ETIMEHREG 0x04
/* RFU */
#define ECMPLREG 0x08
#define ECMPMREG 0x0a
#define ECMPHREG 0x0c
/* RFU */
#define RTCL1LREG 0x10
#define RTCL1HREG 0x12
#define RTCL1CNTLREG 0x14
#define RTCL1CNTHREG 0x16
#define RTCL2LREG 0x18
#define RTCL2HREG 0x1a
#define RTCL2CNTLREG 0x1c
#define RTCL2CNTHREG 0x1e
/* RTC 2 registers */
#define TCLKLREG 0x00
#define TCLKHREG 0x02
#define TCLKCNTLREG 0x04
#define TCLKCNTHREG 0x06
/* RFU */
#define RTCINTREG 0x1e
#define TCLOCK_INT 0x08
#define RTCLONG2_INT 0x04
#define RTCLONG1_INT 0x02
#define ELAPSEDTIME_INT 0x01
#define RTC_FREQUENCY 32768
#define MAX_PERIODIC_RATE 6553
static void __iomem *rtc1_base;
static void __iomem *rtc2_base;
#define rtc1_read(offset) readw(rtc1_base + (offset))
#define rtc1_write(offset, value) writew((value), rtc1_base + (offset))
#define rtc2_read(offset) readw(rtc2_base + (offset))
#define rtc2_write(offset, value) writew((value), rtc2_base + (offset))
static unsigned long epoch = 1970; /* Jan 1 1970 00:00:00 */
static DEFINE_SPINLOCK(rtc_lock);
static char rtc_name[] = "RTC";
static unsigned long periodic_frequency;
static unsigned long periodic_count;
struct resource rtc_resource[2] = {
{ .name = rtc_name,
.flags = IORESOURCE_MEM, },
{ .name = rtc_name,
.flags = IORESOURCE_MEM, },
};
static inline unsigned long read_elapsed_second(void)
{
unsigned long first_low, first_mid, first_high;
unsigned long second_low, second_mid, second_high;
do {
first_low = rtc1_read(ETIMELREG);
first_mid = rtc1_read(ETIMEMREG);
first_high = rtc1_read(ETIMEHREG);
second_low = rtc1_read(ETIMELREG);
second_mid = rtc1_read(ETIMEMREG);
second_high = rtc1_read(ETIMEHREG);
} while (first_low != second_low || first_mid != second_mid ||
first_high != second_high);
return (first_high << 17) | (first_mid << 1) | (first_low >> 15);
}
static inline void write_elapsed_second(unsigned long sec)
{
spin_lock_irq(&rtc_lock);
rtc1_write(ETIMELREG, (uint16_t)(sec << 15));
rtc1_write(ETIMEMREG, (uint16_t)(sec >> 1));
rtc1_write(ETIMEHREG, (uint16_t)(sec >> 17));
spin_unlock_irq(&rtc_lock);
}
static void vr41xx_rtc_release(struct device *dev)
{
spin_lock_irq(&rtc_lock);
rtc1_write(ECMPLREG, 0);
rtc1_write(ECMPMREG, 0);
rtc1_write(ECMPHREG, 0);
rtc1_write(RTCL1LREG, 0);
rtc1_write(RTCL1HREG, 0);
spin_unlock_irq(&rtc_lock);
disable_irq(ELAPSEDTIME_IRQ);
disable_irq(RTCLONG1_IRQ);
}
static int vr41xx_rtc_read_time(struct device *dev, struct rtc_time *time)
{
unsigned long epoch_sec, elapsed_sec;
epoch_sec = mktime(epoch, 1, 1, 0, 0, 0);
elapsed_sec = read_elapsed_second();
rtc_time_to_tm(epoch_sec + elapsed_sec, time);
return 0;
}
static int vr41xx_rtc_set_time(struct device *dev, struct rtc_time *time)
{
unsigned long epoch_sec, current_sec;
epoch_sec = mktime(epoch, 1, 1, 0, 0, 0);
current_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec);
write_elapsed_second(current_sec - epoch_sec);
return 0;
}
static int vr41xx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
unsigned long low, mid, high;
struct rtc_time *time = &wkalrm->time;
spin_lock_irq(&rtc_lock);
low = rtc1_read(ECMPLREG);
mid = rtc1_read(ECMPMREG);
high = rtc1_read(ECMPHREG);
spin_unlock_irq(&rtc_lock);
rtc_time_to_tm((high << 17) | (mid << 1) | (low >> 15), time);
return 0;
}
static int vr41xx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *wkalrm)
{
unsigned long alarm_sec;
struct rtc_time *time = &wkalrm->time;
alarm_sec = mktime(time->tm_year + 1900, time->tm_mon + 1, time->tm_mday,
time->tm_hour, time->tm_min, time->tm_sec);
spin_lock_irq(&rtc_lock);
rtc1_write(ECMPLREG, (uint16_t)(alarm_sec << 15));
rtc1_write(ECMPMREG, (uint16_t)(alarm_sec >> 1));
rtc1_write(ECMPHREG, (uint16_t)(alarm_sec >> 17));
spin_unlock_irq(&rtc_lock);
return 0;
}
static int vr41xx_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
{
unsigned long count;
switch (cmd) {
case RTC_AIE_ON:
enable_irq(ELAPSEDTIME_IRQ);
break;
case RTC_AIE_OFF:
disable_irq(ELAPSEDTIME_IRQ);
break;
case RTC_PIE_ON:
enable_irq(RTCLONG1_IRQ);
break;
case RTC_PIE_OFF:
disable_irq(RTCLONG1_IRQ);
break;
case RTC_IRQP_READ:
return put_user(periodic_frequency, (unsigned long __user *)arg);
break;
case RTC_IRQP_SET:
if (arg > MAX_PERIODIC_RATE)
return -EINVAL;
periodic_frequency = arg;
count = RTC_FREQUENCY;
do_div(count, arg);
periodic_count = count;
spin_lock_irq(&rtc_lock);
rtc1_write(RTCL1LREG, count);
rtc1_write(RTCL1HREG, count >> 16);
spin_unlock_irq(&rtc_lock);
break;
case RTC_EPOCH_READ:
return put_user(epoch, (unsigned long __user *)arg);
case RTC_EPOCH_SET:
/* Doesn't support before 1900 */
if (arg < 1900)
return -EINVAL;
epoch = arg;
break;
default:
return -ENOIOCTLCMD;
}
return 0;
}
static irqreturn_t elapsedtime_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = (struct platform_device *)dev_id;
struct rtc_device *rtc = platform_get_drvdata(pdev);
rtc2_write(RTCINTREG, ELAPSEDTIME_INT);
rtc_update_irq(&rtc->class_dev, 1, RTC_AF);
return IRQ_HANDLED;
}
static irqreturn_t rtclong1_interrupt(int irq, void *dev_id)
{
struct platform_device *pdev = (struct platform_device *)dev_id;
struct rtc_device *rtc = platform_get_drvdata(pdev);
unsigned long count = periodic_count;
rtc2_write(RTCINTREG, RTCLONG1_INT);
rtc1_write(RTCL1LREG, count);
rtc1_write(RTCL1HREG, count >> 16);
rtc_update_irq(&rtc->class_dev, 1, RTC_PF);
return IRQ_HANDLED;
}
static const struct rtc_class_ops vr41xx_rtc_ops = {
.release = vr41xx_rtc_release,
.ioctl = vr41xx_rtc_ioctl,
.read_time = vr41xx_rtc_read_time,
.set_time = vr41xx_rtc_set_time,
.read_alarm = vr41xx_rtc_read_alarm,
.set_alarm = vr41xx_rtc_set_alarm,
};
static int __devinit rtc_probe(struct platform_device *pdev)
{
struct rtc_device *rtc;
unsigned int irq;
int retval;
if (pdev->num_resources != 2)
return -EBUSY;
rtc1_base = ioremap(pdev->resource[0].start, RTC1_SIZE);
if (rtc1_base == NULL)
return -EBUSY;
rtc2_base = ioremap(pdev->resource[1].start, RTC2_SIZE);
if (rtc2_base == NULL) {
iounmap(rtc1_base);
rtc1_base = NULL;
return -EBUSY;
}
rtc = rtc_device_register(rtc_name, &pdev->dev, &vr41xx_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
iounmap(rtc1_base);
iounmap(rtc2_base);
rtc1_base = NULL;
rtc2_base = NULL;
return PTR_ERR(rtc);
}
spin_lock_irq(&rtc_lock);
rtc1_write(ECMPLREG, 0);
rtc1_write(ECMPMREG, 0);
rtc1_write(ECMPHREG, 0);
rtc1_write(RTCL1LREG, 0);
rtc1_write(RTCL1HREG, 0);
spin_unlock_irq(&rtc_lock);
irq = ELAPSEDTIME_IRQ;
retval = request_irq(irq, elapsedtime_interrupt, IRQF_DISABLED,
"elapsed_time", pdev);
if (retval == 0) {
irq = RTCLONG1_IRQ;
retval = request_irq(irq, rtclong1_interrupt, IRQF_DISABLED,
"rtclong1", pdev);
}
if (retval < 0) {
printk(KERN_ERR "rtc: IRQ%d is busy\n", irq);
rtc_device_unregister(rtc);
if (irq == RTCLONG1_IRQ)
free_irq(ELAPSEDTIME_IRQ, NULL);
iounmap(rtc1_base);
iounmap(rtc2_base);
rtc1_base = NULL;
rtc2_base = NULL;
return retval;
}
platform_set_drvdata(pdev, rtc);
disable_irq(ELAPSEDTIME_IRQ);
disable_irq(RTCLONG1_IRQ);
printk(KERN_INFO "rtc: Real Time Clock of NEC VR4100 series\n");
return 0;
}
static int __devexit rtc_remove(struct platform_device *pdev)
{
struct rtc_device *rtc;
rtc = platform_get_drvdata(pdev);
if (rtc != NULL)
rtc_device_unregister(rtc);
platform_set_drvdata(pdev, NULL);
free_irq(ELAPSEDTIME_IRQ, NULL);
free_irq(RTCLONG1_IRQ, NULL);
if (rtc1_base != NULL)
iounmap(rtc1_base);
if (rtc2_base != NULL)
iounmap(rtc2_base);
return 0;
}
static struct platform_device *rtc_platform_device;
static struct platform_driver rtc_platform_driver = {
.probe = rtc_probe,
.remove = __devexit_p(rtc_remove),
.driver = {
.name = rtc_name,
.owner = THIS_MODULE,
},
};
static int __init vr41xx_rtc_init(void)
{
int retval;
switch (current_cpu_data.cputype) {
case CPU_VR4111:
case CPU_VR4121:
rtc_resource[0].start = RTC1_TYPE1_START;
rtc_resource[0].end = RTC1_TYPE1_END;
rtc_resource[1].start = RTC2_TYPE1_START;
rtc_resource[1].end = RTC2_TYPE1_END;
break;
case CPU_VR4122:
case CPU_VR4131:
case CPU_VR4133:
rtc_resource[0].start = RTC1_TYPE2_START;
rtc_resource[0].end = RTC1_TYPE2_END;
rtc_resource[1].start = RTC2_TYPE2_START;
rtc_resource[1].end = RTC2_TYPE2_END;
break;
default:
return -ENODEV;
break;
}
rtc_platform_device = platform_device_alloc("RTC", -1);
if (rtc_platform_device == NULL)
return -ENOMEM;
retval = platform_device_add_resources(rtc_platform_device,
rtc_resource, ARRAY_SIZE(rtc_resource));
if (retval == 0)
retval = platform_device_add(rtc_platform_device);
if (retval < 0) {
platform_device_put(rtc_platform_device);
return retval;
}
retval = platform_driver_register(&rtc_platform_driver);
if (retval < 0)
platform_device_unregister(rtc_platform_device);
return retval;
}
static void __exit vr41xx_rtc_exit(void)
{
platform_driver_unregister(&rtc_platform_driver);
platform_device_unregister(rtc_platform_device);
}
module_init(vr41xx_rtc_init);
module_exit(vr41xx_rtc_exit);

622
drivers/rtc/rtc-x1205.c Normal file
View File

@@ -0,0 +1,622 @@
/*
* An i2c driver for the Xicor/Intersil X1205 RTC
* Copyright 2004 Karen Spearel
* Copyright 2005 Alessandro Zummo
*
* please send all reports to:
* Karen Spearel <kas111 at gmail dot com>
* Alessandro Zummo <a.zummo@towertech.it>
*
* based on a lot of other RTC drivers.
*
* 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/i2c.h>
#include <linux/bcd.h>
#include <linux/rtc.h>
#include <linux/delay.h>
#define DRV_VERSION "1.0.7"
/* Addresses to scan: none. This chip is located at
* 0x6f and uses a two bytes register addressing.
* Two bytes need to be written to read a single register,
* while most other chips just require one and take the second
* one as the data to be written. To prevent corrupting
* unknown chips, the user must explicitely set the probe parameter.
*/
static unsigned short normal_i2c[] = { I2C_CLIENT_END };
/* Insmod parameters */
I2C_CLIENT_INSMOD;
/* offsets into CCR area */
#define CCR_SEC 0
#define CCR_MIN 1
#define CCR_HOUR 2
#define CCR_MDAY 3
#define CCR_MONTH 4
#define CCR_YEAR 5
#define CCR_WDAY 6
#define CCR_Y2K 7
#define X1205_REG_SR 0x3F /* status register */
#define X1205_REG_Y2K 0x37
#define X1205_REG_DW 0x36
#define X1205_REG_YR 0x35
#define X1205_REG_MO 0x34
#define X1205_REG_DT 0x33
#define X1205_REG_HR 0x32
#define X1205_REG_MN 0x31
#define X1205_REG_SC 0x30
#define X1205_REG_DTR 0x13
#define X1205_REG_ATR 0x12
#define X1205_REG_INT 0x11
#define X1205_REG_0 0x10
#define X1205_REG_Y2K1 0x0F
#define X1205_REG_DWA1 0x0E
#define X1205_REG_YRA1 0x0D
#define X1205_REG_MOA1 0x0C
#define X1205_REG_DTA1 0x0B
#define X1205_REG_HRA1 0x0A
#define X1205_REG_MNA1 0x09
#define X1205_REG_SCA1 0x08
#define X1205_REG_Y2K0 0x07
#define X1205_REG_DWA0 0x06
#define X1205_REG_YRA0 0x05
#define X1205_REG_MOA0 0x04
#define X1205_REG_DTA0 0x03
#define X1205_REG_HRA0 0x02
#define X1205_REG_MNA0 0x01
#define X1205_REG_SCA0 0x00
#define X1205_CCR_BASE 0x30 /* Base address of CCR */
#define X1205_ALM0_BASE 0x00 /* Base address of ALARM0 */
#define X1205_SR_RTCF 0x01 /* Clock failure */
#define X1205_SR_WEL 0x02 /* Write Enable Latch */
#define X1205_SR_RWEL 0x04 /* Register Write Enable */
#define X1205_DTR_DTR0 0x01
#define X1205_DTR_DTR1 0x02
#define X1205_DTR_DTR2 0x04
#define X1205_HR_MIL 0x80 /* Set in ccr.hour for 24 hr mode */
/* Prototypes */
static int x1205_attach(struct i2c_adapter *adapter);
static int x1205_detach(struct i2c_client *client);
static int x1205_probe(struct i2c_adapter *adapter, int address, int kind);
static struct i2c_driver x1205_driver = {
.driver = {
.name = "x1205",
},
.id = I2C_DRIVERID_X1205,
.attach_adapter = &x1205_attach,
.detach_client = &x1205_detach,
};
/*
* In the routines that deal directly with the x1205 hardware, we use
* rtc_time -- month 0-11, hour 0-23, yr = calendar year-epoch
* Epoch is initialized as 2000. Time is set to UTC.
*/
static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
unsigned char reg_base)
{
unsigned char dt_addr[2] = { 0, reg_base };
unsigned char buf[8];
struct i2c_msg msgs[] = {
{ client->addr, 0, 2, dt_addr }, /* setup read ptr */
{ client->addr, I2C_M_RD, 8, buf }, /* read date */
};
/* read date registers */
if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
dev_dbg(&client->dev,
"%s: raw read data - sec=%02x, min=%02x, hr=%02x, "
"mday=%02x, mon=%02x, year=%02x, wday=%02x, y2k=%02x\n",
__FUNCTION__,
buf[0], buf[1], buf[2], buf[3],
buf[4], buf[5], buf[6], buf[7]);
tm->tm_sec = BCD2BIN(buf[CCR_SEC]);
tm->tm_min = BCD2BIN(buf[CCR_MIN]);
tm->tm_hour = BCD2BIN(buf[CCR_HOUR] & 0x3F); /* hr is 0-23 */
tm->tm_mday = BCD2BIN(buf[CCR_MDAY]);
tm->tm_mon = BCD2BIN(buf[CCR_MONTH]) - 1; /* mon is 0-11 */
tm->tm_year = BCD2BIN(buf[CCR_YEAR])
+ (BCD2BIN(buf[CCR_Y2K]) * 100) - 1900;
tm->tm_wday = buf[CCR_WDAY];
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d, "
"mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
return 0;
}
static int x1205_get_status(struct i2c_client *client, unsigned char *sr)
{
static unsigned char sr_addr[2] = { 0, X1205_REG_SR };
struct i2c_msg msgs[] = {
{ client->addr, 0, 2, sr_addr }, /* setup read ptr */
{ client->addr, I2C_M_RD, 1, sr }, /* read status */
};
/* read status register */
if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
return 0;
}
static int x1205_set_datetime(struct i2c_client *client, struct rtc_time *tm,
int datetoo, u8 reg_base)
{
int i, xfer;
unsigned char buf[8];
static const unsigned char wel[3] = { 0, X1205_REG_SR,
X1205_SR_WEL };
static const unsigned char rwel[3] = { 0, X1205_REG_SR,
X1205_SR_WEL | X1205_SR_RWEL };
static const unsigned char diswe[3] = { 0, X1205_REG_SR, 0 };
dev_dbg(&client->dev,
"%s: secs=%d, mins=%d, hours=%d\n",
__FUNCTION__,
tm->tm_sec, tm->tm_min, tm->tm_hour);
buf[CCR_SEC] = BIN2BCD(tm->tm_sec);
buf[CCR_MIN] = BIN2BCD(tm->tm_min);
/* set hour and 24hr bit */
buf[CCR_HOUR] = BIN2BCD(tm->tm_hour) | X1205_HR_MIL;
/* should we also set the date? */
if (datetoo) {
dev_dbg(&client->dev,
"%s: mday=%d, mon=%d, year=%d, wday=%d\n",
__FUNCTION__,
tm->tm_mday, tm->tm_mon, tm->tm_year, tm->tm_wday);
buf[CCR_MDAY] = BIN2BCD(tm->tm_mday);
/* month, 1 - 12 */
buf[CCR_MONTH] = BIN2BCD(tm->tm_mon + 1);
/* year, since the rtc epoch*/
buf[CCR_YEAR] = BIN2BCD(tm->tm_year % 100);
buf[CCR_WDAY] = tm->tm_wday & 0x07;
buf[CCR_Y2K] = BIN2BCD(tm->tm_year / 100);
}
/* this sequence is required to unlock the chip */
if ((xfer = i2c_master_send(client, wel, 3)) != 3) {
dev_err(&client->dev, "%s: wel - %d\n", __FUNCTION__, xfer);
return -EIO;
}
if ((xfer = i2c_master_send(client, rwel, 3)) != 3) {
dev_err(&client->dev, "%s: rwel - %d\n", __FUNCTION__, xfer);
return -EIO;
}
/* write register's data */
for (i = 0; i < (datetoo ? 8 : 3); i++) {
unsigned char rdata[3] = { 0, reg_base + i, buf[i] };
xfer = i2c_master_send(client, rdata, 3);
if (xfer != 3) {
dev_err(&client->dev,
"%s: xfer=%d addr=%02x, data=%02x\n",
__FUNCTION__,
xfer, rdata[1], rdata[2]);
return -EIO;
}
};
/* disable further writes */
if ((xfer = i2c_master_send(client, diswe, 3)) != 3) {
dev_err(&client->dev, "%s: diswe - %d\n", __FUNCTION__, xfer);
return -EIO;
}
return 0;
}
static int x1205_fix_osc(struct i2c_client *client)
{
int err;
struct rtc_time tm;
tm.tm_hour = tm.tm_min = tm.tm_sec = 0;
if ((err = x1205_set_datetime(client, &tm, 0, X1205_CCR_BASE)) < 0)
dev_err(&client->dev,
"unable to restart the oscillator\n");
return err;
}
static int x1205_get_dtrim(struct i2c_client *client, int *trim)
{
unsigned char dtr;
static unsigned char dtr_addr[2] = { 0, X1205_REG_DTR };
struct i2c_msg msgs[] = {
{ client->addr, 0, 2, dtr_addr }, /* setup read ptr */
{ client->addr, I2C_M_RD, 1, &dtr }, /* read dtr */
};
/* read dtr register */
if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
dev_dbg(&client->dev, "%s: raw dtr=%x\n", __FUNCTION__, dtr);
*trim = 0;
if (dtr & X1205_DTR_DTR0)
*trim += 20;
if (dtr & X1205_DTR_DTR1)
*trim += 10;
if (dtr & X1205_DTR_DTR2)
*trim = -*trim;
return 0;
}
static int x1205_get_atrim(struct i2c_client *client, int *trim)
{
s8 atr;
static unsigned char atr_addr[2] = { 0, X1205_REG_ATR };
struct i2c_msg msgs[] = {
{ client->addr, 0, 2, atr_addr }, /* setup read ptr */
{ client->addr, I2C_M_RD, 1, &atr }, /* read atr */
};
/* read atr register */
if ((i2c_transfer(client->adapter, &msgs[0], 2)) != 2) {
dev_err(&client->dev, "%s: read error\n", __FUNCTION__);
return -EIO;
}
dev_dbg(&client->dev, "%s: raw atr=%x\n", __FUNCTION__, atr);
/* atr is a two's complement value on 6 bits,
* perform sign extension. The formula is
* Catr = (atr * 0.25pF) + 11.00pF.
*/
if (atr & 0x20)
atr |= 0xC0;
dev_dbg(&client->dev, "%s: raw atr=%x (%d)\n", __FUNCTION__, atr, atr);
*trim = (atr * 250) + 11000;
dev_dbg(&client->dev, "%s: real=%d\n", __FUNCTION__, *trim);
return 0;
}
struct x1205_limit
{
unsigned char reg, mask, min, max;
};
static int x1205_validate_client(struct i2c_client *client)
{
int i, xfer;
/* Probe array. We will read the register at the specified
* address and check if the given bits are zero.
*/
static const unsigned char probe_zero_pattern[] = {
/* register, mask */
X1205_REG_SR, 0x18,
X1205_REG_DTR, 0xF8,
X1205_REG_ATR, 0xC0,
X1205_REG_INT, 0x18,
X1205_REG_0, 0xFF,
};
static const struct x1205_limit probe_limits_pattern[] = {
/* register, mask, min, max */
{ X1205_REG_Y2K, 0xFF, 19, 20 },
{ X1205_REG_DW, 0xFF, 0, 6 },
{ X1205_REG_YR, 0xFF, 0, 99 },
{ X1205_REG_MO, 0xFF, 0, 12 },
{ X1205_REG_DT, 0xFF, 0, 31 },
{ X1205_REG_HR, 0x7F, 0, 23 },
{ X1205_REG_MN, 0xFF, 0, 59 },
{ X1205_REG_SC, 0xFF, 0, 59 },
{ X1205_REG_Y2K1, 0xFF, 19, 20 },
{ X1205_REG_Y2K0, 0xFF, 19, 20 },
};
/* check that registers have bits a 0 where expected */
for (i = 0; i < ARRAY_SIZE(probe_zero_pattern); i += 2) {
unsigned char buf;
unsigned char addr[2] = { 0, probe_zero_pattern[i] };
struct i2c_msg msgs[2] = {
{ client->addr, 0, 2, addr },
{ client->addr, I2C_M_RD, 1, &buf },
};
if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {
dev_err(&client->dev,
"%s: could not read register %x\n",
__FUNCTION__, probe_zero_pattern[i]);
return -EIO;
}
if ((buf & probe_zero_pattern[i+1]) != 0) {
dev_err(&client->dev,
"%s: register=%02x, zero pattern=%d, value=%x\n",
__FUNCTION__, probe_zero_pattern[i], i, buf);
return -ENODEV;
}
}
/* check limits (only registers with bcd values) */
for (i = 0; i < ARRAY_SIZE(probe_limits_pattern); i++) {
unsigned char reg, value;
unsigned char addr[2] = { 0, probe_limits_pattern[i].reg };
struct i2c_msg msgs[2] = {
{ client->addr, 0, 2, addr },
{ client->addr, I2C_M_RD, 1, &reg },
};
if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {
dev_err(&client->dev,
"%s: could not read register %x\n",
__FUNCTION__, probe_limits_pattern[i].reg);
return -EIO;
}
value = BCD2BIN(reg & probe_limits_pattern[i].mask);
if (value > probe_limits_pattern[i].max ||
value < probe_limits_pattern[i].min) {
dev_dbg(&client->dev,
"%s: register=%x, lim pattern=%d, value=%d\n",
__FUNCTION__, probe_limits_pattern[i].reg,
i, value);
return -ENODEV;
}
}
return 0;
}
static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
return x1205_get_datetime(to_i2c_client(dev),
&alrm->time, X1205_ALM0_BASE);
}
static int x1205_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
return x1205_set_datetime(to_i2c_client(dev),
&alrm->time, 1, X1205_ALM0_BASE);
}
static int x1205_rtc_read_time(struct device *dev, struct rtc_time *tm)
{
return x1205_get_datetime(to_i2c_client(dev),
tm, X1205_CCR_BASE);
}
static int x1205_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
return x1205_set_datetime(to_i2c_client(dev),
tm, 1, X1205_CCR_BASE);
}
static int x1205_rtc_proc(struct device *dev, struct seq_file *seq)
{
int err, dtrim, atrim;
if ((err = x1205_get_dtrim(to_i2c_client(dev), &dtrim)) == 0)
seq_printf(seq, "digital_trim\t: %d ppm\n", dtrim);
if ((err = x1205_get_atrim(to_i2c_client(dev), &atrim)) == 0)
seq_printf(seq, "analog_trim\t: %d.%02d pF\n",
atrim / 1000, atrim % 1000);
return 0;
}
static const struct rtc_class_ops x1205_rtc_ops = {
.proc = x1205_rtc_proc,
.read_time = x1205_rtc_read_time,
.set_time = x1205_rtc_set_time,
.read_alarm = x1205_rtc_read_alarm,
.set_alarm = x1205_rtc_set_alarm,
};
static ssize_t x1205_sysfs_show_atrim(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err, atrim;
err = x1205_get_atrim(to_i2c_client(dev), &atrim);
if (err)
return err;
return sprintf(buf, "%d.%02d pF\n", atrim / 1000, atrim % 1000);
}
static DEVICE_ATTR(atrim, S_IRUGO, x1205_sysfs_show_atrim, NULL);
static ssize_t x1205_sysfs_show_dtrim(struct device *dev,
struct device_attribute *attr, char *buf)
{
int err, dtrim;
err = x1205_get_dtrim(to_i2c_client(dev), &dtrim);
if (err)
return err;
return sprintf(buf, "%d ppm\n", dtrim);
}
static DEVICE_ATTR(dtrim, S_IRUGO, x1205_sysfs_show_dtrim, NULL);
static int x1205_attach(struct i2c_adapter *adapter)
{
return i2c_probe(adapter, &addr_data, x1205_probe);
}
static int x1205_probe(struct i2c_adapter *adapter, int address, int kind)
{
int err = 0;
unsigned char sr;
struct i2c_client *client;
struct rtc_device *rtc;
dev_dbg(&adapter->dev, "%s\n", __FUNCTION__);
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
err = -ENODEV;
goto exit;
}
if (!(client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL))) {
err = -ENOMEM;
goto exit;
}
/* I2C client */
client->addr = address;
client->driver = &x1205_driver;
client->adapter = adapter;
strlcpy(client->name, x1205_driver.driver.name, I2C_NAME_SIZE);
/* Verify the chip is really an X1205 */
if (kind < 0) {
if (x1205_validate_client(client) < 0) {
err = -ENODEV;
goto exit_kfree;
}
}
/* Inform the i2c layer */
if ((err = i2c_attach_client(client)))
goto exit_kfree;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
rtc = rtc_device_register(x1205_driver.driver.name, &client->dev,
&x1205_rtc_ops, THIS_MODULE);
if (IS_ERR(rtc)) {
err = PTR_ERR(rtc);
goto exit_detach;
}
i2c_set_clientdata(client, rtc);
/* Check for power failures and eventualy enable the osc */
if ((err = x1205_get_status(client, &sr)) == 0) {
if (sr & X1205_SR_RTCF) {
dev_err(&client->dev,
"power failure detected, "
"please set the clock\n");
udelay(50);
x1205_fix_osc(client);
}
}
else
dev_err(&client->dev, "couldn't read status\n");
err = device_create_file(&client->dev, &dev_attr_atrim);
if (err) goto exit_devreg;
err = device_create_file(&client->dev, &dev_attr_dtrim);
if (err) goto exit_atrim;
return 0;
exit_atrim:
device_remove_file(&client->dev, &dev_attr_atrim);
exit_devreg:
rtc_device_unregister(rtc);
exit_detach:
i2c_detach_client(client);
exit_kfree:
kfree(client);
exit:
return err;
}
static int x1205_detach(struct i2c_client *client)
{
int err;
struct rtc_device *rtc = i2c_get_clientdata(client);
if (rtc)
rtc_device_unregister(rtc);
if ((err = i2c_detach_client(client)))
return err;
kfree(client);
return 0;
}
static int __init x1205_init(void)
{
return i2c_add_driver(&x1205_driver);
}
static void __exit x1205_exit(void)
{
i2c_del_driver(&x1205_driver);
}
MODULE_AUTHOR(
"Karen Spearel <kas111 at gmail dot com>, "
"Alessandro Zummo <a.zummo@towertech.it>");
MODULE_DESCRIPTION("Xicor/Intersil X1205 RTC driver");
MODULE_LICENSE("GPL");
MODULE_VERSION(DRV_VERSION);
module_init(x1205_init);
module_exit(x1205_exit);