From 5fa44f86910ca9ee477cbd944c18f9349cdca30d Mon Sep 17 00:00:00 2001 From: Ben Gardner Date: Thu, 4 Oct 2012 17:13:44 -0700 Subject: drivers/rtc/rtc-isl1208.c: add support for the ISL1218 The ISL1218 chip is identical to the ISL1208, except that it has 6 additional user-storage registers. This patch does not enable access to those additional registers, but only adds the chip name to the list. Signed-off-by: Ben Gardner Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl1208.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index dd2aeee6c66a..0ea86acdffbc 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -697,6 +697,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); -- cgit v1.2.3 From 92589c986b3360ce15d239fd5113a856412a0b3f Mon Sep 17 00:00:00 2001 From: "Kim, Milo" Date: Thu, 4 Oct 2012 17:13:45 -0700 Subject: rtc-proc: permit the /proc/driver/rtc device to use other devices To get time information via /proc/driver/rtc, only the first device (rtc0) is used. If the rtcN (eg. rtc1 or rtc2) is used for the system clock, there is no way to get information of rtcN via /proc/driver/rtc. With this patch, the time data can be retrieved from the system clock RTC. If the RTC_HCTOSYS_DEVICE is not defined, then rtc0 is used by default. Signed-off-by: Milo(Woogyom) Kim Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- Documentation/rtc.txt | 5 +++-- drivers/rtc/Kconfig | 10 ++++++---- drivers/rtc/rtc-proc.c | 24 ++++++++++++++++++++++-- 3 files changed, 31 insertions(+), 8 deletions(-) (limited to 'drivers/rtc') diff --git a/Documentation/rtc.txt b/Documentation/rtc.txt index 250160469d83..32aa4002de4a 100644 --- a/Documentation/rtc.txt +++ b/Documentation/rtc.txt @@ -119,8 +119,9 @@ three different userspace interfaces: * /sys/class/rtc/rtcN ... sysfs attributes support readonly access to some RTC attributes. - * /proc/driver/rtc ... the first RTC (rtc0) may expose itself - using a procfs interface. More information is (currently) shown + * /proc/driver/rtc ... the system clock RTC may expose itself + using a procfs interface. If there is no RTC for the system clock, + rtc0 is used by default. More information is (currently) shown here than through sysfs. The RTC Class framework supports a wide variety of RTCs, ranging from those diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index fabc99a75c65..a4a805c273bb 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -69,13 +69,15 @@ 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. diff --git a/drivers/rtc/rtc-proc.c b/drivers/rtc/rtc-proc.c index 0a59fda5c09d..e96236ac2e78 100644 --- a/drivers/rtc/rtc-proc.c +++ b/drivers/rtc/rtc-proc.c @@ -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); } -- cgit v1.2.3 From 7418a1198991beb5bf6656b2ac0bcbde378c5539 Mon Sep 17 00:00:00 2001 From: Sven Schnelle Date: Thu, 4 Oct 2012 17:13:47 -0700 Subject: rtc: add Dallas DS2404 driver [akpm@linux-foundation.org: fix warning] Signed-off-by: Sven Schnelle Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 9 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-ds2404.c | 303 +++++++++++++++++++++++++++++++++++++++++++++ include/linux/rtc-ds2404.h | 20 +++ 4 files changed, 333 insertions(+) create mode 100644 drivers/rtc/rtc-ds2404.c create mode 100644 include/linux/rtc-ds2404.h (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index a4a805c273bb..d4681c97f7cc 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -660,6 +660,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 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 0d5b2b66f90d..7ecdb2983dde 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -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 diff --git a/drivers/rtc/rtc-ds2404.c b/drivers/rtc/rtc-ds2404.c new file mode 100644 index 000000000000..5ea9df7c8c31 --- /dev/null +++ b/drivers/rtc/rtc-ds2404.c @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2012 Sven Schnelle + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#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"); diff --git a/include/linux/rtc-ds2404.h b/include/linux/rtc-ds2404.h new file mode 100644 index 000000000000..22c53825528f --- /dev/null +++ b/include/linux/rtc-ds2404.h @@ -0,0 +1,20 @@ +/* + * ds2404.h - platform data structure for the DS2404 RTC. + * + * This file is subject to the terms and conditions of the GNU General Public + * License. See the file "COPYING" in the main directory of this archive + * for more details. + * + * Copyright (C) 2012 Sven Schnelle + */ + +#ifndef __LINUX_DS2404_H +#define __LINUX_DS2404_H + +struct ds2404_platform_data { + + unsigned int gpio_rst; + unsigned int gpio_clk; + unsigned int gpio_dq; +}; +#endif -- cgit v1.2.3 From 179a502f8c4605c502f798e18f66ae096fe42402 Mon Sep 17 00:00:00 2001 From: Shawn Guo Date: Thu, 4 Oct 2012 17:13:49 -0700 Subject: rtc: snvs: add Freescale rtc-snvs driver Add an RTC driver for Freescale Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC. Signed-off-by: Shawn Guo Reviewed-by: Stephen Boyd Cc: Alessandro Zummo Cc: Kim Phillips Cc: Sascha Hauer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- .../devicetree/bindings/crypto/fsl-sec4.txt | 51 +++ Documentation/devicetree/bindings/rtc/snvs-rtc.txt | 1 + drivers/rtc/Kconfig | 11 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-snvs.c | 350 +++++++++++++++++++++ 5 files changed, 414 insertions(+) create mode 100644 Documentation/devicetree/bindings/rtc/snvs-rtc.txt create mode 100644 drivers/rtc/rtc-snvs.c (limited to 'drivers/rtc') diff --git a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt index bf57ecd5d73a..bd7ce120bc13 100644 --- a/Documentation/devicetree/bindings/crypto/fsl-sec4.txt +++ b/Documentation/devicetree/bindings/crypto/fsl-sec4.txt @@ -9,6 +9,7 @@ Copyright (C) 2008-2011 Freescale Semiconductor Inc. -Run Time Integrity Check (RTIC) Node -Run Time Integrity Check (RTIC) Memory Node -Secure Non-Volatile Storage (SNVS) Node + -Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node -Full Example NOTE: the SEC 4 is also known as Freescale's Cryptographic Accelerator @@ -294,6 +295,27 @@ Secure Non-Volatile Storage (SNVS) Node address and length of the SEC4 configuration registers. + - #address-cells + Usage: required + Value type: + Definition: A standard property. Defines the number of cells + for representing physical addresses in child nodes. Must + have a value of 1. + + - #size-cells + Usage: required + Value type: + Definition: A standard property. Defines the number of cells + for representing the size of physical addresses in + child nodes. Must have a value of 1. + + - ranges + Usage: required + Value type: + Definition: A standard property. Specifies the physical address + range of the SNVS register space. A triplet that includes + the child address, parent address, & length. + - interrupts Usage: required Value type: @@ -314,10 +336,33 @@ EXAMPLE sec_mon@314000 { compatible = "fsl,sec-v4.0-mon"; reg = <0x314000 0x1000>; + ranges = <0 0x314000 0x1000>; interrupt-parent = <&mpic>; interrupts = <93 2>; }; +===================================================================== +Secure Non-Volatile Storage (SNVS) Low Power (LP) RTC Node + + A SNVS child node that defines SNVS LP RTC. + + - compatible + Usage: required + Value type: + Definition: Must include "fsl,sec-v4.0-mon-rtc-lp". + + - reg + Usage: required + Value type: + Definition: A standard property. Specifies the physical + address and length of the SNVS LP configuration registers. + +EXAMPLE + sec_mon_rtc_lp@314000 { + compatible = "fsl,sec-v4.0-mon-rtc-lp"; + reg = <0x34 0x58>; + }; + ===================================================================== FULL EXAMPLE @@ -390,8 +435,14 @@ FULL EXAMPLE sec_mon: sec_mon@314000 { compatible = "fsl,sec-v4.0-mon"; reg = <0x314000 0x1000>; + ranges = <0 0x314000 0x1000>; interrupt-parent = <&mpic>; interrupts = <93 2>; + + sec_mon_rtc_lp@34 { + compatible = "fsl,sec-v4.0-mon-rtc-lp"; + reg = <0x34 0x58>; + }; }; ===================================================================== diff --git a/Documentation/devicetree/bindings/rtc/snvs-rtc.txt b/Documentation/devicetree/bindings/rtc/snvs-rtc.txt new file mode 100644 index 000000000000..fb61ed77ada3 --- /dev/null +++ b/Documentation/devicetree/bindings/rtc/snvs-rtc.txt @@ -0,0 +1 @@ +See Documentation/devicetree/bindings/crypto/fsl-sec4.txt for details. diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index d4681c97f7cc..f87bfea6718c 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -1109,4 +1109,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 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 7ecdb2983dde..6056f71033db 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -97,6 +97,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 diff --git a/drivers/rtc/rtc-snvs.c b/drivers/rtc/rtc-snvs.c new file mode 100644 index 000000000000..3c0da333f465 --- /dev/null +++ b/drivers/rtc/rtc-snvs.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +/* 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"); -- cgit v1.2.3 From 2830a6d20139df2198d63235df7957712adb28e5 Mon Sep 17 00:00:00 2001 From: Vincent Palatin Date: Thu, 4 Oct 2012 17:13:52 -0700 Subject: rtc: recycle id when unloading a rtc driver When calling rtc_device_unregister, we are not freeing the id used by the driver. So when doing a unload/load cycle for a RTC driver (e.g. rmmod rtc_cmos && modprobe rtc_cmos), its id is incremented by one. As a consequence, we no longer have neither an rtc0 driver nor a /proc/driver/rtc (as it only exists for the first driver). Signed-off-by: Vincent Palatin Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/class.c | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index dc4c2748bbc3..37b1d82fda08 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -238,6 +238,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); } -- cgit v1.2.3 From 477d30d787b969e4c11fc958077cc7954805e236 Mon Sep 17 00:00:00 2001 From: Devendra Naga Date: Thu, 4 Oct 2012 17:13:54 -0700 Subject: drivers/rtc/rtc-at91sam9.c: use module_platform_driver() macro This driver does seems to do only platform_driver_register in the init function and platform_driver_unregister in the exit function, so replace all this code including the module_init and module_exit with module_platform_driver macro... Signed-off-by: Devendra Naga Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-at91sam9.c | 13 +------------ 1 file changed, 1 insertion(+), 12 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-at91sam9.c b/drivers/rtc/rtc-at91sam9.c index 1dd61f402b04..2dfe7a2fb998 100644 --- a/drivers/rtc/rtc-at91sam9.c +++ b/drivers/rtc/rtc-at91sam9.c @@ -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"); -- cgit v1.2.3 From 0e783980b84e7253ba98552d092d1f45ed262d66 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 4 Oct 2012 17:13:55 -0700 Subject: rtc: tps65910: add RTC driver for TPS65910 PMIC RTC TPS65910 PMIC is a MFD with RTC as one of the device. Adding RTC driver for supporting RTC device present inside TPS65910 PMIC. Only support for RTC alarm is implemented as part of this patch. Signed-off-by: Venu Byravarasu Cc: Alessandro Zummo Cc: Mark Brown Cc: Samuel Ortiz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-tps65910.c | 352 +++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/tps65910.h | 10 ++ 4 files changed, 373 insertions(+) create mode 100644 drivers/rtc/rtc-tps65910.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f87bfea6718c..6309df4e2654 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -335,6 +335,16 @@ 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_S35390A tristate "Seiko Instruments S-35390A" select BITREVERSE diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 6056f71033db..5c8f106237da 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -107,6 +107,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 diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c new file mode 100644 index 000000000000..7c4ca8605741 --- /dev/null +++ b/drivers/rtc/rtc-tps65910.c @@ -0,0 +1,352 @@ +/* + * rtc-tps65910.c -- TPS65910 Real Time Clock interface + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Venu Byravarasu + * + * Based on original TI driver rtc-twl.c + * Copyright (C) 2007 MontaVista Software, Inc + * Author: Alexandre Rusev + * + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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; + struct tps65910_board *pmic_plat_data; + 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; + + pmic_plat_data = dev_get_platdata(tps65910->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 += TPS65910_IRQ_RTC_ALARM; + 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 "); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mfd/tps65910.h b/include/linux/mfd/tps65910.h index 9bf8767818b4..ba2eb25f1094 100644 --- a/include/linux/mfd/tps65910.h +++ b/include/linux/mfd/tps65910.h @@ -132,6 +132,16 @@ * */ +/* RTC_CTRL_REG bitfields */ +#define TPS65910_RTC_CTRL_STOP_RTC 0x01 /*0=stop, 1=run */ +#define TPS65910_RTC_CTRL_GET_TIME 0x40 + +/* RTC_STATUS_REG bitfields */ +#define TPS65910_RTC_STATUS_ALARM 0x40 + +/* RTC_INTERRUPTS_REG bitfields */ +#define TPS65910_RTC_INTERRUPTS_EVERY 0x03 +#define TPS65910_RTC_INTERRUPTS_IT_ALARM 0x08 /*Register BCK1 (0x80) register.RegisterDescription */ #define BCK1_BCKUP_MASK 0xFF -- cgit v1.2.3 From 94c01ab6d754435333a989e3c6f4a20f57537754 Mon Sep 17 00:00:00 2001 From: Stephen Warren Date: Thu, 4 Oct 2012 17:13:56 -0700 Subject: rtc: add MAX8907 RTC driver The MAX8907 is an I2C-based power-management IC containing voltage regulators, a reset controller, a real-time clock, and a touch-screen controller. The driver is based on an original by or fixed by: * Tom Cherry * Prashant Gaikwad * Joseph Yoon During upstreaming, I (swarren): * Converted to regmap. * Fixed handling of RTC_HOUR register containing 12. * Fixed handling of RTC_WEEKDAY register. * General cleanup. Signed-off-by: Stephen Warren Cc: Tom Cherry Cc: Prashant Gaikwad Cc: Joseph Yoon Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-max8907.c | 244 ++++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 255 insertions(+) create mode 100644 drivers/rtc/rtc-max8907.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 6309df4e2654..499fb9984abe 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -205,6 +205,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 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 5c8f106237da..d9526b549f87 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -65,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 diff --git a/drivers/rtc/rtc-max8907.c b/drivers/rtc/rtc-max8907.c new file mode 100644 index 000000000000..e094ffa434f8 --- /dev/null +++ b/drivers/rtc/rtc-max8907.c @@ -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 +#include +#include +#include +#include +#include +#include +#include + +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"); -- cgit v1.2.3 From 6b8029fab64164b5895d58d23229b75c82e3a6fc Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 4 Oct 2012 17:13:59 -0700 Subject: rtc: kconfig: remove unnecessary dependencies. Remove "depends on RTC_CLASS = y" for multiple Kconfig definitions, as all of them are already placed under "if RTC_CLASS". Signed-off-by: Venu Byravarasu Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 26 ++++++++------------------ 1 file changed, 8 insertions(+), 18 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 499fb9984abe..f92d17bc9566 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -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. @@ -71,7 +68,6 @@ config RTC_INTF_SYSFS config RTC_INTF_PROC boolean "/proc/driver/rtc (procfs for rtcN)" depends on PROC_FS - default RTC_CLASS help Say yes here if you want to use your system clock RTC through the proc interface, /proc/driver/rtc. @@ -83,7 +79,6 @@ config RTC_INTF_PROC 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 @@ -129,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. @@ -139,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. @@ -167,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 @@ -187,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 @@ -337,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. @@ -560,7 +555,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. @@ -605,7 +599,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. @@ -742,7 +735,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. @@ -762,7 +755,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 @@ -822,7 +814,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. @@ -1054,7 +1046,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 @@ -1084,7 +1075,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. @@ -1121,7 +1112,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. -- cgit v1.2.3 From 7c6a52a090ce7f680077ed42326e11cf17e713dc Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 4 Oct 2012 17:14:00 -0700 Subject: drivers/rtc/rtc-jz4740.c: fix IRQ error check The irq field of the jz4740_irc struct is unsigned. Yet we assign the result of platform_get_irq() to it. platform_get_irq() may return a negative error code and the code checks for this condition by checking if 'irq' is less than zero. But since 'irq' is unsigned this test will always be false. Fix it by making 'irq' signed. The issue was found using the following coccinelle semantic patch: // @@ type T; unsigned T i; @@ ( *i < 0 | *i >= 0 ) // Signed-off-by: Lars-Peter Clausen Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-jz4740.c | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-jz4740.c b/drivers/rtc/rtc-jz4740.c index 05ab227eeff7..1224182d3eab 100644 --- a/drivers/rtc/rtc-jz4740.c +++ b/drivers/rtc/rtc-jz4740.c @@ -42,7 +42,7 @@ struct jz4740_rtc { struct rtc_device *rtc; - unsigned int irq; + int irq; spinlock_t lock; }; -- cgit v1.2.3 From a16e8393ac03cc24fd9b838f42823b4242ceac88 Mon Sep 17 00:00:00 2001 From: Lars-Peter Clausen Date: Thu, 4 Oct 2012 17:14:01 -0700 Subject: drivers/rtc/rtc-spear.c: fix several error checks There are several comparisons of a unsigned int to less than zero int spear RTC driver. Such a check will always be true. In all these cases a signed int is assigned to the unsigned variable, which is checked, before. So the right fix is to make the checked variable signed as well. In one case the check can be dropped completely, because all it does it returns 'err' if 'err' is less than zero, otherwise it returns 0. Since in this particular case 'err' is always either 0 or less this is the same as just returning 'err'. The issue has been found using the following coccinelle semantic patch: // @@ type T; unsigned T i; @@ ( *i < 0 | *i >= 0 ) // Signed-off-by: Lars-Peter Clausen Cc: Viresh Kumar Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-spear.c | 12 +++++------- 1 file changed, 5 insertions(+), 7 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-spear.c b/drivers/rtc/rtc-spear.c index e2785479113c..bb507d23f6ce 100644 --- a/drivers/rtc/rtc-spear.c +++ b/drivers/rtc/rtc-spear.c @@ -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); -- cgit v1.2.3 From 90829c089ef622e54ce1153eb6dcda9ce98cb7c2 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 4 Oct 2012 17:14:04 -0700 Subject: rtc: rc5t583: add ricoh rc5t583 RTC driver Add an RTC driver for the RTC device on Ricoh MFD Rc5t583. Ricoh RTC has 3 types of alarms. The current patch adds support for the Y-Alarm of RC5t583 RTC. Signed-off-by: Venu Byravarasu Cc: Alessandro Zummo Cc: Mark Brown Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 10 ++ drivers/rtc/Makefile | 1 + drivers/rtc/rtc-rc5t583.c | 331 ++++++++++++++++++++++++++++++++++++++++++++ include/linux/mfd/rc5t583.h | 22 +++ 4 files changed, 364 insertions(+) create mode 100644 drivers/rtc/rtc-rc5t583.c (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index f92d17bc9566..94764560d110 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -350,6 +350,16 @@ config RTC_DRV_TPS65910 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 diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index d9526b549f87..56297f0fd388 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -87,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 diff --git a/drivers/rtc/rtc-rc5t583.c b/drivers/rtc/rtc-rc5t583.c new file mode 100644 index 000000000000..cdb140c29c56 --- /dev/null +++ b/drivers/rtc/rtc-rc5t583.c @@ -0,0 +1,331 @@ +/* + * rtc-rc5t583.c -- RICOH RC5T583 Real Time Clock + * + * Copyright (c) 2012, NVIDIA CORPORATION. All rights reserved. + * Author: Venu Byravarasu + * + * 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 . */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +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 "); +MODULE_LICENSE("GPL v2"); diff --git a/include/linux/mfd/rc5t583.h b/include/linux/mfd/rc5t583.h index 3661c59aa1e9..36c242e52ef1 100644 --- a/include/linux/mfd/rc5t583.h +++ b/include/linux/mfd/rc5t583.h @@ -146,6 +146,28 @@ #define RC5T583_GPIO_MON_IOIN 0xAB #define RC5T583_GPIO_GPOFUNC 0xAC +/* RTC registers */ +#define RC5T583_RTC_SEC 0xE0 +#define RC5T583_RTC_MIN 0xE1 +#define RC5T583_RTC_HOUR 0xE2 +#define RC5T583_RTC_WDAY 0xE3 +#define RC5T583_RTC_DAY 0xE4 +#define RC5T583_RTC_MONTH 0xE5 +#define RC5T583_RTC_YEAR 0xE6 +#define RC5T583_RTC_ADJ 0xE7 +#define RC5T583_RTC_AW_MIN 0xE8 +#define RC5T583_RTC_AW_HOUR 0xE9 +#define RC5T583_RTC_AW_WEEK 0xEA +#define RC5T583_RTC_AD_MIN 0xEB +#define RC5T583_RTC_AD_HOUR 0xEC +#define RC5T583_RTC_CTL1 0xED +#define RC5T583_RTC_CTL2 0xEE +#define RC5T583_RTC_AY_MIN 0xF0 +#define RC5T583_RTC_AY_HOUR 0xF1 +#define RC5T583_RTC_AY_DAY 0xF2 +#define RC5T583_RTC_AY_MONTH 0xF3 +#define RC5T583_RTC_AY_YEAR 0xF4 + /* RICOH_RC5T583 IRQ definitions */ enum { RC5T583_IRQ_ONKEY, -- cgit v1.2.3 From 828296debf7853752e47b6abfa608ab0172e53ec Mon Sep 17 00:00:00 2001 From: Julia Lawall Date: Thu, 4 Oct 2012 17:14:07 -0700 Subject: drivers/rtc/rtc-coh901331.c: use clk_prepare_enable() and clk_disable_unprepare() clk_prepare_enable and clk_disable_unprepare combine clk_prepare and clk_enable, and clk_disable and clk_unprepare. They make the code more concise, and ensure that clk_unprepare is called when clk_enable fails. A simplified version of the semantic patch that introduces calls to these functions is as follows: (http://coccinelle.lip6.fr/) // @@ expression e; @@ - clk_prepare(e); - clk_enable(e); + clk_prepare_enable(e); @@ expression e; @@ - clk_disable(e); - clk_unprepare(e); + clk_disable_unprepare(e); // Signed-off-by: Julia Lawall Acked-by: Linus Walleij Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-coh901331.c | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-coh901331.c b/drivers/rtc/rtc-coh901331.c index 76b2156d3c62..c8115b83e5ab 100644 --- a/drivers/rtc/rtc-coh901331.c +++ b/drivers/rtc/rtc-coh901331.c @@ -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 = { -- cgit v1.2.3 From 0f3cde536d5015a918c4b3fd55fbe335428096e5 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 4 Oct 2012 17:14:09 -0700 Subject: rtc: rtc-mxc: adapt to the new i.mx clock framework i.mx drivers should use clk_prepare_enable/clk_disable_unprepare() in order to avoid clk warnings. While at it, convert to devm_clk_get() since other devm_ functions are used in this driver and it can also save some clk_put() calls. Signed-off-by: Fabio Estevam Acked-by: Sascha Hauer Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-mxc.c | 10 ++++------ 1 file changed, 4 insertions(+), 6 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index e3e50d69baf8..334c43e0ce0e 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -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,8 +426,7 @@ 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: @@ -440,8 +439,7 @@ static int __exit mxc_rtc_remove(struct platform_device *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; -- cgit v1.2.3 From be8b6d510072461b50958527e7b157f53e5388d7 Mon Sep 17 00:00:00 2001 From: Fabio Estevam Date: Thu, 4 Oct 2012 17:14:10 -0700 Subject: rtc: rtc-mxc: convert to module_platform_driver Converting to module_platform_driver can make the code smaller and cleaner. Signed-off-by: Fabio Estevam Cc: Sascha Hauer Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-mxc.c | 20 +++++--------------- 1 file changed, 5 insertions(+), 15 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-mxc.c b/drivers/rtc/rtc-mxc.c index 334c43e0ce0e..cd0106293a49 100644 --- a/drivers/rtc/rtc-mxc.c +++ b/drivers/rtc/rtc-mxc.c @@ -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; @@ -433,7 +433,7 @@ 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); @@ -480,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 "); MODULE_DESCRIPTION("RTC driver for Freescale MXC"); -- cgit v1.2.3 From 4c24e29e65843ed912c14cdc293ed922e33efdcc Mon Sep 17 00:00:00 2001 From: David Fries Date: Thu, 4 Oct 2012 17:14:12 -0700 Subject: rtc_sysfs_show_hctosys(): display 0 if resume failed MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Without this patch /sys/class/rtc/$CONFIG_RTC_HCTOSYS_DEVICE/hctosys contains a 1 (meaning "This rtc was used to initialize the system clock") even if setting the time by do_settimeofday() at bootup failed. The RTC can also be used to set the clock on resume, if it did 1, otherwise 0. Previously there was no indication if the RTC was used to set the clock in resume. This uses only CONFIG_RTC_HCTOSYS_DEVICE for conditional compilation instead of it and CONFIG_RTC_HCTOSYS to be more consistent. rtc_hctosys_ret was moved to class.c so class.c no longer depends on hctosys.c. [sfr@canb.auug.org.au: fix build] Signed-off-by: David Fries Cc: Matthew Garrett Cc: Alessandro Zummo Cc: Uwe Kleine-König Signed-off-by: Stephen Rothwell Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/class.c | 8 +++++++- drivers/rtc/hctosys.c | 4 +--- drivers/rtc/rtc-sysfs.c | 6 ++++++ include/linux/rtc.h | 2 +- 4 files changed, 15 insertions(+), 5 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/class.c b/drivers/rtc/class.c index 37b1d82fda08..f8a0aab218cb 100644 --- a/drivers/rtc/class.c +++ b/drivers/rtc/class.c @@ -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; } diff --git a/drivers/rtc/hctosys.c b/drivers/rtc/hctosys.c index bc90b091f195..4aa60d74004e 100644 --- a/drivers/rtc/hctosys.c +++ b/drivers/rtc/hctosys.c @@ -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 " diff --git a/drivers/rtc/rtc-sysfs.c b/drivers/rtc/rtc-sysfs.c index 380083ca572f..b70e2bb63645 100644 --- a/drivers/rtc/rtc-sysfs.c +++ b/drivers/rtc/rtc-sysfs.c @@ -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) diff --git a/include/linux/rtc.h b/include/linux/rtc.h index f071b3922c67..20ec4d3bed73 100644 --- a/include/linux/rtc.h +++ b/include/linux/rtc.h @@ -276,7 +276,7 @@ static inline bool is_leap_year(unsigned int year) return (!(year % 4) && (year % 100)) || !(year % 400); } -#ifdef CONFIG_RTC_HCTOSYS +#ifdef CONFIG_RTC_HCTOSYS_DEVICE extern int rtc_hctosys_ret; #else #define rtc_hctosys_ret -ENODEV -- cgit v1.2.3 From 1ee8c0ca2a6aef9af4b78f860cc690f33fc8f287 Mon Sep 17 00:00:00 2001 From: Sachin Kamat Date: Thu, 4 Oct 2012 17:14:14 -0700 Subject: drivers/rtc/rtc-s3c.c: fix return value in s3c_rtc_probe() Return the value returned by platform_get_irq() instead of -ENOENT; Signed-off-by: Sachin Kamat Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s3c.c | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index bfbd92c8d1c9..77823d21d314 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -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", -- cgit v1.2.3 From 2bfc37dffa844e5b28a240feec5e2fa8ec2ea8ba Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 4 Oct 2012 17:14:17 -0700 Subject: drivers/rtc/rtc-ds1672.c: convert struct i2c_msg initialization to C99 format Convert the struct i2c_msg initialization to C99 format. This makes maintaining and editing the code simpler. Also helps once other fields like transferred are added in future. Signed-off-by: Shubhrajyoti D Reviewed-by: Felipe Balbi Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-ds1672.c | 26 ++++++++++++++++++++++---- 1 file changed, 22 insertions(+), 4 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-ds1672.c b/drivers/rtc/rtc-ds1672.c index 7fa67d0df172..45d65c0b3a85 100644 --- a/drivers/rtc/rtc-ds1672.c +++ b/drivers/rtc/rtc-ds1672.c @@ -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 */ -- cgit v1.2.3 From c3fe92b7c03061ef8acb472aead3d86586e8149f Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 4 Oct 2012 17:14:18 -0700 Subject: drivers/rtc/rtc-x1205.c: convert struct i2c_msg initialization to C99 format Convert the struct i2c_msg initialization to C99 format. This makes maintaining and editing the code simpler. Also helps once other fields like transferred are added in future. Signed-off-by: Shubhrajyoti D Reviewed-by: Felipe Balbi Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-x1205.c | 92 +++++++++++++++++++++++++++++++++++++++++-------- 1 file changed, 78 insertions(+), 14 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-x1205.c b/drivers/rtc/rtc-x1205.c index 403b3d41d101..f36e59c6bc01 100644 --- a/drivers/rtc/rtc-x1205.c +++ b/drivers/rtc/rtc-x1205.c @@ -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 */ -- cgit v1.2.3 From 65659f6391e5fc7e980958efc2a06276c7891586 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 4 Oct 2012 17:14:21 -0700 Subject: drivers/rtc/rtc-s35390a.c: convert struct i2c_msg initialization to C99 format Convert the struct i2c_msg initialization to C99 format. This makes maintaining and editing the code simpler. Also helps once other fields like transferred are added in future. Signed-off-by: Shubhrajyoti D Reviewed-by: Felipe Balbi Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s35390a.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index c9562ceedef3..cc5c5166b018 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -50,7 +50,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 +67,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) -- cgit v1.2.3 From a606757ff7be87c8c6a72b7d6178833efc25f812 Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 4 Oct 2012 17:14:22 -0700 Subject: drivers/rtc/rtc-rs5c372.c: convert struct i2c_msg initialization to C99 format Convert the struct i2c_msg initialization to C99 format. This makes maintaining and editing the code simpler. Also helps once other fields like transferred are added in future. while at it also fix a checkpatch warn WARNING: sizeof rs5c->buf should be sizeof(rs5c->buf) Signed-off-by: Shubhrajyoti D Reviewed-by: Felipe Balbi Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-rs5c372.c | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-rs5c372.c b/drivers/rtc/rtc-rs5c372.c index fb4842c3544e..76f565ae384d 100644 --- a/drivers/rtc/rtc-rs5c372.c +++ b/drivers/rtc/rtc-rs5c372.c @@ -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 -- cgit v1.2.3 From 755e4a4bdc6506a61ce455a650f92d180ef577ff Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 4 Oct 2012 17:14:25 -0700 Subject: drivers/rtc/rtc-pcf8563.c: convert struct i2c_msg initialization to C99 format Convert the struct i2c_msg initialization to C99 format. This makes maintaining and editing the code simpler. Also helps once other fields like transferred are added in future. Signed-off-by: Shubhrajyoti D Reviewed-by: Felipe Balbi Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-pcf8563.c | 13 +++++++++++-- 1 file changed, 11 insertions(+), 2 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index c2fe426a6ef2..98e3a2b681e6 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -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 */ -- cgit v1.2.3 From 885ccbb357163271db6a6865b2b4b1888180094c Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 4 Oct 2012 17:14:27 -0700 Subject: drivers/rtc/rtc-isl1208.c: convert struct i2c_msg initialization to C99 format Convert the struct i2c_msg initialization to C99 format. This makes maintaining and editing the code simpler. Also helps once other fields like transferred are added in future. Signed-off-by: Shubhrajyoti D Reviewed-by: Felipe Balbi Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-isl1208.c | 20 ++++++++++++++++---- 1 file changed, 16 insertions(+), 4 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 0ea86acdffbc..26c81f233606 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -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; -- cgit v1.2.3 From 84cab86cb816aa439905c2112931c3649a67427f Mon Sep 17 00:00:00 2001 From: Shubhrajyoti D Date: Thu, 4 Oct 2012 17:14:29 -0700 Subject: drivers/rtc/rtc-em3027.c: convert struct i2c_msg initialization to C99 format Convert the struct i2c_msg initialization to C99 format. This makes maintaining and editing the code simpler. Also helps once other fields like transferred are added in future. Signed-off-by: Shubhrajyoti D Reviewed-by: Felipe Balbi Cc: Alessandro Zummo Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-em3027.c | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-em3027.c b/drivers/rtc/rtc-em3027.c index 0104ea7ebe50..f6c24ce35d36 100644 --- a/drivers/rtc/rtc-em3027.c +++ b/drivers/rtc/rtc-em3027.c @@ -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; -- cgit v1.2.3 From 651fb48034ee2cf36d4c8d1cc86ba923a3c82b0d Mon Sep 17 00:00:00 2001 From: Ulf Hansson Date: Thu, 4 Oct 2012 17:14:32 -0700 Subject: rtc: kconfig: fix up dependency for ab8500 Signed-off-by: Ulf Hansson Acked-by: Linus Walleij Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/Kconfig | 1 + 1 file changed, 1 insertion(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 94764560d110..e069f176a82d 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -738,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 -- cgit v1.2.3 From 06f77d18e4be0837868ebba8dff4097103e484c4 Mon Sep 17 00:00:00 2001 From: Venu Byravarasu Date: Thu, 4 Oct 2012 17:14:34 -0700 Subject: drivers/rtc/rtc-tps65910.c: use platform_get_irq() to get RTC irq details As RTC driver needs only irq number from platform data, using platform_get_irq(), instead of generic dev_get_platdata(). Signed-off-by: Venu Byravarasu Acked-by: Stephen Warren Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-tps65910.c | 5 +---- 1 file changed, 1 insertion(+), 4 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-tps65910.c b/drivers/rtc/rtc-tps65910.c index 7c4ca8605741..7a82337e4dee 100644 --- a/drivers/rtc/rtc-tps65910.c +++ b/drivers/rtc/rtc-tps65910.c @@ -226,7 +226,6 @@ static int __devinit tps65910_rtc_probe(struct platform_device *pdev) { struct tps65910 *tps65910 = NULL; struct tps65910_rtc *tps_rtc = NULL; - struct tps65910_board *pmic_plat_data; int ret; int irq; u32 rtc_reg; @@ -253,15 +252,13 @@ static int __devinit tps65910_rtc_probe(struct platform_device *pdev) if (ret < 0) return ret; - pmic_plat_data = dev_get_platdata(tps65910->dev); - irq = pmic_plat_data->irq_base; + 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; } - irq += TPS65910_IRQ_RTC_ALARM; ret = devm_request_threaded_irq(&pdev->dev, irq, NULL, tps65910_rtc_interrupt, IRQF_TRIGGER_LOW, "rtc-tps65910", &pdev->dev); -- cgit v1.2.3 From 48e9766726ebb8f5d98823fe6b32dff570bc04d8 Mon Sep 17 00:00:00 2001 From: Paul Bolle Date: Thu, 4 Oct 2012 17:14:35 -0700 Subject: drivers/rtc/rtc-m41t80.c: remove disabled alarm functionality Commit c3b79770e51a ("rtc: m41t80: Workaround broken alarm functionality") disabled m41t80's alarm functions. But since those functions were not touched, building this driver triggers these GCC warnings: drivers/rtc/rtc-m41t80.c:216:12: warning: 'm41t80_rtc_alarm_irq_enable' defined but not used [-Wunused-function] drivers/rtc/rtc-m41t80.c:238:12: warning: 'm41t80_rtc_set_alarm' defined but not used [-Wunused-function] drivers/rtc/rtc-m41t80.c:308:12: warning: 'm41t80_rtc_read_alarm' defined but not used [-Wunused-function] Remove these functions (and the commented out references to them) to silence these warnings. Anyone wanting to fix the alarm irq functionality can easily find the removed code in the git log of this file or through some web searches. Signed-off-by: Paul Bolle Cc: John Stultz Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-m41t80.c | 157 ++--------------------------------------------- 1 file changed, 4 insertions(+), 153 deletions(-) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 4e0f84af99a7..b885bcd08908 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -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) -- cgit v1.2.3 From 542dd33a4925757c93b2c811b19434822a6c1a73 Mon Sep 17 00:00:00 2001 From: Michael Langer Date: Thu, 4 Oct 2012 17:14:37 -0700 Subject: drivers/rtc/rtc-s35390a.c: add wakealarm support for rtc-s35390A rtc chip Add basic get/set alarm support for the Seiko Instruments S-35390A. The chip is used on the QNAP TS-219P+ NAS device. Signed-off-by: Michael Langer Signed-off-by: Andrew Morton Signed-off-by: Linus Torvalds --- drivers/rtc/rtc-s35390a.c | 116 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 116 insertions(+) (limited to 'drivers/rtc') diff --git a/drivers/rtc/rtc-s35390a.c b/drivers/rtc/rtc-s35390a.c index cc5c5166b018..8a092325188d 100644 --- a/drivers/rtc/rtc-s35390a.c +++ b/drivers/rtc/rtc-s35390a.c @@ -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 }, { } @@ -193,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); @@ -206,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; @@ -270,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); -- cgit v1.2.3