Merge branch 'akpm' (Andrew's patch-bomb)
Merge misc patches from Andrew Morton: "The MM tree is rather stuck while I wait to find out what the heck is happening with sched/numa. Probably I'll need to route around all the code which was added to -next, sigh. So this is "everything else", or at least most of it - other small bits are still awaiting resolutions of various kinds." * emailed patches from Andrew Morton <akpm@linux-foundation.org>: (180 commits) lib/decompress.c add __init to decompress_method and data kernel/resource.c: fix stack overflow in __reserve_region_with_split() omfs: convert to use beXX_add_cpu() taskstats: cgroupstats_user_cmd() may leak on error aoe: update aoe-internal version number to 50 aoe: update documentation to better reflect aoe-plus-udev usage aoe: remove unused code aoe: make dynamic block minor numbers the default aoe: update and specify AoE address guards and error messages aoe: retain static block device numbers for backwards compatibility aoe: support more AoE addresses with dynamic block device minor numbers aoe: update documentation with new URL and VM settings reference aoe: update copyright year in touched files aoe: update internal version number to 49 aoe: remove unused code and add cosmetic improvements aoe: increase net_device reference count while using it aoe: associate frames with the AoE storage target aoe: disallow unsupported AoE minor addresses aoe: do revalidation steps in order aoe: failover remote interface based on aoe_deadsecs parameter ...
This commit is contained in:
@@ -19,7 +19,6 @@ if RTC_CLASS
|
||||
|
||||
config RTC_HCTOSYS
|
||||
bool "Set system time from RTC on startup and resume"
|
||||
depends on RTC_CLASS = y
|
||||
default y
|
||||
help
|
||||
If you say yes here, the system time (wall clock) will be set using
|
||||
@@ -51,7 +50,6 @@ config RTC_HCTOSYS_DEVICE
|
||||
|
||||
config RTC_DEBUG
|
||||
bool "RTC debug support"
|
||||
depends on RTC_CLASS = y
|
||||
help
|
||||
Say yes here to enable debugging support in the RTC framework
|
||||
and individual RTC drivers.
|
||||
@@ -61,7 +59,6 @@ comment "RTC interfaces"
|
||||
config RTC_INTF_SYSFS
|
||||
boolean "/sys/class/rtc/rtcN (sysfs)"
|
||||
depends on SYSFS
|
||||
default RTC_CLASS
|
||||
help
|
||||
Say yes here if you want to use your RTCs using sysfs interfaces,
|
||||
/sys/class/rtc/rtc0 through /sys/.../rtcN.
|
||||
@@ -69,19 +66,19 @@ config RTC_INTF_SYSFS
|
||||
If unsure, say Y.
|
||||
|
||||
config RTC_INTF_PROC
|
||||
boolean "/proc/driver/rtc (procfs for rtc0)"
|
||||
boolean "/proc/driver/rtc (procfs for rtcN)"
|
||||
depends on PROC_FS
|
||||
default RTC_CLASS
|
||||
help
|
||||
Say yes here if you want to use your first RTC through the proc
|
||||
interface, /proc/driver/rtc. Other RTCs will not be available
|
||||
through that API.
|
||||
Say yes here if you want to use your system clock RTC through
|
||||
the proc interface, /proc/driver/rtc.
|
||||
Other RTCs will not be available through that API.
|
||||
If there is no RTC for the system clock, then the first RTC(rtc0)
|
||||
is used by default.
|
||||
|
||||
If unsure, say Y.
|
||||
|
||||
config RTC_INTF_DEV
|
||||
boolean "/dev/rtcN (character devices)"
|
||||
default RTC_CLASS
|
||||
help
|
||||
Say yes here if you want to use your RTCs using the /dev
|
||||
interfaces, which "udev" sets up as /dev/rtc0 through
|
||||
@@ -127,7 +124,7 @@ if I2C
|
||||
|
||||
config RTC_DRV_88PM860X
|
||||
tristate "Marvell 88PM860x"
|
||||
depends on RTC_CLASS && I2C && MFD_88PM860X
|
||||
depends on I2C && MFD_88PM860X
|
||||
help
|
||||
If you say yes here you get support for RTC function in Marvell
|
||||
88PM860x chips.
|
||||
@@ -137,7 +134,7 @@ config RTC_DRV_88PM860X
|
||||
|
||||
config RTC_DRV_88PM80X
|
||||
tristate "Marvell 88PM80x"
|
||||
depends on RTC_CLASS && I2C && MFD_88PM800
|
||||
depends on I2C && MFD_88PM800
|
||||
help
|
||||
If you say yes here you get support for RTC function in Marvell
|
||||
88PM80x chips.
|
||||
@@ -165,7 +162,7 @@ config RTC_DRV_DS1307
|
||||
|
||||
config RTC_DRV_DS1374
|
||||
tristate "Dallas/Maxim DS1374"
|
||||
depends on RTC_CLASS && I2C
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for Dallas Semiconductor
|
||||
DS1374 real-time clock chips. If an interrupt is associated
|
||||
@@ -185,7 +182,7 @@ config RTC_DRV_DS1672
|
||||
|
||||
config RTC_DRV_DS3232
|
||||
tristate "Dallas/Maxim DS3232"
|
||||
depends on RTC_CLASS && I2C
|
||||
depends on I2C
|
||||
help
|
||||
If you say yes here you get support for Dallas Semiconductor
|
||||
DS3232 real-time clock chips. If an interrupt is associated
|
||||
@@ -203,6 +200,16 @@ config RTC_DRV_MAX6900
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-max6900.
|
||||
|
||||
config RTC_DRV_MAX8907
|
||||
tristate "Maxim MAX8907"
|
||||
depends on MFD_MAX8907
|
||||
help
|
||||
If you say yes here you will get support for the
|
||||
RTC of Maxim MAX8907 PMIC.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-max8907.
|
||||
|
||||
config RTC_DRV_MAX8925
|
||||
tristate "Maxim MAX8925"
|
||||
depends on MFD_MAX8925
|
||||
@@ -325,7 +332,7 @@ config RTC_DRV_TWL92330
|
||||
|
||||
config RTC_DRV_TWL4030
|
||||
tristate "TI TWL4030/TWL5030/TWL6030/TPS659x0"
|
||||
depends on RTC_CLASS && TWL4030_CORE
|
||||
depends on TWL4030_CORE
|
||||
help
|
||||
If you say yes here you get support for the RTC on the
|
||||
TWL4030/TWL5030/TWL6030 family chips, used mostly with OMAP3 platforms.
|
||||
@@ -333,6 +340,26 @@ config RTC_DRV_TWL4030
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-twl.
|
||||
|
||||
config RTC_DRV_TPS65910
|
||||
tristate "TI TPS65910 RTC driver"
|
||||
depends on RTC_CLASS && MFD_TPS65910
|
||||
help
|
||||
If you say yes here you get support for the RTC on the
|
||||
TPS65910 chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-tps65910.
|
||||
|
||||
config RTC_DRV_RC5T583
|
||||
tristate "RICOH 5T583 RTC driver"
|
||||
depends on MFD_RC5T583
|
||||
help
|
||||
If you say yes here you get support for the RTC on the
|
||||
RICOH 5T583 chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-rc5t583.
|
||||
|
||||
config RTC_DRV_S35390A
|
||||
tristate "Seiko Instruments S-35390A"
|
||||
select BITREVERSE
|
||||
@@ -538,7 +565,6 @@ config RTC_DRV_DS1302
|
||||
|
||||
config RTC_DRV_DS1511
|
||||
tristate "Dallas DS1511"
|
||||
depends on RTC_CLASS
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
Dallas DS1511 timekeeping/watchdog chip.
|
||||
@@ -583,7 +609,6 @@ config RTC_DRV_EFI
|
||||
|
||||
config RTC_DRV_STK17TA8
|
||||
tristate "Simtek STK17TA8"
|
||||
depends on RTC_CLASS
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
Simtek STK17TA8 timekeeping chip.
|
||||
@@ -658,6 +683,15 @@ config RTC_DRV_V3020
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-v3020.
|
||||
|
||||
config RTC_DRV_DS2404
|
||||
tristate "Dallas DS2404"
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
Dallas DS2404 RTC chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-ds2404.
|
||||
|
||||
config RTC_DRV_WM831X
|
||||
tristate "Wolfson Microelectronics WM831x RTC"
|
||||
depends on MFD_WM831X
|
||||
@@ -704,6 +738,7 @@ config RTC_DRV_AB3100
|
||||
config RTC_DRV_AB8500
|
||||
tristate "ST-Ericsson AB8500 RTC"
|
||||
depends on AB8500_CORE
|
||||
select RTC_INTF_DEV
|
||||
select RTC_INTF_DEV_UIE_EMUL
|
||||
help
|
||||
Select this to enable the ST-Ericsson AB8500 power management IC RTC
|
||||
@@ -711,7 +746,7 @@ config RTC_DRV_AB8500
|
||||
|
||||
config RTC_DRV_NUC900
|
||||
tristate "NUC910/NUC920 RTC driver"
|
||||
depends on RTC_CLASS && ARCH_W90X900
|
||||
depends on ARCH_W90X900
|
||||
help
|
||||
If you say yes here you get support for the RTC subsystem of the
|
||||
NUC910/NUC920 used in embedded systems.
|
||||
@@ -731,7 +766,6 @@ config RTC_DRV_DAVINCI
|
||||
config RTC_DRV_IMXDI
|
||||
tristate "Freescale IMX DryIce Real Time Clock"
|
||||
depends on SOC_IMX25
|
||||
depends on RTC_CLASS
|
||||
help
|
||||
Support for Freescale IMX DryIce RTC
|
||||
|
||||
@@ -791,7 +825,7 @@ config RTC_DRV_SA1100
|
||||
|
||||
config RTC_DRV_SH
|
||||
tristate "SuperH On-Chip RTC"
|
||||
depends on RTC_CLASS && SUPERH && HAVE_CLK
|
||||
depends on SUPERH && HAVE_CLK
|
||||
help
|
||||
Say Y here to enable support for the on-chip RTC found in
|
||||
most SuperH processors.
|
||||
@@ -1023,7 +1057,6 @@ config RTC_DRV_MPC5121
|
||||
|
||||
config RTC_DRV_JZ4740
|
||||
tristate "Ingenic JZ4740 SoC"
|
||||
depends on RTC_CLASS
|
||||
depends on MACH_JZ4740
|
||||
help
|
||||
If you say yes here you get support for the Ingenic JZ4740 SoC RTC
|
||||
@@ -1053,7 +1086,7 @@ config RTC_DRV_PM8XXX
|
||||
|
||||
config RTC_DRV_TEGRA
|
||||
tristate "NVIDIA Tegra Internal RTC driver"
|
||||
depends on RTC_CLASS && ARCH_TEGRA
|
||||
depends on ARCH_TEGRA
|
||||
help
|
||||
If you say yes here you get support for the
|
||||
Tegra 200 series internal RTC module.
|
||||
@@ -1090,7 +1123,6 @@ config RTC_DRV_LOONGSON1
|
||||
config RTC_DRV_MXC
|
||||
tristate "Freescale MXC Real Time Clock"
|
||||
depends on ARCH_MXC
|
||||
depends on RTC_CLASS
|
||||
help
|
||||
If you say yes here you get support for the Freescale MXC
|
||||
RTC module.
|
||||
@@ -1098,4 +1130,15 @@ config RTC_DRV_MXC
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-mxc".
|
||||
|
||||
config RTC_DRV_SNVS
|
||||
tristate "Freescale SNVS RTC support"
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
help
|
||||
If you say yes here you get support for the Freescale SNVS
|
||||
Low Power (LP) RTC module.
|
||||
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-snvs".
|
||||
|
||||
endif # RTC_CLASS
|
||||
|
@@ -43,6 +43,7 @@ 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_DS2404) += rtc-ds2404.o
|
||||
obj-$(CONFIG_RTC_DRV_DS3232) += rtc-ds3232.o
|
||||
obj-$(CONFIG_RTC_DRV_DS3234) += rtc-ds3234.o
|
||||
obj-$(CONFIG_RTC_DRV_EFI) += rtc-efi.o
|
||||
@@ -64,6 +65,7 @@ obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o
|
||||
obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o
|
||||
obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX8907) += rtc-max8907.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX8925) += rtc-max8925.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX8998) += rtc-max8998.o
|
||||
obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o
|
||||
@@ -85,6 +87,7 @@ obj-$(CONFIG_RTC_DRV_PS3) += rtc-ps3.o
|
||||
obj-$(CONFIG_RTC_DRV_PUV3) += rtc-puv3.o
|
||||
obj-$(CONFIG_RTC_DRV_PXA) += rtc-pxa.o
|
||||
obj-$(CONFIG_RTC_DRV_R9701) += rtc-r9701.o
|
||||
obj-$(CONFIG_RTC_DRV_RC5T583) += rtc-rc5t583.o
|
||||
obj-$(CONFIG_RTC_DRV_RP5C01) += rtc-rp5c01.o
|
||||
obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
|
||||
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
|
||||
@@ -96,6 +99,7 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
|
||||
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
|
||||
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
|
||||
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
|
||||
obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
|
||||
obj-$(CONFIG_RTC_DRV_SPEAR) += rtc-spear.o
|
||||
obj-$(CONFIG_RTC_DRV_STARFIRE) += rtc-starfire.o
|
||||
obj-$(CONFIG_RTC_DRV_STK17TA8) += rtc-stk17ta8.o
|
||||
@@ -105,6 +109,7 @@ obj-$(CONFIG_RTC_DRV_TEGRA) += rtc-tegra.o
|
||||
obj-$(CONFIG_RTC_DRV_TEST) += rtc-test.o
|
||||
obj-$(CONFIG_RTC_DRV_TILE) += rtc-tile.o
|
||||
obj-$(CONFIG_RTC_DRV_TWL4030) += rtc-twl.o
|
||||
obj-$(CONFIG_RTC_DRV_TPS65910) += rtc-tps65910.o
|
||||
obj-$(CONFIG_RTC_DRV_TX4939) += rtc-tx4939.o
|
||||
obj-$(CONFIG_RTC_DRV_V3020) += rtc-v3020.o
|
||||
obj-$(CONFIG_RTC_DRV_VR41XX) += rtc-vr41xx.o
|
||||
|
@@ -31,8 +31,12 @@ static void rtc_device_release(struct device *dev)
|
||||
kfree(rtc);
|
||||
}
|
||||
|
||||
#if defined(CONFIG_PM) && defined(CONFIG_RTC_HCTOSYS_DEVICE)
|
||||
#ifdef CONFIG_RTC_HCTOSYS_DEVICE
|
||||
/* Result of the last RTC to system clock attempt. */
|
||||
int rtc_hctosys_ret = -ENODEV;
|
||||
#endif
|
||||
|
||||
#if defined(CONFIG_PM) && defined(CONFIG_RTC_HCTOSYS_DEVICE)
|
||||
/*
|
||||
* On suspend(), measure the delta between one RTC and the
|
||||
* system's wall clock; restore it on resume().
|
||||
@@ -84,6 +88,7 @@ static int rtc_resume(struct device *dev)
|
||||
struct timespec new_system, new_rtc;
|
||||
struct timespec sleep_time;
|
||||
|
||||
rtc_hctosys_ret = -ENODEV;
|
||||
if (strcmp(dev_name(&rtc->dev), CONFIG_RTC_HCTOSYS_DEVICE) != 0)
|
||||
return 0;
|
||||
|
||||
@@ -117,6 +122,7 @@ static int rtc_resume(struct device *dev)
|
||||
|
||||
if (sleep_time.tv_sec >= 0)
|
||||
timekeeping_inject_sleeptime(&sleep_time);
|
||||
rtc_hctosys_ret = 0;
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -238,6 +244,7 @@ void rtc_device_unregister(struct rtc_device *rtc)
|
||||
rtc_proc_del_device(rtc);
|
||||
device_unregister(&rtc->dev);
|
||||
rtc->ops = NULL;
|
||||
ida_simple_remove(&rtc_ida, rtc->id);
|
||||
mutex_unlock(&rtc->ops_lock);
|
||||
put_device(&rtc->dev);
|
||||
}
|
||||
|
@@ -22,8 +22,6 @@
|
||||
* the best guess is to add 0.5s.
|
||||
*/
|
||||
|
||||
int rtc_hctosys_ret = -ENODEV;
|
||||
|
||||
static int __init rtc_hctosys(void)
|
||||
{
|
||||
int err = -ENODEV;
|
||||
@@ -56,7 +54,7 @@ static int __init rtc_hctosys(void)
|
||||
|
||||
rtc_tm_to_time(&tm, &tv.tv_sec);
|
||||
|
||||
do_settimeofday(&tv);
|
||||
err = do_settimeofday(&tv);
|
||||
|
||||
dev_info(rtc->dev.parent,
|
||||
"setting system clock to "
|
||||
|
@@ -473,18 +473,7 @@ static struct platform_driver at91_rtc_driver = {
|
||||
},
|
||||
};
|
||||
|
||||
static int __init at91_rtc_init(void)
|
||||
{
|
||||
return platform_driver_register(&at91_rtc_driver);
|
||||
}
|
||||
module_init(at91_rtc_init);
|
||||
|
||||
static void __exit at91_rtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&at91_rtc_driver);
|
||||
}
|
||||
module_exit(at91_rtc_exit);
|
||||
|
||||
module_platform_driver(at91_rtc_driver);
|
||||
|
||||
MODULE_AUTHOR("Michel Benoit");
|
||||
MODULE_DESCRIPTION("RTC driver for Atmel AT91SAM9x");
|
||||
|
@@ -276,8 +276,7 @@ static void coh901331_shutdown(struct platform_device *pdev)
|
||||
|
||||
clk_enable(rtap->clk);
|
||||
writel(0, rtap->virtbase + COH901331_IRQ_MASK);
|
||||
clk_disable(rtap->clk);
|
||||
clk_unprepare(rtap->clk);
|
||||
clk_disable_unprepare(rtap->clk);
|
||||
}
|
||||
|
||||
static struct platform_driver coh901331_driver = {
|
||||
|
@@ -37,8 +37,17 @@ 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 */
|
||||
{/* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 1,
|
||||
.buf = &addr
|
||||
},
|
||||
{/* read date */
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 4,
|
||||
.buf = buf
|
||||
},
|
||||
};
|
||||
|
||||
/* read date registers */
|
||||
@@ -99,8 +108,17 @@ 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 */
|
||||
{/* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 1,
|
||||
.buf = &addr
|
||||
},
|
||||
{/* read control */
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = status
|
||||
},
|
||||
};
|
||||
|
||||
/* read control register */
|
||||
|
303
drivers/rtc/rtc-ds2404.c
Normal file
303
drivers/rtc/rtc-ds2404.c
Normal file
@@ -0,0 +1,303 @@
|
||||
/*
|
||||
* Copyright (C) 2012 Sven Schnelle <svens@stackframe.org>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify
|
||||
* it under the terms of the GNU General Public License version 2 as
|
||||
* published by the Free Software Foundation.
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/rtc-ds2404.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#define DS2404_STATUS_REG 0x200
|
||||
#define DS2404_CONTROL_REG 0x201
|
||||
#define DS2404_RTC_REG 0x202
|
||||
|
||||
#define DS2404_WRITE_SCRATCHPAD_CMD 0x0f
|
||||
#define DS2404_READ_SCRATCHPAD_CMD 0xaa
|
||||
#define DS2404_COPY_SCRATCHPAD_CMD 0x55
|
||||
#define DS2404_READ_MEMORY_CMD 0xf0
|
||||
|
||||
struct ds2404;
|
||||
|
||||
struct ds2404_chip_ops {
|
||||
int (*map_io)(struct ds2404 *chip, struct platform_device *pdev,
|
||||
struct ds2404_platform_data *pdata);
|
||||
void (*unmap_io)(struct ds2404 *chip);
|
||||
};
|
||||
|
||||
#define DS2404_RST 0
|
||||
#define DS2404_CLK 1
|
||||
#define DS2404_DQ 2
|
||||
|
||||
struct ds2404_gpio {
|
||||
const char *name;
|
||||
unsigned int gpio;
|
||||
};
|
||||
|
||||
struct ds2404 {
|
||||
struct ds2404_gpio *gpio;
|
||||
struct ds2404_chip_ops *ops;
|
||||
struct rtc_device *rtc;
|
||||
};
|
||||
|
||||
static struct ds2404_gpio ds2404_gpio[] = {
|
||||
{ "RTC RST", 0 },
|
||||
{ "RTC CLK", 0 },
|
||||
{ "RTC DQ", 0 },
|
||||
};
|
||||
|
||||
static int ds2404_gpio_map(struct ds2404 *chip, struct platform_device *pdev,
|
||||
struct ds2404_platform_data *pdata)
|
||||
{
|
||||
int i, err;
|
||||
|
||||
ds2404_gpio[DS2404_RST].gpio = pdata->gpio_rst;
|
||||
ds2404_gpio[DS2404_CLK].gpio = pdata->gpio_clk;
|
||||
ds2404_gpio[DS2404_DQ].gpio = pdata->gpio_dq;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++) {
|
||||
err = gpio_request(ds2404_gpio[i].gpio, ds2404_gpio[i].name);
|
||||
if (err) {
|
||||
printk(KERN_ERR "error mapping gpio %s: %d\n",
|
||||
ds2404_gpio[i].name, err);
|
||||
goto err_request;
|
||||
}
|
||||
if (i != DS2404_DQ)
|
||||
gpio_direction_output(ds2404_gpio[i].gpio, 1);
|
||||
}
|
||||
|
||||
chip->gpio = ds2404_gpio;
|
||||
return 0;
|
||||
|
||||
err_request:
|
||||
while (--i >= 0)
|
||||
gpio_free(ds2404_gpio[i].gpio);
|
||||
return err;
|
||||
}
|
||||
|
||||
static void ds2404_gpio_unmap(struct ds2404 *chip)
|
||||
{
|
||||
int i;
|
||||
|
||||
for (i = 0; i < ARRAY_SIZE(ds2404_gpio); i++)
|
||||
gpio_free(ds2404_gpio[i].gpio);
|
||||
}
|
||||
|
||||
static struct ds2404_chip_ops ds2404_gpio_ops = {
|
||||
.map_io = ds2404_gpio_map,
|
||||
.unmap_io = ds2404_gpio_unmap,
|
||||
};
|
||||
|
||||
static void ds2404_reset(struct device *dev)
|
||||
{
|
||||
gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 0);
|
||||
udelay(1000);
|
||||
gpio_set_value(ds2404_gpio[DS2404_RST].gpio, 1);
|
||||
gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0);
|
||||
gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 0);
|
||||
udelay(10);
|
||||
}
|
||||
|
||||
static void ds2404_write_byte(struct device *dev, u8 byte)
|
||||
{
|
||||
int i;
|
||||
|
||||
gpio_direction_output(ds2404_gpio[DS2404_DQ].gpio, 1);
|
||||
for (i = 0; i < 8; i++) {
|
||||
gpio_set_value(ds2404_gpio[DS2404_DQ].gpio, byte & (1 << i));
|
||||
udelay(10);
|
||||
gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1);
|
||||
udelay(10);
|
||||
gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0);
|
||||
udelay(10);
|
||||
}
|
||||
}
|
||||
|
||||
static u8 ds2404_read_byte(struct device *dev)
|
||||
{
|
||||
int i;
|
||||
u8 ret = 0;
|
||||
|
||||
gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio);
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 0);
|
||||
udelay(10);
|
||||
if (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio))
|
||||
ret |= 1 << i;
|
||||
gpio_set_value(ds2404_gpio[DS2404_CLK].gpio, 1);
|
||||
udelay(10);
|
||||
}
|
||||
return ret;
|
||||
}
|
||||
|
||||
static void ds2404_read_memory(struct device *dev, u16 offset,
|
||||
int length, u8 *out)
|
||||
{
|
||||
ds2404_reset(dev);
|
||||
ds2404_write_byte(dev, DS2404_READ_MEMORY_CMD);
|
||||
ds2404_write_byte(dev, offset & 0xff);
|
||||
ds2404_write_byte(dev, (offset >> 8) & 0xff);
|
||||
while (length--)
|
||||
*out++ = ds2404_read_byte(dev);
|
||||
}
|
||||
|
||||
static void ds2404_write_memory(struct device *dev, u16 offset,
|
||||
int length, u8 *out)
|
||||
{
|
||||
int i;
|
||||
u8 ta01, ta02, es;
|
||||
|
||||
ds2404_reset(dev);
|
||||
ds2404_write_byte(dev, DS2404_WRITE_SCRATCHPAD_CMD);
|
||||
ds2404_write_byte(dev, offset & 0xff);
|
||||
ds2404_write_byte(dev, (offset >> 8) & 0xff);
|
||||
|
||||
for (i = 0; i < length; i++)
|
||||
ds2404_write_byte(dev, out[i]);
|
||||
|
||||
ds2404_reset(dev);
|
||||
ds2404_write_byte(dev, DS2404_READ_SCRATCHPAD_CMD);
|
||||
|
||||
ta01 = ds2404_read_byte(dev);
|
||||
ta02 = ds2404_read_byte(dev);
|
||||
es = ds2404_read_byte(dev);
|
||||
|
||||
for (i = 0; i < length; i++) {
|
||||
if (out[i] != ds2404_read_byte(dev)) {
|
||||
printk(KERN_ERR "read invalid data\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
ds2404_reset(dev);
|
||||
ds2404_write_byte(dev, DS2404_COPY_SCRATCHPAD_CMD);
|
||||
ds2404_write_byte(dev, ta01);
|
||||
ds2404_write_byte(dev, ta02);
|
||||
ds2404_write_byte(dev, es);
|
||||
|
||||
gpio_direction_input(ds2404_gpio[DS2404_DQ].gpio);
|
||||
while (gpio_get_value(ds2404_gpio[DS2404_DQ].gpio))
|
||||
;
|
||||
}
|
||||
|
||||
static void ds2404_enable_osc(struct device *dev)
|
||||
{
|
||||
u8 in[1] = { 0x10 }; /* enable oscillator */
|
||||
ds2404_write_memory(dev, 0x201, 1, in);
|
||||
}
|
||||
|
||||
static int ds2404_read_time(struct device *dev, struct rtc_time *dt)
|
||||
{
|
||||
unsigned long time = 0;
|
||||
|
||||
ds2404_read_memory(dev, 0x203, 4, (u8 *)&time);
|
||||
time = le32_to_cpu(time);
|
||||
|
||||
rtc_time_to_tm(time, dt);
|
||||
return rtc_valid_tm(dt);
|
||||
}
|
||||
|
||||
static int ds2404_set_mmss(struct device *dev, unsigned long secs)
|
||||
{
|
||||
u32 time = cpu_to_le32(secs);
|
||||
ds2404_write_memory(dev, 0x203, 4, (u8 *)&time);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops ds2404_rtc_ops = {
|
||||
.read_time = ds2404_read_time,
|
||||
.set_mmss = ds2404_set_mmss,
|
||||
};
|
||||
|
||||
static int rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct ds2404_platform_data *pdata = pdev->dev.platform_data;
|
||||
struct ds2404 *chip;
|
||||
int retval = -EBUSY;
|
||||
|
||||
chip = kzalloc(sizeof(struct ds2404), GFP_KERNEL);
|
||||
if (!chip)
|
||||
return -ENOMEM;
|
||||
|
||||
chip->ops = &ds2404_gpio_ops;
|
||||
|
||||
retval = chip->ops->map_io(chip, pdev, pdata);
|
||||
if (retval)
|
||||
goto err_chip;
|
||||
|
||||
dev_info(&pdev->dev, "using GPIOs RST:%d, CLK:%d, DQ:%d\n",
|
||||
chip->gpio[DS2404_RST].gpio, chip->gpio[DS2404_CLK].gpio,
|
||||
chip->gpio[DS2404_DQ].gpio);
|
||||
|
||||
platform_set_drvdata(pdev, chip);
|
||||
|
||||
chip->rtc = rtc_device_register("ds2404",
|
||||
&pdev->dev, &ds2404_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(chip->rtc)) {
|
||||
retval = PTR_ERR(chip->rtc);
|
||||
goto err_io;
|
||||
}
|
||||
|
||||
ds2404_enable_osc(&pdev->dev);
|
||||
return 0;
|
||||
|
||||
err_io:
|
||||
chip->ops->unmap_io(chip);
|
||||
err_chip:
|
||||
kfree(chip);
|
||||
return retval;
|
||||
}
|
||||
|
||||
static int rtc_remove(struct platform_device *dev)
|
||||
{
|
||||
struct ds2404 *chip = platform_get_drvdata(dev);
|
||||
struct rtc_device *rtc = chip->rtc;
|
||||
|
||||
if (rtc)
|
||||
rtc_device_unregister(rtc);
|
||||
|
||||
chip->ops->unmap_io(chip);
|
||||
kfree(chip);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver rtc_device_driver = {
|
||||
.probe = rtc_probe,
|
||||
.remove = rtc_remove,
|
||||
.driver = {
|
||||
.name = "ds2404",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
};
|
||||
|
||||
static __init int ds2404_init(void)
|
||||
{
|
||||
return platform_driver_register(&rtc_device_driver);
|
||||
}
|
||||
|
||||
static __exit void ds2404_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&rtc_device_driver);
|
||||
}
|
||||
|
||||
module_init(ds2404_init);
|
||||
module_exit(ds2404_exit);
|
||||
|
||||
MODULE_DESCRIPTION("DS2404 RTC");
|
||||
MODULE_AUTHOR("Sven Schnelle");
|
||||
MODULE_LICENSE("GPL");
|
||||
MODULE_ALIAS("platform:ds2404");
|
@@ -49,8 +49,17 @@ static int em3027_get_time(struct device *dev, struct rtc_time *tm)
|
||||
unsigned char buf[7];
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{client->addr, 0, 1, &addr}, /* setup read addr */
|
||||
{client->addr, I2C_M_RD, 7, buf}, /* read time/date */
|
||||
{/* setup read addr */
|
||||
.addr = client->addr,
|
||||
.len = 1,
|
||||
.buf = &addr
|
||||
},
|
||||
{/* read time/date */
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 7,
|
||||
.buf = buf
|
||||
},
|
||||
};
|
||||
|
||||
/* read time/date registers */
|
||||
@@ -76,7 +85,9 @@ static int em3027_set_time(struct device *dev, struct rtc_time *tm)
|
||||
unsigned char buf[8];
|
||||
|
||||
struct i2c_msg msg = {
|
||||
client->addr, 0, 8, buf, /* write time/date */
|
||||
.addr = client->addr,
|
||||
.len = 8,
|
||||
.buf = buf, /* write time/date */
|
||||
};
|
||||
|
||||
buf[0] = EM3027_REG_WATCH_SEC;
|
||||
|
@@ -68,9 +68,17 @@ isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[],
|
||||
{
|
||||
u8 reg_addr[1] = { reg };
|
||||
struct i2c_msg msgs[2] = {
|
||||
{client->addr, 0, sizeof(reg_addr), reg_addr}
|
||||
,
|
||||
{client->addr, I2C_M_RD, len, buf}
|
||||
{
|
||||
.addr = client->addr,
|
||||
.len = sizeof(reg_addr),
|
||||
.buf = reg_addr
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = len,
|
||||
.buf = buf
|
||||
}
|
||||
};
|
||||
int ret;
|
||||
|
||||
@@ -90,7 +98,11 @@ isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[],
|
||||
{
|
||||
u8 i2c_buf[ISL1208_REG_USR2 + 2];
|
||||
struct i2c_msg msgs[1] = {
|
||||
{client->addr, 0, len + 1, i2c_buf}
|
||||
{
|
||||
.addr = client->addr,
|
||||
.len = len + 1,
|
||||
.buf = i2c_buf
|
||||
}
|
||||
};
|
||||
int ret;
|
||||
|
||||
@@ -697,6 +709,7 @@ isl1208_remove(struct i2c_client *client)
|
||||
|
||||
static const struct i2c_device_id isl1208_id[] = {
|
||||
{ "isl1208", 0 },
|
||||
{ "isl1218", 0 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(i2c, isl1208_id);
|
||||
|
@@ -42,7 +42,7 @@ struct jz4740_rtc {
|
||||
|
||||
struct rtc_device *rtc;
|
||||
|
||||
unsigned int irq;
|
||||
int irq;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
@@ -213,163 +213,14 @@ static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
return m41t80_set_datetime(to_i2c_client(dev), tm);
|
||||
}
|
||||
|
||||
static int m41t80_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int rc;
|
||||
|
||||
rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
|
||||
if (rc < 0)
|
||||
goto err;
|
||||
|
||||
if (enabled)
|
||||
rc |= M41T80_ALMON_AFE;
|
||||
else
|
||||
rc &= ~M41T80_ALMON_AFE;
|
||||
|
||||
if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, rc) < 0)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
err:
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
static int m41t80_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 wbuf[1 + M41T80_ALARM_REG_SIZE];
|
||||
u8 *buf = &wbuf[1];
|
||||
u8 *reg = buf - M41T80_REG_ALARM_MON;
|
||||
u8 dt_addr[1] = { M41T80_REG_ALARM_MON };
|
||||
struct i2c_msg msgs_in[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = dt_addr,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = M41T80_ALARM_REG_SIZE,
|
||||
.buf = buf,
|
||||
},
|
||||
};
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = 1 + M41T80_ALARM_REG_SIZE,
|
||||
.buf = wbuf,
|
||||
},
|
||||
};
|
||||
|
||||
if (i2c_transfer(client->adapter, msgs_in, 2) < 0) {
|
||||
dev_err(&client->dev, "read error\n");
|
||||
return -EIO;
|
||||
}
|
||||
reg[M41T80_REG_ALARM_MON] &= ~(0x1f | M41T80_ALMON_AFE);
|
||||
reg[M41T80_REG_ALARM_DAY] = 0;
|
||||
reg[M41T80_REG_ALARM_HOUR] &= ~(0x3f | 0x80);
|
||||
reg[M41T80_REG_ALARM_MIN] = 0;
|
||||
reg[M41T80_REG_ALARM_SEC] = 0;
|
||||
|
||||
wbuf[0] = M41T80_REG_ALARM_MON; /* offset into rtc's regs */
|
||||
reg[M41T80_REG_ALARM_SEC] |= t->time.tm_sec >= 0 ?
|
||||
bin2bcd(t->time.tm_sec) : 0x80;
|
||||
reg[M41T80_REG_ALARM_MIN] |= t->time.tm_min >= 0 ?
|
||||
bin2bcd(t->time.tm_min) : 0x80;
|
||||
reg[M41T80_REG_ALARM_HOUR] |= t->time.tm_hour >= 0 ?
|
||||
bin2bcd(t->time.tm_hour) : 0x80;
|
||||
reg[M41T80_REG_ALARM_DAY] |= t->time.tm_mday >= 0 ?
|
||||
bin2bcd(t->time.tm_mday) : 0x80;
|
||||
if (t->time.tm_mon >= 0)
|
||||
reg[M41T80_REG_ALARM_MON] |= bin2bcd(t->time.tm_mon + 1);
|
||||
else
|
||||
reg[M41T80_REG_ALARM_DAY] |= 0x40;
|
||||
|
||||
if (i2c_transfer(client->adapter, msgs, 1) != 1) {
|
||||
dev_err(&client->dev, "write error\n");
|
||||
return -EIO;
|
||||
}
|
||||
|
||||
if (t->enabled) {
|
||||
reg[M41T80_REG_ALARM_MON] |= M41T80_ALMON_AFE;
|
||||
if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
|
||||
reg[M41T80_REG_ALARM_MON]) < 0) {
|
||||
dev_err(&client->dev, "write error\n");
|
||||
return -EIO;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int m41t80_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
u8 buf[M41T80_ALARM_REG_SIZE + 1]; /* all alarm regs and flags */
|
||||
u8 dt_addr[1] = { M41T80_REG_ALARM_MON };
|
||||
u8 *reg = buf - M41T80_REG_ALARM_MON;
|
||||
struct i2c_msg msgs[] = {
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = 0,
|
||||
.len = 1,
|
||||
.buf = dt_addr,
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = M41T80_ALARM_REG_SIZE + 1,
|
||||
.buf = buf,
|
||||
},
|
||||
};
|
||||
|
||||
if (i2c_transfer(client->adapter, msgs, 2) < 0) {
|
||||
dev_err(&client->dev, "read error\n");
|
||||
return -EIO;
|
||||
}
|
||||
t->time.tm_sec = -1;
|
||||
t->time.tm_min = -1;
|
||||
t->time.tm_hour = -1;
|
||||
t->time.tm_mday = -1;
|
||||
t->time.tm_mon = -1;
|
||||
if (!(reg[M41T80_REG_ALARM_SEC] & 0x80))
|
||||
t->time.tm_sec = bcd2bin(reg[M41T80_REG_ALARM_SEC] & 0x7f);
|
||||
if (!(reg[M41T80_REG_ALARM_MIN] & 0x80))
|
||||
t->time.tm_min = bcd2bin(reg[M41T80_REG_ALARM_MIN] & 0x7f);
|
||||
if (!(reg[M41T80_REG_ALARM_HOUR] & 0x80))
|
||||
t->time.tm_hour = bcd2bin(reg[M41T80_REG_ALARM_HOUR] & 0x3f);
|
||||
if (!(reg[M41T80_REG_ALARM_DAY] & 0x80))
|
||||
t->time.tm_mday = bcd2bin(reg[M41T80_REG_ALARM_DAY] & 0x3f);
|
||||
if (!(reg[M41T80_REG_ALARM_DAY] & 0x40))
|
||||
t->time.tm_mon = bcd2bin(reg[M41T80_REG_ALARM_MON] & 0x1f) - 1;
|
||||
t->time.tm_year = -1;
|
||||
t->time.tm_wday = -1;
|
||||
t->time.tm_yday = -1;
|
||||
t->time.tm_isdst = -1;
|
||||
t->enabled = !!(reg[M41T80_REG_ALARM_MON] & M41T80_ALMON_AFE);
|
||||
t->pending = !!(reg[M41T80_REG_FLAGS] & M41T80_FLAGS_AF);
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* XXX - m41t80 alarm functionality is reported broken.
|
||||
* until it is fixed, don't register alarm functions.
|
||||
*/
|
||||
static struct rtc_class_ops m41t80_rtc_ops = {
|
||||
.read_time = m41t80_rtc_read_time,
|
||||
.set_time = m41t80_rtc_set_time,
|
||||
/*
|
||||
* XXX - m41t80 alarm functionality is reported broken.
|
||||
* until it is fixed, don't register alarm functions.
|
||||
*
|
||||
.read_alarm = m41t80_rtc_read_alarm,
|
||||
.set_alarm = m41t80_rtc_set_alarm,
|
||||
*/
|
||||
.proc = m41t80_rtc_proc,
|
||||
/*
|
||||
* See above comment on broken alarm
|
||||
*
|
||||
.alarm_irq_enable = m41t80_rtc_alarm_irq_enable,
|
||||
*/
|
||||
};
|
||||
|
||||
#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
|
||||
|
244
drivers/rtc/rtc-max8907.c
Normal file
244
drivers/rtc/rtc-max8907.c
Normal file
@@ -0,0 +1,244 @@
|
||||
/*
|
||||
* RTC driver for Maxim MAX8907
|
||||
*
|
||||
* Copyright (c) 2011-2012, NVIDIA Corporation.
|
||||
*
|
||||
* Based on drivers/rtc/rtc-max8925.c,
|
||||
* Copyright (C) 2009-2010 Marvell International 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.
|
||||
*/
|
||||
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/max8907.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
|
||||
enum {
|
||||
RTC_SEC = 0,
|
||||
RTC_MIN,
|
||||
RTC_HOUR,
|
||||
RTC_WEEKDAY,
|
||||
RTC_DATE,
|
||||
RTC_MONTH,
|
||||
RTC_YEAR1,
|
||||
RTC_YEAR2,
|
||||
};
|
||||
|
||||
#define TIME_NUM 8
|
||||
#define ALARM_1SEC (1 << 7)
|
||||
#define HOUR_12 (1 << 7)
|
||||
#define HOUR_AM_PM (1 << 5)
|
||||
#define ALARM0_IRQ (1 << 3)
|
||||
#define ALARM1_IRQ (1 << 2)
|
||||
#define ALARM0_STATUS (1 << 2)
|
||||
#define ALARM1_STATUS (1 << 1)
|
||||
|
||||
struct max8907_rtc {
|
||||
struct max8907 *max8907;
|
||||
struct regmap *regmap;
|
||||
struct rtc_device *rtc_dev;
|
||||
int irq;
|
||||
};
|
||||
|
||||
static irqreturn_t max8907_irq_handler(int irq, void *data)
|
||||
{
|
||||
struct max8907_rtc *rtc = data;
|
||||
|
||||
regmap_update_bits(rtc->regmap, MAX8907_REG_ALARM0_CNTL, 0x7f, 0);
|
||||
|
||||
rtc_update_irq(rtc->rtc_dev, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static void regs_to_tm(u8 *regs, struct rtc_time *tm)
|
||||
{
|
||||
tm->tm_year = bcd2bin(regs[RTC_YEAR2]) * 100 +
|
||||
bcd2bin(regs[RTC_YEAR1]) - 1900;
|
||||
tm->tm_mon = bcd2bin(regs[RTC_MONTH] & 0x1f) - 1;
|
||||
tm->tm_mday = bcd2bin(regs[RTC_DATE] & 0x3f);
|
||||
tm->tm_wday = (regs[RTC_WEEKDAY] & 0x07) - 1;
|
||||
if (regs[RTC_HOUR] & HOUR_12) {
|
||||
tm->tm_hour = bcd2bin(regs[RTC_HOUR] & 0x01f);
|
||||
if (tm->tm_hour == 12)
|
||||
tm->tm_hour = 0;
|
||||
if (regs[RTC_HOUR] & HOUR_AM_PM)
|
||||
tm->tm_hour += 12;
|
||||
} else {
|
||||
tm->tm_hour = bcd2bin(regs[RTC_HOUR] & 0x03f);
|
||||
}
|
||||
tm->tm_min = bcd2bin(regs[RTC_MIN] & 0x7f);
|
||||
tm->tm_sec = bcd2bin(regs[RTC_SEC] & 0x7f);
|
||||
}
|
||||
|
||||
static void tm_to_regs(struct rtc_time *tm, u8 *regs)
|
||||
{
|
||||
u8 high, low;
|
||||
|
||||
high = (tm->tm_year + 1900) / 100;
|
||||
low = tm->tm_year % 100;
|
||||
regs[RTC_YEAR2] = bin2bcd(high);
|
||||
regs[RTC_YEAR1] = bin2bcd(low);
|
||||
regs[RTC_MONTH] = bin2bcd(tm->tm_mon + 1);
|
||||
regs[RTC_DATE] = bin2bcd(tm->tm_mday);
|
||||
regs[RTC_WEEKDAY] = tm->tm_wday + 1;
|
||||
regs[RTC_HOUR] = bin2bcd(tm->tm_hour);
|
||||
regs[RTC_MIN] = bin2bcd(tm->tm_min);
|
||||
regs[RTC_SEC] = bin2bcd(tm->tm_sec);
|
||||
}
|
||||
|
||||
static int max8907_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct max8907_rtc *rtc = dev_get_drvdata(dev);
|
||||
u8 regs[TIME_NUM];
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(rtc->regmap, MAX8907_REG_RTC_SEC, regs,
|
||||
TIME_NUM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
regs_to_tm(regs, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8907_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct max8907_rtc *rtc = dev_get_drvdata(dev);
|
||||
u8 regs[TIME_NUM];
|
||||
|
||||
tm_to_regs(tm, regs);
|
||||
|
||||
return regmap_bulk_write(rtc->regmap, MAX8907_REG_RTC_SEC, regs,
|
||||
TIME_NUM);
|
||||
}
|
||||
|
||||
static int max8907_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct max8907_rtc *rtc = dev_get_drvdata(dev);
|
||||
u8 regs[TIME_NUM];
|
||||
unsigned int val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(rtc->regmap, MAX8907_REG_ALARM0_SEC, regs,
|
||||
TIME_NUM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
regs_to_tm(regs, &alrm->time);
|
||||
|
||||
ret = regmap_read(rtc->regmap, MAX8907_REG_ALARM0_CNTL, &val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
alrm->enabled = !!(val & 0x7f);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int max8907_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct max8907_rtc *rtc = dev_get_drvdata(dev);
|
||||
u8 regs[TIME_NUM];
|
||||
int ret;
|
||||
|
||||
tm_to_regs(&alrm->time, regs);
|
||||
|
||||
/* Disable alarm while we update the target time */
|
||||
ret = regmap_update_bits(rtc->regmap, MAX8907_REG_ALARM0_CNTL, 0x7f, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_bulk_write(rtc->regmap, MAX8907_REG_ALARM0_SEC, regs,
|
||||
TIME_NUM);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (alrm->enabled)
|
||||
ret = regmap_update_bits(rtc->regmap, MAX8907_REG_ALARM0_CNTL,
|
||||
0x7f, 0x7f);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops max8907_rtc_ops = {
|
||||
.read_time = max8907_rtc_read_time,
|
||||
.set_time = max8907_rtc_set_time,
|
||||
.read_alarm = max8907_rtc_read_alarm,
|
||||
.set_alarm = max8907_rtc_set_alarm,
|
||||
};
|
||||
|
||||
static int __devinit max8907_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct max8907 *max8907 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct max8907_rtc *rtc;
|
||||
int ret;
|
||||
|
||||
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
|
||||
if (!rtc)
|
||||
return -ENOMEM;
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
rtc->max8907 = max8907;
|
||||
rtc->regmap = max8907->regmap_rtc;
|
||||
|
||||
rtc->rtc_dev = rtc_device_register("max8907-rtc", &pdev->dev,
|
||||
&max8907_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc->rtc_dev)) {
|
||||
ret = PTR_ERR(rtc->rtc_dev);
|
||||
dev_err(&pdev->dev, "Failed to register RTC device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rtc->irq = regmap_irq_get_virq(max8907->irqc_rtc,
|
||||
MAX8907_IRQ_RTC_ALARM0);
|
||||
if (rtc->irq < 0) {
|
||||
ret = rtc->irq;
|
||||
goto err_unregister;
|
||||
}
|
||||
|
||||
ret = request_threaded_irq(rtc->irq, NULL, max8907_irq_handler,
|
||||
IRQF_ONESHOT, "max8907-alarm0", rtc);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "Failed to request IRQ%d: %d\n",
|
||||
rtc->irq, ret);
|
||||
goto err_unregister;
|
||||
}
|
||||
|
||||
return 0;
|
||||
|
||||
err_unregister:
|
||||
rtc_device_unregister(rtc->rtc_dev);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __devexit max8907_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct max8907_rtc *rtc = platform_get_drvdata(pdev);
|
||||
|
||||
free_irq(rtc->irq, rtc);
|
||||
rtc_device_unregister(rtc->rtc_dev);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct platform_driver max8907_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "max8907-rtc",
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.probe = max8907_rtc_probe,
|
||||
.remove = __devexit_p(max8907_rtc_remove),
|
||||
};
|
||||
module_platform_driver(max8907_rtc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("Maxim MAX8907 RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -343,7 +343,7 @@ static struct rtc_class_ops mxc_rtc_ops = {
|
||||
.alarm_irq_enable = mxc_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int __init mxc_rtc_probe(struct platform_device *pdev)
|
||||
static int __devinit mxc_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct rtc_device *rtc;
|
||||
@@ -367,14 +367,14 @@ static int __init mxc_rtc_probe(struct platform_device *pdev)
|
||||
pdata->ioaddr = devm_ioremap(&pdev->dev, res->start,
|
||||
resource_size(res));
|
||||
|
||||
pdata->clk = clk_get(&pdev->dev, "rtc");
|
||||
pdata->clk = devm_clk_get(&pdev->dev, NULL);
|
||||
if (IS_ERR(pdata->clk)) {
|
||||
dev_err(&pdev->dev, "unable to get clock!\n");
|
||||
ret = PTR_ERR(pdata->clk);
|
||||
goto exit_free_pdata;
|
||||
}
|
||||
|
||||
clk_enable(pdata->clk);
|
||||
clk_prepare_enable(pdata->clk);
|
||||
rate = clk_get_rate(pdata->clk);
|
||||
|
||||
if (rate == 32768)
|
||||
@@ -426,22 +426,20 @@ static int __init mxc_rtc_probe(struct platform_device *pdev)
|
||||
exit_clr_drvdata:
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
exit_put_clk:
|
||||
clk_disable(pdata->clk);
|
||||
clk_put(pdata->clk);
|
||||
clk_disable_unprepare(pdata->clk);
|
||||
|
||||
exit_free_pdata:
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int __exit mxc_rtc_remove(struct platform_device *pdev)
|
||||
static int __devexit mxc_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
|
||||
rtc_device_unregister(pdata->rtc);
|
||||
|
||||
clk_disable(pdata->clk);
|
||||
clk_put(pdata->clk);
|
||||
clk_disable_unprepare(pdata->clk);
|
||||
platform_set_drvdata(pdev, NULL);
|
||||
|
||||
return 0;
|
||||
@@ -482,21 +480,11 @@ static struct platform_driver mxc_rtc_driver = {
|
||||
#endif
|
||||
.owner = THIS_MODULE,
|
||||
},
|
||||
.remove = __exit_p(mxc_rtc_remove),
|
||||
.probe = mxc_rtc_probe,
|
||||
.remove = __devexit_p(mxc_rtc_remove),
|
||||
};
|
||||
|
||||
static int __init mxc_rtc_init(void)
|
||||
{
|
||||
return platform_driver_probe(&mxc_rtc_driver, mxc_rtc_probe);
|
||||
}
|
||||
|
||||
static void __exit mxc_rtc_exit(void)
|
||||
{
|
||||
platform_driver_unregister(&mxc_rtc_driver);
|
||||
}
|
||||
|
||||
module_init(mxc_rtc_init);
|
||||
module_exit(mxc_rtc_exit);
|
||||
module_platform_driver(mxc_rtc_driver)
|
||||
|
||||
MODULE_AUTHOR("Daniel Mack <daniel@caiaq.de>");
|
||||
MODULE_DESCRIPTION("RTC driver for Freescale MXC");
|
||||
|
@@ -78,8 +78,17 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
unsigned char buf[13] = { PCF8563_REG_ST1 };
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{ client->addr, 0, 1, buf }, /* setup read ptr */
|
||||
{ client->addr, I2C_M_RD, 13, buf }, /* read status + date */
|
||||
{/* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 1,
|
||||
.buf = buf
|
||||
},
|
||||
{/* read status + date */
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 13,
|
||||
.buf = buf
|
||||
},
|
||||
};
|
||||
|
||||
/* read registers */
|
||||
|
@@ -18,6 +18,26 @@
|
||||
|
||||
#include "rtc-core.h"
|
||||
|
||||
#define NAME_SIZE 10
|
||||
|
||||
#if defined(CONFIG_RTC_HCTOSYS_DEVICE)
|
||||
static bool is_rtc_hctosys(struct rtc_device *rtc)
|
||||
{
|
||||
int size;
|
||||
char name[NAME_SIZE];
|
||||
|
||||
size = scnprintf(name, NAME_SIZE, "rtc%d", rtc->id);
|
||||
if (size > NAME_SIZE)
|
||||
return false;
|
||||
|
||||
return !strncmp(name, CONFIG_RTC_HCTOSYS_DEVICE, NAME_SIZE);
|
||||
}
|
||||
#else
|
||||
static bool is_rtc_hctosys(struct rtc_device *rtc)
|
||||
{
|
||||
return (rtc->id == 0);
|
||||
}
|
||||
#endif
|
||||
|
||||
static int rtc_proc_show(struct seq_file *seq, void *offset)
|
||||
{
|
||||
@@ -117,12 +137,12 @@ static const struct file_operations rtc_proc_fops = {
|
||||
|
||||
void rtc_proc_add_device(struct rtc_device *rtc)
|
||||
{
|
||||
if (rtc->id == 0)
|
||||
if (is_rtc_hctosys(rtc))
|
||||
proc_create_data("driver/rtc", 0, NULL, &rtc_proc_fops, rtc);
|
||||
}
|
||||
|
||||
void rtc_proc_del_device(struct rtc_device *rtc)
|
||||
{
|
||||
if (rtc->id == 0)
|
||||
if (is_rtc_hctosys(rtc))
|
||||
remove_proc_entry("driver/rtc", NULL);
|
||||
}
|
||||
|
331
drivers/rtc/rtc-rc5t583.c
Normal file
331
drivers/rtc/rtc-rc5t583.c
Normal file
@@ -0,0 +1,331 @@
|
||||
/*
|
||||
* rtc-rc5t583.c -- RICOH RC5T583 Real Time Clock
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
* Author: Venu Byravarasu <vbyravarasu@nvidia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or modify it
|
||||
* under the terms and conditions of the GNU General Public License,
|
||||
* version 2, as published by the Free Software Foundation.
|
||||
*
|
||||
* This program is distributed in the hope it will be useful, but WITHOUT
|
||||
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
|
||||
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
|
||||
* more details.
|
||||
*
|
||||
* You should have received a copy of the GNU General Public License
|
||||
* along with this program. If not, see <http://www.gnu.org/licenses/>. */
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/rc5t583.h>
|
||||
|
||||
struct rc5t583_rtc {
|
||||
struct rtc_device *rtc;
|
||||
/* To store the list of enabled interrupts, during system suspend */
|
||||
u32 irqen;
|
||||
};
|
||||
|
||||
/* Total number of RTC registers needed to set time*/
|
||||
#define NUM_TIME_REGS (RC5T583_RTC_YEAR - RC5T583_RTC_SEC + 1)
|
||||
|
||||
/* Total number of RTC registers needed to set Y-Alarm*/
|
||||
#define NUM_YAL_REGS (RC5T583_RTC_AY_YEAR - RC5T583_RTC_AY_MIN + 1)
|
||||
|
||||
/* Set Y-Alarm interrupt */
|
||||
#define SET_YAL BIT(5)
|
||||
|
||||
/* Get Y-Alarm interrupt status*/
|
||||
#define GET_YAL_STATUS BIT(3)
|
||||
|
||||
static int rc5t583_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
|
||||
{
|
||||
struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
|
||||
u8 val;
|
||||
|
||||
/* Set Y-Alarm, based on 'enabled' */
|
||||
val = enabled ? SET_YAL : 0;
|
||||
|
||||
return regmap_update_bits(rc5t583->regmap, RC5T583_RTC_CTL1, SET_YAL,
|
||||
val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets current rc5t583 RTC time and date parameters.
|
||||
*
|
||||
* The RTC's time/alarm representation is not what gmtime(3) requires
|
||||
* Linux to use:
|
||||
*
|
||||
* - Months are 1..12 vs Linux 0-11
|
||||
* - Years are 0..99 vs Linux 1900..N (we assume 21st century)
|
||||
*/
|
||||
static int rc5t583_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
|
||||
u8 rtc_data[NUM_TIME_REGS];
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data,
|
||||
NUM_TIME_REGS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "RTC read time failed with err:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tm->tm_sec = bcd2bin(rtc_data[0]);
|
||||
tm->tm_min = bcd2bin(rtc_data[1]);
|
||||
tm->tm_hour = bcd2bin(rtc_data[2]);
|
||||
tm->tm_wday = bcd2bin(rtc_data[3]);
|
||||
tm->tm_mday = bcd2bin(rtc_data[4]);
|
||||
tm->tm_mon = bcd2bin(rtc_data[5]) - 1;
|
||||
tm->tm_year = bcd2bin(rtc_data[6]) + 100;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rc5t583_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
|
||||
unsigned char rtc_data[NUM_TIME_REGS];
|
||||
int ret;
|
||||
|
||||
rtc_data[0] = bin2bcd(tm->tm_sec);
|
||||
rtc_data[1] = bin2bcd(tm->tm_min);
|
||||
rtc_data[2] = bin2bcd(tm->tm_hour);
|
||||
rtc_data[3] = bin2bcd(tm->tm_wday);
|
||||
rtc_data[4] = bin2bcd(tm->tm_mday);
|
||||
rtc_data[5] = bin2bcd(tm->tm_mon + 1);
|
||||
rtc_data[6] = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_SEC, rtc_data,
|
||||
NUM_TIME_REGS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "RTC set time failed with error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rc5t583_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
|
||||
unsigned char alarm_data[NUM_YAL_REGS];
|
||||
u32 interrupt_enable;
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data,
|
||||
NUM_YAL_REGS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "rtc_read_alarm error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
alm->time.tm_min = bcd2bin(alarm_data[0]);
|
||||
alm->time.tm_hour = bcd2bin(alarm_data[1]);
|
||||
alm->time.tm_mday = bcd2bin(alarm_data[2]);
|
||||
alm->time.tm_mon = bcd2bin(alarm_data[3]) - 1;
|
||||
alm->time.tm_year = bcd2bin(alarm_data[4]) + 100;
|
||||
|
||||
ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1, &interrupt_enable);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* check if YALE is set */
|
||||
if (interrupt_enable & SET_YAL)
|
||||
alm->enabled = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rc5t583_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
|
||||
unsigned char alarm_data[NUM_YAL_REGS];
|
||||
int ret;
|
||||
|
||||
ret = rc5t583_rtc_alarm_irq_enable(dev, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alarm_data[0] = bin2bcd(alm->time.tm_min);
|
||||
alarm_data[1] = bin2bcd(alm->time.tm_hour);
|
||||
alarm_data[2] = bin2bcd(alm->time.tm_mday);
|
||||
alarm_data[3] = bin2bcd(alm->time.tm_mon + 1);
|
||||
alarm_data[4] = bin2bcd(alm->time.tm_year - 100);
|
||||
|
||||
ret = regmap_bulk_write(rc5t583->regmap, RC5T583_RTC_AY_MIN, alarm_data,
|
||||
NUM_YAL_REGS);
|
||||
if (ret) {
|
||||
dev_err(dev, "rtc_set_alarm error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (alm->enabled)
|
||||
ret = rc5t583_rtc_alarm_irq_enable(dev, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t rc5t583_rtc_interrupt(int irq, void *rtc)
|
||||
{
|
||||
struct device *dev = rtc;
|
||||
struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
|
||||
struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
|
||||
unsigned long events = 0;
|
||||
int ret;
|
||||
u32 rtc_reg;
|
||||
|
||||
ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL2, &rtc_reg);
|
||||
if (ret < 0)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (rtc_reg & GET_YAL_STATUS) {
|
||||
events = RTC_IRQF | RTC_AF;
|
||||
/* clear pending Y-alarm interrupt bit */
|
||||
rtc_reg &= ~GET_YAL_STATUS;
|
||||
}
|
||||
|
||||
ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, rtc_reg);
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Notify RTC core on event */
|
||||
rtc_update_irq(rc5t583_rtc->rtc, 1, events);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops rc5t583_rtc_ops = {
|
||||
.read_time = rc5t583_rtc_read_time,
|
||||
.set_time = rc5t583_rtc_set_time,
|
||||
.read_alarm = rc5t583_rtc_read_alarm,
|
||||
.set_alarm = rc5t583_rtc_set_alarm,
|
||||
.alarm_irq_enable = rc5t583_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int __devinit rc5t583_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct rc5t583 *rc5t583 = dev_get_drvdata(pdev->dev.parent);
|
||||
struct rc5t583_rtc *ricoh_rtc;
|
||||
struct rc5t583_platform_data *pmic_plat_data;
|
||||
int ret;
|
||||
int irq;
|
||||
|
||||
ricoh_rtc = devm_kzalloc(&pdev->dev, sizeof(struct rc5t583_rtc),
|
||||
GFP_KERNEL);
|
||||
if (!ricoh_rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, ricoh_rtc);
|
||||
|
||||
/* Clear pending interrupts */
|
||||
ret = regmap_write(rc5t583->regmap, RC5T583_RTC_CTL2, 0);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* clear RTC Adjust register */
|
||||
ret = regmap_write(rc5t583->regmap, RC5T583_RTC_ADJ, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "unable to program rtc_adjust reg\n");
|
||||
return -EBUSY;
|
||||
}
|
||||
|
||||
pmic_plat_data = dev_get_platdata(rc5t583->dev);
|
||||
irq = pmic_plat_data->irq_base;
|
||||
if (irq <= 0) {
|
||||
dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n",
|
||||
irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
irq += RC5T583_IRQ_YALE;
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
rc5t583_rtc_interrupt, IRQF_TRIGGER_LOW,
|
||||
"rtc-rc5t583", &pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "IRQ is not free.\n");
|
||||
return ret;
|
||||
}
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
ricoh_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
|
||||
&rc5t583_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(ricoh_rtc->rtc)) {
|
||||
ret = PTR_ERR(ricoh_rtc->rtc);
|
||||
dev_err(&pdev->dev, "RTC device register: err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable rc5t583 RTC interrupts.
|
||||
* Sets status flag to free.
|
||||
*/
|
||||
static int __devexit rc5t583_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(&pdev->dev);
|
||||
|
||||
rc5t583_rtc_alarm_irq_enable(&rc5t583_rtc->rtc->dev, 0);
|
||||
|
||||
rtc_device_unregister(rc5t583_rtc->rtc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int rc5t583_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
|
||||
struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
/* Store current list of enabled interrupts*/
|
||||
ret = regmap_read(rc5t583->regmap, RC5T583_RTC_CTL1,
|
||||
&rc5t583_rtc->irqen);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int rc5t583_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct rc5t583 *rc5t583 = dev_get_drvdata(dev->parent);
|
||||
struct rc5t583_rtc *rc5t583_rtc = dev_get_drvdata(dev);
|
||||
|
||||
/* Restore list of enabled interrupts before suspend */
|
||||
return regmap_write(rc5t583->regmap, RC5T583_RTC_CTL1,
|
||||
rc5t583_rtc->irqen);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops rc5t583_rtc_pm_ops = {
|
||||
.suspend = rc5t583_rtc_suspend,
|
||||
.resume = rc5t583_rtc_resume,
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS (&rc5t583_rtc_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver rc5t583_rtc_driver = {
|
||||
.probe = rc5t583_rtc_probe,
|
||||
.remove = __devexit_p(rc5t583_rtc_remove),
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "rtc-rc5t583",
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(rc5t583_rtc_driver);
|
||||
MODULE_ALIAS("platform:rtc-rc5t583");
|
||||
MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
|
||||
MODULE_LICENSE("GPL v2");
|
@@ -104,7 +104,12 @@ static int rs5c_get_regs(struct rs5c372 *rs5c)
|
||||
{
|
||||
struct i2c_client *client = rs5c->client;
|
||||
struct i2c_msg msgs[] = {
|
||||
{ client->addr, I2C_M_RD, sizeof rs5c->buf, rs5c->buf },
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = sizeof(rs5c->buf),
|
||||
.buf = rs5c->buf
|
||||
},
|
||||
};
|
||||
|
||||
/* This implements the third reading method from the datasheet, using
|
||||
|
@@ -19,6 +19,8 @@
|
||||
#define S35390A_CMD_STATUS1 0
|
||||
#define S35390A_CMD_STATUS2 1
|
||||
#define S35390A_CMD_TIME1 2
|
||||
#define S35390A_CMD_TIME2 3
|
||||
#define S35390A_CMD_INT2_REG1 5
|
||||
|
||||
#define S35390A_BYTE_YEAR 0
|
||||
#define S35390A_BYTE_MONTH 1
|
||||
@@ -28,12 +30,23 @@
|
||||
#define S35390A_BYTE_MINS 5
|
||||
#define S35390A_BYTE_SECS 6
|
||||
|
||||
#define S35390A_ALRM_BYTE_WDAY 0
|
||||
#define S35390A_ALRM_BYTE_HOURS 1
|
||||
#define S35390A_ALRM_BYTE_MINS 2
|
||||
|
||||
#define S35390A_FLAG_POC 0x01
|
||||
#define S35390A_FLAG_BLD 0x02
|
||||
#define S35390A_FLAG_24H 0x40
|
||||
#define S35390A_FLAG_RESET 0x80
|
||||
#define S35390A_FLAG_TEST 0x01
|
||||
|
||||
#define S35390A_INT2_MODE_MASK 0xF0
|
||||
|
||||
#define S35390A_INT2_MODE_NOINTR 0x00
|
||||
#define S35390A_INT2_MODE_FREQ 0x10
|
||||
#define S35390A_INT2_MODE_ALARM 0x40
|
||||
#define S35390A_INT2_MODE_PMIN_EDG 0x20
|
||||
|
||||
static const struct i2c_device_id s35390a_id[] = {
|
||||
{ "s35390a", 0 },
|
||||
{ }
|
||||
@@ -50,7 +63,11 @@ static int s35390a_set_reg(struct s35390a *s35390a, int reg, char *buf, int len)
|
||||
{
|
||||
struct i2c_client *client = s35390a->client[reg];
|
||||
struct i2c_msg msg[] = {
|
||||
{ client->addr, 0, len, buf },
|
||||
{
|
||||
.addr = client->addr,
|
||||
.len = len,
|
||||
.buf = buf
|
||||
},
|
||||
};
|
||||
|
||||
if ((i2c_transfer(client->adapter, msg, 1)) != 1)
|
||||
@@ -63,7 +80,12 @@ static int s35390a_get_reg(struct s35390a *s35390a, int reg, char *buf, int len)
|
||||
{
|
||||
struct i2c_client *client = s35390a->client[reg];
|
||||
struct i2c_msg msg[] = {
|
||||
{ client->addr, I2C_M_RD, len, buf },
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = len,
|
||||
.buf = buf
|
||||
},
|
||||
};
|
||||
|
||||
if ((i2c_transfer(client->adapter, msg, 1)) != 1)
|
||||
@@ -184,6 +206,104 @@ static int s35390a_get_datetime(struct i2c_client *client, struct rtc_time *tm)
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int s35390a_set_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct s35390a *s35390a = i2c_get_clientdata(client);
|
||||
char buf[3], sts = 0;
|
||||
int err, i;
|
||||
|
||||
dev_dbg(&client->dev, "%s: alm is secs=%d, mins=%d, hours=%d mday=%d, "\
|
||||
"mon=%d, year=%d, wday=%d\n", __func__, alm->time.tm_sec,
|
||||
alm->time.tm_min, alm->time.tm_hour, alm->time.tm_mday,
|
||||
alm->time.tm_mon, alm->time.tm_year, alm->time.tm_wday);
|
||||
|
||||
/* disable interrupt */
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* clear pending interrupt, if any */
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS1, &sts, sizeof(sts));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (alm->enabled)
|
||||
sts = S35390A_INT2_MODE_ALARM;
|
||||
else
|
||||
sts = S35390A_INT2_MODE_NOINTR;
|
||||
|
||||
/* This chip expects the bits of each byte to be in reverse order */
|
||||
sts = bitrev8(sts);
|
||||
|
||||
/* set interupt mode*/
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (alm->time.tm_wday != -1)
|
||||
buf[S35390A_ALRM_BYTE_WDAY] = bin2bcd(alm->time.tm_wday) | 0x80;
|
||||
|
||||
buf[S35390A_ALRM_BYTE_HOURS] = s35390a_hr2reg(s35390a,
|
||||
alm->time.tm_hour) | 0x80;
|
||||
buf[S35390A_ALRM_BYTE_MINS] = bin2bcd(alm->time.tm_min) | 0x80;
|
||||
|
||||
if (alm->time.tm_hour >= 12)
|
||||
buf[S35390A_ALRM_BYTE_HOURS] |= 0x40;
|
||||
|
||||
for (i = 0; i < 3; ++i)
|
||||
buf[i] = bitrev8(buf[i]);
|
||||
|
||||
err = s35390a_set_reg(s35390a, S35390A_CMD_INT2_REG1, buf,
|
||||
sizeof(buf));
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static int s35390a_read_alarm(struct i2c_client *client, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct s35390a *s35390a = i2c_get_clientdata(client);
|
||||
char buf[3], sts;
|
||||
int i, err;
|
||||
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_STATUS2, &sts, sizeof(sts));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
if (bitrev8(sts) != S35390A_INT2_MODE_ALARM)
|
||||
return -EINVAL;
|
||||
|
||||
err = s35390a_get_reg(s35390a, S35390A_CMD_INT2_REG1, buf, sizeof(buf));
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* This chip returns the bits of each byte in reverse order */
|
||||
for (i = 0; i < 3; ++i) {
|
||||
buf[i] = bitrev8(buf[i]);
|
||||
buf[i] &= ~0x80;
|
||||
}
|
||||
|
||||
alm->time.tm_wday = bcd2bin(buf[S35390A_ALRM_BYTE_WDAY]);
|
||||
alm->time.tm_hour = s35390a_reg2hr(s35390a,
|
||||
buf[S35390A_ALRM_BYTE_HOURS]);
|
||||
alm->time.tm_min = bcd2bin(buf[S35390A_ALRM_BYTE_MINS]);
|
||||
|
||||
dev_dbg(&client->dev, "%s: alm is mins=%d, hours=%d, wday=%d\n",
|
||||
__func__, alm->time.tm_min, alm->time.tm_hour,
|
||||
alm->time.tm_wday);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int s35390a_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
return s35390a_read_alarm(to_i2c_client(dev), alm);
|
||||
}
|
||||
|
||||
static int s35390a_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
return s35390a_set_alarm(to_i2c_client(dev), alm);
|
||||
}
|
||||
|
||||
static int s35390a_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
return s35390a_get_datetime(to_i2c_client(dev), tm);
|
||||
@@ -197,6 +317,9 @@ static int s35390a_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
static const struct rtc_class_ops s35390a_rtc_ops = {
|
||||
.read_time = s35390a_rtc_read_time,
|
||||
.set_time = s35390a_rtc_set_time,
|
||||
.set_alarm = s35390a_rtc_set_alarm,
|
||||
.read_alarm = s35390a_rtc_read_alarm,
|
||||
|
||||
};
|
||||
|
||||
static struct i2c_driver s35390a_driver;
|
||||
@@ -261,6 +384,8 @@ static int s35390a_probe(struct i2c_client *client,
|
||||
if (s35390a_get_datetime(client, &tm) < 0)
|
||||
dev_warn(&client->dev, "clock needs to be set\n");
|
||||
|
||||
device_set_wakeup_capable(&client->dev, 1);
|
||||
|
||||
s35390a->rtc = rtc_device_register(s35390a_driver.driver.name,
|
||||
&client->dev, &s35390a_rtc_ops, THIS_MODULE);
|
||||
|
||||
|
@@ -476,13 +476,13 @@ static int __devinit s3c_rtc_probe(struct platform_device *pdev)
|
||||
s3c_rtc_tickno = platform_get_irq(pdev, 1);
|
||||
if (s3c_rtc_tickno < 0) {
|
||||
dev_err(&pdev->dev, "no irq for rtc tick\n");
|
||||
return -ENOENT;
|
||||
return s3c_rtc_tickno;
|
||||
}
|
||||
|
||||
s3c_rtc_alarmno = platform_get_irq(pdev, 0);
|
||||
if (s3c_rtc_alarmno < 0) {
|
||||
dev_err(&pdev->dev, "no irq for alarm\n");
|
||||
return -ENOENT;
|
||||
return s3c_rtc_alarmno;
|
||||
}
|
||||
|
||||
pr_debug("s3c2410_rtc: tick irq %d, alarm irq %d\n",
|
||||
|
350
drivers/rtc/rtc-snvs.c
Normal file
350
drivers/rtc/rtc-snvs.c
Normal file
@@ -0,0 +1,350 @@
|
||||
/*
|
||||
* Copyright (C) 2011-2012 Freescale Semiconductor, Inc.
|
||||
*
|
||||
* The code contained herein is licensed under the GNU General Public
|
||||
* License. You may obtain a copy of the GNU General Public License
|
||||
* Version 2 or later at the following locations:
|
||||
*
|
||||
* http://www.opensource.org/licenses/gpl-license.html
|
||||
* http://www.gnu.org/copyleft/gpl.html
|
||||
*/
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
/* These register offsets are relative to LP (Low Power) range */
|
||||
#define SNVS_LPCR 0x04
|
||||
#define SNVS_LPSR 0x18
|
||||
#define SNVS_LPSRTCMR 0x1c
|
||||
#define SNVS_LPSRTCLR 0x20
|
||||
#define SNVS_LPTAR 0x24
|
||||
#define SNVS_LPPGDR 0x30
|
||||
|
||||
#define SNVS_LPCR_SRTC_ENV (1 << 0)
|
||||
#define SNVS_LPCR_LPTA_EN (1 << 1)
|
||||
#define SNVS_LPCR_LPWUI_EN (1 << 3)
|
||||
#define SNVS_LPSR_LPTA (1 << 0)
|
||||
|
||||
#define SNVS_LPPGDR_INIT 0x41736166
|
||||
#define CNTR_TO_SECS_SH 15
|
||||
|
||||
struct snvs_rtc_data {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *ioaddr;
|
||||
int irq;
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
static u32 rtc_read_lp_counter(void __iomem *ioaddr)
|
||||
{
|
||||
u64 read1, read2;
|
||||
|
||||
do {
|
||||
read1 = readl(ioaddr + SNVS_LPSRTCMR);
|
||||
read1 <<= 32;
|
||||
read1 |= readl(ioaddr + SNVS_LPSRTCLR);
|
||||
|
||||
read2 = readl(ioaddr + SNVS_LPSRTCMR);
|
||||
read2 <<= 32;
|
||||
read2 |= readl(ioaddr + SNVS_LPSRTCLR);
|
||||
} while (read1 != read2);
|
||||
|
||||
/* Convert 47-bit counter to 32-bit raw second count */
|
||||
return (u32) (read1 >> CNTR_TO_SECS_SH);
|
||||
}
|
||||
|
||||
static void rtc_write_sync_lp(void __iomem *ioaddr)
|
||||
{
|
||||
u32 count1, count2, count3;
|
||||
int i;
|
||||
|
||||
/* Wait for 3 CKIL cycles */
|
||||
for (i = 0; i < 3; i++) {
|
||||
do {
|
||||
count1 = readl(ioaddr + SNVS_LPSRTCLR);
|
||||
count2 = readl(ioaddr + SNVS_LPSRTCLR);
|
||||
} while (count1 != count2);
|
||||
|
||||
/* Now wait until counter value changes */
|
||||
do {
|
||||
do {
|
||||
count2 = readl(ioaddr + SNVS_LPSRTCLR);
|
||||
count3 = readl(ioaddr + SNVS_LPSRTCLR);
|
||||
} while (count2 != count3);
|
||||
} while (count3 == count1);
|
||||
}
|
||||
}
|
||||
|
||||
static int snvs_rtc_enable(struct snvs_rtc_data *data, bool enable)
|
||||
{
|
||||
unsigned long flags;
|
||||
int timeout = 1000;
|
||||
u32 lpcr;
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
|
||||
lpcr = readl(data->ioaddr + SNVS_LPCR);
|
||||
if (enable)
|
||||
lpcr |= SNVS_LPCR_SRTC_ENV;
|
||||
else
|
||||
lpcr &= ~SNVS_LPCR_SRTC_ENV;
|
||||
writel(lpcr, data->ioaddr + SNVS_LPCR);
|
||||
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
|
||||
while (--timeout) {
|
||||
lpcr = readl(data->ioaddr + SNVS_LPCR);
|
||||
|
||||
if (enable) {
|
||||
if (lpcr & SNVS_LPCR_SRTC_ENV)
|
||||
break;
|
||||
} else {
|
||||
if (!(lpcr & SNVS_LPCR_SRTC_ENV))
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (!timeout)
|
||||
return -ETIMEDOUT;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snvs_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct snvs_rtc_data *data = dev_get_drvdata(dev);
|
||||
unsigned long time = rtc_read_lp_counter(data->ioaddr);
|
||||
|
||||
rtc_time_to_tm(time, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snvs_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct snvs_rtc_data *data = dev_get_drvdata(dev);
|
||||
unsigned long time;
|
||||
|
||||
rtc_tm_to_time(tm, &time);
|
||||
|
||||
/* Disable RTC first */
|
||||
snvs_rtc_enable(data, false);
|
||||
|
||||
/* Write 32-bit time to 47-bit timer, leaving 15 LSBs blank */
|
||||
writel(time << CNTR_TO_SECS_SH, data->ioaddr + SNVS_LPSRTCLR);
|
||||
writel(time >> (32 - CNTR_TO_SECS_SH), data->ioaddr + SNVS_LPSRTCMR);
|
||||
|
||||
/* Enable RTC again */
|
||||
snvs_rtc_enable(data, true);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snvs_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct snvs_rtc_data *data = dev_get_drvdata(dev);
|
||||
u32 lptar, lpsr;
|
||||
|
||||
lptar = readl(data->ioaddr + SNVS_LPTAR);
|
||||
rtc_time_to_tm(lptar, &alrm->time);
|
||||
|
||||
lpsr = readl(data->ioaddr + SNVS_LPSR);
|
||||
alrm->pending = (lpsr & SNVS_LPSR_LPTA) ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snvs_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
|
||||
{
|
||||
struct snvs_rtc_data *data = dev_get_drvdata(dev);
|
||||
u32 lpcr;
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
|
||||
lpcr = readl(data->ioaddr + SNVS_LPCR);
|
||||
if (enable)
|
||||
lpcr |= (SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
|
||||
else
|
||||
lpcr &= ~(SNVS_LPCR_LPTA_EN | SNVS_LPCR_LPWUI_EN);
|
||||
writel(lpcr, data->ioaddr + SNVS_LPCR);
|
||||
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
|
||||
rtc_write_sync_lp(data->ioaddr);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snvs_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct snvs_rtc_data *data = dev_get_drvdata(dev);
|
||||
struct rtc_time *alrm_tm = &alrm->time;
|
||||
unsigned long time;
|
||||
unsigned long flags;
|
||||
u32 lpcr;
|
||||
|
||||
rtc_tm_to_time(alrm_tm, &time);
|
||||
|
||||
spin_lock_irqsave(&data->lock, flags);
|
||||
|
||||
/* Have to clear LPTA_EN before programming new alarm time in LPTAR */
|
||||
lpcr = readl(data->ioaddr + SNVS_LPCR);
|
||||
lpcr &= ~SNVS_LPCR_LPTA_EN;
|
||||
writel(lpcr, data->ioaddr + SNVS_LPCR);
|
||||
|
||||
spin_unlock_irqrestore(&data->lock, flags);
|
||||
|
||||
writel(time, data->ioaddr + SNVS_LPTAR);
|
||||
|
||||
/* Clear alarm interrupt status bit */
|
||||
writel(SNVS_LPSR_LPTA, data->ioaddr + SNVS_LPSR);
|
||||
|
||||
return snvs_rtc_alarm_irq_enable(dev, alrm->enabled);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops snvs_rtc_ops = {
|
||||
.read_time = snvs_rtc_read_time,
|
||||
.set_time = snvs_rtc_set_time,
|
||||
.read_alarm = snvs_rtc_read_alarm,
|
||||
.set_alarm = snvs_rtc_set_alarm,
|
||||
.alarm_irq_enable = snvs_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static irqreturn_t snvs_rtc_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct device *dev = dev_id;
|
||||
struct snvs_rtc_data *data = dev_get_drvdata(dev);
|
||||
u32 lpsr;
|
||||
u32 events = 0;
|
||||
|
||||
lpsr = readl(data->ioaddr + SNVS_LPSR);
|
||||
|
||||
if (lpsr & SNVS_LPSR_LPTA) {
|
||||
events |= (RTC_AF | RTC_IRQF);
|
||||
|
||||
/* RTC alarm should be one-shot */
|
||||
snvs_rtc_alarm_irq_enable(dev, 0);
|
||||
|
||||
rtc_update_irq(data->rtc, 1, events);
|
||||
}
|
||||
|
||||
/* clear interrupt status */
|
||||
writel(lpsr, data->ioaddr + SNVS_LPSR);
|
||||
|
||||
return events ? IRQ_HANDLED : IRQ_NONE;
|
||||
}
|
||||
|
||||
static int __devinit snvs_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct snvs_rtc_data *data;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
|
||||
if (!data)
|
||||
return -ENOMEM;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
data->ioaddr = devm_request_and_ioremap(&pdev->dev, res);
|
||||
if (!data->ioaddr)
|
||||
return -EADDRNOTAVAIL;
|
||||
|
||||
data->irq = platform_get_irq(pdev, 0);
|
||||
if (data->irq < 0)
|
||||
return data->irq;
|
||||
|
||||
platform_set_drvdata(pdev, data);
|
||||
|
||||
spin_lock_init(&data->lock);
|
||||
|
||||
/* Initialize glitch detect */
|
||||
writel(SNVS_LPPGDR_INIT, data->ioaddr + SNVS_LPPGDR);
|
||||
|
||||
/* Clear interrupt status */
|
||||
writel(0xffffffff, data->ioaddr + SNVS_LPSR);
|
||||
|
||||
/* Enable RTC */
|
||||
snvs_rtc_enable(data, true);
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, data->irq, snvs_rtc_irq_handler,
|
||||
IRQF_SHARED, "rtc alarm", &pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request irq %d: %d\n",
|
||||
data->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
data->rtc = rtc_device_register(pdev->name, &pdev->dev,
|
||||
&snvs_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(data->rtc)) {
|
||||
ret = PTR_ERR(data->rtc);
|
||||
dev_err(&pdev->dev, "failed to register rtc: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int __devexit snvs_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct snvs_rtc_data *data = platform_get_drvdata(pdev);
|
||||
|
||||
rtc_device_unregister(data->rtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int snvs_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct snvs_rtc_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(data->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int snvs_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct snvs_rtc_data *data = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(data->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(snvs_rtc_pm_ops, snvs_rtc_suspend, snvs_rtc_resume);
|
||||
|
||||
static const struct of_device_id __devinitconst snvs_dt_ids[] = {
|
||||
{ .compatible = "fsl,sec-v4.0-mon-rtc-lp", },
|
||||
{ /* sentinel */ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, snvs_dt_ids);
|
||||
|
||||
static struct platform_driver snvs_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "snvs_rtc",
|
||||
.owner = THIS_MODULE,
|
||||
.pm = &snvs_rtc_pm_ops,
|
||||
.of_match_table = snvs_dt_ids,
|
||||
},
|
||||
.probe = snvs_rtc_probe,
|
||||
.remove = __devexit_p(snvs_rtc_remove),
|
||||
};
|
||||
module_platform_driver(snvs_rtc_driver);
|
||||
|
||||
MODULE_AUTHOR("Freescale Semiconductor, Inc.");
|
||||
MODULE_DESCRIPTION("Freescale SNVS RTC Driver");
|
||||
MODULE_LICENSE("GPL");
|
@@ -235,7 +235,7 @@ static int spear_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct spear_rtc_config *config = dev_get_drvdata(dev);
|
||||
unsigned int time, date, err = 0;
|
||||
unsigned int time, date;
|
||||
|
||||
if (tm2bcd(tm) < 0)
|
||||
return -EINVAL;
|
||||
@@ -247,11 +247,8 @@ static int spear_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
(tm->tm_year << YEAR_SHIFT);
|
||||
writel(time, config->ioaddr + TIME_REG);
|
||||
writel(date, config->ioaddr + DATE_REG);
|
||||
err = is_write_complete(config);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
return 0;
|
||||
return is_write_complete(config);
|
||||
}
|
||||
|
||||
/*
|
||||
@@ -295,7 +292,8 @@ static int spear_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
static int spear_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
struct spear_rtc_config *config = dev_get_drvdata(dev);
|
||||
unsigned int time, date, err = 0;
|
||||
unsigned int time, date;
|
||||
int err;
|
||||
|
||||
if (tm2bcd(&alm->time) < 0)
|
||||
return -EINVAL;
|
||||
@@ -357,7 +355,7 @@ static int __devinit spear_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct resource *res;
|
||||
struct spear_rtc_config *config;
|
||||
unsigned int status = 0;
|
||||
int status = 0;
|
||||
int irq;
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
|
@@ -102,6 +102,12 @@ rtc_sysfs_set_max_user_freq(struct device *dev, struct device_attribute *attr,
|
||||
return n;
|
||||
}
|
||||
|
||||
/**
|
||||
* rtc_sysfs_show_hctosys - indicate if the given RTC set the system time
|
||||
*
|
||||
* Returns 1 if the system clock was set by this RTC at the last
|
||||
* boot or resume event.
|
||||
*/
|
||||
static ssize_t
|
||||
rtc_sysfs_show_hctosys(struct device *dev, struct device_attribute *attr,
|
||||
char *buf)
|
||||
|
349
drivers/rtc/rtc-tps65910.c
Normal file
349
drivers/rtc/rtc-tps65910.c
Normal file
@@ -0,0 +1,349 @@
|
||||
/*
|
||||
* rtc-tps65910.c -- TPS65910 Real Time Clock interface
|
||||
*
|
||||
* Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved.
|
||||
* Author: Venu Byravarasu <vbyravarasu@nvidia.com>
|
||||
*
|
||||
* Based on original TI driver rtc-twl.c
|
||||
* Copyright (C) 2007 MontaVista Software, Inc
|
||||
* Author: Alexandre Rusev <source@mvista.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* as published by the Free Software Foundation; either version
|
||||
* 2 of the License, or (at your option) any later version.
|
||||
*/
|
||||
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/types.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/mfd/tps65910.h>
|
||||
|
||||
struct tps65910_rtc {
|
||||
struct rtc_device *rtc;
|
||||
/* To store the list of enabled interrupts */
|
||||
u32 irqstat;
|
||||
};
|
||||
|
||||
/* Total number of RTC registers needed to set time*/
|
||||
#define NUM_TIME_REGS (TPS65910_YEARS - TPS65910_SECONDS + 1)
|
||||
|
||||
static int tps65910_rtc_alarm_irq_enable(struct device *dev, unsigned enabled)
|
||||
{
|
||||
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||
u8 val = 0;
|
||||
|
||||
if (enabled)
|
||||
val = TPS65910_RTC_INTERRUPTS_IT_ALARM;
|
||||
|
||||
return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, val);
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets current tps65910 RTC time and date parameters.
|
||||
*
|
||||
* The RTC's time/alarm representation is not what gmtime(3) requires
|
||||
* Linux to use:
|
||||
*
|
||||
* - Months are 1..12 vs Linux 0-11
|
||||
* - Years are 0..99 vs Linux 1900..N (we assume 21st century)
|
||||
*/
|
||||
static int tps65910_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
unsigned char rtc_data[NUM_TIME_REGS];
|
||||
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||
int ret;
|
||||
|
||||
/* Copy RTC counting registers to static registers or latches */
|
||||
ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL,
|
||||
TPS65910_RTC_CTRL_GET_TIME, TPS65910_RTC_CTRL_GET_TIME);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "RTC CTRL reg update failed with err:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = regmap_bulk_read(tps->regmap, TPS65910_SECONDS, rtc_data,
|
||||
NUM_TIME_REGS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "reading from RTC failed with err:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tm->tm_sec = bcd2bin(rtc_data[0]);
|
||||
tm->tm_min = bcd2bin(rtc_data[1]);
|
||||
tm->tm_hour = bcd2bin(rtc_data[2]);
|
||||
tm->tm_mday = bcd2bin(rtc_data[3]);
|
||||
tm->tm_mon = bcd2bin(rtc_data[4]) - 1;
|
||||
tm->tm_year = bcd2bin(rtc_data[5]) + 100;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tps65910_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
unsigned char rtc_data[NUM_TIME_REGS];
|
||||
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||
int ret;
|
||||
|
||||
rtc_data[0] = bin2bcd(tm->tm_sec);
|
||||
rtc_data[1] = bin2bcd(tm->tm_min);
|
||||
rtc_data[2] = bin2bcd(tm->tm_hour);
|
||||
rtc_data[3] = bin2bcd(tm->tm_mday);
|
||||
rtc_data[4] = bin2bcd(tm->tm_mon + 1);
|
||||
rtc_data[5] = bin2bcd(tm->tm_year - 100);
|
||||
|
||||
/* Stop RTC while updating the RTC time registers */
|
||||
ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL,
|
||||
TPS65910_RTC_CTRL_STOP_RTC, 0);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "RTC stop failed with err:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* update all the time registers in one shot */
|
||||
ret = regmap_bulk_write(tps->regmap, TPS65910_SECONDS, rtc_data,
|
||||
NUM_TIME_REGS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "rtc_set_time error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* Start back RTC */
|
||||
ret = regmap_update_bits(tps->regmap, TPS65910_RTC_CTRL,
|
||||
TPS65910_RTC_CTRL_STOP_RTC, 1);
|
||||
if (ret < 0)
|
||||
dev_err(dev, "RTC start failed with err:%d\n", ret);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
/*
|
||||
* Gets current tps65910 RTC alarm time.
|
||||
*/
|
||||
static int tps65910_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
unsigned char alarm_data[NUM_TIME_REGS];
|
||||
u32 int_val;
|
||||
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||
int ret;
|
||||
|
||||
ret = regmap_bulk_read(tps->regmap, TPS65910_SECONDS, alarm_data,
|
||||
NUM_TIME_REGS);
|
||||
if (ret < 0) {
|
||||
dev_err(dev, "rtc_read_alarm error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
alm->time.tm_sec = bcd2bin(alarm_data[0]);
|
||||
alm->time.tm_min = bcd2bin(alarm_data[1]);
|
||||
alm->time.tm_hour = bcd2bin(alarm_data[2]);
|
||||
alm->time.tm_mday = bcd2bin(alarm_data[3]);
|
||||
alm->time.tm_mon = bcd2bin(alarm_data[4]) - 1;
|
||||
alm->time.tm_year = bcd2bin(alarm_data[5]) + 100;
|
||||
|
||||
ret = regmap_read(tps->regmap, TPS65910_RTC_INTERRUPTS, &int_val);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
if (int_val & TPS65910_RTC_INTERRUPTS_IT_ALARM)
|
||||
alm->enabled = 1;
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int tps65910_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)
|
||||
{
|
||||
unsigned char alarm_data[NUM_TIME_REGS];
|
||||
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||
int ret;
|
||||
|
||||
ret = tps65910_rtc_alarm_irq_enable(dev, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alarm_data[0] = bin2bcd(alm->time.tm_sec);
|
||||
alarm_data[1] = bin2bcd(alm->time.tm_min);
|
||||
alarm_data[2] = bin2bcd(alm->time.tm_hour);
|
||||
alarm_data[3] = bin2bcd(alm->time.tm_mday);
|
||||
alarm_data[4] = bin2bcd(alm->time.tm_mon + 1);
|
||||
alarm_data[5] = bin2bcd(alm->time.tm_year - 100);
|
||||
|
||||
/* update all the alarm registers in one shot */
|
||||
ret = regmap_bulk_write(tps->regmap, TPS65910_ALARM_SECONDS,
|
||||
alarm_data, NUM_TIME_REGS);
|
||||
if (ret) {
|
||||
dev_err(dev, "rtc_set_alarm error %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
if (alm->enabled)
|
||||
ret = tps65910_rtc_alarm_irq_enable(dev, 1);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static irqreturn_t tps65910_rtc_interrupt(int irq, void *rtc)
|
||||
{
|
||||
struct device *dev = rtc;
|
||||
unsigned long events = 0;
|
||||
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||
struct tps65910_rtc *tps_rtc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
u32 rtc_reg;
|
||||
|
||||
ret = regmap_read(tps->regmap, TPS65910_RTC_STATUS, &rtc_reg);
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
|
||||
if (rtc_reg & TPS65910_RTC_STATUS_ALARM)
|
||||
events = RTC_IRQF | RTC_AF;
|
||||
|
||||
ret = regmap_write(tps->regmap, TPS65910_RTC_STATUS, rtc_reg);
|
||||
if (ret)
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Notify RTC core on event */
|
||||
rtc_update_irq(tps_rtc->rtc, 1, events);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops tps65910_rtc_ops = {
|
||||
.read_time = tps65910_rtc_read_time,
|
||||
.set_time = tps65910_rtc_set_time,
|
||||
.read_alarm = tps65910_rtc_read_alarm,
|
||||
.set_alarm = tps65910_rtc_set_alarm,
|
||||
.alarm_irq_enable = tps65910_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static int __devinit tps65910_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct tps65910 *tps65910 = NULL;
|
||||
struct tps65910_rtc *tps_rtc = NULL;
|
||||
int ret;
|
||||
int irq;
|
||||
u32 rtc_reg;
|
||||
|
||||
tps65910 = dev_get_drvdata(pdev->dev.parent);
|
||||
|
||||
tps_rtc = devm_kzalloc(&pdev->dev, sizeof(struct tps65910_rtc),
|
||||
GFP_KERNEL);
|
||||
if (!tps_rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Clear pending interrupts */
|
||||
ret = regmap_read(tps65910->regmap, TPS65910_RTC_STATUS, &rtc_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(tps65910->regmap, TPS65910_RTC_STATUS, rtc_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
dev_dbg(&pdev->dev, "Enabling rtc-tps65910.\n");
|
||||
rtc_reg = TPS65910_RTC_CTRL_STOP_RTC;
|
||||
ret = regmap_write(tps65910->regmap, TPS65910_RTC_CTRL, rtc_reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
if (irq <= 0) {
|
||||
dev_warn(&pdev->dev, "Wake up is not possible as irq = %d\n",
|
||||
irq);
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
|
||||
tps65910_rtc_interrupt, IRQF_TRIGGER_LOW,
|
||||
"rtc-tps65910", &pdev->dev);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "IRQ is not free.\n");
|
||||
return ret;
|
||||
}
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
tps_rtc->rtc = rtc_device_register(pdev->name, &pdev->dev,
|
||||
&tps65910_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(tps_rtc->rtc)) {
|
||||
ret = PTR_ERR(tps_rtc->rtc);
|
||||
dev_err(&pdev->dev, "RTC device register: err %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
platform_set_drvdata(pdev, tps_rtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Disable tps65910 RTC interrupts.
|
||||
* Sets status flag to free.
|
||||
*/
|
||||
static int __devexit tps65910_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
/* leave rtc running, but disable irqs */
|
||||
struct rtc_device *rtc = platform_get_drvdata(pdev);
|
||||
|
||||
tps65910_rtc_alarm_irq_enable(&rtc->dev, 0);
|
||||
|
||||
rtc_device_unregister(rtc);
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
|
||||
static int tps65910_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||
u8 alarm = TPS65910_RTC_INTERRUPTS_IT_ALARM;
|
||||
int ret;
|
||||
|
||||
/* Store current list of enabled interrupts*/
|
||||
ret = regmap_read(tps->regmap, TPS65910_RTC_INTERRUPTS,
|
||||
&tps->rtc->irqstat);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
/* Enable RTC ALARM interrupt only */
|
||||
return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS, alarm);
|
||||
}
|
||||
|
||||
static int tps65910_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct tps65910 *tps = dev_get_drvdata(dev->parent);
|
||||
|
||||
/* Restore list of enabled interrupts before suspend */
|
||||
return regmap_write(tps->regmap, TPS65910_RTC_INTERRUPTS,
|
||||
tps->rtc->irqstat);
|
||||
}
|
||||
|
||||
static const struct dev_pm_ops tps65910_rtc_pm_ops = {
|
||||
.suspend = tps65910_rtc_suspend,
|
||||
.resume = tps65910_rtc_resume,
|
||||
};
|
||||
|
||||
#define DEV_PM_OPS (&tps65910_rtc_pm_ops)
|
||||
#else
|
||||
#define DEV_PM_OPS NULL
|
||||
#endif
|
||||
|
||||
static struct platform_driver tps65910_rtc_driver = {
|
||||
.probe = tps65910_rtc_probe,
|
||||
.remove = __devexit_p(tps65910_rtc_remove),
|
||||
.driver = {
|
||||
.owner = THIS_MODULE,
|
||||
.name = "tps65910-rtc",
|
||||
.pm = DEV_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(tps65910_rtc_driver);
|
||||
MODULE_ALIAS("platform:rtc-tps65910");
|
||||
MODULE_AUTHOR("Venu Byravarasu <vbyravarasu@nvidia.com>");
|
||||
MODULE_LICENSE("GPL");
|
@@ -97,8 +97,17 @@ static int x1205_get_datetime(struct i2c_client *client, struct rtc_time *tm,
|
||||
int i;
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{ client->addr, 0, 2, dt_addr }, /* setup read ptr */
|
||||
{ client->addr, I2C_M_RD, 8, buf }, /* read date */
|
||||
{/* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 2,
|
||||
.buf = dt_addr
|
||||
},
|
||||
{/* read date */
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 8,
|
||||
.buf = buf
|
||||
},
|
||||
};
|
||||
|
||||
/* read date registers */
|
||||
@@ -142,8 +151,17 @@ static int x1205_get_status(struct i2c_client *client, unsigned char *sr)
|
||||
static unsigned char sr_addr[2] = { 0, X1205_REG_SR };
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{ client->addr, 0, 2, sr_addr }, /* setup read ptr */
|
||||
{ client->addr, I2C_M_RD, 1, sr }, /* read status */
|
||||
{ /* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 2,
|
||||
.buf = sr_addr
|
||||
},
|
||||
{ /* read status */
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = sr
|
||||
},
|
||||
};
|
||||
|
||||
/* read status register */
|
||||
@@ -279,8 +297,17 @@ static int x1205_get_dtrim(struct i2c_client *client, int *trim)
|
||||
static unsigned char dtr_addr[2] = { 0, X1205_REG_DTR };
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{ client->addr, 0, 2, dtr_addr }, /* setup read ptr */
|
||||
{ client->addr, I2C_M_RD, 1, &dtr }, /* read dtr */
|
||||
{ /* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 2,
|
||||
.buf = dtr_addr
|
||||
},
|
||||
{ /* read dtr */
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = &dtr
|
||||
},
|
||||
};
|
||||
|
||||
/* read dtr register */
|
||||
@@ -311,8 +338,17 @@ static int x1205_get_atrim(struct i2c_client *client, int *trim)
|
||||
static unsigned char atr_addr[2] = { 0, X1205_REG_ATR };
|
||||
|
||||
struct i2c_msg msgs[] = {
|
||||
{ client->addr, 0, 2, atr_addr }, /* setup read ptr */
|
||||
{ client->addr, I2C_M_RD, 1, &atr }, /* read atr */
|
||||
{/* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 2,
|
||||
.buf = atr_addr
|
||||
},
|
||||
{/* read atr */
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = &atr
|
||||
},
|
||||
};
|
||||
|
||||
/* read atr register */
|
||||
@@ -381,8 +417,17 @@ static int x1205_validate_client(struct i2c_client *client)
|
||||
unsigned char addr[2] = { 0, probe_zero_pattern[i] };
|
||||
|
||||
struct i2c_msg msgs[2] = {
|
||||
{ client->addr, 0, 2, addr },
|
||||
{ client->addr, I2C_M_RD, 1, &buf },
|
||||
{
|
||||
.addr = client->addr,
|
||||
.len = 2,
|
||||
.buf = addr
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = &buf
|
||||
},
|
||||
};
|
||||
|
||||
if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {
|
||||
@@ -409,8 +454,17 @@ static int x1205_validate_client(struct i2c_client *client)
|
||||
unsigned char addr[2] = { 0, probe_limits_pattern[i].reg };
|
||||
|
||||
struct i2c_msg msgs[2] = {
|
||||
{ client->addr, 0, 2, addr },
|
||||
{ client->addr, I2C_M_RD, 1, ® },
|
||||
{
|
||||
.addr = client->addr,
|
||||
.len = 2,
|
||||
.buf = addr
|
||||
},
|
||||
{
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = ®
|
||||
},
|
||||
};
|
||||
|
||||
if ((xfer = i2c_transfer(client->adapter, msgs, 2)) != 2) {
|
||||
@@ -444,8 +498,18 @@ static int x1205_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
static unsigned char int_addr[2] = { 0, X1205_REG_INT };
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
struct i2c_msg msgs[] = {
|
||||
{ client->addr, 0, 2, int_addr }, /* setup read ptr */
|
||||
{ client->addr, I2C_M_RD, 1, &intreg }, /* read INT register */
|
||||
{ /* setup read ptr */
|
||||
.addr = client->addr,
|
||||
.len = 2,
|
||||
.buf = int_addr
|
||||
},
|
||||
{/* read INT register */
|
||||
|
||||
.addr = client->addr,
|
||||
.flags = I2C_M_RD,
|
||||
.len = 1,
|
||||
.buf = &intreg
|
||||
},
|
||||
};
|
||||
|
||||
/* read interrupt register and status register */
|
||||
|
Reference in New Issue
Block a user