mirror of
https://github.com/BobTheBlinker/android_kernel_motorola_sm6375.git
synced 2026-10-29 03:01:39 -04:00
Merge branch 'master' of git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
Conflicts: Documentation/kernel-parameters.txt arch/sh/include/asm/elf.h
This commit is contained in:
commit
4cb40f795a
6752 changed files with 538692 additions and 172011 deletions
|
|
@ -220,22 +220,22 @@ config RTC_DRV_PCF8583
|
|||
will be called rtc-pcf8583.
|
||||
|
||||
config RTC_DRV_M41T80
|
||||
tristate "ST M41T80/81/82/83/84/85/87"
|
||||
tristate "ST M41T65/M41T80/81/82/83/84/85/87"
|
||||
help
|
||||
If you say Y here you will get support for the
|
||||
ST M41T80 RTC chips series. Currently following chips are
|
||||
supported: M41T80, M41T81, M41T82, M41T83, M41ST84, M41ST85
|
||||
and M41ST87.
|
||||
If you say Y here you will get support for the ST M41T60
|
||||
and M41T80 RTC chips series. Currently, the following chips are
|
||||
supported: M41T65, M41T80, M41T81, M41T82, M41T83, M41ST84,
|
||||
M41ST85, and M41ST87.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-m41t80.
|
||||
|
||||
config RTC_DRV_M41T80_WDT
|
||||
bool "ST M41T80 series RTC watchdog timer"
|
||||
bool "ST M41T65/M41T80 series RTC watchdog timer"
|
||||
depends on RTC_DRV_M41T80
|
||||
help
|
||||
If you say Y here you will get support for the
|
||||
watchdog timer in ST M41T80 RTC chips series.
|
||||
watchdog timer in the ST M41T60 and M41T80 RTC chips series.
|
||||
|
||||
config RTC_DRV_TWL92330
|
||||
boolean "TI TWL92330/Menelaus"
|
||||
|
|
@ -319,6 +319,15 @@ config RTC_DRV_RS5C348
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-rs5c348.
|
||||
|
||||
config RTC_DRV_DS3234
|
||||
tristate "Maxim/Dallas DS3234"
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
Maxim/Dallas DS3234 SPI RTC chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ds3234.
|
||||
|
||||
endif # SPI_MASTER
|
||||
|
||||
comment "Platform RTC drivers"
|
||||
|
|
@ -329,7 +338,7 @@ comment "Platform RTC drivers"
|
|||
|
||||
config RTC_DRV_CMOS
|
||||
tristate "PC-style 'CMOS'"
|
||||
depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS
|
||||
depends on X86 || ALPHA || ARM || M32R || ATARI || PPC || MIPS || SPARC64
|
||||
default y if X86
|
||||
help
|
||||
Say "yes" here to get direct support for the real time clock
|
||||
|
|
@ -352,6 +361,11 @@ config RTC_DRV_DS1216
|
|||
help
|
||||
If you say yes here you get support for the Dallas DS1216 RTC chips.
|
||||
|
||||
config RTC_DRV_DS1286
|
||||
tristate "Dallas DS1286"
|
||||
help
|
||||
If you say yes here you get support for the Dallas DS1286 RTC chips.
|
||||
|
||||
config RTC_DRV_DS1302
|
||||
tristate "Dallas DS1302"
|
||||
depends on SH_SECUREEDGE5410
|
||||
|
|
@ -405,15 +419,36 @@ config RTC_DRV_M48T86
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-m48t86.
|
||||
|
||||
config RTC_DRV_M48T59
|
||||
tristate "ST M48T59"
|
||||
config RTC_DRV_M48T35
|
||||
tristate "ST M48T35"
|
||||
help
|
||||
If you say Y here you will get support for the
|
||||
ST M48T59 RTC chip.
|
||||
ST M48T35 RTC chip.
|
||||
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-m48t35".
|
||||
|
||||
config RTC_DRV_M48T59
|
||||
tristate "ST M48T59/M48T08/M48T02"
|
||||
help
|
||||
If you say Y here you will get support for the
|
||||
ST M48T59 RTC chip and compatible ST M48T08 and M48T02.
|
||||
|
||||
These chips are usually found in Sun SPARC and UltraSPARC
|
||||
workstations.
|
||||
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-m48t59".
|
||||
|
||||
config RTC_DRV_BQ4802
|
||||
tristate "TI BQ4802"
|
||||
help
|
||||
If you say Y here you will get support for the TI
|
||||
BQ4802 RTC chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-bq4802.
|
||||
|
||||
config RTC_DRV_V3020
|
||||
tristate "EM Microelectronic V3020"
|
||||
help
|
||||
|
|
@ -577,10 +612,24 @@ config RTC_DRV_RS5C313
|
|||
|
||||
config RTC_DRV_PPC
|
||||
tristate "PowerPC machine dependent RTC support"
|
||||
depends on PPC_MERGE
|
||||
depends on PPC
|
||||
help
|
||||
The PowerPC kernel has machine-specific functions for accessing
|
||||
the RTC. This exposes that functionality through the generic RTC
|
||||
class.
|
||||
|
||||
config RTC_DRV_SUN4V
|
||||
bool "SUN4V Hypervisor RTC"
|
||||
depends on SPARC64
|
||||
help
|
||||
If you say Y here you will get support for the Hypervisor
|
||||
based RTC on SUN4V systems.
|
||||
|
||||
config RTC_DRV_STARFIRE
|
||||
bool "Starfire RTC"
|
||||
depends on SPARC64
|
||||
help
|
||||
If you say Y here you will get support for the RTC found on
|
||||
Starfire systems.
|
||||
|
||||
endif # RTC_CLASS
|
||||
|
|
|
|||
|
|
@ -23,6 +23,7 @@ obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
|
|||
obj-$(CONFIG_RTC_DRV_BFIN) += rtc-bfin.o
|
||||
obj-$(CONFIG_RTC_DRV_CMOS) += rtc-cmos.o
|
||||
obj-$(CONFIG_RTC_DRV_DS1216) += rtc-ds1216.o
|
||||
obj-$(CONFIG_RTC_DRV_DS1286) += rtc-ds1286.o
|
||||
obj-$(CONFIG_RTC_DRV_DS1302) += rtc-ds1302.o
|
||||
obj-$(CONFIG_RTC_DRV_DS1305) += rtc-ds1305.o
|
||||
obj-$(CONFIG_RTC_DRV_DS1307) += rtc-ds1307.o
|
||||
|
|
@ -31,13 +32,18 @@ obj-$(CONFIG_RTC_DRV_DS1511) += rtc-ds1511.o
|
|||
obj-$(CONFIG_RTC_DRV_DS1553) += rtc-ds1553.o
|
||||
obj-$(CONFIG_RTC_DRV_DS1672) += rtc-ds1672.o
|
||||
obj-$(CONFIG_RTC_DRV_DS1742) += rtc-ds1742.o
|
||||
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
|
||||
obj-$(CONFIG_RTC_DRV_EP93XX) += rtc-ep93xx.o
|
||||
obj-$(CONFIG_RTC_DRV_FM3130) += rtc-fm3130.o
|
||||
obj-$(CONFIG_RTC_DRV_ISL1208) += rtc-isl1208.o
|
||||
obj-$(CONFIG_RTC_DRV_M41T80) += rtc-m41t80.o
|
||||
obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o
|
||||
obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o
|
||||
obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
|
||||
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
|
||||
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
|
||||
obj-$(CONFIG_RTC_DRV_SUN4V) += rtc-sun4v.o
|
||||
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
|
||||
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
|
||||
|
|
|
|||
|
|
@ -29,10 +29,10 @@
|
|||
#include <linux/completion.h>
|
||||
|
||||
#include <asm/uaccess.h>
|
||||
|
||||
#include <mach/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);
|
||||
|
|
@ -228,8 +228,6 @@ static int at91_rtc_proc(struct device *dev, struct seq_file *seq)
|
|||
(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;
|
||||
}
|
||||
|
|
|
|||
230
drivers/rtc/rtc-bq4802.c
Normal file
230
drivers/rtc/rtc-bq4802.c
Normal file
|
|
@ -0,0 +1,230 @@
|
|||
/* rtc-bq4802.c: TI BQ4802 RTC driver.
|
||||
*
|
||||
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/bcd.h>
|
||||
|
||||
MODULE_AUTHOR("David S. Miller <[email protected]>");
|
||||
MODULE_DESCRIPTION("TI BQ4802 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct bq4802 {
|
||||
void __iomem *regs;
|
||||
unsigned long ioport;
|
||||
struct rtc_device *rtc;
|
||||
spinlock_t lock;
|
||||
struct resource *r;
|
||||
u8 (*read)(struct bq4802 *, int);
|
||||
void (*write)(struct bq4802 *, int, u8);
|
||||
};
|
||||
|
||||
static u8 bq4802_read_io(struct bq4802 *p, int off)
|
||||
{
|
||||
return inb(p->ioport + off);
|
||||
}
|
||||
|
||||
static void bq4802_write_io(struct bq4802 *p, int off, u8 val)
|
||||
{
|
||||
outb(val, p->ioport + off);
|
||||
}
|
||||
|
||||
static u8 bq4802_read_mem(struct bq4802 *p, int off)
|
||||
{
|
||||
return readb(p->regs + off);
|
||||
}
|
||||
|
||||
static void bq4802_write_mem(struct bq4802 *p, int off, u8 val)
|
||||
{
|
||||
writeb(val, p->regs + off);
|
||||
}
|
||||
|
||||
static int bq4802_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct bq4802 *p = platform_get_drvdata(pdev);
|
||||
unsigned long flags;
|
||||
unsigned int century;
|
||||
u8 val;
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
|
||||
val = p->read(p, 0x0e);
|
||||
p->write(p, 0xe, val | 0x08);
|
||||
|
||||
tm->tm_sec = p->read(p, 0x00);
|
||||
tm->tm_min = p->read(p, 0x02);
|
||||
tm->tm_hour = p->read(p, 0x04);
|
||||
tm->tm_mday = p->read(p, 0x06);
|
||||
tm->tm_mon = p->read(p, 0x09);
|
||||
tm->tm_year = p->read(p, 0x0a);
|
||||
tm->tm_wday = p->read(p, 0x08);
|
||||
century = p->read(p, 0x0f);
|
||||
|
||||
p->write(p, 0x0e, val);
|
||||
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
||||
BCD_TO_BIN(tm->tm_sec);
|
||||
BCD_TO_BIN(tm->tm_min);
|
||||
BCD_TO_BIN(tm->tm_hour);
|
||||
BCD_TO_BIN(tm->tm_mday);
|
||||
BCD_TO_BIN(tm->tm_mon);
|
||||
BCD_TO_BIN(tm->tm_year);
|
||||
BCD_TO_BIN(tm->tm_wday);
|
||||
BCD_TO_BIN(century);
|
||||
|
||||
tm->tm_year += (century * 100);
|
||||
tm->tm_year -= 1900;
|
||||
|
||||
tm->tm_mon--;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bq4802_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct bq4802 *p = platform_get_drvdata(pdev);
|
||||
u8 sec, min, hrs, day, mon, yrs, century, val;
|
||||
unsigned long flags;
|
||||
unsigned int year;
|
||||
|
||||
year = tm->tm_year + 1900;
|
||||
century = year / 100;
|
||||
yrs = year % 100;
|
||||
|
||||
mon = tm->tm_mon + 1; /* tm_mon starts at zero */
|
||||
day = tm->tm_mday;
|
||||
hrs = tm->tm_hour;
|
||||
min = tm->tm_min;
|
||||
sec = tm->tm_sec;
|
||||
|
||||
BIN_TO_BCD(sec);
|
||||
BIN_TO_BCD(min);
|
||||
BIN_TO_BCD(hrs);
|
||||
BIN_TO_BCD(day);
|
||||
BIN_TO_BCD(mon);
|
||||
BIN_TO_BCD(yrs);
|
||||
BIN_TO_BCD(century);
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
|
||||
val = p->read(p, 0x0e);
|
||||
p->write(p, 0x0e, val | 0x08);
|
||||
|
||||
p->write(p, 0x00, sec);
|
||||
p->write(p, 0x02, min);
|
||||
p->write(p, 0x04, hrs);
|
||||
p->write(p, 0x06, day);
|
||||
p->write(p, 0x09, mon);
|
||||
p->write(p, 0x0a, yrs);
|
||||
p->write(p, 0x0f, century);
|
||||
|
||||
p->write(p, 0x0e, val);
|
||||
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops bq4802_ops = {
|
||||
.read_time = bq4802_read_time,
|
||||
.set_time = bq4802_set_time,
|
||||
};
|
||||
|
||||
static int __devinit bq4802_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct bq4802 *p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
int err = -ENOMEM;
|
||||
|
||||
if (!p)
|
||||
goto out;
|
||||
|
||||
spin_lock_init(&p->lock);
|
||||
|
||||
p->r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!p->r) {
|
||||
p->r = platform_get_resource(pdev, IORESOURCE_IO, 0);
|
||||
err = -EINVAL;
|
||||
if (!p->r)
|
||||
goto out_free;
|
||||
}
|
||||
if (p->r->flags & IORESOURCE_IO) {
|
||||
p->ioport = p->r->start;
|
||||
p->read = bq4802_read_io;
|
||||
p->write = bq4802_write_io;
|
||||
} else if (p->r->flags & IORESOURCE_MEM) {
|
||||
p->regs = ioremap(p->r->start, resource_size(p->r));
|
||||
p->read = bq4802_read_mem;
|
||||
p->write = bq4802_write_mem;
|
||||
} else {
|
||||
err = -EINVAL;
|
||||
goto out_free;
|
||||
}
|
||||
|
||||
p->rtc = rtc_device_register("bq4802", &pdev->dev,
|
||||
&bq4802_ops, THIS_MODULE);
|
||||
if (IS_ERR(p->rtc)) {
|
||||
err = PTR_ERR(p->rtc);
|
||||
goto out_iounmap;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, p);
|
||||
err = 0;
|
||||
out:
|
||||
return err;
|
||||
|
||||
out_iounmap:
|
||||
if (p->r->flags & IORESOURCE_MEM)
|
||||
iounmap(p->regs);
|
||||
out_free:
|
||||
kfree(p);
|
||||
goto out;
|
||||
}
|
||||
|
||||
static int __devexit bq4802_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct bq4802 *p = platform_get_drvdata(pdev);
|
||||
|
||||
rtc_device_unregister(p->rtc);
|
||||
if (p->r->flags & IORESOURCE_MEM)
|
||||
iounmap(p->regs);
|
||||
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
kfree(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* work with hotplug and coldplug */
|
||||
MODULE_ALIAS("platform:rtc-bq4802");
|
||||
|
||||
static struct platform_driver bq4802_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-bq4802",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = bq4802_probe,
|
||||
.remove = __devexit_p(bq4802_remove),
|
||||
};
|
||||
|
||||
static int __init bq4802_init(void)
|
||||
{
|
||||
return platform_driver_register(&bq4802_driver);
|
||||
}
|
||||
|
||||
static void __exit bq4802_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&bq4802_driver);
|
||||
}
|
||||
|
||||
module_init(bq4802_init);
|
||||
module_exit(bq4802_exit);
|
||||
|
|
@ -636,7 +636,7 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
|
|||
*/
|
||||
#if defined(CONFIG_ATARI)
|
||||
address_space = 64;
|
||||
#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__)
|
||||
#elif defined(__i386__) || defined(__x86_64__) || defined(__arm__) || defined(__sparc__)
|
||||
address_space = 128;
|
||||
#else
|
||||
#warning Assuming 128 bytes of RTC+NVRAM address space, not 64 bytes.
|
||||
|
|
@ -699,7 +699,8 @@ cmos_do_probe(struct device *dev, struct resource *ports, int rtc_irq)
|
|||
/* 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))) {
|
||||
if (is_valid_irq(rtc_irq) &&
|
||||
(!(rtc_control & RTC_24H) || (rtc_control & (RTC_DM_BINARY)))) {
|
||||
dev_dbg(dev, "only 24-hr BCD mode supported\n");
|
||||
retval = -ENXIO;
|
||||
goto cleanup1;
|
||||
|
|
@ -912,6 +913,92 @@ static inline int cmos_poweroff(struct device *dev)
|
|||
* predate even PNPBIOS should set up platform_bus devices.
|
||||
*/
|
||||
|
||||
#ifdef CONFIG_ACPI
|
||||
|
||||
#include <linux/acpi.h>
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static u32 rtc_handler(void *context)
|
||||
{
|
||||
acpi_clear_event(ACPI_EVENT_RTC);
|
||||
acpi_disable_event(ACPI_EVENT_RTC, 0);
|
||||
return ACPI_INTERRUPT_HANDLED;
|
||||
}
|
||||
|
||||
static inline void rtc_wake_setup(void)
|
||||
{
|
||||
acpi_install_fixed_event_handler(ACPI_EVENT_RTC, rtc_handler, NULL);
|
||||
/*
|
||||
* After the RTC handler is installed, the Fixed_RTC event should
|
||||
* be disabled. Only when the RTC alarm is set will it be enabled.
|
||||
*/
|
||||
acpi_clear_event(ACPI_EVENT_RTC);
|
||||
acpi_disable_event(ACPI_EVENT_RTC, 0);
|
||||
}
|
||||
|
||||
static void rtc_wake_on(struct device *dev)
|
||||
{
|
||||
acpi_clear_event(ACPI_EVENT_RTC);
|
||||
acpi_enable_event(ACPI_EVENT_RTC, 0);
|
||||
}
|
||||
|
||||
static void rtc_wake_off(struct device *dev)
|
||||
{
|
||||
acpi_disable_event(ACPI_EVENT_RTC, 0);
|
||||
}
|
||||
#else
|
||||
#define rtc_wake_setup() do{}while(0)
|
||||
#define rtc_wake_on NULL
|
||||
#define rtc_wake_off NULL
|
||||
#endif
|
||||
|
||||
/* Every ACPI platform has a mc146818 compatible "cmos rtc". Here we find
|
||||
* its device node and pass extra config data. This helps its driver use
|
||||
* capabilities that the now-obsolete mc146818 didn't have, and informs it
|
||||
* that this board's RTC is wakeup-capable (per ACPI spec).
|
||||
*/
|
||||
static struct cmos_rtc_board_info acpi_rtc_info;
|
||||
|
||||
static void __devinit
|
||||
cmos_wake_setup(struct device *dev)
|
||||
{
|
||||
if (acpi_disabled)
|
||||
return;
|
||||
|
||||
rtc_wake_setup();
|
||||
acpi_rtc_info.wake_on = rtc_wake_on;
|
||||
acpi_rtc_info.wake_off = rtc_wake_off;
|
||||
|
||||
/* workaround bug in some ACPI tables */
|
||||
if (acpi_gbl_FADT.month_alarm && !acpi_gbl_FADT.day_alarm) {
|
||||
dev_dbg(dev, "bogus FADT month_alarm (%d)\n",
|
||||
acpi_gbl_FADT.month_alarm);
|
||||
acpi_gbl_FADT.month_alarm = 0;
|
||||
}
|
||||
|
||||
acpi_rtc_info.rtc_day_alarm = acpi_gbl_FADT.day_alarm;
|
||||
acpi_rtc_info.rtc_mon_alarm = acpi_gbl_FADT.month_alarm;
|
||||
acpi_rtc_info.rtc_century = acpi_gbl_FADT.century;
|
||||
|
||||
/* NOTE: S4_RTC_WAKE is NOT currently useful to Linux */
|
||||
if (acpi_gbl_FADT.flags & ACPI_FADT_S4_RTC_WAKE)
|
||||
dev_info(dev, "RTC can wake from S4\n");
|
||||
|
||||
dev->platform_data = &acpi_rtc_info;
|
||||
|
||||
/* RTC always wakes from S1/S2/S3, and often S4/STD */
|
||||
device_init_wakeup(dev, 1);
|
||||
}
|
||||
|
||||
#else
|
||||
|
||||
static void __devinit
|
||||
cmos_wake_setup(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PNP
|
||||
|
||||
#include <linux/pnp.h>
|
||||
|
|
@ -919,6 +1006,8 @@ static inline int cmos_poweroff(struct device *dev)
|
|||
static int __devinit
|
||||
cmos_pnp_probe(struct pnp_dev *pnp, const struct pnp_device_id *id)
|
||||
{
|
||||
cmos_wake_setup(&pnp->dev);
|
||||
|
||||
if (pnp_port_start(pnp,0) == 0x70 && !pnp_irq_valid(pnp,0))
|
||||
/* Some machines contain a PNP entry for the RTC, but
|
||||
* don't define the IRQ. It should always be safe to
|
||||
|
|
@ -996,6 +1085,7 @@ static struct pnp_driver cmos_pnp_driver = {
|
|||
|
||||
static int __init cmos_platform_probe(struct platform_device *pdev)
|
||||
{
|
||||
cmos_wake_setup(&pdev->dev);
|
||||
return cmos_do_probe(&pdev->dev,
|
||||
platform_get_resource(pdev, IORESOURCE_IO, 0),
|
||||
platform_get_irq(pdev, 0));
|
||||
|
|
@ -1030,29 +1120,32 @@ static struct platform_driver cmos_platform_driver = {
|
|||
|
||||
static int __init cmos_init(void)
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
#ifdef CONFIG_PNP
|
||||
if (pnp_platform_devices)
|
||||
return pnp_register_driver(&cmos_pnp_driver);
|
||||
else
|
||||
return platform_driver_probe(&cmos_platform_driver,
|
||||
cmos_platform_probe);
|
||||
#else
|
||||
return platform_driver_probe(&cmos_platform_driver,
|
||||
cmos_platform_probe);
|
||||
#endif /* CONFIG_PNP */
|
||||
pnp_register_driver(&cmos_pnp_driver);
|
||||
#endif
|
||||
|
||||
if (!cmos_rtc.dev)
|
||||
retval = platform_driver_probe(&cmos_platform_driver,
|
||||
cmos_platform_probe);
|
||||
|
||||
if (retval == 0)
|
||||
return 0;
|
||||
|
||||
#ifdef CONFIG_PNP
|
||||
pnp_unregister_driver(&cmos_pnp_driver);
|
||||
#endif
|
||||
return retval;
|
||||
}
|
||||
module_init(cmos_init);
|
||||
|
||||
static void __exit cmos_exit(void)
|
||||
{
|
||||
#ifdef CONFIG_PNP
|
||||
if (pnp_platform_devices)
|
||||
pnp_unregister_driver(&cmos_pnp_driver);
|
||||
else
|
||||
platform_driver_unregister(&cmos_platform_driver);
|
||||
#else
|
||||
pnp_unregister_driver(&cmos_pnp_driver);
|
||||
#endif
|
||||
platform_driver_unregister(&cmos_platform_driver);
|
||||
#endif /* CONFIG_PNP */
|
||||
}
|
||||
module_exit(cmos_exit);
|
||||
|
||||
|
|
|
|||
|
|
@ -422,28 +422,37 @@ done:
|
|||
return err;
|
||||
}
|
||||
|
||||
static int rtc_dev_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
struct rtc_device *rtc = file->private_data;
|
||||
return fasync_helper(fd, file, on, &rtc->async_queue);
|
||||
}
|
||||
|
||||
static int rtc_dev_release(struct inode *inode, struct file *file)
|
||||
{
|
||||
struct rtc_device *rtc = file->private_data;
|
||||
|
||||
#ifdef CONFIG_RTC_INTF_DEV_UIE_EMUL
|
||||
clear_uie(rtc);
|
||||
#endif
|
||||
/* We shut down the repeating IRQs that userspace enabled,
|
||||
* since nothing is listening to them.
|
||||
* - Update (UIE) ... currently only managed through ioctls
|
||||
* - Periodic (PIE) ... also used through rtc_*() interface calls
|
||||
*
|
||||
* Leave the alarm alone; it may be set to trigger a system wakeup
|
||||
* later, or be used by kernel code, and is a one-shot event anyway.
|
||||
*/
|
||||
rtc_dev_ioctl(file, RTC_UIE_OFF, 0);
|
||||
rtc_irq_set_state(rtc, NULL, 0);
|
||||
|
||||
if (rtc->ops->release)
|
||||
rtc->ops->release(rtc->dev.parent);
|
||||
|
||||
if (file->f_flags & FASYNC)
|
||||
rtc_dev_fasync(-1, file, 0);
|
||||
|
||||
clear_bit_unlock(RTC_DEV_BUSY, &rtc->flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rtc_dev_fasync(int fd, struct file *file, int on)
|
||||
{
|
||||
struct rtc_device *rtc = 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,
|
||||
|
|
|
|||
410
drivers/rtc/rtc-ds1286.c
Normal file
410
drivers/rtc/rtc-ds1286.c
Normal file
|
|
@ -0,0 +1,410 @@
|
|||
/*
|
||||
* DS1286 Real Time Clock interface for Linux
|
||||
*
|
||||
* Copyright (C) 1998, 1999, 2000 Ralf Baechle
|
||||
* Copyright (C) 2008 Thomas Bogendoerfer
|
||||
*
|
||||
* Based on code written 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/rtc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/ds1286.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_VERSION "1.0"
|
||||
|
||||
struct ds1286_priv {
|
||||
struct rtc_device *rtc;
|
||||
u32 __iomem *rtcregs;
|
||||
size_t size;
|
||||
unsigned long baseaddr;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static inline u8 ds1286_rtc_read(struct ds1286_priv *priv, int reg)
|
||||
{
|
||||
return __raw_readl(&priv->rtcregs[reg]) & 0xff;
|
||||
}
|
||||
|
||||
static inline void ds1286_rtc_write(struct ds1286_priv *priv, u8 data, int reg)
|
||||
{
|
||||
__raw_writel(data, &priv->rtcregs[reg]);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_RTC_INTF_DEV
|
||||
|
||||
static int ds1286_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct ds1286_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned long flags;
|
||||
unsigned char val;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_AIE_OFF:
|
||||
/* Mask alarm int. enab. bit */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
val = ds1286_rtc_read(priv, RTC_CMD);
|
||||
val |= RTC_TDM;
|
||||
ds1286_rtc_write(priv, val, RTC_CMD);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
break;
|
||||
case RTC_AIE_ON:
|
||||
/* Allow alarm interrupts. */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
val = ds1286_rtc_read(priv, RTC_CMD);
|
||||
val &= ~RTC_TDM;
|
||||
ds1286_rtc_write(priv, val, RTC_CMD);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
break;
|
||||
case RTC_WIE_OFF:
|
||||
/* Mask watchdog int. enab. bit */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
val = ds1286_rtc_read(priv, RTC_CMD);
|
||||
val |= RTC_WAM;
|
||||
ds1286_rtc_write(priv, val, RTC_CMD);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
break;
|
||||
case RTC_WIE_ON:
|
||||
/* Allow watchdog interrupts. */
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
val = ds1286_rtc_read(priv, RTC_CMD);
|
||||
val &= ~RTC_WAM;
|
||||
ds1286_rtc_write(priv, val, RTC_CMD);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
break;
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define ds1286_ioctl NULL
|
||||
#endif
|
||||
|
||||
#ifdef CONFIG_PROC_FS
|
||||
|
||||
static int ds1286_proc(struct device *dev, struct seq_file *seq)
|
||||
{
|
||||
struct ds1286_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned char month, cmd, amode;
|
||||
const char *s;
|
||||
|
||||
month = ds1286_rtc_read(priv, RTC_MONTH);
|
||||
seq_printf(seq,
|
||||
"oscillator\t: %s\n"
|
||||
"square_wave\t: %s\n",
|
||||
(month & RTC_EOSC) ? "disabled" : "enabled",
|
||||
(month & RTC_ESQW) ? "disabled" : "enabled");
|
||||
|
||||
amode = ((ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x80) >> 5) |
|
||||
((ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x80) >> 6) |
|
||||
((ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x80) >> 7);
|
||||
switch (amode) {
|
||||
case 7:
|
||||
s = "each minute";
|
||||
break;
|
||||
case 3:
|
||||
s = "minutes match";
|
||||
break;
|
||||
case 1:
|
||||
s = "hours and minutes match";
|
||||
break;
|
||||
case 0:
|
||||
s = "days, hours and minutes match";
|
||||
break;
|
||||
default:
|
||||
s = "invalid";
|
||||
break;
|
||||
}
|
||||
seq_printf(seq, "alarm_mode\t: %s\n", s);
|
||||
|
||||
cmd = ds1286_rtc_read(priv, RTC_CMD);
|
||||
seq_printf(seq,
|
||||
"alarm_enable\t: %s\n"
|
||||
"wdog_alarm\t: %s\n"
|
||||
"alarm_mask\t: %s\n"
|
||||
"wdog_alarm_mask\t: %s\n"
|
||||
"interrupt_mode\t: %s\n"
|
||||
"INTB_mode\t: %s_active\n"
|
||||
"interrupt_pins\t: %s\n",
|
||||
(cmd & RTC_TDF) ? "yes" : "no",
|
||||
(cmd & RTC_WAF) ? "yes" : "no",
|
||||
(cmd & RTC_TDM) ? "disabled" : "enabled",
|
||||
(cmd & RTC_WAM) ? "disabled" : "enabled",
|
||||
(cmd & RTC_PU_LVL) ? "pulse" : "level",
|
||||
(cmd & RTC_IBH_LO) ? "low" : "high",
|
||||
(cmd & RTC_IPSW) ? "unswapped" : "swapped");
|
||||
return 0;
|
||||
}
|
||||
|
||||
#else
|
||||
#define ds1286_proc NULL
|
||||
#endif
|
||||
|
||||
static int ds1286_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct ds1286_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned char save_control;
|
||||
unsigned long flags;
|
||||
unsigned long uip_watchdog = jiffies;
|
||||
|
||||
/*
|
||||
* read RTC once any update in progress is done. The update
|
||||
* can take just over 2ms. We wait 10 to 20ms. There is no need to
|
||||
* to poll-wait (up to 1s - eeccch) for the falling edge of RTC_UIP.
|
||||
* If you need to know *exactly* when a second has started, enable
|
||||
* periodic update complete interrupts, (via ioctl) and then
|
||||
* immediately read /dev/rtc which will block until you get the IRQ.
|
||||
* Once the read clears, read the RTC time (again via ioctl). Easy.
|
||||
*/
|
||||
|
||||
if (ds1286_rtc_read(priv, RTC_CMD) & RTC_TE)
|
||||
while (time_before(jiffies, uip_watchdog + 2*HZ/100))
|
||||
barrier();
|
||||
|
||||
/*
|
||||
* Only the values that we read from the RTC are set. We leave
|
||||
* tm_wday, tm_yday and tm_isdst untouched. Even though the
|
||||
* RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
|
||||
* by the RTC when initially set to a non-zero value.
|
||||
*/
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
save_control = ds1286_rtc_read(priv, RTC_CMD);
|
||||
ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD);
|
||||
|
||||
tm->tm_sec = ds1286_rtc_read(priv, RTC_SECONDS);
|
||||
tm->tm_min = ds1286_rtc_read(priv, RTC_MINUTES);
|
||||
tm->tm_hour = ds1286_rtc_read(priv, RTC_HOURS) & 0x3f;
|
||||
tm->tm_mday = ds1286_rtc_read(priv, RTC_DATE);
|
||||
tm->tm_mon = ds1286_rtc_read(priv, RTC_MONTH) & 0x1f;
|
||||
tm->tm_year = ds1286_rtc_read(priv, RTC_YEAR);
|
||||
|
||||
ds1286_rtc_write(priv, save_control, RTC_CMD);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
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);
|
||||
tm->tm_year = bcd2bin(tm->tm_year);
|
||||
|
||||
/*
|
||||
* Account for differences between how the RTC uses the values
|
||||
* and how they are defined in a struct rtc_time;
|
||||
*/
|
||||
if (tm->tm_year < 45)
|
||||
tm->tm_year += 30;
|
||||
tm->tm_year += 40;
|
||||
if (tm->tm_year < 70)
|
||||
tm->tm_year += 100;
|
||||
|
||||
tm->tm_mon--;
|
||||
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int ds1286_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct ds1286_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned char mon, day, hrs, min, sec;
|
||||
unsigned char save_control;
|
||||
unsigned int yrs;
|
||||
unsigned long flags;
|
||||
|
||||
yrs = tm->tm_year + 1900;
|
||||
mon = tm->tm_mon + 1; /* tm_mon starts at zero */
|
||||
day = tm->tm_mday;
|
||||
hrs = tm->tm_hour;
|
||||
min = tm->tm_min;
|
||||
sec = tm->tm_sec;
|
||||
|
||||
if (yrs < 1970)
|
||||
return -EINVAL;
|
||||
|
||||
yrs -= 1940;
|
||||
if (yrs > 255) /* They are unsigned */
|
||||
return -EINVAL;
|
||||
|
||||
if (yrs >= 100)
|
||||
yrs -= 100;
|
||||
|
||||
sec = bin2bcd(sec);
|
||||
min = bin2bcd(min);
|
||||
hrs = bin2bcd(hrs);
|
||||
day = bin2bcd(day);
|
||||
mon = bin2bcd(mon);
|
||||
yrs = bin2bcd(yrs);
|
||||
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
save_control = ds1286_rtc_read(priv, RTC_CMD);
|
||||
ds1286_rtc_write(priv, (save_control|RTC_TE), RTC_CMD);
|
||||
|
||||
ds1286_rtc_write(priv, yrs, RTC_YEAR);
|
||||
ds1286_rtc_write(priv, mon, RTC_MONTH);
|
||||
ds1286_rtc_write(priv, day, RTC_DATE);
|
||||
ds1286_rtc_write(priv, hrs, RTC_HOURS);
|
||||
ds1286_rtc_write(priv, min, RTC_MINUTES);
|
||||
ds1286_rtc_write(priv, sec, RTC_SECONDS);
|
||||
ds1286_rtc_write(priv, 0, RTC_HUNDREDTH_SECOND);
|
||||
|
||||
ds1286_rtc_write(priv, save_control, RTC_CMD);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds1286_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct ds1286_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned char cmd;
|
||||
unsigned long flags;
|
||||
|
||||
/*
|
||||
* Only the values that we read from the RTC are set. That
|
||||
* means only tm_wday, tm_hour, tm_min.
|
||||
*/
|
||||
spin_lock_irqsave(&priv->lock, flags);
|
||||
alm->time.tm_min = ds1286_rtc_read(priv, RTC_MINUTES_ALARM) & 0x7f;
|
||||
alm->time.tm_hour = ds1286_rtc_read(priv, RTC_HOURS_ALARM) & 0x1f;
|
||||
alm->time.tm_wday = ds1286_rtc_read(priv, RTC_DAY_ALARM) & 0x07;
|
||||
cmd = ds1286_rtc_read(priv, RTC_CMD);
|
||||
spin_unlock_irqrestore(&priv->lock, flags);
|
||||
|
||||
alm->time.tm_min = bcd2bin(alm->time.tm_min);
|
||||
alm->time.tm_hour = bcd2bin(alm->time.tm_hour);
|
||||
alm->time.tm_sec = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds1286_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct ds1286_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned char hrs, min, sec;
|
||||
|
||||
hrs = alm->time.tm_hour;
|
||||
min = alm->time.tm_min;
|
||||
sec = alm->time.tm_sec;
|
||||
|
||||
if (hrs >= 24)
|
||||
hrs = 0xff;
|
||||
|
||||
if (min >= 60)
|
||||
min = 0xff;
|
||||
|
||||
if (sec != 0)
|
||||
return -EINVAL;
|
||||
|
||||
min = bin2bcd(min);
|
||||
hrs = bin2bcd(hrs);
|
||||
|
||||
spin_lock(&priv->lock);
|
||||
ds1286_rtc_write(priv, hrs, RTC_HOURS_ALARM);
|
||||
ds1286_rtc_write(priv, min, RTC_MINUTES_ALARM);
|
||||
spin_unlock(&priv->lock);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops ds1286_ops = {
|
||||
.ioctl = ds1286_ioctl,
|
||||
.proc = ds1286_proc,
|
||||
.read_time = ds1286_read_time,
|
||||
.set_time = ds1286_set_time,
|
||||
.read_alarm = ds1286_read_alarm,
|
||||
.set_alarm = ds1286_set_alarm,
|
||||
};
|
||||
|
||||
static int __devinit ds1286_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
struct resource *res;
|
||||
struct ds1286_priv *priv;
|
||||
int ret = 0;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
priv = kzalloc(sizeof(struct ds1286_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->size = res->end - res->start + 1;
|
||||
if (!request_mem_region(res->start, priv->size, pdev->name)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
priv->baseaddr = res->start;
|
||||
priv->rtcregs = ioremap(priv->baseaddr, priv->size);
|
||||
if (!priv->rtcregs) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
spin_lock_init(&priv->lock);
|
||||
rtc = rtc_device_register("ds1286", &pdev->dev,
|
||||
&ds1286_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc)) {
|
||||
ret = PTR_ERR(rtc);
|
||||
goto out;
|
||||
}
|
||||
priv->rtc = rtc;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (priv->rtc)
|
||||
rtc_device_unregister(priv->rtc);
|
||||
if (priv->rtcregs)
|
||||
iounmap(priv->rtcregs);
|
||||
if (priv->baseaddr)
|
||||
release_mem_region(priv->baseaddr, priv->size);
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit ds1286_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct ds1286_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
rtc_device_unregister(priv->rtc);
|
||||
iounmap(priv->rtcregs);
|
||||
release_mem_region(priv->baseaddr, priv->size);
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver ds1286_platform_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-ds1286",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ds1286_probe,
|
||||
.remove = __devexit_p(ds1286_remove),
|
||||
};
|
||||
|
||||
static int __init ds1286_init(void)
|
||||
{
|
||||
return platform_driver_register(&ds1286_platform_driver);
|
||||
}
|
||||
|
||||
static void __exit ds1286_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&ds1286_platform_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Thomas Bogendoerfer <[email protected]>");
|
||||
MODULE_DESCRIPTION("DS1286 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
MODULE_ALIAS("platform:rtc-ds1286");
|
||||
|
||||
module_init(ds1286_init);
|
||||
module_exit(ds1286_exit);
|
||||
|
|
@ -23,10 +23,6 @@
|
|||
* 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.
|
||||
*
|
||||
* This is currently a simple no-alarms driver. If your board has the
|
||||
* alarm irq wired up on a ds1337 or ds1339, and you want to use that,
|
||||
* then look at the rtc-rs5c372 driver for code to steal...
|
||||
*/
|
||||
enum ds_type {
|
||||
ds_1307,
|
||||
|
|
@ -67,6 +63,7 @@ enum ds_type {
|
|||
# define DS1307_BIT_RS0 0x01
|
||||
#define DS1337_REG_CONTROL 0x0e
|
||||
# define DS1337_BIT_nEOSC 0x80
|
||||
# define DS1339_BIT_BBSQI 0x20
|
||||
# define DS1337_BIT_RS2 0x10
|
||||
# define DS1337_BIT_RS1 0x08
|
||||
# define DS1337_BIT_INTCN 0x04
|
||||
|
|
@ -83,19 +80,22 @@ enum ds_type {
|
|||
# define DS1337_BIT_OSF 0x80
|
||||
# define DS1337_BIT_A2I 0x02
|
||||
# define DS1337_BIT_A1I 0x01
|
||||
#define DS1339_REG_ALARM1_SECS 0x07
|
||||
#define DS1339_REG_TRICKLE 0x10
|
||||
|
||||
|
||||
|
||||
struct ds1307 {
|
||||
u8 reg_addr;
|
||||
bool has_nvram;
|
||||
u8 regs[8];
|
||||
u8 regs[11];
|
||||
enum ds_type type;
|
||||
unsigned long flags;
|
||||
#define HAS_NVRAM 0 /* bit 0 == sysfs file active */
|
||||
#define HAS_ALARM 1 /* bit 1 == irq claimed */
|
||||
struct i2c_msg msg[2];
|
||||
struct i2c_client *client;
|
||||
struct i2c_client dev;
|
||||
struct rtc_device *rtc;
|
||||
struct work_struct work;
|
||||
};
|
||||
|
||||
struct chip_desc {
|
||||
|
|
@ -132,12 +132,79 @@ static const struct i2c_device_id ds1307_id[] = {
|
|||
};
|
||||
MODULE_DEVICE_TABLE(i2c, ds1307_id);
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
/*
|
||||
* The IRQ logic includes a "real" handler running in IRQ context just
|
||||
* long enough to schedule this workqueue entry. We need a task context
|
||||
* to talk to the RTC, since I2C I/O calls require that; and disable the
|
||||
* IRQ until we clear its status on the chip, so that this handler can
|
||||
* work with any type of triggering (not just falling edge).
|
||||
*
|
||||
* The ds1337 and ds1339 both have two alarms, but we only use the first
|
||||
* one (with a "seconds" field). For ds1337 we expect nINTA is our alarm
|
||||
* signal; ds1339 chips have only one alarm signal.
|
||||
*/
|
||||
static void ds1307_work(struct work_struct *work)
|
||||
{
|
||||
struct ds1307 *ds1307;
|
||||
struct i2c_client *client;
|
||||
struct mutex *lock;
|
||||
int stat, control;
|
||||
|
||||
ds1307 = container_of(work, struct ds1307, work);
|
||||
client = ds1307->client;
|
||||
lock = &ds1307->rtc->ops_lock;
|
||||
|
||||
mutex_lock(lock);
|
||||
stat = i2c_smbus_read_byte_data(client, DS1337_REG_STATUS);
|
||||
if (stat < 0)
|
||||
goto out;
|
||||
|
||||
if (stat & DS1337_BIT_A1I) {
|
||||
stat &= ~DS1337_BIT_A1I;
|
||||
i2c_smbus_write_byte_data(client, DS1337_REG_STATUS, stat);
|
||||
|
||||
control = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
|
||||
if (control < 0)
|
||||
goto out;
|
||||
|
||||
control &= ~DS1337_BIT_A1IE;
|
||||
i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL, control);
|
||||
|
||||
/* rtc_update_irq() assumes that it is called
|
||||
* from IRQ-disabled context.
|
||||
*/
|
||||
local_irq_disable();
|
||||
rtc_update_irq(ds1307->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
local_irq_enable();
|
||||
}
|
||||
|
||||
out:
|
||||
if (test_bit(HAS_ALARM, &ds1307->flags))
|
||||
enable_irq(client->irq);
|
||||
mutex_unlock(lock);
|
||||
}
|
||||
|
||||
static irqreturn_t ds1307_irq(int irq, void *dev_id)
|
||||
{
|
||||
struct i2c_client *client = dev_id;
|
||||
struct ds1307 *ds1307 = i2c_get_clientdata(client);
|
||||
|
||||
disable_irq_nosync(irq);
|
||||
schedule_work(&ds1307->work);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
||||
static int ds1307_get_time(struct device *dev, struct rtc_time *t)
|
||||
{
|
||||
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
||||
int tmp;
|
||||
|
||||
/* read the RTC date and time registers all at once */
|
||||
ds1307->reg_addr = 0;
|
||||
ds1307->msg[1].flags = I2C_M_RD;
|
||||
ds1307->msg[1].len = 7;
|
||||
|
||||
|
|
@ -231,9 +298,186 @@ static int ds1307_set_time(struct device *dev, struct rtc_time *t)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int ds1307_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ds1307 *ds1307 = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
||||
return -EINVAL;
|
||||
|
||||
/* read all ALARM1, ALARM2, and status registers at once */
|
||||
ds1307->reg_addr = DS1339_REG_ALARM1_SECS;
|
||||
ds1307->msg[1].flags = I2C_M_RD;
|
||||
ds1307->msg[1].len = 9;
|
||||
|
||||
ret = i2c_transfer(to_i2c_adapter(client->dev.parent),
|
||||
ds1307->msg, 2);
|
||||
if (ret != 2) {
|
||||
dev_err(dev, "%s error %d\n", "alarm read", ret);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "%s: %02x %02x %02x %02x, %02x %02x %02x, %02x %02x\n",
|
||||
"alarm read",
|
||||
ds1307->regs[0], ds1307->regs[1],
|
||||
ds1307->regs[2], ds1307->regs[3],
|
||||
ds1307->regs[4], ds1307->regs[5],
|
||||
ds1307->regs[6], ds1307->regs[7],
|
||||
ds1307->regs[8]);
|
||||
|
||||
/* report alarm time (ALARM1); assume 24 hour and day-of-month modes,
|
||||
* and that all four fields are checked matches
|
||||
*/
|
||||
t->time.tm_sec = bcd2bin(ds1307->regs[0] & 0x7f);
|
||||
t->time.tm_min = bcd2bin(ds1307->regs[1] & 0x7f);
|
||||
t->time.tm_hour = bcd2bin(ds1307->regs[2] & 0x3f);
|
||||
t->time.tm_mday = bcd2bin(ds1307->regs[3] & 0x3f);
|
||||
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 = !!(ds1307->regs[7] & DS1337_BIT_A1IE);
|
||||
t->pending = !!(ds1307->regs[8] & DS1337_BIT_A1I);
|
||||
|
||||
dev_dbg(dev, "%s secs=%d, mins=%d, "
|
||||
"hours=%d, mday=%d, enabled=%d, pending=%d\n",
|
||||
"alarm read", t->time.tm_sec, t->time.tm_min,
|
||||
t->time.tm_hour, t->time.tm_mday,
|
||||
t->enabled, t->pending);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds1307_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ds1307 *ds1307 = i2c_get_clientdata(client);
|
||||
unsigned char *buf = ds1307->regs;
|
||||
u8 control, status;
|
||||
int ret;
|
||||
|
||||
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
||||
return -EINVAL;
|
||||
|
||||
dev_dbg(dev, "%s secs=%d, mins=%d, "
|
||||
"hours=%d, mday=%d, enabled=%d, pending=%d\n",
|
||||
"alarm set", t->time.tm_sec, t->time.tm_min,
|
||||
t->time.tm_hour, t->time.tm_mday,
|
||||
t->enabled, t->pending);
|
||||
|
||||
/* read current status of both alarms and the chip */
|
||||
ds1307->reg_addr = DS1339_REG_ALARM1_SECS;
|
||||
ds1307->msg[1].flags = I2C_M_RD;
|
||||
ds1307->msg[1].len = 9;
|
||||
|
||||
ret = i2c_transfer(to_i2c_adapter(client->dev.parent),
|
||||
ds1307->msg, 2);
|
||||
if (ret != 2) {
|
||||
dev_err(dev, "%s error %d\n", "alarm write", ret);
|
||||
return -EIO;
|
||||
}
|
||||
control = ds1307->regs[7];
|
||||
status = ds1307->regs[8];
|
||||
|
||||
dev_dbg(dev, "%s: %02x %02x %02x %02x, %02x %02x %02x, %02x %02x\n",
|
||||
"alarm set (old status)",
|
||||
ds1307->regs[0], ds1307->regs[1],
|
||||
ds1307->regs[2], ds1307->regs[3],
|
||||
ds1307->regs[4], ds1307->regs[5],
|
||||
ds1307->regs[6], control, status);
|
||||
|
||||
/* set ALARM1, using 24 hour and day-of-month modes */
|
||||
*buf++ = DS1339_REG_ALARM1_SECS; /* first register addr */
|
||||
buf[0] = bin2bcd(t->time.tm_sec);
|
||||
buf[1] = bin2bcd(t->time.tm_min);
|
||||
buf[2] = bin2bcd(t->time.tm_hour);
|
||||
buf[3] = bin2bcd(t->time.tm_mday);
|
||||
|
||||
/* set ALARM2 to non-garbage */
|
||||
buf[4] = 0;
|
||||
buf[5] = 0;
|
||||
buf[6] = 0;
|
||||
|
||||
/* optionally enable ALARM1 */
|
||||
buf[7] = control & ~(DS1337_BIT_A1IE | DS1337_BIT_A2IE);
|
||||
if (t->enabled) {
|
||||
dev_dbg(dev, "alarm IRQ armed\n");
|
||||
buf[7] |= DS1337_BIT_A1IE; /* only ALARM1 is used */
|
||||
}
|
||||
buf[8] = status & ~(DS1337_BIT_A1I | DS1337_BIT_A2I);
|
||||
|
||||
ds1307->msg[1].flags = 0;
|
||||
ds1307->msg[1].len = 10;
|
||||
|
||||
ret = i2c_transfer(to_i2c_adapter(client->dev.parent),
|
||||
&ds1307->msg[1], 1);
|
||||
if (ret != 1) {
|
||||
dev_err(dev, "can't set alarm time\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds1307_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct ds1307 *ds1307 = i2c_get_clientdata(client);
|
||||
int ret;
|
||||
|
||||
switch (cmd) {
|
||||
case RTC_AIE_OFF:
|
||||
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
||||
return -ENOTTY;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret &= ~DS1337_BIT_A1IE;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
DS1337_REG_CONTROL, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
|
||||
case RTC_AIE_ON:
|
||||
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
||||
return -ENOTTY;
|
||||
|
||||
ret = i2c_smbus_read_byte_data(client, DS1337_REG_CONTROL);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret |= DS1337_BIT_A1IE;
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client,
|
||||
DS1337_REG_CONTROL, ret);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
break;
|
||||
|
||||
default:
|
||||
return -ENOIOCTLCMD;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops ds13xx_rtc_ops = {
|
||||
.read_time = ds1307_get_time,
|
||||
.set_time = ds1307_set_time,
|
||||
.read_alarm = ds1307_read_alarm,
|
||||
.set_alarm = ds1307_set_alarm,
|
||||
.ioctl = ds1307_ioctl,
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
|
@ -327,6 +571,7 @@ static int __devinit ds1307_probe(struct i2c_client *client,
|
|||
int tmp;
|
||||
const struct chip_desc *chip = &chips[id->driver_data];
|
||||
struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent);
|
||||
int want_irq = false;
|
||||
|
||||
if (!i2c_check_functionality(adapter,
|
||||
I2C_FUNC_I2C | I2C_FUNC_SMBUS_WRITE_BYTE_DATA))
|
||||
|
|
@ -353,6 +598,12 @@ static int __devinit ds1307_probe(struct i2c_client *client,
|
|||
switch (ds1307->type) {
|
||||
case ds_1337:
|
||||
case ds_1339:
|
||||
/* has IRQ? */
|
||||
if (ds1307->client->irq > 0 && chip->alarm) {
|
||||
INIT_WORK(&ds1307->work, ds1307_work);
|
||||
want_irq = true;
|
||||
}
|
||||
|
||||
ds1307->reg_addr = DS1337_REG_CONTROL;
|
||||
ds1307->msg[1].len = 2;
|
||||
|
||||
|
|
@ -369,8 +620,20 @@ static int __devinit ds1307_probe(struct i2c_client *client,
|
|||
|
||||
/* oscillator off? turn it on, so clock can tick. */
|
||||
if (ds1307->regs[0] & DS1337_BIT_nEOSC)
|
||||
i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL,
|
||||
ds1307->regs[0] & ~DS1337_BIT_nEOSC);
|
||||
ds1307->regs[0] &= ~DS1337_BIT_nEOSC;
|
||||
|
||||
/* Using IRQ? Disable the square wave and both alarms.
|
||||
* For ds1339, be sure alarms can trigger when we're
|
||||
* running on Vbackup (BBSQI); we assume ds1337 will
|
||||
* ignore that bit
|
||||
*/
|
||||
if (want_irq) {
|
||||
ds1307->regs[0] |= DS1337_BIT_INTCN | DS1339_BIT_BBSQI;
|
||||
ds1307->regs[0] &= ~(DS1337_BIT_A2IE | DS1337_BIT_A1IE);
|
||||
}
|
||||
|
||||
i2c_smbus_write_byte_data(client, DS1337_REG_CONTROL,
|
||||
ds1307->regs[0]);
|
||||
|
||||
/* oscillator fault? clear flag, and warn */
|
||||
if (ds1307->regs[1] & DS1337_BIT_OSF) {
|
||||
|
|
@ -495,10 +758,22 @@ read_rtc:
|
|||
goto exit_free;
|
||||
}
|
||||
|
||||
if (want_irq) {
|
||||
err = request_irq(client->irq, ds1307_irq, 0,
|
||||
ds1307->rtc->name, client);
|
||||
if (err) {
|
||||
dev_err(&client->dev,
|
||||
"unable to request IRQ!\n");
|
||||
goto exit_irq;
|
||||
}
|
||||
set_bit(HAS_ALARM, &ds1307->flags);
|
||||
dev_dbg(&client->dev, "got IRQ %d\n", client->irq);
|
||||
}
|
||||
|
||||
if (chip->nvram56) {
|
||||
err = sysfs_create_bin_file(&client->dev.kobj, &nvram);
|
||||
if (err == 0) {
|
||||
ds1307->has_nvram = true;
|
||||
set_bit(HAS_NVRAM, &ds1307->flags);
|
||||
dev_info(&client->dev, "56 bytes nvram\n");
|
||||
}
|
||||
}
|
||||
|
|
@ -512,7 +787,9 @@ exit_bad:
|
|||
ds1307->regs[2], ds1307->regs[3],
|
||||
ds1307->regs[4], ds1307->regs[5],
|
||||
ds1307->regs[6]);
|
||||
|
||||
exit_irq:
|
||||
if (ds1307->rtc)
|
||||
rtc_device_unregister(ds1307->rtc);
|
||||
exit_free:
|
||||
kfree(ds1307);
|
||||
return err;
|
||||
|
|
@ -520,9 +797,14 @@ exit_free:
|
|||
|
||||
static int __devexit ds1307_remove(struct i2c_client *client)
|
||||
{
|
||||
struct ds1307 *ds1307 = i2c_get_clientdata(client);
|
||||
struct ds1307 *ds1307 = i2c_get_clientdata(client);
|
||||
|
||||
if (ds1307->has_nvram)
|
||||
if (test_and_clear_bit(HAS_ALARM, &ds1307->flags)) {
|
||||
free_irq(client->irq, client);
|
||||
cancel_work_sync(&ds1307->work);
|
||||
}
|
||||
|
||||
if (test_and_clear_bit(HAS_NVRAM, &ds1307->flags))
|
||||
sysfs_remove_bin_file(&client->dev.kobj, &nvram);
|
||||
|
||||
rtc_device_unregister(ds1307->rtc);
|
||||
|
|
|
|||
|
|
@ -429,12 +429,33 @@ static int __devexit ds1374_remove(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM
|
||||
static int ds1374_suspend(struct i2c_client *client, pm_message_t state)
|
||||
{
|
||||
if (client->irq >= 0 && device_may_wakeup(&client->dev))
|
||||
enable_irq_wake(client->irq);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds1374_resume(struct i2c_client *client)
|
||||
{
|
||||
if (client->irq >= 0 && device_may_wakeup(&client->dev))
|
||||
disable_irq_wake(client->irq);
|
||||
return 0;
|
||||
}
|
||||
#else
|
||||
#define ds1374_suspend NULL
|
||||
#define ds1374_resume NULL
|
||||
#endif
|
||||
|
||||
static struct i2c_driver ds1374_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-ds1374",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ds1374_probe,
|
||||
.suspend = ds1374_suspend,
|
||||
.resume = ds1374_resume,
|
||||
.remove = __devexit_p(ds1374_remove),
|
||||
.id_table = ds1374_id,
|
||||
};
|
||||
|
|
|
|||
|
|
@ -379,18 +379,6 @@ ds1511_interrupt(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void
|
||||
ds1511_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;
|
||||
ds1511_rtc_update_alarm(pdata);
|
||||
}
|
||||
}
|
||||
|
||||
static int
|
||||
ds1511_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
|
|
@ -428,7 +416,6 @@ static const struct rtc_class_ops ds1511_rtc_ops = {
|
|||
.set_time = ds1511_rtc_set_time,
|
||||
.read_alarm = ds1511_rtc_read_alarm,
|
||||
.set_alarm = ds1511_rtc_set_alarm,
|
||||
.release = ds1511_rtc_release,
|
||||
.ioctl = ds1511_rtc_ioctl,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -207,17 +207,6 @@ static irqreturn_t ds1553_rtc_interrupt(int irq, void *dev_id)
|
|||
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)
|
||||
{
|
||||
|
|
@ -254,7 +243,6 @@ static const struct rtc_class_ops ds1553_rtc_ops = {
|
|||
.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,
|
||||
};
|
||||
|
||||
|
|
|
|||
|
|
@ -9,17 +9,10 @@
|
|||
* 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 const unsigned short normal_i2c[] = { I2C_CLIENT_END };
|
||||
|
||||
/* Insmod parameters */
|
||||
I2C_CLIENT_INSMOD;
|
||||
#define DRV_VERSION "0.4"
|
||||
|
||||
/* Registers */
|
||||
|
||||
|
|
@ -29,8 +22,7 @@ I2C_CLIENT_INSMOD;
|
|||
|
||||
#define DS1672_REG_CONTROL_EOSC 0x80
|
||||
|
||||
/* Prototypes */
|
||||
static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind);
|
||||
static struct i2c_driver ds1672_driver;
|
||||
|
||||
/*
|
||||
* In the routines that deal directly with the ds1672 hardware, we use
|
||||
|
|
@ -44,8 +36,8 @@ static int ds1672_get_datetime(struct i2c_client *client, struct rtc_time *tm)
|
|||
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 */
|
||||
{client->addr, 0, 1, &addr}, /* setup read ptr */
|
||||
{client->addr, I2C_M_RD, 4, buf}, /* read date */
|
||||
};
|
||||
|
||||
/* read date registers */
|
||||
|
|
@ -80,7 +72,7 @@ static int ds1672_set_mmss(struct i2c_client *client, unsigned long secs)
|
|||
buf[2] = (secs & 0x0000FF00) >> 8;
|
||||
buf[3] = (secs & 0x00FF0000) >> 16;
|
||||
buf[4] = (secs & 0xFF000000) >> 24;
|
||||
buf[5] = 0; /* set control reg to enable counting */
|
||||
buf[5] = 0; /* set control reg to enable counting */
|
||||
|
||||
xfer = i2c_master_send(client, buf, 6);
|
||||
if (xfer != 6) {
|
||||
|
|
@ -127,8 +119,8 @@ 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 */
|
||||
{client->addr, 0, 1, &addr}, /* setup read ptr */
|
||||
{client->addr, I2C_M_RD, 1, status}, /* read control */
|
||||
};
|
||||
|
||||
/* read control register */
|
||||
|
|
@ -141,7 +133,8 @@ static int ds1672_get_control(struct i2c_client *client, u8 *status)
|
|||
}
|
||||
|
||||
/* following are the sysfs callback functions */
|
||||
static ssize_t show_control(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
static ssize_t show_control(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 control;
|
||||
|
|
@ -152,85 +145,46 @@ static ssize_t show_control(struct device *dev, struct device_attribute *attr, c
|
|||
return err;
|
||||
|
||||
return sprintf(buf, "%s\n", (control & DS1672_REG_CONTROL_EOSC)
|
||||
? "disabled" : "enabled");
|
||||
? "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,
|
||||
.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)
|
||||
static int ds1672_remove(struct i2c_client *client)
|
||||
{
|
||||
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)
|
||||
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)
|
||||
static int ds1672_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int err = 0;
|
||||
u8 control;
|
||||
struct i2c_client *client;
|
||||
struct rtc_device *rtc;
|
||||
|
||||
dev_dbg(&adapter->dev, "%s\n", __func__);
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
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;
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
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);
|
||||
&ds1672_rtc_ops, THIS_MODULE);
|
||||
|
||||
if (IS_ERR(rtc)) {
|
||||
err = PTR_ERR(rtc);
|
||||
goto exit_detach;
|
||||
}
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
|
||||
i2c_set_clientdata(client, rtc);
|
||||
|
||||
|
|
@ -241,7 +195,7 @@ static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind)
|
|||
|
||||
if (control & DS1672_REG_CONTROL_EOSC)
|
||||
dev_warn(&client->dev, "Oscillator not enabled. "
|
||||
"Set time to enable.\n");
|
||||
"Set time to enable.\n");
|
||||
|
||||
/* Register sysfs hooks */
|
||||
err = device_create_file(&client->dev, &dev_attr_control);
|
||||
|
|
@ -250,19 +204,19 @@ static int ds1672_probe(struct i2c_adapter *adapter, int address, int kind)
|
|||
|
||||
return 0;
|
||||
|
||||
exit_devreg:
|
||||
exit_devreg:
|
||||
rtc_device_unregister(rtc);
|
||||
|
||||
exit_detach:
|
||||
i2c_detach_client(client);
|
||||
|
||||
exit_kfree:
|
||||
kfree(client);
|
||||
|
||||
exit:
|
||||
return err;
|
||||
}
|
||||
|
||||
static struct i2c_driver ds1672_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-ds1672",
|
||||
},
|
||||
.probe = &ds1672_probe,
|
||||
.remove = &ds1672_remove,
|
||||
};
|
||||
|
||||
static int __init ds1672_init(void)
|
||||
{
|
||||
return i2c_add_driver(&ds1672_driver);
|
||||
|
|
|
|||
290
drivers/rtc/rtc-ds3234.c
Normal file
290
drivers/rtc/rtc-ds3234.c
Normal file
|
|
@ -0,0 +1,290 @@
|
|||
/* drivers/rtc/rtc-ds3234.c
|
||||
*
|
||||
* Driver for Dallas Semiconductor (DS3234) SPI RTC with Integrated Crystal
|
||||
* and SRAM.
|
||||
*
|
||||
* Copyright (C) 2008 MIMOMax Wireless 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.
|
||||
*
|
||||
* Changelog:
|
||||
*
|
||||
* 07-May-2008: Dennis Aberilla <denzzzhome@yahoo.com>
|
||||
* - Created based on the max6902 code. Only implements the
|
||||
* date/time keeping functions; no SRAM yet.
|
||||
*/
|
||||
|
||||
#include <linux/device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/spi/spi.h>
|
||||
#include <linux/bcd.h>
|
||||
|
||||
#define DS3234_REG_SECONDS 0x00
|
||||
#define DS3234_REG_MINUTES 0x01
|
||||
#define DS3234_REG_HOURS 0x02
|
||||
#define DS3234_REG_DAY 0x03
|
||||
#define DS3234_REG_DATE 0x04
|
||||
#define DS3234_REG_MONTH 0x05
|
||||
#define DS3234_REG_YEAR 0x06
|
||||
#define DS3234_REG_CENTURY (1 << 7) /* Bit 7 of the Month register */
|
||||
|
||||
#define DS3234_REG_CONTROL 0x0E
|
||||
#define DS3234_REG_CONT_STAT 0x0F
|
||||
|
||||
#undef DS3234_DEBUG
|
||||
|
||||
struct ds3234 {
|
||||
struct rtc_device *rtc;
|
||||
u8 buf[8]; /* Burst read: addr + 7 regs */
|
||||
u8 tx_buf[2];
|
||||
u8 rx_buf[2];
|
||||
};
|
||||
|
||||
static void ds3234_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 '1' to indicate write */
|
||||
buf[0] = address | 0x80;
|
||||
buf[1] = data;
|
||||
|
||||
spi_write(spi, buf, 2);
|
||||
}
|
||||
|
||||
static int ds3234_get_reg(struct device *dev, unsigned char address,
|
||||
unsigned char *data)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct ds3234 *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));
|
||||
|
||||
/* Address + dummy tx byte */
|
||||
xfer.len = 2;
|
||||
xfer.tx_buf = chip->tx_buf;
|
||||
xfer.rx_buf = chip->rx_buf;
|
||||
|
||||
chip->tx_buf[0] = address;
|
||||
chip->tx_buf[1] = 0xff;
|
||||
|
||||
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 ds3234_get_datetime(struct device *dev, struct rtc_time *dt)
|
||||
{
|
||||
struct spi_device *spi = to_spi_device(dev);
|
||||
struct ds3234 *chip = dev_get_drvdata(dev);
|
||||
struct spi_message message;
|
||||
struct spi_transfer xfer;
|
||||
int status;
|
||||
|
||||
/* build the message */
|
||||
spi_message_init(&message);
|
||||
memset(&xfer, 0, sizeof(xfer));
|
||||
xfer.len = 1 + 7; /* Addr + 7 registers */
|
||||
xfer.tx_buf = chip->buf;
|
||||
xfer.rx_buf = chip->buf;
|
||||
chip->buf[0] = 0x00; /* Start address */
|
||||
spi_message_add_tail(&xfer, &message);
|
||||
|
||||
/* do the i/o */
|
||||
status = spi_sync(spi, &message);
|
||||
if (status == 0)
|
||||
status = message.status;
|
||||
else
|
||||
return status;
|
||||
|
||||
/* Seconds, Minutes, Hours, Day, Date, Month, Year */
|
||||
dt->tm_sec = bcd2bin(chip->buf[1]);
|
||||
dt->tm_min = bcd2bin(chip->buf[2]);
|
||||
dt->tm_hour = bcd2bin(chip->buf[3] & 0x3f);
|
||||
dt->tm_wday = bcd2bin(chip->buf[4]) - 1; /* 0 = Sun */
|
||||
dt->tm_mday = bcd2bin(chip->buf[5]);
|
||||
dt->tm_mon = bcd2bin(chip->buf[6] & 0x1f) - 1; /* 0 = Jan */
|
||||
dt->tm_year = bcd2bin(chip->buf[7] & 0xff) + 100; /* Assume 20YY */
|
||||
|
||||
#ifdef DS3234_DEBUG
|
||||
dev_dbg(dev, "\n%s : Read RTC values\n", __func__);
|
||||
dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour);
|
||||
dev_dbg(dev, "tm_min : %i\n", dt->tm_min);
|
||||
dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec);
|
||||
dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday);
|
||||
dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday);
|
||||
dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon);
|
||||
dev_dbg(dev, "tm_year: %i\n", dt->tm_year);
|
||||
#endif
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds3234_set_datetime(struct device *dev, struct rtc_time *dt)
|
||||
{
|
||||
#ifdef DS3234_DEBUG
|
||||
dev_dbg(dev, "\n%s : Setting RTC values\n", __func__);
|
||||
dev_dbg(dev, "tm_sec : %i\n", dt->tm_sec);
|
||||
dev_dbg(dev, "tm_min : %i\n", dt->tm_min);
|
||||
dev_dbg(dev, "tm_hour: %i\n", dt->tm_hour);
|
||||
dev_dbg(dev, "tm_wday: %i\n", dt->tm_wday);
|
||||
dev_dbg(dev, "tm_mday: %i\n", dt->tm_mday);
|
||||
dev_dbg(dev, "tm_mon : %i\n", dt->tm_mon);
|
||||
dev_dbg(dev, "tm_year: %i\n", dt->tm_year);
|
||||
#endif
|
||||
|
||||
ds3234_set_reg(dev, DS3234_REG_SECONDS, bin2bcd(dt->tm_sec));
|
||||
ds3234_set_reg(dev, DS3234_REG_MINUTES, bin2bcd(dt->tm_min));
|
||||
ds3234_set_reg(dev, DS3234_REG_HOURS, bin2bcd(dt->tm_hour) & 0x3f);
|
||||
|
||||
/* 0 = Sun */
|
||||
ds3234_set_reg(dev, DS3234_REG_DAY, bin2bcd(dt->tm_wday + 1));
|
||||
ds3234_set_reg(dev, DS3234_REG_DATE, bin2bcd(dt->tm_mday));
|
||||
|
||||
/* 0 = Jan */
|
||||
ds3234_set_reg(dev, DS3234_REG_MONTH, bin2bcd(dt->tm_mon + 1));
|
||||
|
||||
/* Assume 20YY although we just want to make sure not to go negative. */
|
||||
if (dt->tm_year > 100)
|
||||
dt->tm_year -= 100;
|
||||
|
||||
ds3234_set_reg(dev, DS3234_REG_YEAR, bin2bcd(dt->tm_year));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds3234_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
return ds3234_get_datetime(dev, tm);
|
||||
}
|
||||
|
||||
static int ds3234_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
return ds3234_set_datetime(dev, tm);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops ds3234_rtc_ops = {
|
||||
.read_time = ds3234_read_time,
|
||||
.set_time = ds3234_set_time,
|
||||
};
|
||||
|
||||
static int ds3234_probe(struct spi_device *spi)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
unsigned char tmp;
|
||||
struct ds3234 *chip;
|
||||
int res;
|
||||
|
||||
rtc = rtc_device_register("ds3234",
|
||||
&spi->dev, &ds3234_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(struct ds3234), GFP_KERNEL);
|
||||
if (!chip) {
|
||||
rtc_device_unregister(rtc);
|
||||
return -ENOMEM;
|
||||
}
|
||||
chip->rtc = rtc;
|
||||
dev_set_drvdata(&spi->dev, chip);
|
||||
|
||||
res = ds3234_get_reg(&spi->dev, DS3234_REG_SECONDS, &tmp);
|
||||
if (res) {
|
||||
rtc_device_unregister(rtc);
|
||||
return res;
|
||||
}
|
||||
|
||||
/* Control settings
|
||||
*
|
||||
* CONTROL_REG
|
||||
* BIT 7 6 5 4 3 2 1 0
|
||||
* EOSC BBSQW CONV RS2 RS1 INTCN A2IE A1IE
|
||||
*
|
||||
* 0 0 0 1 1 1 0 0
|
||||
*
|
||||
* CONTROL_STAT_REG
|
||||
* BIT 7 6 5 4 3 2 1 0
|
||||
* OSF BB32kHz CRATE1 CRATE0 EN32kHz BSY A2F A1F
|
||||
*
|
||||
* 1 0 0 0 1 0 0 0
|
||||
*/
|
||||
ds3234_get_reg(&spi->dev, DS3234_REG_CONTROL, &tmp);
|
||||
ds3234_set_reg(&spi->dev, DS3234_REG_CONTROL, tmp & 0x1c);
|
||||
|
||||
ds3234_get_reg(&spi->dev, DS3234_REG_CONT_STAT, &tmp);
|
||||
ds3234_set_reg(&spi->dev, DS3234_REG_CONT_STAT, tmp & 0x88);
|
||||
|
||||
/* Print our settings */
|
||||
ds3234_get_reg(&spi->dev, DS3234_REG_CONTROL, &tmp);
|
||||
dev_info(&spi->dev, "Control Reg: 0x%02x\n", tmp);
|
||||
|
||||
ds3234_get_reg(&spi->dev, DS3234_REG_CONT_STAT, &tmp);
|
||||
dev_info(&spi->dev, "Ctrl/Stat Reg: 0x%02x\n", tmp);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __exit ds3234_remove(struct spi_device *spi)
|
||||
{
|
||||
struct ds3234 *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 ds3234_driver = {
|
||||
.driver = {
|
||||
.name = "ds3234",
|
||||
.bus = &spi_bus_type,
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = ds3234_probe,
|
||||
.remove = __devexit_p(ds3234_remove),
|
||||
};
|
||||
|
||||
static __init int ds3234_init(void)
|
||||
{
|
||||
printk(KERN_INFO "DS3234 SPI RTC Driver\n");
|
||||
return spi_register_driver(&ds3234_driver);
|
||||
}
|
||||
module_init(ds3234_init);
|
||||
|
||||
static __exit void ds3234_exit(void)
|
||||
{
|
||||
spi_unregister_driver(&ds3234_driver);
|
||||
}
|
||||
module_exit(ds3234_exit);
|
||||
|
||||
MODULE_DESCRIPTION("DS3234 SPI RTC driver");
|
||||
MODULE_AUTHOR("Dennis Aberilla <[email protected]>");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
@ -56,21 +56,27 @@
|
|||
#define M41T80_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */
|
||||
#define M41T80_FLAGS_AF (1 << 6) /* AF: Alarm Flag Bit */
|
||||
#define M41T80_FLAGS_BATT_LOW (1 << 4) /* BL: Battery Low Bit */
|
||||
#define M41T80_WATCHDOG_RB2 (1 << 7) /* RB: Watchdog resolution */
|
||||
#define M41T80_WATCHDOG_RB1 (1 << 1) /* RB: Watchdog resolution */
|
||||
#define M41T80_WATCHDOG_RB0 (1 << 0) /* RB: Watchdog resolution */
|
||||
|
||||
#define M41T80_FEATURE_HT (1 << 0)
|
||||
#define M41T80_FEATURE_BL (1 << 1)
|
||||
#define M41T80_FEATURE_HT (1 << 0) /* Halt feature */
|
||||
#define M41T80_FEATURE_BL (1 << 1) /* Battery low indicator */
|
||||
#define M41T80_FEATURE_SQ (1 << 2) /* Squarewave feature */
|
||||
#define M41T80_FEATURE_WD (1 << 3) /* Extra watchdog resolution */
|
||||
|
||||
#define DRV_VERSION "0.05"
|
||||
|
||||
static const struct i2c_device_id m41t80_id[] = {
|
||||
{ "m41t80", 0 },
|
||||
{ "m41t81", M41T80_FEATURE_HT },
|
||||
{ "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL },
|
||||
{ "m41t82", M41T80_FEATURE_HT | M41T80_FEATURE_BL },
|
||||
{ "m41t83", M41T80_FEATURE_HT | M41T80_FEATURE_BL },
|
||||
{ "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL },
|
||||
{ "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL },
|
||||
{ "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL },
|
||||
{ "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD },
|
||||
{ "m41t80", M41T80_FEATURE_SQ },
|
||||
{ "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ},
|
||||
{ "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
|
||||
{ "m41t82", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
|
||||
{ "m41t83", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
|
||||
{ "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
|
||||
{ "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
|
||||
{ "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, m41t80_id);
|
||||
|
|
@ -386,8 +392,12 @@ static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev,
|
|||
struct device_attribute *attr, char *buf)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct m41t80_data *clientdata = i2c_get_clientdata(client);
|
||||
int val;
|
||||
|
||||
if (!(clientdata->features & M41T80_FEATURE_SQ))
|
||||
return -EINVAL;
|
||||
|
||||
val = i2c_smbus_read_byte_data(client, M41T80_REG_SQW);
|
||||
if (val < 0)
|
||||
return -EIO;
|
||||
|
|
@ -408,9 +418,13 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev,
|
|||
const char *buf, size_t count)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct m41t80_data *clientdata = i2c_get_clientdata(client);
|
||||
int almon, sqw;
|
||||
int val = simple_strtoul(buf, NULL, 0);
|
||||
|
||||
if (!(clientdata->features & M41T80_FEATURE_SQ))
|
||||
return -EINVAL;
|
||||
|
||||
if (val) {
|
||||
if (!is_power_of_2(val))
|
||||
return -EINVAL;
|
||||
|
|
@ -499,6 +513,8 @@ static void wdt_ping(void)
|
|||
.buf = i2c_data,
|
||||
},
|
||||
};
|
||||
struct m41t80_data *clientdata = i2c_get_clientdata(save_client);
|
||||
|
||||
i2c_data[0] = 0x09; /* watchdog register */
|
||||
|
||||
if (wdt_margin > 31)
|
||||
|
|
@ -509,6 +525,13 @@ static void wdt_ping(void)
|
|||
*/
|
||||
i2c_data[1] = wdt_margin<<2 | 0x82;
|
||||
|
||||
/*
|
||||
* M41T65 has three bits for watchdog resolution. Don't set bit 7, as
|
||||
* that would be an invalid resolution.
|
||||
*/
|
||||
if (clientdata->features & M41T80_FEATURE_WD)
|
||||
i2c_data[1] &= ~M41T80_WATCHDOG_RB2;
|
||||
|
||||
i2c_transfer(save_client->adapter, msgs1, 1);
|
||||
}
|
||||
|
||||
|
|
|
|||
235
drivers/rtc/rtc-m48t35.c
Normal file
235
drivers/rtc/rtc-m48t35.c
Normal file
|
|
@ -0,0 +1,235 @@
|
|||
/*
|
||||
* Driver for the SGS-Thomson M48T35 Timekeeper RAM chip
|
||||
*
|
||||
* Copyright (C) 2000 Silicon Graphics, Inc.
|
||||
* Written by Ulf Carlsson (ulfc@engr.sgi.com)
|
||||
*
|
||||
* Copyright (C) 2008 Thomas Bogendoerfer
|
||||
*
|
||||
* Based on code written 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/rtc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DRV_VERSION "1.0"
|
||||
|
||||
struct m48t35_rtc {
|
||||
u8 pad[0x7ff8]; /* starts at 0x7ff8 */
|
||||
u8 control;
|
||||
u8 sec;
|
||||
u8 min;
|
||||
u8 hour;
|
||||
u8 day;
|
||||
u8 date;
|
||||
u8 month;
|
||||
u8 year;
|
||||
};
|
||||
|
||||
#define M48T35_RTC_SET 0x80
|
||||
#define M48T35_RTC_READ 0x40
|
||||
|
||||
struct m48t35_priv {
|
||||
struct rtc_device *rtc;
|
||||
struct m48t35_rtc __iomem *reg;
|
||||
size_t size;
|
||||
unsigned long baseaddr;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static int m48t35_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct m48t35_priv *priv = dev_get_drvdata(dev);
|
||||
u8 control;
|
||||
|
||||
/*
|
||||
* Only the values that we read from the RTC are set. We leave
|
||||
* tm_wday, tm_yday and tm_isdst untouched. Even though the
|
||||
* RTC has RTC_DAY_OF_WEEK, we ignore it, as it is only updated
|
||||
* by the RTC when initially set to a non-zero value.
|
||||
*/
|
||||
spin_lock_irq(&priv->lock);
|
||||
control = readb(&priv->reg->control);
|
||||
writeb(control | M48T35_RTC_READ, &priv->reg->control);
|
||||
tm->tm_sec = readb(&priv->reg->sec);
|
||||
tm->tm_min = readb(&priv->reg->min);
|
||||
tm->tm_hour = readb(&priv->reg->hour);
|
||||
tm->tm_mday = readb(&priv->reg->date);
|
||||
tm->tm_mon = readb(&priv->reg->month);
|
||||
tm->tm_year = readb(&priv->reg->year);
|
||||
writeb(control, &priv->reg->control);
|
||||
spin_unlock_irq(&priv->lock);
|
||||
|
||||
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);
|
||||
tm->tm_year = bcd2bin(tm->tm_year);
|
||||
|
||||
/*
|
||||
* Account for differences between how the RTC uses the values
|
||||
* and how they are defined in a struct rtc_time;
|
||||
*/
|
||||
tm->tm_year += 70;
|
||||
if (tm->tm_year <= 69)
|
||||
tm->tm_year += 100;
|
||||
|
||||
tm->tm_mon--;
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int m48t35_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct m48t35_priv *priv = dev_get_drvdata(dev);
|
||||
unsigned char mon, day, hrs, min, sec;
|
||||
unsigned int yrs;
|
||||
u8 control;
|
||||
|
||||
yrs = tm->tm_year + 1900;
|
||||
mon = tm->tm_mon + 1; /* tm_mon starts at zero */
|
||||
day = tm->tm_mday;
|
||||
hrs = tm->tm_hour;
|
||||
min = tm->tm_min;
|
||||
sec = tm->tm_sec;
|
||||
|
||||
if (yrs < 1970)
|
||||
return -EINVAL;
|
||||
|
||||
yrs -= 1970;
|
||||
if (yrs > 255) /* They are unsigned */
|
||||
return -EINVAL;
|
||||
|
||||
if (yrs > 169)
|
||||
return -EINVAL;
|
||||
|
||||
if (yrs >= 100)
|
||||
yrs -= 100;
|
||||
|
||||
sec = bin2bcd(sec);
|
||||
min = bin2bcd(min);
|
||||
hrs = bin2bcd(hrs);
|
||||
day = bin2bcd(day);
|
||||
mon = bin2bcd(mon);
|
||||
yrs = bin2bcd(yrs);
|
||||
|
||||
spin_lock_irq(&priv->lock);
|
||||
control = readb(&priv->reg->control);
|
||||
writeb(control | M48T35_RTC_SET, &priv->reg->control);
|
||||
writeb(yrs, &priv->reg->year);
|
||||
writeb(mon, &priv->reg->month);
|
||||
writeb(day, &priv->reg->date);
|
||||
writeb(hrs, &priv->reg->hour);
|
||||
writeb(min, &priv->reg->min);
|
||||
writeb(sec, &priv->reg->sec);
|
||||
writeb(control, &priv->reg->control);
|
||||
spin_unlock_irq(&priv->lock);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops m48t35_ops = {
|
||||
.read_time = m48t35_read_time,
|
||||
.set_time = m48t35_set_time,
|
||||
};
|
||||
|
||||
static int __devinit m48t35_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rtc_device *rtc;
|
||||
struct resource *res;
|
||||
struct m48t35_priv *priv;
|
||||
int ret = 0;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
if (!res)
|
||||
return -ENODEV;
|
||||
priv = kzalloc(sizeof(struct m48t35_priv), GFP_KERNEL);
|
||||
if (!priv)
|
||||
return -ENOMEM;
|
||||
|
||||
priv->size = res->end - res->start + 1;
|
||||
/*
|
||||
* kludge: remove the #ifndef after ioc3 resource
|
||||
* conflicts are resolved
|
||||
*/
|
||||
#ifndef CONFIG_SGI_IP27
|
||||
if (!request_mem_region(res->start, priv->size, pdev->name)) {
|
||||
ret = -EBUSY;
|
||||
goto out;
|
||||
}
|
||||
#endif
|
||||
priv->baseaddr = res->start;
|
||||
priv->reg = ioremap(priv->baseaddr, priv->size);
|
||||
if (!priv->reg) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
spin_lock_init(&priv->lock);
|
||||
rtc = rtc_device_register("m48t35", &pdev->dev,
|
||||
&m48t35_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc)) {
|
||||
ret = PTR_ERR(rtc);
|
||||
goto out;
|
||||
}
|
||||
priv->rtc = rtc;
|
||||
platform_set_drvdata(pdev, priv);
|
||||
return 0;
|
||||
|
||||
out:
|
||||
if (priv->rtc)
|
||||
rtc_device_unregister(priv->rtc);
|
||||
if (priv->reg)
|
||||
iounmap(priv->reg);
|
||||
if (priv->baseaddr)
|
||||
release_mem_region(priv->baseaddr, priv->size);
|
||||
kfree(priv);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit m48t35_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct m48t35_priv *priv = platform_get_drvdata(pdev);
|
||||
|
||||
rtc_device_unregister(priv->rtc);
|
||||
iounmap(priv->reg);
|
||||
#ifndef CONFIG_SGI_IP27
|
||||
release_mem_region(priv->baseaddr, priv->size);
|
||||
#endif
|
||||
kfree(priv);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver m48t35_platform_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-m48t35",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = m48t35_probe,
|
||||
.remove = __devexit_p(m48t35_remove),
|
||||
};
|
||||
|
||||
static int __init m48t35_init(void)
|
||||
{
|
||||
return platform_driver_register(&m48t35_platform_driver);
|
||||
}
|
||||
|
||||
static void __exit m48t35_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&m48t35_platform_driver);
|
||||
}
|
||||
|
||||
MODULE_AUTHOR("Thomas Bogendoerfer <[email protected]>");
|
||||
MODULE_DESCRIPTION("M48T35 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
MODULE_ALIAS("platform:rtc-m48t35");
|
||||
|
||||
module_init(m48t35_init);
|
||||
module_exit(m48t35_exit);
|
||||
|
|
@ -24,8 +24,9 @@
|
|||
#define NO_IRQ (-1)
|
||||
#endif
|
||||
|
||||
#define M48T59_READ(reg) pdata->read_byte(dev, reg)
|
||||
#define M48T59_WRITE(val, reg) pdata->write_byte(dev, reg, val)
|
||||
#define M48T59_READ(reg) (pdata->read_byte(dev, pdata->offset + reg))
|
||||
#define M48T59_WRITE(val, reg) \
|
||||
(pdata->write_byte(dev, pdata->offset + reg, val))
|
||||
|
||||
#define M48T59_SET_BITS(mask, reg) \
|
||||
M48T59_WRITE((M48T59_READ(reg) | (mask)), (reg))
|
||||
|
|
@ -34,7 +35,6 @@
|
|||
|
||||
struct m48t59_private {
|
||||
void __iomem *ioaddr;
|
||||
unsigned int size; /* iomem size */
|
||||
int irq;
|
||||
struct rtc_device *rtc;
|
||||
spinlock_t lock; /* serialize the NVRAM and RTC access */
|
||||
|
|
@ -82,7 +82,8 @@ static int m48t59_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
tm->tm_mday = BCD2BIN(M48T59_READ(M48T59_MDAY));
|
||||
|
||||
val = M48T59_READ(M48T59_WDAY);
|
||||
if ((val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
|
||||
if ((pdata->type == M48T59RTC_TYPE_M48T59) &&
|
||||
(val & M48T59_WDAY_CEB) && (val & M48T59_WDAY_CB)) {
|
||||
dev_dbg(dev, "Century bit is enabled\n");
|
||||
tm->tm_year += 100; /* one century */
|
||||
}
|
||||
|
|
@ -126,7 +127,7 @@ static int m48t59_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
M48T59_WRITE((BIN2BCD(tm->tm_mon + 1) & 0x1F), M48T59_MONTH);
|
||||
M48T59_WRITE(BIN2BCD(tm->tm_year % 100), M48T59_YEAR);
|
||||
|
||||
if (tm->tm_year/100)
|
||||
if (pdata->type == M48T59RTC_TYPE_M48T59 && (tm->tm_year / 100))
|
||||
val = (M48T59_WDAY_CEB | M48T59_WDAY_CB);
|
||||
val |= (BIN2BCD(tm->tm_wday) & 0x07);
|
||||
M48T59_WRITE(val, M48T59_WDAY);
|
||||
|
|
@ -310,6 +311,11 @@ static const struct rtc_class_ops m48t59_rtc_ops = {
|
|||
.proc = m48t59_rtc_proc,
|
||||
};
|
||||
|
||||
static const struct rtc_class_ops m48t02_rtc_ops = {
|
||||
.read_time = m48t59_rtc_read_time,
|
||||
.set_time = m48t59_rtc_set_time,
|
||||
};
|
||||
|
||||
static ssize_t m48t59_nvram_read(struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t pos, size_t size)
|
||||
|
|
@ -321,7 +327,7 @@ static ssize_t m48t59_nvram_read(struct kobject *kobj,
|
|||
ssize_t cnt = 0;
|
||||
unsigned long flags;
|
||||
|
||||
for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) {
|
||||
for (; size > 0 && pos < pdata->offset; cnt++, size--) {
|
||||
spin_lock_irqsave(&m48t59->lock, flags);
|
||||
*buf++ = M48T59_READ(cnt);
|
||||
spin_unlock_irqrestore(&m48t59->lock, flags);
|
||||
|
|
@ -341,7 +347,7 @@ static ssize_t m48t59_nvram_write(struct kobject *kobj,
|
|||
ssize_t cnt = 0;
|
||||
unsigned long flags;
|
||||
|
||||
for (; size > 0 && pos < M48T59_NVRAM_SIZE; cnt++, size--) {
|
||||
for (; size > 0 && pos < pdata->offset; cnt++, size--) {
|
||||
spin_lock_irqsave(&m48t59->lock, flags);
|
||||
M48T59_WRITE(*buf++, cnt);
|
||||
spin_unlock_irqrestore(&m48t59->lock, flags);
|
||||
|
|
@ -358,7 +364,6 @@ static struct bin_attribute m48t59_nvram_attr = {
|
|||
},
|
||||
.read = m48t59_nvram_read,
|
||||
.write = m48t59_nvram_write,
|
||||
.size = M48T59_NVRAM_SIZE,
|
||||
};
|
||||
|
||||
static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
|
||||
|
|
@ -367,6 +372,8 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
|
|||
struct m48t59_private *m48t59 = NULL;
|
||||
struct resource *res;
|
||||
int ret = -ENOMEM;
|
||||
char *name;
|
||||
const struct rtc_class_ops *ops;
|
||||
|
||||
/* This chip could be memory-mapped or I/O-mapped */
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
|
@ -391,6 +398,8 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
|
|||
/* Ensure we only kmalloc platform data once */
|
||||
pdev->dev.platform_data = pdata;
|
||||
}
|
||||
if (!pdata->type)
|
||||
pdata->type = M48T59RTC_TYPE_M48T59;
|
||||
|
||||
/* Try to use the generic memory read/write ops */
|
||||
if (!pdata->write_byte)
|
||||
|
|
@ -403,10 +412,14 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
|
|||
if (!m48t59)
|
||||
return -ENOMEM;
|
||||
|
||||
m48t59->size = res->end - res->start + 1;
|
||||
m48t59->ioaddr = ioremap(res->start, m48t59->size);
|
||||
if (!m48t59->ioaddr)
|
||||
goto out;
|
||||
m48t59->ioaddr = pdata->ioaddr;
|
||||
|
||||
if (!m48t59->ioaddr) {
|
||||
/* ioaddr not mapped externally */
|
||||
m48t59->ioaddr = ioremap(res->start, res->end - res->start + 1);
|
||||
if (!m48t59->ioaddr)
|
||||
goto out;
|
||||
}
|
||||
|
||||
/* Try to get irq number. We also can work in
|
||||
* the mode without IRQ.
|
||||
|
|
@ -421,14 +434,36 @@ static int __devinit m48t59_rtc_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto out;
|
||||
}
|
||||
switch (pdata->type) {
|
||||
case M48T59RTC_TYPE_M48T59:
|
||||
name = "m48t59";
|
||||
ops = &m48t59_rtc_ops;
|
||||
pdata->offset = 0x1ff0;
|
||||
break;
|
||||
case M48T59RTC_TYPE_M48T02:
|
||||
name = "m48t02";
|
||||
ops = &m48t02_rtc_ops;
|
||||
pdata->offset = 0x7f0;
|
||||
break;
|
||||
case M48T59RTC_TYPE_M48T08:
|
||||
name = "m48t08";
|
||||
ops = &m48t02_rtc_ops;
|
||||
pdata->offset = 0x1ff0;
|
||||
break;
|
||||
default:
|
||||
dev_err(&pdev->dev, "Unknown RTC type\n");
|
||||
ret = -ENODEV;
|
||||
goto out;
|
||||
}
|
||||
|
||||
m48t59->rtc = rtc_device_register("m48t59", &pdev->dev,
|
||||
&m48t59_rtc_ops, THIS_MODULE);
|
||||
m48t59->rtc = rtc_device_register(name, &pdev->dev, ops, THIS_MODULE);
|
||||
if (IS_ERR(m48t59->rtc)) {
|
||||
ret = PTR_ERR(m48t59->rtc);
|
||||
goto out;
|
||||
}
|
||||
|
||||
m48t59_nvram_attr.size = pdata->offset;
|
||||
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
|
||||
if (ret)
|
||||
goto out;
|
||||
|
|
@ -452,11 +487,12 @@ out:
|
|||
static int __devexit m48t59_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct m48t59_private *m48t59 = platform_get_drvdata(pdev);
|
||||
struct m48t59_plat_data *pdata = pdev->dev.platform_data;
|
||||
|
||||
sysfs_remove_bin_file(&pdev->dev.kobj, &m48t59_nvram_attr);
|
||||
if (!IS_ERR(m48t59->rtc))
|
||||
rtc_device_unregister(m48t59->rtc);
|
||||
if (m48t59->ioaddr)
|
||||
if (m48t59->ioaddr && !pdata->ioaddr)
|
||||
iounmap(m48t59->ioaddr);
|
||||
if (m48t59->irq != NO_IRQ)
|
||||
free_irq(m48t59->irq, &pdev->dev);
|
||||
|
|
@ -491,5 +527,5 @@ module_init(m48t59_rtc_init);
|
|||
module_exit(m48t59_rtc_exit);
|
||||
|
||||
MODULE_AUTHOR("Mark Zhan <[email protected]>");
|
||||
MODULE_DESCRIPTION("M48T59 RTC driver");
|
||||
MODULE_DESCRIPTION("M48T59/M48T02/M48T08 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
|
|
|||
|
|
@ -17,19 +17,18 @@
|
|||
#include <linux/rtc.h>
|
||||
#include <linux/delay.h>
|
||||
|
||||
#define DRV_NAME "max6900"
|
||||
#define DRV_VERSION "0.1"
|
||||
#define DRV_VERSION "0.2"
|
||||
|
||||
/*
|
||||
* register indices
|
||||
*/
|
||||
#define MAX6900_REG_SC 0 /* seconds 00-59 */
|
||||
#define MAX6900_REG_MN 1 /* minutes 00-59 */
|
||||
#define MAX6900_REG_HR 2 /* hours 00-23 */
|
||||
#define MAX6900_REG_DT 3 /* day of month 00-31 */
|
||||
#define MAX6900_REG_MO 4 /* month 01-12 */
|
||||
#define MAX6900_REG_DW 5 /* day of week 1-7 */
|
||||
#define MAX6900_REG_YR 6 /* year 00-99 */
|
||||
#define MAX6900_REG_SC 0 /* seconds 00-59 */
|
||||
#define MAX6900_REG_MN 1 /* minutes 00-59 */
|
||||
#define MAX6900_REG_HR 2 /* hours 00-23 */
|
||||
#define MAX6900_REG_DT 3 /* day of month 00-31 */
|
||||
#define MAX6900_REG_MO 4 /* month 01-12 */
|
||||
#define MAX6900_REG_DW 5 /* day of week 1-7 */
|
||||
#define MAX6900_REG_YR 6 /* year 00-99 */
|
||||
#define MAX6900_REG_CT 7 /* control */
|
||||
/* register 8 is undocumented */
|
||||
#define MAX6900_REG_CENTURY 9 /* century */
|
||||
|
|
@ -39,7 +38,6 @@
|
|||
|
||||
#define MAX6900_REG_CT_WP (1 << 7) /* Write Protect */
|
||||
|
||||
|
||||
/*
|
||||
* register read/write commands
|
||||
*/
|
||||
|
|
@ -52,16 +50,7 @@
|
|||
|
||||
#define MAX6900_IDLE_TIME_AFTER_WRITE 3 /* specification says 2.5 mS */
|
||||
|
||||
#define MAX6900_I2C_ADDR 0xa0
|
||||
|
||||
static const unsigned short normal_i2c[] = {
|
||||
MAX6900_I2C_ADDR >> 1,
|
||||
I2C_CLIENT_END
|
||||
};
|
||||
|
||||
I2C_CLIENT_INSMOD; /* defines addr_data */
|
||||
|
||||
static int max6900_probe(struct i2c_adapter *adapter, int addr, int kind);
|
||||
static struct i2c_driver max6900_driver;
|
||||
|
||||
static int max6900_i2c_read_regs(struct i2c_client *client, u8 *buf)
|
||||
{
|
||||
|
|
@ -69,36 +58,35 @@ static int max6900_i2c_read_regs(struct i2c_client *client, u8 *buf)
|
|||
u8 reg_century_read[1] = { MAX6900_REG_CENTURY_READ };
|
||||
struct i2c_msg msgs[4] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0, /* write */
|
||||
.len = sizeof(reg_burst_read),
|
||||
.buf = reg_burst_read
|
||||
},
|
||||
.addr = client->addr,
|
||||
.flags = 0, /* write */
|
||||
.len = sizeof(reg_burst_read),
|
||||
.buf = reg_burst_read}
|
||||
,
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = MAX6900_BURST_LEN,
|
||||
.buf = buf
|
||||
},
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = MAX6900_BURST_LEN,
|
||||
.buf = buf}
|
||||
,
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0, /* write */
|
||||
.len = sizeof(reg_century_read),
|
||||
.buf = reg_century_read
|
||||
},
|
||||
.addr = client->addr,
|
||||
.flags = 0, /* write */
|
||||
.len = sizeof(reg_century_read),
|
||||
.buf = reg_century_read}
|
||||
,
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = sizeof(buf[MAX6900_REG_CENTURY]),
|
||||
.buf = &buf[MAX6900_REG_CENTURY]
|
||||
}
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = sizeof(buf[MAX6900_REG_CENTURY]),
|
||||
.buf = &buf[MAX6900_REG_CENTURY]
|
||||
}
|
||||
};
|
||||
int rc;
|
||||
|
||||
rc = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
|
||||
if (rc != ARRAY_SIZE(msgs)) {
|
||||
dev_err(&client->dev, "%s: register read failed\n",
|
||||
__func__);
|
||||
dev_err(&client->dev, "%s: register read failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
return 0;
|
||||
|
|
@ -109,20 +97,18 @@ static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf)
|
|||
u8 i2c_century_buf[1 + 1] = { MAX6900_REG_CENTURY_WRITE };
|
||||
struct i2c_msg century_msgs[1] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0, /* write */
|
||||
.len = sizeof(i2c_century_buf),
|
||||
.buf = i2c_century_buf
|
||||
}
|
||||
.addr = client->addr,
|
||||
.flags = 0, /* write */
|
||||
.len = sizeof(i2c_century_buf),
|
||||
.buf = i2c_century_buf}
|
||||
};
|
||||
u8 i2c_burst_buf[MAX6900_BURST_LEN + 1] = { MAX6900_REG_BURST_WRITE };
|
||||
struct i2c_msg burst_msgs[1] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0, /* write */
|
||||
.len = sizeof(i2c_burst_buf),
|
||||
.buf = i2c_burst_buf
|
||||
}
|
||||
.addr = client->addr,
|
||||
.flags = 0, /* write */
|
||||
.len = sizeof(i2c_burst_buf),
|
||||
.buf = i2c_burst_buf}
|
||||
};
|
||||
int rc;
|
||||
|
||||
|
|
@ -133,10 +119,12 @@ static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf)
|
|||
* bit as part of the burst write.
|
||||
*/
|
||||
i2c_century_buf[1] = buf[MAX6900_REG_CENTURY];
|
||||
|
||||
rc = i2c_transfer(client->adapter, century_msgs,
|
||||
ARRAY_SIZE(century_msgs));
|
||||
if (rc != ARRAY_SIZE(century_msgs))
|
||||
goto write_failed;
|
||||
|
||||
msleep(MAX6900_IDLE_TIME_AFTER_WRITE);
|
||||
|
||||
memcpy(&i2c_burst_buf[1], buf, MAX6900_BURST_LEN);
|
||||
|
|
@ -148,45 +136,11 @@ static int max6900_i2c_write_regs(struct i2c_client *client, u8 const *buf)
|
|||
|
||||
return 0;
|
||||
|
||||
write_failed:
|
||||
dev_err(&client->dev, "%s: register write failed\n",
|
||||
__func__);
|
||||
write_failed:
|
||||
dev_err(&client->dev, "%s: register write failed\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int max6900_i2c_validate_client(struct i2c_client *client)
|
||||
{
|
||||
u8 regs[MAX6900_REG_LEN];
|
||||
u8 zero_mask[] = {
|
||||
0x80, /* seconds */
|
||||
0x80, /* minutes */
|
||||
0x40, /* hours */
|
||||
0xc0, /* day of month */
|
||||
0xe0, /* month */
|
||||
0xf8, /* day of week */
|
||||
0x00, /* year */
|
||||
0x7f, /* control */
|
||||
};
|
||||
int i;
|
||||
int rc;
|
||||
int reserved;
|
||||
|
||||
reserved = i2c_smbus_read_byte_data(client, MAX6900_REG_RESERVED_READ);
|
||||
if (reserved != 0x07)
|
||||
return -ENODEV;
|
||||
|
||||
rc = max6900_i2c_read_regs(client, regs);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(zero_mask); ++i) {
|
||||
if (regs[i] & zero_mask[i])
|
||||
return -ENODEV;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
|
||||
{
|
||||
int rc;
|
||||
|
|
@ -202,7 +156,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
|
|||
tm->tm_mday = BCD2BIN(regs[MAX6900_REG_DT]);
|
||||
tm->tm_mon = BCD2BIN(regs[MAX6900_REG_MO]) - 1;
|
||||
tm->tm_year = BCD2BIN(regs[MAX6900_REG_YR]) +
|
||||
BCD2BIN(regs[MAX6900_REG_CENTURY]) * 100 - 1900;
|
||||
BCD2BIN(regs[MAX6900_REG_CENTURY]) * 100 - 1900;
|
||||
tm->tm_wday = BCD2BIN(regs[MAX6900_REG_DW]);
|
||||
|
||||
return 0;
|
||||
|
|
@ -211,7 +165,7 @@ static int max6900_i2c_read_time(struct i2c_client *client, struct rtc_time *tm)
|
|||
static int max6900_i2c_clear_write_protect(struct i2c_client *client)
|
||||
{
|
||||
int rc;
|
||||
rc = i2c_smbus_write_byte_data (client, MAX6900_REG_CONTROL_WRITE, 0);
|
||||
rc = i2c_smbus_write_byte_data(client, MAX6900_REG_CONTROL_WRITE, 0);
|
||||
if (rc < 0) {
|
||||
dev_err(&client->dev, "%s: control register write failed\n",
|
||||
__func__);
|
||||
|
|
@ -220,8 +174,8 @@ static int max6900_i2c_clear_write_protect(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int max6900_i2c_set_time(struct i2c_client *client,
|
||||
struct rtc_time const *tm)
|
||||
static int
|
||||
max6900_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
|
||||
{
|
||||
u8 regs[MAX6900_REG_LEN];
|
||||
int rc;
|
||||
|
|
@ -258,89 +212,49 @@ static int max6900_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
return max6900_i2c_set_time(to_i2c_client(dev), tm);
|
||||
}
|
||||
|
||||
static int max6900_attach_adapter(struct i2c_adapter *adapter)
|
||||
static int max6900_remove(struct i2c_client *client)
|
||||
{
|
||||
return i2c_probe(adapter, &addr_data, max6900_probe);
|
||||
}
|
||||
|
||||
static int max6900_detach_client(struct i2c_client *client)
|
||||
{
|
||||
struct rtc_device *const rtc = i2c_get_clientdata(client);
|
||||
struct rtc_device *rtc = i2c_get_clientdata(client);
|
||||
|
||||
if (rtc)
|
||||
rtc_device_unregister(rtc);
|
||||
|
||||
return i2c_detach_client(client);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct i2c_driver max6900_driver = {
|
||||
.driver = {
|
||||
.name = DRV_NAME,
|
||||
},
|
||||
.id = I2C_DRIVERID_MAX6900,
|
||||
.attach_adapter = max6900_attach_adapter,
|
||||
.detach_client = max6900_detach_client,
|
||||
};
|
||||
|
||||
static const struct rtc_class_ops max6900_rtc_ops = {
|
||||
.read_time = max6900_rtc_read_time,
|
||||
.set_time = max6900_rtc_set_time,
|
||||
.read_time = max6900_rtc_read_time,
|
||||
.set_time = max6900_rtc_set_time,
|
||||
};
|
||||
|
||||
static int max6900_probe(struct i2c_adapter *adapter, int addr, int kind)
|
||||
static int
|
||||
max6900_probe(struct i2c_client *client, const struct i2c_device_id *id)
|
||||
{
|
||||
int rc = 0;
|
||||
struct i2c_client *client = NULL;
|
||||
struct rtc_device *rtc = NULL;
|
||||
struct rtc_device *rtc;
|
||||
|
||||
if (!i2c_check_functionality(adapter, I2C_FUNC_I2C)) {
|
||||
rc = -ENODEV;
|
||||
goto failout;
|
||||
}
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
client = kzalloc(sizeof(struct i2c_client), GFP_KERNEL);
|
||||
if (client == NULL) {
|
||||
rc = -ENOMEM;
|
||||
goto failout;
|
||||
}
|
||||
|
||||
client->addr = addr;
|
||||
client->adapter = adapter;
|
||||
client->driver = &max6900_driver;
|
||||
strlcpy(client->name, DRV_NAME, I2C_NAME_SIZE);
|
||||
|
||||
if (kind < 0) {
|
||||
rc = max6900_i2c_validate_client(client);
|
||||
if (rc < 0)
|
||||
goto failout;
|
||||
}
|
||||
|
||||
rc = i2c_attach_client(client);
|
||||
if (rc < 0)
|
||||
goto failout;
|
||||
|
||||
dev_info(&client->dev,
|
||||
"chip found, driver version " DRV_VERSION "\n");
|
||||
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
|
||||
|
||||
rtc = rtc_device_register(max6900_driver.driver.name,
|
||||
&client->dev,
|
||||
&max6900_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc)) {
|
||||
rc = PTR_ERR(rtc);
|
||||
goto failout_detach;
|
||||
}
|
||||
&client->dev, &max6900_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
|
||||
i2c_set_clientdata(client, rtc);
|
||||
|
||||
return 0;
|
||||
|
||||
failout_detach:
|
||||
i2c_detach_client(client);
|
||||
failout:
|
||||
kfree(client);
|
||||
return rc;
|
||||
}
|
||||
|
||||
static struct i2c_driver max6900_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-max6900",
|
||||
},
|
||||
.probe = max6900_probe,
|
||||
.remove = max6900_remove,
|
||||
};
|
||||
|
||||
static int __init max6900_init(void)
|
||||
{
|
||||
return i2c_add_driver(&max6900_driver);
|
||||
|
|
@ -352,6 +266,7 @@ static void __exit max6900_exit(void)
|
|||
}
|
||||
|
||||
MODULE_DESCRIPTION("Maxim MAX6900 RTC driver");
|
||||
MODULE_AUTHOR("Dale Farnsworth <[email protected]>");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
|
||||
|
|
|
|||
|
|
@ -179,58 +179,6 @@ struct pcf8563_limit
|
|||
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",
|
||||
__func__, 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",
|
||||
__func__, 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);
|
||||
|
|
@ -262,12 +210,6 @@ static int pcf8563_probe(struct i2c_client *client,
|
|||
if (!pcf8563)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Verify the chip is really an PCF8563 */
|
||||
if (pcf8563_validate_client(client) < 0) {
|
||||
err = -ENODEV;
|
||||
goto exit_kfree;
|
||||
}
|
||||
|
||||
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
|
||||
|
||||
pcf8563->rtc = rtc_device_register(pcf8563_driver.driver.name,
|
||||
|
|
|
|||
|
|
@ -34,15 +34,6 @@ static irqreturn_t pl030_interrupt(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pl030_open(struct device *dev)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void pl030_release(struct device *dev)
|
||||
{
|
||||
}
|
||||
|
||||
static int pl030_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
||||
{
|
||||
return -ENOIOCTLCMD;
|
||||
|
|
@ -104,8 +95,6 @@ static int pl030_set_time(struct device *dev, struct rtc_time *tm)
|
|||
}
|
||||
|
||||
static const struct rtc_class_ops pl030_ops = {
|
||||
.open = pl030_open,
|
||||
.release = pl030_release,
|
||||
.ioctl = pl030_ioctl,
|
||||
.read_time = pl030_read_time,
|
||||
.set_time = pl030_set_time,
|
||||
|
|
|
|||
|
|
@ -45,18 +45,6 @@ static irqreturn_t pl031_interrupt(int irq, void *dev_id)
|
|||
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);
|
||||
|
|
@ -118,8 +106,6 @@ static int pl031_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
|||
}
|
||||
|
||||
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,
|
||||
|
|
|
|||
|
|
@ -1,8 +1,9 @@
|
|||
/*
|
||||
* An I2C driver for Ricoh RS5C372 and RV5C38[67] RTCs
|
||||
* An I2C driver for Ricoh RS5C372, R2025S/D and RV5C38[67] RTCs
|
||||
*
|
||||
* Copyright (C) 2005 Pavel Mironchik <pmironchik@optifacio.net>
|
||||
* Copyright (C) 2006 Tower Technologies
|
||||
* Copyright (C) 2008 Paul Mundt
|
||||
*
|
||||
* 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
|
||||
|
|
@ -13,7 +14,7 @@
|
|||
#include <linux/rtc.h>
|
||||
#include <linux/bcd.h>
|
||||
|
||||
#define DRV_VERSION "0.5"
|
||||
#define DRV_VERSION "0.6"
|
||||
|
||||
|
||||
/*
|
||||
|
|
@ -51,7 +52,8 @@
|
|||
# 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 R2025_CTRL2_XST (1 << 5)
|
||||
# define RS5C_CTRL2_XSTP (1 << 4) /* only if !R2025S/D */
|
||||
# define RS5C_CTRL2_CTFG (1 << 2)
|
||||
# define RS5C_CTRL2_AAFG (1 << 1) /* or WAFG */
|
||||
# define RS5C_CTRL2_BAFG (1 << 0) /* or DAFG */
|
||||
|
|
@ -63,6 +65,7 @@
|
|||
|
||||
enum rtc_type {
|
||||
rtc_undef = 0,
|
||||
rtc_r2025sd,
|
||||
rtc_rs5c372a,
|
||||
rtc_rs5c372b,
|
||||
rtc_rv5c386,
|
||||
|
|
@ -70,6 +73,7 @@ enum rtc_type {
|
|||
};
|
||||
|
||||
static const struct i2c_device_id rs5c372_id[] = {
|
||||
{ "r2025sd", rtc_r2025sd },
|
||||
{ "rs5c372a", rtc_rs5c372a },
|
||||
{ "rs5c372b", rtc_rs5c372b },
|
||||
{ "rv5c386", rtc_rv5c386 },
|
||||
|
|
@ -89,6 +93,7 @@ struct rs5c372 {
|
|||
enum rtc_type type;
|
||||
unsigned time24:1;
|
||||
unsigned has_irq:1;
|
||||
unsigned smbus:1;
|
||||
char buf[17];
|
||||
char *regs;
|
||||
};
|
||||
|
|
@ -106,10 +111,25 @@ static int rs5c_get_regs(struct rs5c372 *rs5c)
|
|||
*
|
||||
* The first method doesn't work with the iop3xx adapter driver, on at
|
||||
* least 80219 chips; this works around that bug.
|
||||
*
|
||||
* The third method on the other hand doesn't work for the SMBus-only
|
||||
* configurations, so we use the the first method there, stripping off
|
||||
* the extra register in the process.
|
||||
*/
|
||||
if ((i2c_transfer(client->adapter, msgs, 1)) != 1) {
|
||||
dev_warn(&client->dev, "can't read registers\n");
|
||||
return -EIO;
|
||||
if (rs5c->smbus) {
|
||||
int addr = RS5C_ADDR(RS5C372_REG_SECS);
|
||||
int size = sizeof(rs5c->buf) - 1;
|
||||
|
||||
if (i2c_smbus_read_i2c_block_data(client, addr, size,
|
||||
rs5c->buf + 1) != size) {
|
||||
dev_warn(&client->dev, "can't read registers\n");
|
||||
return -EIO;
|
||||
}
|
||||
} else {
|
||||
if ((i2c_transfer(client->adapter, msgs, 1)) != 1) {
|
||||
dev_warn(&client->dev, "can't read registers\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
|
||||
dev_dbg(&client->dev,
|
||||
|
|
@ -187,6 +207,7 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm)
|
|||
{
|
||||
struct rs5c372 *rs5c = i2c_get_clientdata(client);
|
||||
unsigned char buf[8];
|
||||
int addr;
|
||||
|
||||
dev_dbg(&client->dev, "%s: tm is secs=%d, mins=%d, hours=%d "
|
||||
"mday=%d, mon=%d, year=%d, wday=%d\n",
|
||||
|
|
@ -194,16 +215,16 @@ static int rs5c372_set_datetime(struct i2c_client *client, struct rtc_time *tm)
|
|||
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);
|
||||
addr = RS5C_ADDR(RS5C372_REG_SECS);
|
||||
buf[0] = BIN2BCD(tm->tm_sec);
|
||||
buf[1] = BIN2BCD(tm->tm_min);
|
||||
buf[2] = rs5c_hr2reg(rs5c, tm->tm_hour);
|
||||
buf[3] = BIN2BCD(tm->tm_wday);
|
||||
buf[4] = BIN2BCD(tm->tm_mday);
|
||||
buf[5] = BIN2BCD(tm->tm_mon + 1);
|
||||
buf[6] = BIN2BCD(tm->tm_year - 100);
|
||||
|
||||
if ((i2c_master_send(client, buf, 8)) != 8) {
|
||||
if (i2c_smbus_write_i2c_block_data(client, addr, sizeof(buf), buf) < 0) {
|
||||
dev_err(&client->dev, "%s: write error\n", __func__);
|
||||
return -EIO;
|
||||
}
|
||||
|
|
@ -266,16 +287,16 @@ 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;
|
||||
unsigned char buf;
|
||||
int status, addr;
|
||||
|
||||
buf[1] = rs5c->regs[RS5C_REG_CTRL1];
|
||||
buf = 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))
|
||||
&& (buf & RS5C372A_CTRL1_SL1))
|
||||
return -ENOIOCTLCMD;
|
||||
case RTC_AIE_OFF:
|
||||
case RTC_AIE_ON:
|
||||
|
|
@ -293,28 +314,30 @@ rs5c_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
|
|||
if (status < 0)
|
||||
return status;
|
||||
|
||||
buf[0] = RS5C_ADDR(RS5C_REG_CTRL1);
|
||||
addr = RS5C_ADDR(RS5C_REG_CTRL1);
|
||||
switch (cmd) {
|
||||
case RTC_AIE_OFF: /* alarm off */
|
||||
buf[1] &= ~RS5C_CTRL1_AALE;
|
||||
buf &= ~RS5C_CTRL1_AALE;
|
||||
break;
|
||||
case RTC_AIE_ON: /* alarm on */
|
||||
buf[1] |= RS5C_CTRL1_AALE;
|
||||
buf |= RS5C_CTRL1_AALE;
|
||||
break;
|
||||
case RTC_UIE_OFF: /* update off */
|
||||
buf[1] &= ~RS5C_CTRL1_CT_MASK;
|
||||
buf &= ~RS5C_CTRL1_CT_MASK;
|
||||
break;
|
||||
case RTC_UIE_ON: /* update on */
|
||||
buf[1] &= ~RS5C_CTRL1_CT_MASK;
|
||||
buf[1] |= RS5C_CTRL1_CT4;
|
||||
buf &= ~RS5C_CTRL1_CT_MASK;
|
||||
buf |= RS5C_CTRL1_CT4;
|
||||
break;
|
||||
}
|
||||
if ((i2c_master_send(client, buf, 2)) != 2) {
|
||||
|
||||
if (i2c_smbus_write_byte_data(client, addr, buf) < 0) {
|
||||
printk(KERN_WARNING "%s: can't update alarm\n",
|
||||
rs5c->rtc->name);
|
||||
status = -EIO;
|
||||
} else
|
||||
rs5c->regs[RS5C_REG_CTRL1] = buf[1];
|
||||
rs5c->regs[RS5C_REG_CTRL1] = buf;
|
||||
|
||||
return status;
|
||||
}
|
||||
|
||||
|
|
@ -364,8 +387,8 @@ 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];
|
||||
int status, addr, i;
|
||||
unsigned char buf[3];
|
||||
|
||||
/* only handle up to 24 hours in the future, like RTC_ALM_SET */
|
||||
if (t->time.tm_mday != -1
|
||||
|
|
@ -380,33 +403,36 @@ static int rs5c_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|||
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) {
|
||||
addr = RS5C_ADDR(RS5C_REG_CTRL1);
|
||||
buf[0] = rs5c->regs[RS5C_REG_CTRL1] & ~RS5C_CTRL1_AALE;
|
||||
if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0) {
|
||||
pr_debug("%s: can't disable alarm\n", rs5c->rtc->name);
|
||||
return -EIO;
|
||||
}
|
||||
rs5c->regs[RS5C_REG_CTRL1] = buf[1];
|
||||
rs5c->regs[RS5C_REG_CTRL1] = buf[0];
|
||||
}
|
||||
|
||||
/* 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;
|
||||
buf[0] = BIN2BCD(t->time.tm_min);
|
||||
buf[1] = rs5c_hr2reg(rs5c, t->time.tm_hour);
|
||||
buf[2] = 0x7f; /* any/all days */
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
addr = RS5C_ADDR(RS5C_REG_ALARM_A_MIN + i);
|
||||
if (i2c_smbus_write_byte_data(client, addr, buf[i]) < 0) {
|
||||
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)
|
||||
addr = RS5C_ADDR(RS5C_REG_CTRL1);
|
||||
buf[0] = rs5c->regs[RS5C_REG_CTRL1] | RS5C_CTRL1_AALE;
|
||||
if (i2c_smbus_write_byte_data(client, addr, buf[0]) < 0)
|
||||
printk(KERN_WARNING "%s: can't enable alarm\n",
|
||||
rs5c->rtc->name);
|
||||
rs5c->regs[RS5C_REG_CTRL1] = buf[1];
|
||||
rs5c->regs[RS5C_REG_CTRL1] = buf[0];
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
|
@ -503,18 +529,81 @@ static void rs5c_sysfs_unregister(struct device *dev)
|
|||
|
||||
static struct i2c_driver rs5c372_driver;
|
||||
|
||||
static int rs5c_oscillator_setup(struct rs5c372 *rs5c372)
|
||||
{
|
||||
unsigned char buf[2];
|
||||
int addr, i, ret = 0;
|
||||
|
||||
if (rs5c372->type == rtc_r2025sd) {
|
||||
if (!(rs5c372->regs[RS5C_REG_CTRL2] & R2025_CTRL2_XST))
|
||||
return ret;
|
||||
rs5c372->regs[RS5C_REG_CTRL2] &= ~R2025_CTRL2_XST;
|
||||
} else {
|
||||
if (!(rs5c372->regs[RS5C_REG_CTRL2] & RS5C_CTRL2_XSTP))
|
||||
return ret;
|
||||
rs5c372->regs[RS5C_REG_CTRL2] &= ~RS5C_CTRL2_XSTP;
|
||||
}
|
||||
|
||||
addr = RS5C_ADDR(RS5C_REG_CTRL1);
|
||||
buf[0] = rs5c372->regs[RS5C_REG_CTRL1];
|
||||
buf[1] = rs5c372->regs[RS5C_REG_CTRL2];
|
||||
|
||||
/* use 24hr mode */
|
||||
switch (rs5c372->type) {
|
||||
case rtc_rs5c372a:
|
||||
case rtc_rs5c372b:
|
||||
buf[1] |= RS5C372_CTRL2_24;
|
||||
rs5c372->time24 = 1;
|
||||
break;
|
||||
case rtc_r2025sd:
|
||||
case rtc_rv5c386:
|
||||
case rtc_rv5c387a:
|
||||
buf[0] |= RV5C387_CTRL1_24;
|
||||
rs5c372->time24 = 1;
|
||||
break;
|
||||
default:
|
||||
/* impossible */
|
||||
break;
|
||||
}
|
||||
|
||||
for (i = 0; i < sizeof(buf); i++) {
|
||||
addr = RS5C_ADDR(RS5C_REG_CTRL1 + i);
|
||||
ret = i2c_smbus_write_byte_data(rs5c372->client, addr, buf[i]);
|
||||
if (unlikely(ret < 0))
|
||||
return ret;
|
||||
}
|
||||
|
||||
rs5c372->regs[RS5C_REG_CTRL1] = buf[0];
|
||||
rs5c372->regs[RS5C_REG_CTRL2] = buf[1];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rs5c372_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
int err = 0;
|
||||
int smbus_mode = 0;
|
||||
struct rs5c372 *rs5c372;
|
||||
struct rtc_time tm;
|
||||
|
||||
dev_dbg(&client->dev, "%s\n", __func__);
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) {
|
||||
err = -ENODEV;
|
||||
goto exit;
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
|
||||
I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK)) {
|
||||
/*
|
||||
* If we don't have any master mode adapter, try breaking
|
||||
* it down in to the barest of capabilities.
|
||||
*/
|
||||
if (i2c_check_functionality(client->adapter,
|
||||
I2C_FUNC_SMBUS_BYTE_DATA |
|
||||
I2C_FUNC_SMBUS_I2C_BLOCK))
|
||||
smbus_mode = 1;
|
||||
else {
|
||||
/* Still no good, give up */
|
||||
err = -ENODEV;
|
||||
goto exit;
|
||||
}
|
||||
}
|
||||
|
||||
if (!(rs5c372 = kzalloc(sizeof(struct rs5c372), GFP_KERNEL))) {
|
||||
|
|
@ -528,6 +617,7 @@ static int rs5c372_probe(struct i2c_client *client,
|
|||
|
||||
/* we read registers 0x0f then 0x00-0x0f; skip the first one */
|
||||
rs5c372->regs = &rs5c372->buf[1];
|
||||
rs5c372->smbus = smbus_mode;
|
||||
|
||||
err = rs5c_get_regs(rs5c372);
|
||||
if (err < 0)
|
||||
|
|
@ -543,6 +633,7 @@ static int rs5c372_probe(struct i2c_client *client,
|
|||
if (rs5c372->regs[RS5C_REG_CTRL2] & RS5C372_CTRL2_24)
|
||||
rs5c372->time24 = 1;
|
||||
break;
|
||||
case rtc_r2025sd:
|
||||
case rtc_rv5c386:
|
||||
case rtc_rv5c387a:
|
||||
if (rs5c372->regs[RS5C_REG_CTRL1] & RV5C387_CTRL1_24)
|
||||
|
|
@ -558,39 +649,14 @@ static int rs5c372_probe(struct i2c_client *client,
|
|||
|
||||
/* if the oscillator lost power and no other software (like
|
||||
* the bootloader) set it up, do it here.
|
||||
*
|
||||
* The R2025S/D does this a little differently than the other
|
||||
* parts, so we special case that..
|
||||
*/
|
||||
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_kfree;
|
||||
}
|
||||
rs5c372->regs[RS5C_REG_CTRL1] = buf[1];
|
||||
rs5c372->regs[RS5C_REG_CTRL2] = buf[2];
|
||||
err = rs5c_oscillator_setup(rs5c372);
|
||||
if (unlikely(err < 0)) {
|
||||
dev_err(&client->dev, "setup error\n");
|
||||
goto exit_kfree;
|
||||
}
|
||||
|
||||
if (rs5c372_get_datetime(client, &tm) < 0)
|
||||
|
|
@ -598,6 +664,7 @@ static int rs5c372_probe(struct i2c_client *client,
|
|||
|
||||
dev_info(&client->dev, "%s found, %s, driver version " DRV_VERSION "\n",
|
||||
({ char *s; switch (rs5c372->type) {
|
||||
case rtc_r2025sd: s = "r2025sd"; break;
|
||||
case rtc_rs5c372a: s = "rs5c372a"; break;
|
||||
case rtc_rs5c372b: s = "rs5c372b"; break;
|
||||
case rtc_rv5c386: s = "rv5c386"; break;
|
||||
|
|
@ -667,7 +734,8 @@ module_exit(rs5c372_exit);
|
|||
|
||||
MODULE_AUTHOR(
|
||||
"Pavel Mironchik <[email protected]>, "
|
||||
"Alessandro Zummo <[email protected]>");
|
||||
"Alessandro Zummo <[email protected]>, "
|
||||
"Paul Mundt <[email protected]>");
|
||||
MODULE_DESCRIPTION("Ricoh RS5C372 RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_VERSION(DRV_VERSION);
|
||||
|
|
|
|||
|
|
@ -257,12 +257,6 @@ static inline void sh_rtc_setaie(struct device *dev, unsigned int enable)
|
|||
spin_unlock_irq(&rtc->lock);
|
||||
}
|
||||
|
||||
static void sh_rtc_release(struct device *dev)
|
||||
{
|
||||
sh_rtc_setpie(dev, 0);
|
||||
sh_rtc_setaie(dev, 0);
|
||||
}
|
||||
|
||||
static int sh_rtc_proc(struct device *dev, struct seq_file *seq)
|
||||
{
|
||||
struct sh_rtc *rtc = dev_get_drvdata(dev);
|
||||
|
|
@ -559,7 +553,6 @@ static int sh_rtc_irq_set_freq(struct device *dev, int freq)
|
|||
}
|
||||
|
||||
static struct rtc_class_ops sh_rtc_ops = {
|
||||
.release = sh_rtc_release,
|
||||
.ioctl = sh_rtc_ioctl,
|
||||
.read_time = sh_rtc_read_time,
|
||||
.set_time = sh_rtc_set_time,
|
||||
|
|
|
|||
120
drivers/rtc/rtc-starfire.c
Normal file
120
drivers/rtc/rtc-starfire.c
Normal file
|
|
@ -0,0 +1,120 @@
|
|||
/* rtc-starfire.c: Starfire platform RTC driver.
|
||||
*
|
||||
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/oplib.h>
|
||||
|
||||
MODULE_AUTHOR("David S. Miller <[email protected]>");
|
||||
MODULE_DESCRIPTION("Starfire RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct starfire_rtc {
|
||||
struct rtc_device *rtc;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static u32 starfire_get_time(void)
|
||||
{
|
||||
static char obp_gettod[32];
|
||||
static u32 unix_tod;
|
||||
|
||||
sprintf(obp_gettod, "h# %08x unix-gettod",
|
||||
(unsigned int) (long) &unix_tod);
|
||||
prom_feval(obp_gettod);
|
||||
|
||||
return unix_tod;
|
||||
}
|
||||
|
||||
static int starfire_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct starfire_rtc *p = dev_get_drvdata(dev);
|
||||
unsigned long flags, secs;
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
secs = starfire_get_time();
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
||||
rtc_time_to_tm(secs, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int starfire_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
unsigned long secs;
|
||||
int err;
|
||||
|
||||
err = rtc_tm_to_time(tm, &secs);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* Do nothing, time is set using the service processor
|
||||
* console on this platform.
|
||||
*/
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops starfire_rtc_ops = {
|
||||
.read_time = starfire_read_time,
|
||||
.set_time = starfire_set_time,
|
||||
};
|
||||
|
||||
static int __devinit starfire_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct starfire_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&p->lock);
|
||||
|
||||
p->rtc = rtc_device_register("starfire", &pdev->dev,
|
||||
&starfire_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(p->rtc)) {
|
||||
int err = PTR_ERR(p->rtc);
|
||||
kfree(p);
|
||||
return err;
|
||||
}
|
||||
platform_set_drvdata(pdev, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit starfire_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct starfire_rtc *p = platform_get_drvdata(pdev);
|
||||
|
||||
rtc_device_unregister(p->rtc);
|
||||
kfree(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver starfire_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-starfire",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = starfire_rtc_probe,
|
||||
.remove = __devexit_p(starfire_rtc_remove),
|
||||
};
|
||||
|
||||
static int __init starfire_rtc_init(void)
|
||||
{
|
||||
return platform_driver_register(&starfire_rtc_driver);
|
||||
}
|
||||
|
||||
static void __exit starfire_rtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&starfire_rtc_driver);
|
||||
}
|
||||
|
||||
module_init(starfire_rtc_init);
|
||||
module_exit(starfire_rtc_exit);
|
||||
|
|
@ -215,17 +215,6 @@ static irqreturn_t stk17ta8_rtc_interrupt(int irq, void *dev_id)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void stk17ta8_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;
|
||||
stk17ta8_rtc_update_alarm(pdata);
|
||||
}
|
||||
}
|
||||
|
||||
static int stk17ta8_rtc_ioctl(struct device *dev, unsigned int cmd,
|
||||
unsigned long arg)
|
||||
{
|
||||
|
|
@ -254,7 +243,6 @@ static const struct rtc_class_ops stk17ta8_rtc_ops = {
|
|||
.set_time = stk17ta8_rtc_set_time,
|
||||
.read_alarm = stk17ta8_rtc_read_alarm,
|
||||
.set_alarm = stk17ta8_rtc_set_alarm,
|
||||
.release = stk17ta8_rtc_release,
|
||||
.ioctl = stk17ta8_rtc_ioctl,
|
||||
};
|
||||
|
||||
|
|
|
|||
153
drivers/rtc/rtc-sun4v.c
Normal file
153
drivers/rtc/rtc-sun4v.c
Normal file
|
|
@ -0,0 +1,153 @@
|
|||
/* rtc-sun4c.c: Hypervisor based RTC for SUN4V systems.
|
||||
*
|
||||
* Copyright (C) 2008 David S. Miller <davem@davemloft.net>
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/time.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
||||
#include <asm/hypervisor.h>
|
||||
|
||||
MODULE_AUTHOR("David S. Miller <[email protected]>");
|
||||
MODULE_DESCRIPTION("SUN4V RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
||||
|
||||
struct sun4v_rtc {
|
||||
struct rtc_device *rtc;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static unsigned long hypervisor_get_time(void)
|
||||
{
|
||||
unsigned long ret, time;
|
||||
int retries = 10000;
|
||||
|
||||
retry:
|
||||
ret = sun4v_tod_get(&time);
|
||||
if (ret == HV_EOK)
|
||||
return time;
|
||||
if (ret == HV_EWOULDBLOCK) {
|
||||
if (--retries > 0) {
|
||||
udelay(100);
|
||||
goto retry;
|
||||
}
|
||||
printk(KERN_WARNING "SUN4V: tod_get() timed out.\n");
|
||||
return 0;
|
||||
}
|
||||
printk(KERN_WARNING "SUN4V: tod_get() not supported.\n");
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sun4v_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct sun4v_rtc *p = dev_get_drvdata(dev);
|
||||
unsigned long flags, secs;
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
secs = hypervisor_get_time();
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
||||
rtc_time_to_tm(secs, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int hypervisor_set_time(unsigned long secs)
|
||||
{
|
||||
unsigned long ret;
|
||||
int retries = 10000;
|
||||
|
||||
retry:
|
||||
ret = sun4v_tod_set(secs);
|
||||
if (ret == HV_EOK)
|
||||
return 0;
|
||||
if (ret == HV_EWOULDBLOCK) {
|
||||
if (--retries > 0) {
|
||||
udelay(100);
|
||||
goto retry;
|
||||
}
|
||||
printk(KERN_WARNING "SUN4V: tod_set() timed out.\n");
|
||||
return -EAGAIN;
|
||||
}
|
||||
printk(KERN_WARNING "SUN4V: tod_set() not supported.\n");
|
||||
return -EOPNOTSUPP;
|
||||
}
|
||||
|
||||
static int sun4v_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct sun4v_rtc *p = dev_get_drvdata(dev);
|
||||
unsigned long flags, secs;
|
||||
int err;
|
||||
|
||||
err = rtc_tm_to_time(tm, &secs);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
spin_lock_irqsave(&p->lock, flags);
|
||||
err = hypervisor_set_time(secs);
|
||||
spin_unlock_irqrestore(&p->lock, flags);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops sun4v_rtc_ops = {
|
||||
.read_time = sun4v_read_time,
|
||||
.set_time = sun4v_set_time,
|
||||
};
|
||||
|
||||
static int __devinit sun4v_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct sun4v_rtc *p = kzalloc(sizeof(*p), GFP_KERNEL);
|
||||
|
||||
if (!p)
|
||||
return -ENOMEM;
|
||||
|
||||
spin_lock_init(&p->lock);
|
||||
|
||||
p->rtc = rtc_device_register("sun4v", &pdev->dev,
|
||||
&sun4v_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(p->rtc)) {
|
||||
int err = PTR_ERR(p->rtc);
|
||||
kfree(p);
|
||||
return err;
|
||||
}
|
||||
platform_set_drvdata(pdev, p);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit sun4v_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct sun4v_rtc *p = platform_get_drvdata(pdev);
|
||||
|
||||
rtc_device_unregister(p->rtc);
|
||||
kfree(p);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver sun4v_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-sun4v",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = sun4v_rtc_probe,
|
||||
.remove = __devexit_p(sun4v_rtc_remove),
|
||||
};
|
||||
|
||||
static int __init sun4v_rtc_init(void)
|
||||
{
|
||||
return platform_driver_register(&sun4v_rtc_driver);
|
||||
}
|
||||
|
||||
static void __exit sun4v_rtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&sun4v_rtc_driver);
|
||||
}
|
||||
|
||||
module_init(sun4v_rtc_init);
|
||||
module_exit(sun4v_rtc_exit);
|
||||
Loading…
Reference in a new issue