diff options
Diffstat (limited to 'drivers/rtc/rtc-pcf8563.c')
| -rw-r--r-- | drivers/rtc/rtc-pcf8563.c | 120 |
1 files changed, 75 insertions, 45 deletions
diff --git a/drivers/rtc/rtc-pcf8563.c b/drivers/rtc/rtc-pcf8563.c index b42c0c67926..63b558c4819 100644 --- a/drivers/rtc/rtc-pcf8563.c +++ b/drivers/rtc/rtc-pcf8563.c @@ -18,6 +18,9 @@ #include <linux/bcd.h> #include <linux/rtc.h> #include <linux/slab.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/err.h> #define DRV_VERSION "0.4.3" @@ -63,6 +66,7 @@ struct pcf8563 { * 1970...2069. */ int c_polarity; /* 0: MO_C=1 means 19xx, otherwise MO_C=1 means 20xx */ + int voltage_low; /* incicates if a low_voltage was detected */ }; /* @@ -75,8 +79,17 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm) unsigned char buf[13] = { PCF8563_REG_ST1 }; struct i2c_msg msgs[] = { - { client->addr, 0, 1, buf }, /* setup read ptr */ - { client->addr, I2C_M_RD, 13, buf }, /* read status + date */ + {/* setup read ptr */ + .addr = client->addr, + .len = 1, + .buf = buf + }, + {/* read status + date */ + .addr = client->addr, + .flags = I2C_M_RD, + .len = 13, + .buf = buf + }, }; /* read registers */ @@ -85,9 +98,11 @@ static int pcf8563_get_datetime(struct i2c_client *client, struct rtc_time *tm) return -EIO; } - if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) + if (buf[PCF8563_REG_SC] & PCF8563_SC_LV) { + pcf8563->voltage_low = 1; dev_info(&client->dev, "low voltage detected, date/time is not reliable.\n"); + } dev_dbg(&client->dev, "%s: raw data is st1=%02x, st2=%02x, sec=%02x, min=%02x, hr=%02x, " @@ -167,11 +182,49 @@ static int pcf8563_set_datetime(struct i2c_client *client, struct rtc_time *tm) __func__, err, data[0], data[1]); return -EIO; } - }; + } return 0; } +#ifdef CONFIG_RTC_INTF_DEV +static int pcf8563_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg) +{ + struct pcf8563 *pcf8563 = i2c_get_clientdata(to_i2c_client(dev)); + struct rtc_time tm; + + switch (cmd) { + case RTC_VL_READ: + if (pcf8563->voltage_low) + dev_info(dev, "low voltage detected, date/time is not reliable.\n"); + + if (copy_to_user((void __user *)arg, &pcf8563->voltage_low, + sizeof(int))) + return -EFAULT; + return 0; + case RTC_VL_CLR: + /* + * Clear the VL bit in the seconds register in case + * the time has not been set already (which would + * have cleared it). This does not really matter + * because of the cached voltage_low value but do it + * anyway for consistency. + */ + if (pcf8563_get_datetime(to_i2c_client(dev), &tm)) + pcf8563_set_datetime(to_i2c_client(dev), &tm); + + /* Clear the cached value. */ + pcf8563->voltage_low = 0; + + return 0; + default: + return -ENOIOCTLCMD; + } +} +#else +#define pcf8563_rtc_ioctl NULL +#endif + static int pcf8563_rtc_read_time(struct device *dev, struct rtc_time *tm) { return pcf8563_get_datetime(to_i2c_client(dev), tm); @@ -183,6 +236,7 @@ static int pcf8563_rtc_set_time(struct device *dev, struct rtc_time *tm) } static const struct rtc_class_ops pcf8563_rtc_ops = { + .ioctl = pcf8563_rtc_ioctl, .read_time = pcf8563_rtc_read_time, .set_time = pcf8563_rtc_set_time, }; @@ -192,14 +246,13 @@ static int pcf8563_probe(struct i2c_client *client, { struct pcf8563 *pcf8563; - int err = 0; - dev_dbg(&client->dev, "%s\n", __func__); if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; - pcf8563 = kzalloc(sizeof(struct pcf8563), GFP_KERNEL); + pcf8563 = devm_kzalloc(&client->dev, sizeof(struct pcf8563), + GFP_KERNEL); if (!pcf8563) return -ENOMEM; @@ -207,32 +260,11 @@ static int pcf8563_probe(struct i2c_client *client, i2c_set_clientdata(client, pcf8563); - pcf8563->rtc = rtc_device_register(pcf8563_driver.driver.name, - &client->dev, &pcf8563_rtc_ops, THIS_MODULE); + pcf8563->rtc = devm_rtc_device_register(&client->dev, + pcf8563_driver.driver.name, + &pcf8563_rtc_ops, THIS_MODULE); - if (IS_ERR(pcf8563->rtc)) { - err = PTR_ERR(pcf8563->rtc); - goto exit_kfree; - } - - return 0; - -exit_kfree: - kfree(pcf8563); - - return err; -} - -static int pcf8563_remove(struct i2c_client *client) -{ - struct pcf8563 *pcf8563 = i2c_get_clientdata(client); - - if (pcf8563->rtc) - rtc_device_unregister(pcf8563->rtc); - - kfree(pcf8563); - - return 0; + return PTR_ERR_OR_ZERO(pcf8563->rtc); } static const struct i2c_device_id pcf8563_id[] = { @@ -242,29 +274,27 @@ static const struct i2c_device_id pcf8563_id[] = { }; MODULE_DEVICE_TABLE(i2c, pcf8563_id); +#ifdef CONFIG_OF +static const struct of_device_id pcf8563_of_match[] = { + { .compatible = "nxp,pcf8563" }, + {} +}; +MODULE_DEVICE_TABLE(of, pcf8563_of_match); +#endif + static struct i2c_driver pcf8563_driver = { .driver = { .name = "rtc-pcf8563", + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(pcf8563_of_match), }, .probe = pcf8563_probe, - .remove = pcf8563_remove, .id_table = pcf8563_id, }; -static int __init pcf8563_init(void) -{ - return i2c_add_driver(&pcf8563_driver); -} - -static void __exit pcf8563_exit(void) -{ - i2c_del_driver(&pcf8563_driver); -} +module_i2c_driver(pcf8563_driver); MODULE_AUTHOR("Alessandro Zummo <a.zummo@towertech.it>"); MODULE_DESCRIPTION("Philips PCF8563/Epson RTC8564 RTC driver"); MODULE_LICENSE("GPL"); MODULE_VERSION(DRV_VERSION); - -module_init(pcf8563_init); -module_exit(pcf8563_exit); |
