diff options
author | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-10 10:01:21 -0800 |
---|---|---|
committer | Linus Torvalds <torvalds@linux-foundation.org> | 2015-11-10 10:01:21 -0800 |
commit | 7d884710bb3635f94dac152ae226ca54a585a223 (patch) | |
tree | b38909954296f90a954264d1c3be4d597b67d5d4 /drivers/rtc | |
parent | 3b13866869b8407497d20a916450594e117583e6 (diff) | |
parent | 1e3929ef0e1c4c7127b785ce7a236965b3739406 (diff) | |
download | talos-obmc-linux-7d884710bb3635f94dac152ae226ca54a585a223.tar.gz talos-obmc-linux-7d884710bb3635f94dac152ae226ca54a585a223.zip |
Merge tag 'rtc-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux
Pull RTC updates from Alexandre Belloni:
"Core:
- Fix rtctest error path
New drivers:
- Microcrystal RV8803
Subsystem wide cleanups:
- remove misuse of IRQF_NO_SUSPEND flag
Drivers:
- at91rm9200: clear RTC alarm status flag prior to suspending
- davinci: remove incorrect reference to probe function
- ds1307: Fix alarm programming for mcp794xx
- ds1390: trickle charger support, fix ds1390_get_reg
- isl1208: Pass the IRQF_ONESHOT flag
- opal: fix type of token
- pcf2127: fix RTC_READ_VL, remove useless driver version
- pcf85063: return an error when date is invalid
- pcf8563: add CLKOUT to common clock framework
- rx8025: remove unnecessary braces
- s3c: Set year, month, day value for setting alarm
- stmp3xxx: unify register access macros
- License fixes: pcf2127, da9063
- wakeup-source support for isl12057 and opal"
* tag 'rtc-v4.4' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (23 commits)
rtc: Add a driver for Micro Crystal RV8803
rtc: s3c: Set year, month, day value for setting alarm
rtc: ds1307: Fix alarm programming for mcp794xx
rtc: isl12057: enable support for the standard "wakeup-source" property
rtc: opal: enable support for the stardard "wakeup-source" property
rtc: isl1208: Pass the IRQF_ONESHOT flag
rtc: pcf8563: add CLKOUT to common clock framework
rtc: davinci: remove incorrect reference to probe function
rtc: at91rm9200: clear RTC alarm status flag prior to suspending
rtc: pcf2127: remove useless driver version
rtc: pcf2127: fix reading uninitialized value on RTC_READ_VL ioctl
rtc: stmp3xxx: unify register access macros
rtc: da9063: GPL copyright inconsistency fix
rtc: pcf85063: return an error when date is invalid
rtc: rx8025: remove unnecessary braces
rtc: ds1343: remove misuse of IRQF_NO_SUSPEND flag
rtc: ab8500: remove misuse of IRQF_NO_SUSPEND flag
rtc: pl031: remove misuse of IRQF_NO_SUSPEND flag
rtc: opal: fix type of token
rtc: ds1390: Add trickle charger device tree binding
...
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 13 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-ab8500.c | 6 | ||||
-rw-r--r-- | drivers/rtc/rtc-at91rm9200.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-da9063.c | 18 | ||||
-rw-r--r-- | drivers/rtc/rtc-davinci.c | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1307.c | 4 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1343.c | 9 | ||||
-rw-r--r-- | drivers/rtc/rtc-ds1390.c | 65 | ||||
-rw-r--r-- | drivers/rtc/rtc-isl12057.c | 10 | ||||
-rw-r--r-- | drivers/rtc/rtc-isl1208.c | 2 | ||||
-rw-r--r-- | drivers/rtc/rtc-opal.c | 9 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf2127.c | 47 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf85063.c | 8 | ||||
-rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 170 | ||||
-rw-r--r-- | drivers/rtc/rtc-pl031.c | 13 | ||||
-rw-r--r-- | drivers/rtc/rtc-rv8803.c | 521 | ||||
-rw-r--r-- | drivers/rtc/rtc-rx8025.c | 4 | ||||
-rw-r--r-- | drivers/rtc/rtc-s3c.c | 16 | ||||
-rw-r--r-- | drivers/rtc/rtc-stmp3xxx.c | 27 |
20 files changed, 864 insertions, 82 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 9d4290617cee..2a524244afec 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -593,6 +593,15 @@ config RTC_DRV_RV3029C2 This driver can also be built as a module. If so, the module will be called rtc-rv3029c2. +config RTC_DRV_RV8803 + tristate "Micro Crystal RV8803" + help + If you say yes here you get support for the Micro Crystal + RV8803 RTC chips. + + This driver can also be built as a module. If so, the module + will be called rtc-rv8803. + config RTC_DRV_S5M tristate "Samsung S2M/S5M series" depends on MFD_SEC_CORE @@ -666,8 +675,8 @@ config RTC_DRV_DS1390 If you say yes here you get support for the Dallas/Maxim DS1390/93/94 chips. - This driver only supports the RTC feature, and not other chip - features such as alarms and trickle charging. + This driver supports the RTC feature and trickle charging but not + other chip features such as alarms. This driver can also be built as a module. If so, the module will be called rtc-ds1390. diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index e491eb524434..231f76451615 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -126,6 +126,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.o +obj-$(CONFIG_RTC_DRV_RV8803) += rtc-rv8803.o obj-$(CONFIG_RTC_DRV_RX4581) += rtc-rx4581.o obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o diff --git a/drivers/rtc/rtc-ab8500.c b/drivers/rtc/rtc-ab8500.c index 51407c4c7bd2..24a0af650a1b 100644 --- a/drivers/rtc/rtc-ab8500.c +++ b/drivers/rtc/rtc-ab8500.c @@ -18,6 +18,7 @@ #include <linux/mfd/abx500/ab8500.h> #include <linux/delay.h> #include <linux/of.h> +#include <linux/pm_wakeirq.h> #define AB8500_RTC_SOFF_STAT_REG 0x00 #define AB8500_RTC_CC_CONF_REG 0x01 @@ -493,11 +494,12 @@ static int ab8500_rtc_probe(struct platform_device *pdev) } err = devm_request_threaded_irq(&pdev->dev, irq, NULL, - rtc_alarm_handler, IRQF_NO_SUSPEND | IRQF_ONESHOT, + rtc_alarm_handler, IRQF_ONESHOT, "ab8500-rtc", rtc); if (err < 0) return err; + dev_pm_set_wake_irq(&pdev->dev, irq); platform_set_drvdata(pdev, rtc); err = ab8500_sysfs_rtc_register(&pdev->dev); @@ -513,6 +515,8 @@ static int ab8500_rtc_probe(struct platform_device *pdev) static int ab8500_rtc_remove(struct platform_device *pdev) { + dev_pm_clear_wake_irq(&pdev->dev); + device_init_wakeup(&pdev->dev, false); ab8500_sysfs_rtc_unregister(&pdev->dev); return 0; diff --git a/drivers/rtc/rtc-at91rm9200.c b/drivers/rtc/rtc-at91rm9200.c index cb62e214b52a..b60fd477778f 100644 --- a/drivers/rtc/rtc-at91rm9200.c +++ b/drivers/rtc/rtc-at91rm9200.c @@ -495,6 +495,8 @@ static int at91_rtc_suspend(struct device *dev) /* this IRQ is shared with DBGU and other hardware which isn't * necessarily doing PM like we are... */ + at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM); + at91_rtc_imr = at91_rtc_read_imr() & (AT91_RTC_ALARM|AT91_RTC_SECEV); if (at91_rtc_imr) { diff --git a/drivers/rtc/rtc-da9063.c b/drivers/rtc/rtc-da9063.c index 00a8f7f4f87c..284b587da65c 100644 --- a/drivers/rtc/rtc-da9063.c +++ b/drivers/rtc/rtc-da9063.c @@ -1,15 +1,15 @@ /* rtc-da9063.c - Real time clock device driver for DA9063 - * Copyright (C) 2013-14 Dialog Semiconductor Ltd. + * Copyright (C) 2013-2015 Dialog Semiconductor Ltd. * - * This library is free software; you can redistribute it and/or - * modify it under the terms of the GNU Library General Public - * License as published by the Free Software Foundation; either - * version 2 of the License, or (at your option) any later version. + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; either version 2 + * of the License, or (at your option) any later version. * - * This library is distributed in the hope that it will be useful, + * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * Library General Public License for more details. + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. */ #include <linux/delay.h> @@ -516,5 +516,5 @@ module_platform_driver(da9063_rtc_driver); MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); MODULE_DESCRIPTION("Real time clock device driver for Dialog DA9063"); -MODULE_LICENSE("GPL v2"); +MODULE_LICENSE("GPL"); MODULE_ALIAS("platform:" DA9063_DRVNAME_RTC); diff --git a/drivers/rtc/rtc-davinci.c b/drivers/rtc/rtc-davinci.c index c84f46168a52..c5432bf64e1c 100644 --- a/drivers/rtc/rtc-davinci.c +++ b/drivers/rtc/rtc-davinci.c @@ -546,7 +546,6 @@ static int __exit davinci_rtc_remove(struct platform_device *pdev) } static struct platform_driver davinci_rtc_driver = { - .probe = davinci_rtc_probe, .remove = __exit_p(davinci_rtc_remove), .driver = { .name = "rtc_davinci", diff --git a/drivers/rtc/rtc-ds1307.c b/drivers/rtc/rtc-ds1307.c index a705e6490808..188006c55ce0 100644 --- a/drivers/rtc/rtc-ds1307.c +++ b/drivers/rtc/rtc-ds1307.c @@ -718,9 +718,9 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t) regs[3] = bin2bcd(t->time.tm_sec); regs[4] = bin2bcd(t->time.tm_min); regs[5] = bin2bcd(t->time.tm_hour); - regs[6] = bin2bcd(t->time.tm_wday) + 1; + regs[6] = bin2bcd(t->time.tm_wday + 1); regs[7] = bin2bcd(t->time.tm_mday); - regs[8] = bin2bcd(t->time.tm_mon) + 1; + regs[8] = bin2bcd(t->time.tm_mon + 1); /* Clear the alarm 0 interrupt flag. */ regs[6] &= ~MCP794XX_BIT_ALMX_IF; diff --git a/drivers/rtc/rtc-ds1343.c b/drivers/rtc/rtc-ds1343.c index 07371a9e3793..3d389bd8a289 100644 --- a/drivers/rtc/rtc-ds1343.c +++ b/drivers/rtc/rtc-ds1343.c @@ -21,6 +21,7 @@ #include <linux/rtc.h> #include <linux/bcd.h> #include <linux/pm.h> +#include <linux/pm_wakeirq.h> #include <linux/slab.h> #define DS1343_DRV_VERSION "01.00" @@ -663,15 +664,15 @@ static int ds1343_probe(struct spi_device *spi) if (priv->irq >= 0) { res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, - ds1343_thread, - IRQF_NO_SUSPEND | IRQF_ONESHOT, + ds1343_thread, IRQF_ONESHOT, "ds1343", priv); if (res) { priv->irq = -1; dev_err(&spi->dev, "unable to request irq for rtc ds1343\n"); } else { - device_set_wakeup_capable(&spi->dev, 1); + device_init_wakeup(&spi->dev, true); + dev_pm_set_wake_irq(&spi->dev, spi->irq); } } @@ -692,6 +693,8 @@ static int ds1343_remove(struct spi_device *spi) priv->irqen &= ~RTC_AF; mutex_unlock(&priv->mutex); + dev_pm_clear_wake_irq(&spi->dev); + device_init_wakeup(&spi->dev, false); devm_free_irq(&spi->dev, spi->irq, priv); } diff --git a/drivers/rtc/rtc-ds1390.c b/drivers/rtc/rtc-ds1390.c index 4c229c97ef97..aa0d2c6f1edc 100644 --- a/drivers/rtc/rtc-ds1390.c +++ b/drivers/rtc/rtc-ds1390.c @@ -20,6 +20,7 @@ #include <linux/spi/spi.h> #include <linux/bcd.h> #include <linux/slab.h> +#include <linux/of.h> #define DS1390_REG_100THS 0x00 #define DS1390_REG_SECONDS 0x01 @@ -40,11 +41,31 @@ #define DS1390_REG_STATUS 0x0E #define DS1390_REG_TRICKLE 0x0F +#define DS1390_TRICKLE_CHARGER_ENABLE 0xA0 +#define DS1390_TRICKLE_CHARGER_250_OHM 0x01 +#define DS1390_TRICKLE_CHARGER_2K_OHM 0x02 +#define DS1390_TRICKLE_CHARGER_4K_OHM 0x03 +#define DS1390_TRICKLE_CHARGER_NO_DIODE 0x04 +#define DS1390_TRICKLE_CHARGER_DIODE 0x08 + struct ds1390 { struct rtc_device *rtc; u8 txrx_buf[9]; /* cmd + 8 registers */ }; +static void ds1390_set_reg(struct device *dev, unsigned char address, + unsigned char data) +{ + struct spi_device *spi = to_spi_device(dev); + unsigned char buf[2]; + + /* MSB must be '1' to write */ + buf[0] = address | 0x80; + buf[1] = data; + + spi_write(spi, buf, 2); +} + static int ds1390_get_reg(struct device *dev, unsigned char address, unsigned char *data) { @@ -62,11 +83,50 @@ static int ds1390_get_reg(struct device *dev, unsigned char address, if (status != 0) return status; - *data = chip->txrx_buf[1]; + *data = chip->txrx_buf[0]; return 0; } +static void ds1390_trickle_of_init(struct spi_device *spi) +{ + u32 ohms = 0; + u8 value; + + if (of_property_read_u32(spi->dev.of_node, "trickle-resistor-ohms", + &ohms)) + goto out; + + /* Enable charger */ + value = DS1390_TRICKLE_CHARGER_ENABLE; + if (of_property_read_bool(spi->dev.of_node, "trickle-diode-disable")) + value |= DS1390_TRICKLE_CHARGER_NO_DIODE; + else + value |= DS1390_TRICKLE_CHARGER_DIODE; + + /* Resistor select */ + switch (ohms) { + case 250: + value |= DS1390_TRICKLE_CHARGER_250_OHM; + break; + case 2000: + value |= DS1390_TRICKLE_CHARGER_2K_OHM; + break; + case 4000: + value |= DS1390_TRICKLE_CHARGER_4K_OHM; + break; + default: + dev_warn(&spi->dev, + "Unsupported ohm value %02ux in dt\n", ohms); + return; + } + + ds1390_set_reg(&spi->dev, DS1390_REG_TRICKLE, value); + +out: + return; +} + static int ds1390_read_time(struct device *dev, struct rtc_time *dt) { struct spi_device *spi = to_spi_device(dev); @@ -143,6 +203,9 @@ static int ds1390_probe(struct spi_device *spi) return res; } + if (spi->dev.of_node) + ds1390_trickle_of_init(spi); + chip->rtc = devm_rtc_device_register(&spi->dev, "ds1390", &ds1390_rtc_ops, THIS_MODULE); if (IS_ERR(chip->rtc)) { diff --git a/drivers/rtc/rtc-isl12057.c b/drivers/rtc/rtc-isl12057.c index a0462e5430c7..54328d4ac0d3 100644 --- a/drivers/rtc/rtc-isl12057.c +++ b/drivers/rtc/rtc-isl12057.c @@ -466,9 +466,8 @@ static int isl12057_check_rtc_status(struct device *dev, struct regmap *regmap) * is for instance the case on ReadyNAS 102, 104 and 2120. On those * devices with no IRQ driectly connected to the SoC, the RTC chip * can be forced as a wakeup source by stating that explicitly in - * the device's .dts file using the "isil,irq2-can-wakeup-machine" - * boolean property. This will guarantee 'wakealarm' sysfs entry is - * available on the device. + * the device's .dts file using the "wakeup-source" boolean property. + * This will guarantee 'wakealarm' sysfs entry is available on the device. * * The function below returns 1, i.e. the capability of the chip to * wakeup the device, based on IRQ availability or if the boolean @@ -479,8 +478,9 @@ static bool isl12057_can_wakeup_machine(struct device *dev) { struct isl12057_rtc_data *data = dev_get_drvdata(dev); - return (data->irq || of_property_read_bool(dev->of_node, - "isil,irq2-can-wakeup-machine")); + return data->irq || of_property_read_bool(dev->of_node, "wakeup-source") + || of_property_read_bool(dev->of_node, /* legacy */ + "isil,irq2-can-wakeup-machine"); } #else static bool isl12057_can_wakeup_machine(struct device *dev) diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index aa3b8f1b34d9..b57a304ff62c 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -638,7 +638,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) if (client->irq > 0) { rc = devm_request_threaded_irq(&client->dev, client->irq, NULL, isl1208_rtc_interrupt, - IRQF_SHARED, + IRQF_SHARED | IRQF_ONESHOT, isl1208_driver.driver.name, client); if (!rc) { diff --git a/drivers/rtc/rtc-opal.c b/drivers/rtc/rtc-opal.c index 6fbf9e617151..df39ce02a99d 100644 --- a/drivers/rtc/rtc-opal.c +++ b/drivers/rtc/rtc-opal.c @@ -152,10 +152,10 @@ exit: /* Set Timed Power-On */ static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm) { - u64 h_m_s_ms = 0, token; + u64 h_m_s_ms = 0; struct opal_msg msg; u32 y_m_d = 0; - int rc; + int token, rc; tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms); @@ -199,8 +199,9 @@ static int opal_rtc_probe(struct platform_device *pdev) { struct rtc_device *rtc; - if (pdev->dev.of_node && of_get_property(pdev->dev.of_node, "has-tpo", - NULL)) { + if (pdev->dev.of_node && + (of_property_read_bool(pdev->dev.of_node, "wakeup-source") || + of_property_read_bool(pdev->dev.of_node, "has-tpo")/* legacy */)) { device_set_wakeup_capable(&pdev->dev, true); opal_rtc_ops.read_alarm = opal_get_tpo_time; opal_rtc_ops.set_alarm = opal_set_tpo_time; diff --git a/drivers/rtc/rtc-pcf2127.c b/drivers/rtc/rtc-pcf2127.c index 4b11d31f7174..629bfdf8c745 100644 --- a/drivers/rtc/rtc-pcf2127.c +++ b/drivers/rtc/rtc-pcf2127.c @@ -20,11 +20,12 @@ #include <linux/module.h> #include <linux/of.h> -#define DRV_VERSION "0.0.1" - #define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */ #define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */ + #define PCF2127_REG_CTRL3 (0x02) /* Control Register 3 */ +#define PCF2127_REG_CTRL3_BLF BIT(2) + #define PCF2127_REG_SC (0x03) /* datetime */ #define PCF2127_REG_MN (0x04) #define PCF2127_REG_HR (0x05) @@ -39,8 +40,6 @@ static struct i2c_driver pcf2127_driver; struct pcf2127 { struct rtc_device *rtc; - int voltage_low; /* indicates if a low_voltage was detected */ - int oscillator_failed; /* OSF was detected and date is unreliable */ }; /* @@ -49,7 +48,6 @@ struct pcf2127 { */ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm) { - struct pcf2127 *pcf2127 = i2c_get_clientdata(client); unsigned char buf[10] = { PCF2127_REG_CTRL1 }; /* read registers */ @@ -59,18 +57,15 @@ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm) return -EIO; } - if (buf[PCF2127_REG_CTRL3] & 0x04) { - pcf2127->voltage_low = 1; + if (buf[PCF2127_REG_CTRL3] & PCF2127_REG_CTRL3_BLF) dev_info(&client->dev, "low voltage detected, check/replace RTC battery.\n"); - } if (buf[PCF2127_REG_SC] & PCF2127_OSF) { /* * no need clear the flag here, * it will be cleared once the new date is saved */ - pcf2127->oscillator_failed = 1; dev_warn(&client->dev, "oscillator stop detected, date/time is not reliable\n"); return -EINVAL; @@ -107,7 +102,6 @@ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm) static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm) { - struct pcf2127 *pcf2127 = i2c_get_clientdata(client); unsigned char buf[8]; int i = 0, err; @@ -141,9 +135,6 @@ static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm) return -EIO; } - /* clear OSF flag in client data */ - pcf2127->oscillator_failed = 0; - return 0; } @@ -151,17 +142,28 @@ static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm) static int pcf2127_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) { - struct pcf2127 *pcf2127 = i2c_get_clientdata(to_i2c_client(dev)); + struct i2c_client *client = to_i2c_client(dev); + unsigned char buf = PCF2127_REG_CTRL3; + int touser; + int ret; switch (cmd) { case RTC_VL_READ: - if (pcf2127->voltage_low) - dev_info(dev, "low voltage detected, check/replace battery\n"); - if (pcf2127->oscillator_failed) - dev_info(dev, "oscillator stop detected, date/time is not reliable\n"); + ret = i2c_master_send(client, &buf, 1); + if (!ret) + ret = -EIO; + if (ret < 0) + return ret; + + ret = i2c_master_recv(client, &buf, 1); + if (!ret) + ret = -EIO; + if (ret < 0) + return ret; - if (copy_to_user((void __user *)arg, &pcf2127->voltage_low, - sizeof(int))) + touser = buf & PCF2127_REG_CTRL3_BLF ? 1 : 0; + + if (copy_to_user((void __user *)arg, &touser, sizeof(int))) return -EFAULT; return 0; default: @@ -203,8 +205,6 @@ static int pcf2127_probe(struct i2c_client *client, if (!pcf2127) return -ENOMEM; - dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n"); - i2c_set_clientdata(client, pcf2127); pcf2127->rtc = devm_rtc_device_register(&client->dev, @@ -241,5 +241,4 @@ module_i2c_driver(pcf2127_driver); MODULE_AUTHOR("Renaud Cerrato <r.cerrato@til-technologies.fr>"); MODULE_DESCRIPTION("NXP PCF2127 RTC driver"); -MODULE_LICENSE("GPL"); -MODULE_VERSION(DRV_VERSION); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-pcf85063.c b/drivers/rtc/rtc-pcf85063.c index b6d73dd881f2..63334cbeca41 100644 --- a/drivers/rtc/rtc-pcf85063.c +++ b/drivers/rtc/rtc-pcf85063.c @@ -80,13 +80,7 @@ static int pcf85063_get_datetime(struct i2c_client *client, struct rtc_time *tm) pcf85063->c_polarity = (buf[PCF85063_REG_MO] & PCF85063_MO_C) ? (tm->tm_year >= 100) : (tm->tm_year < 100); - /* the clock can give out invalid datetime, but we cannot return - * -EINVAL otherwise hwclock will refuse to set the time on bootup. - */ - if (rtc_valid_tm(tm) < 0) - dev_err(&client->dev, "retrieved date/time is not valid.\n"); - - return 0; + return rtc_valid_tm(tm); } static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm) diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index e569243db57e..c8f95b8e463a 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -14,6 +14,7 @@ * published by the Free Software Foundation. */ +#include <linux/clk-provider.h> #include <linux/i2c.h> #include <linux/bcd.h> #include <linux/rtc.h> @@ -40,7 +41,14 @@ #define PCF8563_REG_AMN 0x09 /* alarm */ -#define PCF8563_REG_CLKO 0x0D /* clock out */ +#define PCF8563_REG_CLKO 0x0D /* clock out */ +#define PCF8563_REG_CLKO_FE 0x80 /* clock out enabled */ +#define PCF8563_REG_CLKO_F_MASK 0x03 /* frequenc mask */ +#define PCF8563_REG_CLKO_F_32768HZ 0x00 +#define PCF8563_REG_CLKO_F_1024HZ 0x01 +#define PCF8563_REG_CLKO_F_32HZ 0x02 +#define PCF8563_REG_CLKO_F_1HZ 0x03 + #define PCF8563_REG_TMRC 0x0E /* timer control */ #define PCF8563_TMRC_ENABLE BIT(7) #define PCF8563_TMRC_4096 0 @@ -76,6 +84,9 @@ struct pcf8563 { int voltage_low; /* incicates if a low_voltage was detected */ struct i2c_client *client; +#ifdef CONFIG_COMMON_CLK + struct clk_hw clkout_hw; +#endif }; static int pcf8563_read_block_data(struct i2c_client *client, unsigned char reg, @@ -390,6 +401,158 @@ static int pcf8563_irq_enable(struct device *dev, unsigned int enabled) return pcf8563_set_alarm_mode(to_i2c_client(dev), !!enabled); } +#ifdef CONFIG_COMMON_CLK +/* + * Handling of the clkout + */ + +#define clkout_hw_to_pcf8563(_hw) container_of(_hw, struct pcf8563, clkout_hw) + +static int clkout_rates[] = { + 32768, + 1024, + 32, + 1, +}; + +static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + struct i2c_client *client = pcf8563->client; + unsigned char buf; + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + + if (ret < 0) + return 0; + + buf &= PCF8563_REG_CLKO_F_MASK; + return clkout_rates[ret]; +} + +static long pcf8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate, + unsigned long *prate) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] <= rate) + return clkout_rates[i]; + + return 0; +} + +static int pcf8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, + unsigned long parent_rate) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + struct i2c_client *client = pcf8563->client; + unsigned char buf; + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + int i; + + if (ret < 0) + return ret; + + for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) + if (clkout_rates[i] == rate) { + buf &= ~PCF8563_REG_CLKO_F_MASK; + buf |= i; + ret = pcf8563_write_block_data(client, + PCF8563_REG_CLKO, 1, + &buf); + return ret; + } + + return -EINVAL; +} + +static int pcf8563_clkout_control(struct clk_hw *hw, bool enable) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + struct i2c_client *client = pcf8563->client; + unsigned char buf; + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + + if (ret < 0) + return ret; + + if (enable) + buf |= PCF8563_REG_CLKO_FE; + else + buf &= ~PCF8563_REG_CLKO_FE; + + ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); + return ret; +} + +static int pcf8563_clkout_prepare(struct clk_hw *hw) +{ + return pcf8563_clkout_control(hw, 1); +} + +static void pcf8563_clkout_unprepare(struct clk_hw *hw) +{ + pcf8563_clkout_control(hw, 0); +} + +static int pcf8563_clkout_is_prepared(struct clk_hw *hw) +{ + struct pcf8563 *pcf8563 = clkout_hw_to_pcf8563(hw); + struct i2c_client *client = pcf8563->client; + unsigned char buf; + int ret = pcf8563_read_block_data(client, PCF8563_REG_CLKO, 1, &buf); + + if (ret < 0) + return ret; + + return !!(buf & PCF8563_REG_CLKO_FE); +} + +static const struct clk_ops pcf8563_clkout_ops = { + .prepare = pcf8563_clkout_prepare, + .unprepare = pcf8563_clkout_unprepare, + .is_prepared = pcf8563_clkout_is_prepared, + .recalc_rate = pcf8563_clkout_recalc_rate, + .round_rate = pcf8563_clkout_round_rate, + .set_rate = pcf8563_clkout_set_rate, +}; + +static struct clk *pcf8563_clkout_register_clk(struct pcf8563 *pcf8563) +{ + struct i2c_client *client = pcf8563->client; + struct device_node *node = client->dev.of_node; + struct clk *clk; + struct clk_init_data init; + int ret; + unsigned char buf; + + /* disable the clkout output */ + buf = 0; + ret = pcf8563_write_block_data(client, PCF8563_REG_CLKO, 1, &buf); + if (ret < 0) + return ERR_PTR(ret); + + init.name = "pcf8563-clkout"; + init.ops = &pcf8563_clkout_ops; + init.flags = CLK_IS_ROOT; + init.parent_names = NULL; + init.num_parents = 0; + pcf8563->clkout_hw.init = &init; + + /* optional override of the clockname */ + of_property_read_string(node, "clock-output-names", &init.name); + + /* register the clock */ + clk = devm_clk_register(&client->dev, &pcf8563->clkout_hw); + + if (!IS_ERR(clk)) + of_clk_add_provider(node, of_clk_src_simple_get, clk); + + return clk; +} +#endif + static const struct rtc_class_ops pcf8563_rtc_ops = { .ioctl = pcf8563_rtc_ioctl, .read_time = pcf8563_rtc_read_time, @@ -459,6 +622,11 @@ static int pcf8563_probe(struct i2c_client *client, } +#ifdef CONFIG_COMMON_CLK + /* register clk in common clk framework */ + pcf8563_clkout_register_clk(pcf8563); +#endif + /* the pcf8563 alarm only supports a minute accuracy */ pcf8563->rtc->uie_unsupported = 1; diff --git a/drivers/rtc/rtc-pl031.c b/drivers/rtc/rtc-pl031.c index 41dcb7ddb906..e1687e19c59f 100644 --- a/drivers/rtc/rtc-pl031.c +++ b/drivers/rtc/rtc-pl031.c @@ -23,6 +23,7 @@ #include <linux/io.h> #include <linux/bcd.h> #include <linux/delay.h> +#include <linux/pm_wakeirq.h> #include <linux/slab.h> /* @@ -305,6 +306,8 @@ static int pl031_remove(struct amba_device *adev) { struct pl031_local *ldata = dev_get_drvdata(&adev->dev); + dev_pm_clear_wake_irq(&adev->dev); + device_init_wakeup(&adev->dev, false); free_irq(adev->irq[0], ldata); rtc_device_unregister(ldata->rtc); iounmap(ldata->base); @@ -370,7 +373,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) } } - device_init_wakeup(&adev->dev, 1); + device_init_wakeup(&adev->dev, true); ldata->rtc = rtc_device_register("pl031", &adev->dev, ops, THIS_MODULE); if (IS_ERR(ldata->rtc)) { @@ -383,7 +386,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id) ret = -EIO; goto out_no_irq; } - + dev_pm_set_wake_irq(&adev->dev, adev->irq[0]); return 0; out_no_irq: @@ -408,7 +411,6 @@ static struct pl031_vendor_data arm_pl031 = { .set_alarm = pl031_set_alarm, .alarm_irq_enable = pl031_alarm_irq_enable, }, - .irqflags = IRQF_NO_SUSPEND, }; /* The First ST derivative */ @@ -422,7 +424,6 @@ static struct pl031_vendor_data stv1_pl031 = { }, .clockwatch = true, .st_weekday = true, - .irqflags = IRQF_NO_SUSPEND, }; /* And the second ST derivative */ @@ -439,8 +440,10 @@ static struct pl031_vendor_data stv2_pl031 = { /* * This variant shares the IRQ with another block and must not * suspend that IRQ line. + * TODO check if it shares with IRQF_NO_SUSPEND user, else we can + * remove IRQF_COND_SUSPEND */ - .irqflags = IRQF_SHARED | IRQF_NO_SUSPEND, + .irqflags = IRQF_SHARED | IRQF_COND_SUSPEND, }; static struct amba_id pl031_ids[] = { diff --git a/drivers/rtc/rtc-rv8803.c b/drivers/rtc/rtc-rv8803.c new file mode 100644 index 000000000000..e7329e21bfe3 --- /dev/null +++ b/drivers/rtc/rtc-rv8803.c @@ -0,0 +1,521 @@ +/* + * RTC driver for the Micro Crystal RV8803 + * + * Copyright (C) 2015 Micro Crystal SA + * + * Alexandre Belloni <alexandre.belloni@free-electrons.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ + +#include <linux/bcd.h> +#include <linux/bitops.h> +#include <linux/i2c.h> +#include <linux/interrupt.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rtc.h> + +#define RV8803_SEC 0x00 +#define RV8803_MIN 0x01 +#define RV8803_HOUR 0x02 +#define RV8803_WEEK 0x03 +#define RV8803_DAY 0x04 +#define RV8803_MONTH 0x05 +#define RV8803_YEAR 0x06 +#define RV8803_RAM 0x07 +#define RV8803_ALARM_MIN 0x08 +#define RV8803_ALARM_HOUR 0x09 +#define RV8803_ALARM_WEEK_OR_DAY 0x0A +#define RV8803_EXT 0x0D +#define RV8803_FLAG 0x0E +#define RV8803_CTRL 0x0F + +#define RV8803_EXT_WADA BIT(6) + +#define RV8803_FLAG_V1F BIT(0) +#define RV8803_FLAG_V2F BIT(1) +#define RV8803_FLAG_AF BIT(3) +#define RV8803_FLAG_TF BIT(4) +#define RV8803_FLAG_UF BIT(5) + +#define RV8803_CTRL_RESET BIT(0) + +#define RV8803_CTRL_EIE BIT(2) +#define RV8803_CTRL_AIE BIT(3) +#define RV8803_CTRL_TIE BIT(4) +#define RV8803_CTRL_UIE BIT(5) + +struct rv8803_data { + struct i2c_client *client; + struct rtc_device *rtc; + spinlock_t flags_lock; + u8 ctrl; +}; + +static irqreturn_t rv8803_handle_irq(int irq, void *dev_id) +{ + struct i2c_client *client = dev_id; + struct rv8803_data *rv8803 = i2c_get_clientdata(client); + unsigned long events = 0; + u8 flags; + + spin_lock(&rv8803->flags_lock); + + flags = i2c_smbus_read_byte_data(client, RV8803_FLAG); + if (flags <= 0) { + spin_unlock(&rv8803->flags_lock); + return IRQ_NONE; + } + + if (flags & RV8803_FLAG_V1F) + dev_warn(&client->dev, "Voltage low, temperature compensation stopped.\n"); + + if (flags & RV8803_FLAG_V2F) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + if (flags & RV8803_FLAG_TF) { + flags &= ~RV8803_FLAG_TF; + rv8803->ctrl &= ~RV8803_CTRL_TIE; + events |= RTC_PF; + } + + if (flags & RV8803_FLAG_AF) { + flags &= ~RV8803_FLAG_AF; + rv8803->ctrl &= ~RV8803_CTRL_AIE; + events |= RTC_AF; + } + + if (flags & RV8803_FLAG_UF) { + flags &= ~RV8803_FLAG_UF; + rv8803->ctrl &= ~RV8803_CTRL_UIE; + events |= RTC_UF; + } + + if (events) { + rtc_update_irq(rv8803->rtc, 1, events); + i2c_smbus_write_byte_data(client, RV8803_FLAG, flags); + i2c_smbus_write_byte_data(rv8803->client, RV8803_CTRL, + rv8803->ctrl); + } + + spin_unlock(&rv8803->flags_lock); + + return IRQ_HANDLED; +} + +static int rv8803_get_time(struct device *dev, struct rtc_time *tm) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + u8 date1[7]; + u8 date2[7]; + u8 *date = date1; + int ret, flags; + + flags = i2c_smbus_read_byte_data(rv8803->client, RV8803_FLAG); + if (flags < 0) + return flags; + + if (flags & RV8803_FLAG_V2F) { + dev_warn(dev, "Voltage low, data is invalid.\n"); + return -EINVAL; + } + + ret = i2c_smbus_read_i2c_block_data(rv8803->client, RV8803_SEC, + 7, date); + if (ret != 7) + return ret < 0 ? ret : -EIO; + + if ((date1[RV8803_SEC] & 0x7f) == bin2bcd(59)) { + ret = i2c_smbus_read_i2c_block_data(rv8803->client, RV8803_SEC, + 7, date2); + if (ret != 7) + return ret < 0 ? ret : -EIO; + + if ((date2[RV8803_SEC] & 0x7f) != bin2bcd(59)) + date = date2; + } + + tm->tm_sec = bcd2bin(date[RV8803_SEC] & 0x7f); + tm->tm_min = bcd2bin(date[RV8803_MIN] & 0x7f); + tm->tm_hour = bcd2bin(date[RV8803_HOUR] & 0x3f); + tm->tm_wday = ffs(date[RV8803_WEEK] & 0x7f); + tm->tm_mday = bcd2bin(date[RV8803_DAY] & 0x3f); + tm->tm_mon = bcd2bin(date[RV8803_MONTH] & 0x1f) - 1; + tm->tm_year = bcd2bin(date[RV8803_YEAR]) + 100; + + return rtc_valid_tm(tm); +} + +static int rv8803_set_time(struct device *dev, struct rtc_time *tm) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + u8 date[7]; + int flags, ret; + unsigned long irqflags; + + if ((tm->tm_year < 100) || (tm->tm_year > 199)) + return -EINVAL; + + date[RV8803_SEC] = bin2bcd(tm->tm_sec); + date[RV8803_MIN] = bin2bcd(tm->tm_min); + date[RV8803_HOUR] = bin2bcd(tm->tm_hour); + date[RV8803_WEEK] = 1 << (tm->tm_wday); + date[RV8803_DAY] = bin2bcd(tm->tm_mday); + date[RV8803_MONTH] = bin2bcd(tm->tm_mon + 1); + date[RV8803_YEAR] = bin2bcd(tm->tm_year - 100); + + ret = i2c_smbus_write_i2c_block_data(rv8803->client, RV8803_SEC, + 7, date); + if (ret < 0) + return ret; + + spin_lock_irqsave(&rv8803->flags_lock, irqflags); + + flags = i2c_smbus_read_byte_data(rv8803->client, RV8803_FLAG); + if (flags < 0) { + spin_unlock_irqrestore(&rv8803->flags_lock, irqflags); + return flags; + } + + ret = i2c_smbus_write_byte_data(rv8803->client, RV8803_FLAG, + flags & ~RV8803_FLAG_V2F); + + spin_unlock_irqrestore(&rv8803->flags_lock, irqflags); + + return ret; +} + +static int rv8803_get_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + struct i2c_client *client = rv8803->client; + u8 alarmvals[3]; + int flags, ret; + + ret = i2c_smbus_read_i2c_block_data(client, RV8803_ALARM_MIN, + 3, alarmvals); + if (ret != 3) + return ret < 0 ? ret : -EIO; + + flags = i2c_smbus_read_byte_data(client, RV8803_FLAG); + if (flags < 0) + return flags; + + alrm->time.tm_sec = 0; + alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f); + alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f); + alrm->time.tm_wday = -1; + alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f); + alrm->time.tm_mon = -1; + alrm->time.tm_year = -1; + + alrm->enabled = !!(rv8803->ctrl & RV8803_CTRL_AIE); + alrm->pending = (flags & RV8803_FLAG_AF) && alrm->enabled; + + return 0; +} + +static int rv8803_set_alarm(struct device *dev, struct rtc_wkalrm *alrm) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + u8 alarmvals[3]; + u8 ctrl[2]; + int ret, err; + unsigned long irqflags; + + /* The alarm has no seconds, round up to nearest minute */ + if (alrm->time.tm_sec) { + time64_t alarm_time = rtc_tm_to_time64(&alrm->time); + + alarm_time += 60 - alrm->time.tm_sec; + rtc_time64_to_tm(alarm_time, &alrm->time); + } + + spin_lock_irqsave(&rv8803->flags_lock, irqflags); + + ret = i2c_smbus_read_i2c_block_data(client, RV8803_FLAG, 2, ctrl); + if (ret != 2) { + spin_unlock_irqrestore(&rv8803->flags_lock, irqflags); + return ret < 0 ? ret : -EIO; + } + + alarmvals[0] = bin2bcd(alrm->time.tm_min); + alarmvals[1] = bin2bcd(alrm->time.tm_hour); + alarmvals[2] = bin2bcd(alrm->time.tm_mday); + + if (rv8803->ctrl & (RV8803_CTRL_AIE | RV8803_CTRL_UIE)) { + rv8803->ctrl &= ~(RV8803_CTRL_AIE | RV8803_CTRL_UIE); + err = i2c_smbus_write_byte_data(rv8803->client, RV8803_CTRL, + rv8803->ctrl); + if (err) { + spin_unlock_irqrestore(&rv8803->flags_lock, irqflags); + return err; + } + } + + ctrl[1] &= ~RV8803_FLAG_AF; + err = i2c_smbus_write_byte_data(rv8803->client, RV8803_FLAG, ctrl[1]); + spin_unlock_irqrestore(&rv8803->flags_lock, irqflags); + if (err) + return err; + + err = i2c_smbus_write_i2c_block_data(rv8803->client, RV8803_ALARM_MIN, + 3, alarmvals); + if (err) + return err; + + if (alrm->enabled) { + if (rv8803->rtc->uie_rtctimer.enabled) + rv8803->ctrl |= RV8803_CTRL_UIE; + if (rv8803->rtc->aie_timer.enabled) + rv8803->ctrl |= RV8803_CTRL_AIE; + + err = i2c_smbus_write_byte_data(rv8803->client, RV8803_CTRL, + rv8803->ctrl); + if (err) + return err; + } + + return 0; +} + +static int rv8803_alarm_irq_enable(struct device *dev, unsigned int enabled) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + int ctrl, flags, err; + unsigned long irqflags; + + ctrl = rv8803->ctrl; + + if (enabled) { + if (rv8803->rtc->uie_rtctimer.enabled) + ctrl |= RV8803_CTRL_UIE; + if (rv8803->rtc->aie_timer.enabled) + ctrl |= RV8803_CTRL_AIE; + } else { + if (!rv8803->rtc->uie_rtctimer.enabled) + ctrl &= ~RV8803_CTRL_UIE; + if (!rv8803->rtc->aie_timer.enabled) + ctrl &= ~RV8803_CTRL_AIE; + } + + spin_lock_irqsave(&rv8803->flags_lock, irqflags); + flags = i2c_smbus_read_byte_data(client, RV8803_FLAG); + if (flags < 0) { + spin_unlock_irqrestore(&rv8803->flags_lock, irqflags); + return flags; + } + flags &= ~(RV8803_FLAG_AF | RV8803_FLAG_UF); + err = i2c_smbus_write_byte_data(client, RV8803_FLAG, flags); + spin_unlock_irqrestore(&rv8803->flags_lock, irqflags); + if (err) + return err; + + if (ctrl != rv8803->ctrl) { + rv8803->ctrl = ctrl; + err = i2c_smbus_write_byte_data(client, RV8803_CTRL, + rv8803->ctrl); + if (err) + return err; + } + + return 0; +} + +static int rv8803_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct i2c_client *client = to_i2c_client(dev); + struct rv8803_data *rv8803 = dev_get_drvdata(dev); + int flags, ret = 0; + unsigned long irqflags; + + switch (cmd) { + case RTC_VL_READ: + flags = i2c_smbus_read_byte_data(client, RV8803_FLAG); + if (flags < 0) + return flags; + + if (flags & RV8803_FLAG_V1F) + dev_warn(&client->dev, "Voltage low, temperature compensation stopped.\n"); + + if (flags & RV8803_FLAG_V2F) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + flags &= RV8803_FLAG_V1F | RV8803_FLAG_V2F; + + if (copy_to_user((void __user *)arg, &flags, sizeof(int))) + return -EFAULT; + + return 0; + + case RTC_VL_CLR: + spin_lock_irqsave(&rv8803->flags_lock, irqflags); + flags = i2c_smbus_read_byte_data(client, RV8803_FLAG); + if (flags < 0) { + spin_unlock_irqrestore(&rv8803->flags_lock, irqflags); + return flags; + } + + flags &= ~(RV8803_FLAG_V1F | RV8803_FLAG_V2F); + ret = i2c_smbus_write_byte_data(client, RV8803_FLAG, flags); + spin_unlock_irqrestore(&rv8803->flags_lock, irqflags); + if (ret < 0) + return ret; + + return 0; + + default: + return -ENOIOCTLCMD; + } +} + +static ssize_t rv8803_nvram_write(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_write_byte_data(client, RV8803_RAM, buf[0]); + if (ret < 0) + return ret; + + return 1; +} + +static ssize_t rv8803_nvram_read(struct file *filp, struct kobject *kobj, + struct bin_attribute *attr, + char *buf, loff_t off, size_t count) +{ + struct device *dev = kobj_to_dev(kobj); + struct i2c_client *client = to_i2c_client(dev); + int ret; + + ret = i2c_smbus_read_byte_data(client, RV8803_RAM); + if (ret < 0) + return ret; + + buf[0] = ret; + + return 1; +} + +static struct bin_attribute rv8803_nvram_attr = { + .attr = { + .name = "nvram", + .mode = S_IRUGO | S_IWUSR, + }, + .size = 1, + .read = rv8803_nvram_read, + .write = rv8803_nvram_write, +}; + +static struct rtc_class_ops rv8803_rtc_ops = { + .read_time = rv8803_get_time, + .set_time = rv8803_set_time, + .ioctl = rv8803_ioctl, +}; + +static int rv8803_probe(struct i2c_client *client, + const struct i2c_device_id *id) +{ + struct i2c_adapter *adapter = to_i2c_adapter(client->dev.parent); + struct rv8803_data *rv8803; + int err, flags; + + if (!i2c_check_functionality(adapter, I2C_FUNC_SMBUS_BYTE_DATA | + I2C_FUNC_SMBUS_I2C_BLOCK)) { + dev_err(&adapter->dev, "doesn't support I2C_FUNC_SMBUS_BYTE_DATA | I2C_FUNC_SMBUS_I2C_BLOCK\n"); + return -EIO; + } + + rv8803 = devm_kzalloc(&client->dev, sizeof(struct rv8803_data), + GFP_KERNEL); + if (!rv8803) + return -ENOMEM; + + rv8803->client = client; + i2c_set_clientdata(client, rv8803); + + flags = i2c_smbus_read_byte_data(client, RV8803_FLAG); + if (flags < 0) + return flags; + + if (flags & RV8803_FLAG_V1F) + dev_warn(&client->dev, "Voltage low, temperature compensation stopped.\n"); + + if (flags & RV8803_FLAG_V2F) + dev_warn(&client->dev, "Voltage low, data loss detected.\n"); + + if (flags & RV8803_FLAG_AF) + dev_warn(&client->dev, "An alarm maybe have been missed.\n"); + + if (client->irq > 0) { + err = devm_request_threaded_irq(&client->dev, client->irq, + NULL, rv8803_handle_irq, + IRQF_TRIGGER_LOW | IRQF_ONESHOT, + "rv8803", client); + if (err) { + dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n"); + client->irq = 0; + } else { + rv8803_rtc_ops.read_alarm = rv8803_get_alarm; + rv8803_rtc_ops.set_alarm = rv8803_set_alarm; + rv8803_rtc_ops.alarm_irq_enable = rv8803_alarm_irq_enable; + } + } + + rv8803->rtc = devm_rtc_device_register(&client->dev, client->name, + &rv8803_rtc_ops, THIS_MODULE); + if (IS_ERR(rv8803->rtc)) { + dev_err(&client->dev, "unable to register the class device\n"); + return PTR_ERR(rv8803->rtc); + } + + err = i2c_smbus_write_byte_data(rv8803->client, RV8803_EXT, + RV8803_EXT_WADA); + if (err) + return err; + + err = device_create_bin_file(&client->dev, &rv8803_nvram_attr); + if (err) + return err; + + rv8803->rtc->max_user_freq = 1; + + return 0; +} + +static int rv8803_remove(struct i2c_client *client) +{ + device_remove_bin_file(&client->dev, &rv8803_nvram_attr); + + return 0; +} + +static const struct i2c_device_id rv8803_id[] = { + { "rv8803", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, rv8803_id); + +static struct i2c_driver rv8803_driver = { + .driver = { + .name = "rtc-rv8803", + }, + .probe = rv8803_probe, + .remove = rv8803_remove, + .id_table = rv8803_id, +}; +module_i2c_driver(rv8803_driver); + +MODULE_AUTHOR("Alexandre Belloni <alexandre.belloni@free-electrons.com>"); +MODULE_DESCRIPTION("Micro Crystal RV8803 RTC driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/rtc/rtc-rx8025.c b/drivers/rtc/rtc-rx8025.c index 24c3d69ce1b9..bd911bafb809 100644 --- a/drivers/rtc/rtc-rx8025.c +++ b/drivers/rtc/rtc-rx8025.c @@ -65,6 +65,7 @@ static const struct i2c_device_id rx8025_id[] = { { "rx8025", 0 }, + { "rv8803", 1 }, { } }; MODULE_DEVICE_TABLE(i2c, rx8025_id); @@ -518,9 +519,8 @@ static int rx8025_probe(struct i2c_client *client, } rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL); - if (!rx8025) { + if (!rx8025) return -ENOMEM; - } rx8025->client = client; i2c_set_clientdata(client, rx8025); diff --git a/drivers/rtc/rtc-s3c.c b/drivers/rtc/rtc-s3c.c index 7cc8f73a3fe8..ffb860d18701 100644 --- a/drivers/rtc/rtc-s3c.c +++ b/drivers/rtc/rtc-s3c.c @@ -302,6 +302,7 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) struct s3c_rtc *info = dev_get_drvdata(dev); struct rtc_time *tm = &alrm->time; unsigned int alrm_en; + int year = tm->tm_year - 100; dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", alrm->enabled, @@ -328,6 +329,21 @@ static int s3c_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm) writeb(bin2bcd(tm->tm_hour), info->base + S3C2410_ALMHOUR); } + if (year < 100 && year >= 0) { + alrm_en |= S3C2410_RTCALM_YEAREN; + writeb(bin2bcd(year), info->base + S3C2410_ALMYEAR); + } + + if (tm->tm_mon < 12 && tm->tm_mon >= 0) { + alrm_en |= S3C2410_RTCALM_MONEN; + writeb(bin2bcd(tm->tm_mon + 1), info->base + S3C2410_ALMMON); + } + + if (tm->tm_mday <= 31 && tm->tm_mday >= 1) { + alrm_en |= S3C2410_RTCALM_DAYEN; + writeb(bin2bcd(tm->tm_mday), info->base + S3C2410_ALMDATE); + } + dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en); writeb(alrm_en, info->base + S3C2410_RTCALM); diff --git a/drivers/rtc/rtc-stmp3xxx.c b/drivers/rtc/rtc-stmp3xxx.c index eb09eddf39b8..ca54d039da31 100644 --- a/drivers/rtc/rtc-stmp3xxx.c +++ b/drivers/rtc/rtc-stmp3xxx.c @@ -32,8 +32,6 @@ #include <linux/stmp3xxx_rtc_wdt.h> #define STMP3XXX_RTC_CTRL 0x0 -#define STMP3XXX_RTC_CTRL_SET 0x4 -#define STMP3XXX_RTC_CTRL_CLR 0x8 #define STMP3XXX_RTC_CTRL_ALARM_IRQ_EN 0x00000001 #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002 #define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004 @@ -52,8 +50,6 @@ #define STMP3XXX_RTC_WATCHDOG 0x50 #define STMP3XXX_RTC_PERSISTENT0 0x60 -#define STMP3XXX_RTC_PERSISTENT0_SET 0x64 -#define STMP3XXX_RTC_PERSISTENT0_CLR 0x68 #define STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE (1 << 0) #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN (1 << 1) #define STMP3XXX_RTC_PERSISTENT0_ALARM_EN (1 << 2) @@ -179,7 +175,7 @@ static irqreturn_t stmp3xxx_rtc_interrupt(int irq, void *dev_id) if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) { writel(STMP3XXX_RTC_CTRL_ALARM_IRQ, - rtc_data->io + STMP3XXX_RTC_CTRL_CLR); + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); rtc_update_irq(rtc_data->rtc, 1, RTC_AF | RTC_IRQF); return IRQ_HANDLED; } @@ -194,15 +190,17 @@ static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled) if (enabled) { writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, - rtc_data->io + STMP3XXX_RTC_PERSISTENT0_SET); + rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + + STMP_OFFSET_REG_SET); writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, - rtc_data->io + STMP3XXX_RTC_CTRL_SET); + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_SET); } else { writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN, - rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); + rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + + STMP_OFFSET_REG_CLR); writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, - rtc_data->io + STMP3XXX_RTC_CTRL_CLR); + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); } return 0; } @@ -245,7 +243,7 @@ static int stmp3xxx_rtc_remove(struct platform_device *pdev) return 0; writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, - rtc_data->io + STMP3XXX_RTC_CTRL_CLR); + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); return 0; } @@ -334,16 +332,17 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev) STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE; } - writel(pers0_set, rtc_data->io + STMP3XXX_RTC_PERSISTENT0_SET); + writel(pers0_set, rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + + STMP_OFFSET_REG_SET); writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE | pers0_clr, - rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); + rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + STMP_OFFSET_REG_CLR); writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN | STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, - rtc_data->io + STMP3XXX_RTC_CTRL_CLR); + rtc_data->io + STMP3XXX_RTC_CTRL + STMP_OFFSET_REG_CLR); rtc_data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name, &stmp3xxx_rtc_ops, THIS_MODULE); @@ -376,7 +375,7 @@ static int stmp3xxx_rtc_resume(struct device *dev) writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, - rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); + rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + STMP_OFFSET_REG_CLR); return 0; } #endif |