RTC for 4.4

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
 -----BEGIN PGP SIGNATURE-----
 Version: GnuPG v1
 
 iQIcBAABCAAGBQJWP0pTAAoJEKbNnwlvZCyzlt8P/0vtyp1BHNhgGphevBb5s3My
 fx84mvcGBj9EMAdoN7rn2YjdQDRn+Cmj3CjZ83XNKb8ulXSFU8NAKPSHL7aIVjKL
 2/ZhSKF1FBDzqD2aGaJsE8z9myHjttWEX+LXmpcKsIAlNKJ7RpXKc6CuN4I075Ef
 CUOWNcDdrbWr2ex2+jd+pRZP45ZSNstI66LTYCD/H6amq31nEeIuZCtRT4YkQ0eG
 zKV+I5M3NHmaExNy0Doj0cbCmekjrYNx2iXiucNEoyiIHzbZxg9i1MbYEUnkUn9J
 3bNS14yf1y6QSh12Muqjw3dYGJj+/aolqLAEABct2IsoH25YPDTX1ajkz5pk46B6
 1CS5xbFU99nVOIgr1luSUZAxsz2ZTWwxdaky5DT3iToBkPTNB9el+AOaoC0WosaU
 5SimYbwQ4taOREjKHnCwSpZwTMWISlYQmUM2q/95IS+S/zwj+Su79EOmFEldBof7
 L/Ni4ns3Lu+G+xfko63PZUOy4RbqJPW240ulp2B3IricT1bXd+6glrqp0c7OFWFr
 7bXlykVb7WVwHYSASi9nB7Gmp9XlafVbe5iJluUf5Xx+NHUKsTS9YkTHeDLLeyv+
 nhDd0rFq/O08sakASfBz9uF5GySkpJgg66RcadgvP/6qRms2SQCqypqSVEEtBgzL
 u7i9UuQ2UkIetJQKVhVu
 =sY2D
 -----END PGP SIGNATURE-----

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
  ...
This commit is contained in:
Linus Torvalds 2015-11-10 10:01:21 -08:00
commit 7d884710bb
23 changed files with 908 additions and 83 deletions

View File

@ -0,0 +1,18 @@
* Dallas DS1390 SPI Serial Real-Time Clock
Required properties:
- compatible: Should contain "dallas,ds1390".
- reg: SPI address for chip
Optional properties:
- trickle-resistor-ohms : Selected resistor for trickle charger
Values usable for ds1390 are 250, 2000, 4000
Should be given if trickle charger should be enabled
- trickle-diode-disable : Do not use internal trickle charger diode
Should be given if internal trickle charger diode should be disabled
Example:
ds1390: rtc@68 {
compatible = "dallas,ds1390";
trickle-resistor-ohms = <250>;
reg = <0>;
};

View File

@ -0,0 +1,25 @@
* Philips PCF8563/Epson RTC8564 Real Time Clock
Philips PCF8563/Epson RTC8564 Real Time Clock
Required properties:
see: Documentation/devicetree/bindings/i2c/trivial-devices.txt
Optional property:
- #clock-cells: Should be 0.
- clock-output-names:
overwrite the default clock name "pcf8563-clkout"
Example:
pcf8563: pcf8563@51 {
compatible = "nxp,pcf8563";
reg = <0x51>;
#clock-cells = <0>;
};
device {
...
clocks = <&pcf8563>;
...
};

View File

@ -593,6 +593,15 @@ config RTC_DRV_RV3029C2
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called rtc-rv3029c2. 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 config RTC_DRV_S5M
tristate "Samsung S2M/S5M series" tristate "Samsung S2M/S5M series"
depends on MFD_SEC_CORE depends on MFD_SEC_CORE
@ -666,8 +675,8 @@ config RTC_DRV_DS1390
If you say yes here you get support for the If you say yes here you get support for the
Dallas/Maxim DS1390/93/94 chips. Dallas/Maxim DS1390/93/94 chips.
This driver only supports the RTC feature, and not other chip This driver supports the RTC feature and trickle charging but not
features such as alarms and trickle charging. other chip features such as alarms.
This driver can also be built as a module. If so, the module This driver can also be built as a module. If so, the module
will be called rtc-ds1390. will be called rtc-ds1390.

View File

@ -126,6 +126,7 @@ obj-$(CONFIG_RTC_DRV_RS5C313) += rtc-rs5c313.o
obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o obj-$(CONFIG_RTC_DRV_RS5C348) += rtc-rs5c348.o
obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o obj-$(CONFIG_RTC_DRV_RS5C372) += rtc-rs5c372.o
obj-$(CONFIG_RTC_DRV_RV3029C2) += rtc-rv3029c2.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_RX4581) += rtc-rx4581.o
obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o obj-$(CONFIG_RTC_DRV_RX8025) += rtc-rx8025.o
obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o obj-$(CONFIG_RTC_DRV_RX8581) += rtc-rx8581.o

View File

@ -18,6 +18,7 @@
#include <linux/mfd/abx500/ab8500.h> #include <linux/mfd/abx500/ab8500.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/of.h> #include <linux/of.h>
#include <linux/pm_wakeirq.h>
#define AB8500_RTC_SOFF_STAT_REG 0x00 #define AB8500_RTC_SOFF_STAT_REG 0x00
#define AB8500_RTC_CC_CONF_REG 0x01 #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, 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); "ab8500-rtc", rtc);
if (err < 0) if (err < 0)
return err; return err;
dev_pm_set_wake_irq(&pdev->dev, irq);
platform_set_drvdata(pdev, rtc); platform_set_drvdata(pdev, rtc);
err = ab8500_sysfs_rtc_register(&pdev->dev); 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) 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); ab8500_sysfs_rtc_unregister(&pdev->dev);
return 0; return 0;

View File

@ -495,6 +495,8 @@ static int at91_rtc_suspend(struct device *dev)
/* this IRQ is shared with DBGU and other hardware which isn't /* this IRQ is shared with DBGU and other hardware which isn't
* necessarily doing PM like we are... * necessarily doing PM like we are...
*/ */
at91_rtc_write(AT91_RTC_SCCR, AT91_RTC_ALARM);
at91_rtc_imr = at91_rtc_read_imr() at91_rtc_imr = at91_rtc_read_imr()
& (AT91_RTC_ALARM|AT91_RTC_SECEV); & (AT91_RTC_ALARM|AT91_RTC_SECEV);
if (at91_rtc_imr) { if (at91_rtc_imr) {

View File

@ -1,15 +1,15 @@
/* rtc-da9063.c - Real time clock device driver for DA9063 /* 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 * This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU Library General Public * modify it under the terms of the GNU General Public License
* License as published by the Free Software Foundation; either * as published by the Free Software Foundation; either version 2
* version 2 of the License, or (at your option) any later version. * 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 * but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* Library General Public License for more details. * GNU General Public License for more details.
*/ */
#include <linux/delay.h> #include <linux/delay.h>
@ -516,5 +516,5 @@ module_platform_driver(da9063_rtc_driver);
MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>"); MODULE_AUTHOR("S Twiss <stwiss.opensource@diasemi.com>");
MODULE_DESCRIPTION("Real time clock device driver for Dialog DA9063"); MODULE_DESCRIPTION("Real time clock device driver for Dialog DA9063");
MODULE_LICENSE("GPL v2"); MODULE_LICENSE("GPL");
MODULE_ALIAS("platform:" DA9063_DRVNAME_RTC); MODULE_ALIAS("platform:" DA9063_DRVNAME_RTC);

View File

@ -546,7 +546,6 @@ static int __exit davinci_rtc_remove(struct platform_device *pdev)
} }
static struct platform_driver davinci_rtc_driver = { static struct platform_driver davinci_rtc_driver = {
.probe = davinci_rtc_probe,
.remove = __exit_p(davinci_rtc_remove), .remove = __exit_p(davinci_rtc_remove),
.driver = { .driver = {
.name = "rtc_davinci", .name = "rtc_davinci",

View File

@ -718,9 +718,9 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
regs[3] = bin2bcd(t->time.tm_sec); regs[3] = bin2bcd(t->time.tm_sec);
regs[4] = bin2bcd(t->time.tm_min); regs[4] = bin2bcd(t->time.tm_min);
regs[5] = bin2bcd(t->time.tm_hour); 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[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. */ /* Clear the alarm 0 interrupt flag. */
regs[6] &= ~MCP794XX_BIT_ALMX_IF; regs[6] &= ~MCP794XX_BIT_ALMX_IF;

View File

@ -21,6 +21,7 @@
#include <linux/rtc.h> #include <linux/rtc.h>
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/pm.h> #include <linux/pm.h>
#include <linux/pm_wakeirq.h>
#include <linux/slab.h> #include <linux/slab.h>
#define DS1343_DRV_VERSION "01.00" #define DS1343_DRV_VERSION "01.00"
@ -663,15 +664,15 @@ static int ds1343_probe(struct spi_device *spi)
if (priv->irq >= 0) { if (priv->irq >= 0) {
res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL, res = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
ds1343_thread, ds1343_thread, IRQF_ONESHOT,
IRQF_NO_SUSPEND | IRQF_ONESHOT,
"ds1343", priv); "ds1343", priv);
if (res) { if (res) {
priv->irq = -1; priv->irq = -1;
dev_err(&spi->dev, dev_err(&spi->dev,
"unable to request irq for rtc ds1343\n"); "unable to request irq for rtc ds1343\n");
} else { } 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; priv->irqen &= ~RTC_AF;
mutex_unlock(&priv->mutex); 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); devm_free_irq(&spi->dev, spi->irq, priv);
} }

View File

@ -20,6 +20,7 @@
#include <linux/spi/spi.h> #include <linux/spi/spi.h>
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/slab.h> #include <linux/slab.h>
#include <linux/of.h>
#define DS1390_REG_100THS 0x00 #define DS1390_REG_100THS 0x00
#define DS1390_REG_SECONDS 0x01 #define DS1390_REG_SECONDS 0x01
@ -40,11 +41,31 @@
#define DS1390_REG_STATUS 0x0E #define DS1390_REG_STATUS 0x0E
#define DS1390_REG_TRICKLE 0x0F #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 ds1390 {
struct rtc_device *rtc; struct rtc_device *rtc;
u8 txrx_buf[9]; /* cmd + 8 registers */ 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, static int ds1390_get_reg(struct device *dev, unsigned char address,
unsigned char *data) unsigned char *data)
{ {
@ -62,11 +83,50 @@ static int ds1390_get_reg(struct device *dev, unsigned char address,
if (status != 0) if (status != 0)
return status; return status;
*data = chip->txrx_buf[1]; *data = chip->txrx_buf[0];
return 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) static int ds1390_read_time(struct device *dev, struct rtc_time *dt)
{ {
struct spi_device *spi = to_spi_device(dev); struct spi_device *spi = to_spi_device(dev);
@ -143,6 +203,9 @@ static int ds1390_probe(struct spi_device *spi)
return res; return res;
} }
if (spi->dev.of_node)
ds1390_trickle_of_init(spi);
chip->rtc = devm_rtc_device_register(&spi->dev, "ds1390", chip->rtc = devm_rtc_device_register(&spi->dev, "ds1390",
&ds1390_rtc_ops, THIS_MODULE); &ds1390_rtc_ops, THIS_MODULE);
if (IS_ERR(chip->rtc)) { if (IS_ERR(chip->rtc)) {

View File

@ -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 * 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 * devices with no IRQ driectly connected to the SoC, the RTC chip
* can be forced as a wakeup source by stating that explicitly in * can be forced as a wakeup source by stating that explicitly in
* the device's .dts file using the "isil,irq2-can-wakeup-machine" * the device's .dts file using the "wakeup-source" boolean property.
* boolean property. This will guarantee 'wakealarm' sysfs entry is * This will guarantee 'wakealarm' sysfs entry is available on the device.
* available on the device.
* *
* The function below returns 1, i.e. the capability of the chip to * The function below returns 1, i.e. the capability of the chip to
* wakeup the device, based on IRQ availability or if the boolean * 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); struct isl12057_rtc_data *data = dev_get_drvdata(dev);
return (data->irq || of_property_read_bool(dev->of_node, return data->irq || of_property_read_bool(dev->of_node, "wakeup-source")
"isil,irq2-can-wakeup-machine")); || of_property_read_bool(dev->of_node, /* legacy */
"isil,irq2-can-wakeup-machine");
} }
#else #else
static bool isl12057_can_wakeup_machine(struct device *dev) static bool isl12057_can_wakeup_machine(struct device *dev)

View File

@ -638,7 +638,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id)
if (client->irq > 0) { if (client->irq > 0) {
rc = devm_request_threaded_irq(&client->dev, client->irq, NULL, rc = devm_request_threaded_irq(&client->dev, client->irq, NULL,
isl1208_rtc_interrupt, isl1208_rtc_interrupt,
IRQF_SHARED, IRQF_SHARED | IRQF_ONESHOT,
isl1208_driver.driver.name, isl1208_driver.driver.name,
client); client);
if (!rc) { if (!rc) {

View File

@ -152,10 +152,10 @@ exit:
/* Set Timed Power-On */ /* Set Timed Power-On */
static int opal_set_tpo_time(struct device *dev, struct rtc_wkalrm *alarm) 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; struct opal_msg msg;
u32 y_m_d = 0; u32 y_m_d = 0;
int rc; int token, rc;
tm_to_opal(&alarm->time, &y_m_d, &h_m_s_ms); 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; struct rtc_device *rtc;
if (pdev->dev.of_node && of_get_property(pdev->dev.of_node, "has-tpo", if (pdev->dev.of_node &&
NULL)) { (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); device_set_wakeup_capable(&pdev->dev, true);
opal_rtc_ops.read_alarm = opal_get_tpo_time; opal_rtc_ops.read_alarm = opal_get_tpo_time;
opal_rtc_ops.set_alarm = opal_set_tpo_time; opal_rtc_ops.set_alarm = opal_set_tpo_time;

View File

@ -20,11 +20,12 @@
#include <linux/module.h> #include <linux/module.h>
#include <linux/of.h> #include <linux/of.h>
#define DRV_VERSION "0.0.1"
#define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */ #define PCF2127_REG_CTRL1 (0x00) /* Control Register 1 */
#define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */ #define PCF2127_REG_CTRL2 (0x01) /* Control Register 2 */
#define PCF2127_REG_CTRL3 (0x02) /* Control Register 3 */ #define PCF2127_REG_CTRL3 (0x02) /* Control Register 3 */
#define PCF2127_REG_CTRL3_BLF BIT(2)
#define PCF2127_REG_SC (0x03) /* datetime */ #define PCF2127_REG_SC (0x03) /* datetime */
#define PCF2127_REG_MN (0x04) #define PCF2127_REG_MN (0x04)
#define PCF2127_REG_HR (0x05) #define PCF2127_REG_HR (0x05)
@ -39,8 +40,6 @@ static struct i2c_driver pcf2127_driver;
struct pcf2127 { struct pcf2127 {
struct rtc_device *rtc; 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) 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 }; unsigned char buf[10] = { PCF2127_REG_CTRL1 };
/* read registers */ /* read registers */
@ -59,18 +57,15 @@ static int pcf2127_get_datetime(struct i2c_client *client, struct rtc_time *tm)
return -EIO; return -EIO;
} }
if (buf[PCF2127_REG_CTRL3] & 0x04) { if (buf[PCF2127_REG_CTRL3] & PCF2127_REG_CTRL3_BLF)
pcf2127->voltage_low = 1;
dev_info(&client->dev, dev_info(&client->dev,
"low voltage detected, check/replace RTC battery.\n"); "low voltage detected, check/replace RTC battery.\n");
}
if (buf[PCF2127_REG_SC] & PCF2127_OSF) { if (buf[PCF2127_REG_SC] & PCF2127_OSF) {
/* /*
* no need clear the flag here, * no need clear the flag here,
* it will be cleared once the new date is saved * it will be cleared once the new date is saved
*/ */
pcf2127->oscillator_failed = 1;
dev_warn(&client->dev, dev_warn(&client->dev,
"oscillator stop detected, date/time is not reliable\n"); "oscillator stop detected, date/time is not reliable\n");
return -EINVAL; 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) static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm)
{ {
struct pcf2127 *pcf2127 = i2c_get_clientdata(client);
unsigned char buf[8]; unsigned char buf[8];
int i = 0, err; int i = 0, err;
@ -141,9 +135,6 @@ static int pcf2127_set_datetime(struct i2c_client *client, struct rtc_time *tm)
return -EIO; return -EIO;
} }
/* clear OSF flag in client data */
pcf2127->oscillator_failed = 0;
return 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, static int pcf2127_rtc_ioctl(struct device *dev,
unsigned int cmd, unsigned long arg) 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) { switch (cmd) {
case RTC_VL_READ: case RTC_VL_READ:
if (pcf2127->voltage_low) ret = i2c_master_send(client, &buf, 1);
dev_info(dev, "low voltage detected, check/replace battery\n"); if (!ret)
if (pcf2127->oscillator_failed) ret = -EIO;
dev_info(dev, "oscillator stop detected, date/time is not reliable\n"); if (ret < 0)
return ret;
if (copy_to_user((void __user *)arg, &pcf2127->voltage_low, ret = i2c_master_recv(client, &buf, 1);
sizeof(int))) if (!ret)
ret = -EIO;
if (ret < 0)
return ret;
touser = buf & PCF2127_REG_CTRL3_BLF ? 1 : 0;
if (copy_to_user((void __user *)arg, &touser, sizeof(int)))
return -EFAULT; return -EFAULT;
return 0; return 0;
default: default:
@ -203,8 +205,6 @@ static int pcf2127_probe(struct i2c_client *client,
if (!pcf2127) if (!pcf2127)
return -ENOMEM; return -ENOMEM;
dev_info(&client->dev, "chip found, driver version " DRV_VERSION "\n");
i2c_set_clientdata(client, pcf2127); i2c_set_clientdata(client, pcf2127);
pcf2127->rtc = devm_rtc_device_register(&client->dev, 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_AUTHOR("Renaud Cerrato <r.cerrato@til-technologies.fr>");
MODULE_DESCRIPTION("NXP PCF2127 RTC driver"); MODULE_DESCRIPTION("NXP PCF2127 RTC driver");
MODULE_LICENSE("GPL"); MODULE_LICENSE("GPL v2");
MODULE_VERSION(DRV_VERSION);

View File

@ -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) ? pcf85063->c_polarity = (buf[PCF85063_REG_MO] & PCF85063_MO_C) ?
(tm->tm_year >= 100) : (tm->tm_year < 100); (tm->tm_year >= 100) : (tm->tm_year < 100);
/* the clock can give out invalid datetime, but we cannot return return rtc_valid_tm(tm);
* -EINVAL otherwise hwclock will refuse to set the time on bootup.
*/
if (rtc_valid_tm(tm) < 0)
dev_err(&client->dev, "retrieved date/time is not valid.\n");
return 0;
} }
static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm) static int pcf85063_set_datetime(struct i2c_client *client, struct rtc_time *tm)

View File

@ -14,6 +14,7 @@
* published by the Free Software Foundation. * published by the Free Software Foundation.
*/ */
#include <linux/clk-provider.h>
#include <linux/i2c.h> #include <linux/i2c.h>
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/rtc.h> #include <linux/rtc.h>
@ -40,7 +41,14 @@
#define PCF8563_REG_AMN 0x09 /* alarm */ #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_REG_TMRC 0x0E /* timer control */
#define PCF8563_TMRC_ENABLE BIT(7) #define PCF8563_TMRC_ENABLE BIT(7)
#define PCF8563_TMRC_4096 0 #define PCF8563_TMRC_4096 0
@ -76,6 +84,9 @@ struct pcf8563 {
int voltage_low; /* incicates if a low_voltage was detected */ int voltage_low; /* incicates if a low_voltage was detected */
struct i2c_client *client; 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, 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); 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 = { static const struct rtc_class_ops pcf8563_rtc_ops = {
.ioctl = pcf8563_rtc_ioctl, .ioctl = pcf8563_rtc_ioctl,
.read_time = pcf8563_rtc_read_time, .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 */ /* the pcf8563 alarm only supports a minute accuracy */
pcf8563->rtc->uie_unsupported = 1; pcf8563->rtc->uie_unsupported = 1;

View File

@ -23,6 +23,7 @@
#include <linux/io.h> #include <linux/io.h>
#include <linux/bcd.h> #include <linux/bcd.h>
#include <linux/delay.h> #include <linux/delay.h>
#include <linux/pm_wakeirq.h>
#include <linux/slab.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); 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); free_irq(adev->irq[0], ldata);
rtc_device_unregister(ldata->rtc); rtc_device_unregister(ldata->rtc);
iounmap(ldata->base); 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, ldata->rtc = rtc_device_register("pl031", &adev->dev, ops,
THIS_MODULE); THIS_MODULE);
if (IS_ERR(ldata->rtc)) { if (IS_ERR(ldata->rtc)) {
@ -383,7 +386,7 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
ret = -EIO; ret = -EIO;
goto out_no_irq; goto out_no_irq;
} }
dev_pm_set_wake_irq(&adev->dev, adev->irq[0]);
return 0; return 0;
out_no_irq: out_no_irq:
@ -408,7 +411,6 @@ static struct pl031_vendor_data arm_pl031 = {
.set_alarm = pl031_set_alarm, .set_alarm = pl031_set_alarm,
.alarm_irq_enable = pl031_alarm_irq_enable, .alarm_irq_enable = pl031_alarm_irq_enable,
}, },
.irqflags = IRQF_NO_SUSPEND,
}; };
/* The First ST derivative */ /* The First ST derivative */
@ -422,7 +424,6 @@ static struct pl031_vendor_data stv1_pl031 = {
}, },
.clockwatch = true, .clockwatch = true,
.st_weekday = true, .st_weekday = true,
.irqflags = IRQF_NO_SUSPEND,
}; };
/* And the second ST derivative */ /* 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 * This variant shares the IRQ with another block and must not
* suspend that IRQ line. * 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[] = { static struct amba_id pl031_ids[] = {

521
drivers/rtc/rtc-rv8803.c Normal file
View File

@ -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");

View File

@ -65,6 +65,7 @@
static const struct i2c_device_id rx8025_id[] = { static const struct i2c_device_id rx8025_id[] = {
{ "rx8025", 0 }, { "rx8025", 0 },
{ "rv8803", 1 },
{ } { }
}; };
MODULE_DEVICE_TABLE(i2c, rx8025_id); 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); rx8025 = devm_kzalloc(&client->dev, sizeof(*rx8025), GFP_KERNEL);
if (!rx8025) { if (!rx8025)
return -ENOMEM; return -ENOMEM;
}
rx8025->client = client; rx8025->client = client;
i2c_set_clientdata(client, rx8025); i2c_set_clientdata(client, rx8025);

View File

@ -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 s3c_rtc *info = dev_get_drvdata(dev);
struct rtc_time *tm = &alrm->time; struct rtc_time *tm = &alrm->time;
unsigned int alrm_en; unsigned int alrm_en;
int year = tm->tm_year - 100;
dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n", dev_dbg(dev, "s3c_rtc_setalarm: %d, %04d.%02d.%02d %02d:%02d:%02d\n",
alrm->enabled, 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); 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); dev_dbg(dev, "setting S3C2410_RTCALM to %08x\n", alrm_en);
writeb(alrm_en, info->base + S3C2410_RTCALM); writeb(alrm_en, info->base + S3C2410_RTCALM);

View File

@ -32,8 +32,6 @@
#include <linux/stmp3xxx_rtc_wdt.h> #include <linux/stmp3xxx_rtc_wdt.h>
#define STMP3XXX_RTC_CTRL 0x0 #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_ALARM_IRQ_EN 0x00000001
#define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002 #define STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN 0x00000002
#define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004 #define STMP3XXX_RTC_CTRL_ALARM_IRQ 0x00000004
@ -52,8 +50,6 @@
#define STMP3XXX_RTC_WATCHDOG 0x50 #define STMP3XXX_RTC_WATCHDOG 0x50
#define STMP3XXX_RTC_PERSISTENT0 0x60 #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_CLOCKSOURCE (1 << 0)
#define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN (1 << 1) #define STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN (1 << 1)
#define STMP3XXX_RTC_PERSISTENT0_ALARM_EN (1 << 2) #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) { if (status & STMP3XXX_RTC_CTRL_ALARM_IRQ) {
writel(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); rtc_update_irq(rtc_data->rtc, 1, RTC_AF | RTC_IRQF);
return IRQ_HANDLED; return IRQ_HANDLED;
} }
@ -194,15 +190,17 @@ static int stmp3xxx_alarm_irq_enable(struct device *dev, unsigned int enabled)
if (enabled) { if (enabled) {
writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_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, 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 { } else {
writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_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, 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; return 0;
} }
@ -245,7 +243,7 @@ static int stmp3xxx_rtc_remove(struct platform_device *pdev)
return 0; return 0;
writel(STMP3XXX_RTC_CTRL_ALARM_IRQ_EN, 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; return 0;
} }
@ -334,16 +332,17 @@ static int stmp3xxx_rtc_probe(struct platform_device *pdev)
STMP3XXX_RTC_PERSISTENT0_CLOCKSOURCE; 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 | writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE | pers0_clr, 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 | writel(STMP3XXX_RTC_CTRL_ONEMSEC_IRQ_EN |
STMP3XXX_RTC_CTRL_ALARM_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, rtc_data->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
&stmp3xxx_rtc_ops, THIS_MODULE); &stmp3xxx_rtc_ops, THIS_MODULE);
@ -376,7 +375,7 @@ static int stmp3xxx_rtc_resume(struct device *dev)
writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN | writel(STMP3XXX_RTC_PERSISTENT0_ALARM_EN |
STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN | STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE_EN |
STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE, STMP3XXX_RTC_PERSISTENT0_ALARM_WAKE,
rtc_data->io + STMP3XXX_RTC_PERSISTENT0_CLR); rtc_data->io + STMP3XXX_RTC_PERSISTENT0 + STMP_OFFSET_REG_CLR);
return 0; return 0;
} }
#endif #endif

View File

@ -61,7 +61,7 @@ int main(int argc, char **argv)
/* Turn on update interrupts (one per second) */ /* Turn on update interrupts (one per second) */
retval = ioctl(fd, RTC_UIE_ON, 0); retval = ioctl(fd, RTC_UIE_ON, 0);
if (retval == -1) { if (retval == -1) {
if (errno == ENOTTY) { if (errno == EINVAL) {
fprintf(stderr, fprintf(stderr,
"\n...Update IRQs not supported.\n"); "\n...Update IRQs not supported.\n");
goto test_READ; goto test_READ;