aboutsummaryrefslogtreecommitdiff
path: root/drivers/rtc/rtc-m41t80.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc/rtc-m41t80.c')
-rw-r--r--drivers/rtc/rtc-m41t80.c300
1 files changed, 58 insertions, 242 deletions
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c
index 5a8daa35806..7ff7427c2e6 100644
--- a/drivers/rtc/rtc-m41t80.c
+++ b/drivers/rtc/rtc-m41t80.c
@@ -66,8 +66,6 @@
#define M41T80_FEATURE_WD (1 << 3) /* Extra watchdog resolution */
#define M41T80_FEATURE_SQ_ALT (1 << 4) /* RSx bits are in reg 4 */
-#define DRV_VERSION "0.05"
-
static DEFINE_MUTEX(m41t80_rtc_mutex);
static const struct i2c_device_id m41t80_id[] = {
{ "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT },
@@ -80,6 +78,7 @@ static const struct i2c_device_id m41t80_id[] = {
{ "m41st84", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
{ "m41st85", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
{ "m41st87", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ },
+ { "rv4162", M41T80_FEATURE_SQ | M41T80_FEATURE_WD | M41T80_FEATURE_SQ_ALT },
{ }
};
MODULE_DEVICE_TABLE(i2c, m41t80_id);
@@ -168,7 +167,7 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm)
buf[M41T80_REG_MIN] =
bin2bcd(tm->tm_min) | (buf[M41T80_REG_MIN] & ~0x7f);
buf[M41T80_REG_HOUR] =
- bin2bcd(tm->tm_hour) | (buf[M41T80_REG_HOUR] & ~0x3f) ;
+ bin2bcd(tm->tm_hour) | (buf[M41T80_REG_HOUR] & ~0x3f);
buf[M41T80_REG_WDAY] =
(tm->tm_wday & 0x07) | (buf[M41T80_REG_WDAY] & ~0x07);
buf[M41T80_REG_DAY] =
@@ -213,168 +212,14 @@ static int m41t80_rtc_set_time(struct device *dev, struct rtc_time *tm)
return m41t80_set_datetime(to_i2c_client(dev), tm);
}
-#if defined(CONFIG_RTC_INTF_DEV) || defined(CONFIG_RTC_INTF_DEV_MODULE)
-static int
-m41t80_rtc_ioctl(struct device *dev, unsigned int cmd, unsigned long arg)
-{
- struct i2c_client *client = to_i2c_client(dev);
- int rc;
-
- switch (cmd) {
- case RTC_AIE_OFF:
- case RTC_AIE_ON:
- break;
- default:
- return -ENOIOCTLCMD;
- }
-
- rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
- if (rc < 0)
- goto err;
- switch (cmd) {
- case RTC_AIE_OFF:
- rc &= ~M41T80_ALMON_AFE;
- break;
- case RTC_AIE_ON:
- rc |= M41T80_ALMON_AFE;
- break;
- }
- if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, rc) < 0)
- goto err;
- return 0;
-err:
- return -EIO;
-}
-#else
-#define m41t80_rtc_ioctl NULL
-#endif
-
-static int m41t80_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *t)
-{
- struct i2c_client *client = to_i2c_client(dev);
- u8 wbuf[1 + M41T80_ALARM_REG_SIZE];
- u8 *buf = &wbuf[1];
- u8 *reg = buf - M41T80_REG_ALARM_MON;
- u8 dt_addr[1] = { M41T80_REG_ALARM_MON };
- struct i2c_msg msgs_in[] = {
- {
- .addr = client->addr,
- .flags = 0,
- .len = 1,
- .buf = dt_addr,
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = M41T80_ALARM_REG_SIZE,
- .buf = buf,
- },
- };
- struct i2c_msg msgs[] = {
- {
- .addr = client->addr,
- .flags = 0,
- .len = 1 + M41T80_ALARM_REG_SIZE,
- .buf = wbuf,
- },
- };
-
- if (i2c_transfer(client->adapter, msgs_in, 2) < 0) {
- dev_err(&client->dev, "read error\n");
- return -EIO;
- }
- reg[M41T80_REG_ALARM_MON] &= ~(0x1f | M41T80_ALMON_AFE);
- reg[M41T80_REG_ALARM_DAY] = 0;
- reg[M41T80_REG_ALARM_HOUR] &= ~(0x3f | 0x80);
- reg[M41T80_REG_ALARM_MIN] = 0;
- reg[M41T80_REG_ALARM_SEC] = 0;
-
- wbuf[0] = M41T80_REG_ALARM_MON; /* offset into rtc's regs */
- reg[M41T80_REG_ALARM_SEC] |= t->time.tm_sec >= 0 ?
- bin2bcd(t->time.tm_sec) : 0x80;
- reg[M41T80_REG_ALARM_MIN] |= t->time.tm_min >= 0 ?
- bin2bcd(t->time.tm_min) : 0x80;
- reg[M41T80_REG_ALARM_HOUR] |= t->time.tm_hour >= 0 ?
- bin2bcd(t->time.tm_hour) : 0x80;
- reg[M41T80_REG_ALARM_DAY] |= t->time.tm_mday >= 0 ?
- bin2bcd(t->time.tm_mday) : 0x80;
- if (t->time.tm_mon >= 0)
- reg[M41T80_REG_ALARM_MON] |= bin2bcd(t->time.tm_mon + 1);
- else
- reg[M41T80_REG_ALARM_DAY] |= 0x40;
-
- if (i2c_transfer(client->adapter, msgs, 1) != 1) {
- dev_err(&client->dev, "write error\n");
- return -EIO;
- }
-
- if (t->enabled) {
- reg[M41T80_REG_ALARM_MON] |= M41T80_ALMON_AFE;
- if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
- reg[M41T80_REG_ALARM_MON]) < 0) {
- dev_err(&client->dev, "write error\n");
- return -EIO;
- }
- }
- return 0;
-}
-
-static int m41t80_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *t)
-{
- struct i2c_client *client = to_i2c_client(dev);
- u8 buf[M41T80_ALARM_REG_SIZE + 1]; /* all alarm regs and flags */
- u8 dt_addr[1] = { M41T80_REG_ALARM_MON };
- u8 *reg = buf - M41T80_REG_ALARM_MON;
- struct i2c_msg msgs[] = {
- {
- .addr = client->addr,
- .flags = 0,
- .len = 1,
- .buf = dt_addr,
- },
- {
- .addr = client->addr,
- .flags = I2C_M_RD,
- .len = M41T80_ALARM_REG_SIZE + 1,
- .buf = buf,
- },
- };
-
- if (i2c_transfer(client->adapter, msgs, 2) < 0) {
- dev_err(&client->dev, "read error\n");
- return -EIO;
- }
- t->time.tm_sec = -1;
- t->time.tm_min = -1;
- t->time.tm_hour = -1;
- t->time.tm_mday = -1;
- t->time.tm_mon = -1;
- if (!(reg[M41T80_REG_ALARM_SEC] & 0x80))
- t->time.tm_sec = bcd2bin(reg[M41T80_REG_ALARM_SEC] & 0x7f);
- if (!(reg[M41T80_REG_ALARM_MIN] & 0x80))
- t->time.tm_min = bcd2bin(reg[M41T80_REG_ALARM_MIN] & 0x7f);
- if (!(reg[M41T80_REG_ALARM_HOUR] & 0x80))
- t->time.tm_hour = bcd2bin(reg[M41T80_REG_ALARM_HOUR] & 0x3f);
- if (!(reg[M41T80_REG_ALARM_DAY] & 0x80))
- t->time.tm_mday = bcd2bin(reg[M41T80_REG_ALARM_DAY] & 0x3f);
- if (!(reg[M41T80_REG_ALARM_DAY] & 0x40))
- t->time.tm_mon = bcd2bin(reg[M41T80_REG_ALARM_MON] & 0x1f) - 1;
- t->time.tm_year = -1;
- t->time.tm_wday = -1;
- t->time.tm_yday = -1;
- t->time.tm_isdst = -1;
- t->enabled = !!(reg[M41T80_REG_ALARM_MON] & M41T80_ALMON_AFE);
- t->pending = !!(reg[M41T80_REG_FLAGS] & M41T80_FLAGS_AF);
- return 0;
-}
-
+/*
+ * XXX - m41t80 alarm functionality is reported broken.
+ * until it is fixed, don't register alarm functions.
+ */
static struct rtc_class_ops m41t80_rtc_ops = {
.read_time = m41t80_rtc_read_time,
.set_time = m41t80_rtc_set_time,
- .read_alarm = m41t80_rtc_read_alarm,
- .set_alarm = m41t80_rtc_set_alarm,
.proc = m41t80_rtc_proc,
- .ioctl = m41t80_rtc_ioctl,
};
#if defined(CONFIG_RTC_INTF_SYSFS) || defined(CONFIG_RTC_INTF_SYSFS_MODULE)
@@ -386,7 +231,7 @@ static ssize_t m41t80_sysfs_show_flags(struct device *dev,
val = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS);
if (val < 0)
- return -EIO;
+ return val;
return sprintf(buf, "%#x\n", val);
}
static DEVICE_ATTR(flags, S_IRUGO, m41t80_sysfs_show_flags, NULL);
@@ -406,7 +251,7 @@ static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev,
reg_sqw = M41T80_REG_WDAY;
val = i2c_smbus_read_byte_data(client, reg_sqw);
if (val < 0)
- return -EIO;
+ return val;
val = (val >> 4) & 0xf;
switch (val) {
case 0:
@@ -425,7 +270,7 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev,
{
struct i2c_client *client = to_i2c_client(dev);
struct m41t80_data *clientdata = i2c_get_clientdata(client);
- int almon, sqw, reg_sqw;
+ int almon, sqw, reg_sqw, rc;
int val = simple_strtoul(buf, NULL, 0);
if (!(clientdata->features & M41T80_FEATURE_SQ))
@@ -445,21 +290,30 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev,
/* disable SQW, set SQW frequency & re-enable */
almon = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
if (almon < 0)
- return -EIO;
+ return almon;
reg_sqw = M41T80_REG_SQW;
if (clientdata->features & M41T80_FEATURE_SQ_ALT)
reg_sqw = M41T80_REG_WDAY;
sqw = i2c_smbus_read_byte_data(client, reg_sqw);
if (sqw < 0)
- return -EIO;
+ return sqw;
sqw = (sqw & 0x0f) | (val << 4);
- if (i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
- almon & ~M41T80_ALMON_SQWE) < 0 ||
- i2c_smbus_write_byte_data(client, reg_sqw, sqw) < 0)
- return -EIO;
- if (val && i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
- almon | M41T80_ALMON_SQWE) < 0)
- return -EIO;
+
+ rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
+ almon & ~M41T80_ALMON_SQWE);
+ if (rc < 0)
+ return rc;
+
+ if (val) {
+ rc = i2c_smbus_write_byte_data(client, reg_sqw, sqw);
+ if (rc < 0)
+ return rc;
+
+ rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON,
+ almon | M41T80_ALMON_SQWE);
+ if (rc <0)
+ return rc;
+ }
return count;
}
static DEVICE_ATTR(sqwfreq, S_IRUGO | S_IWUSR,
@@ -783,38 +637,28 @@ static int m41t80_probe(struct i2c_client *client,
struct m41t80_data *clientdata = NULL;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C
- | I2C_FUNC_SMBUS_BYTE_DATA)) {
- rc = -ENODEV;
- goto exit;
- }
+ | I2C_FUNC_SMBUS_BYTE_DATA))
+ return -ENODEV;
- dev_info(&client->dev,
- "chip found, driver version " DRV_VERSION "\n");
+ clientdata = devm_kzalloc(&client->dev, sizeof(*clientdata),
+ GFP_KERNEL);
+ if (!clientdata)
+ return -ENOMEM;
- clientdata = kzalloc(sizeof(*clientdata), GFP_KERNEL);
- if (!clientdata) {
- rc = -ENOMEM;
- goto exit;
- }
+ clientdata->features = id->driver_data;
+ i2c_set_clientdata(client, clientdata);
- rtc = rtc_device_register(client->name, &client->dev,
- &m41t80_rtc_ops, THIS_MODULE);
- if (IS_ERR(rtc)) {
- rc = PTR_ERR(rtc);
- rtc = NULL;
- goto exit;
- }
+ rtc = devm_rtc_device_register(&client->dev, client->name,
+ &m41t80_rtc_ops, THIS_MODULE);
+ if (IS_ERR(rtc))
+ return PTR_ERR(rtc);
clientdata->rtc = rtc;
- clientdata->features = id->driver_data;
- i2c_set_clientdata(client, clientdata);
/* Make sure HT (Halt Update) bit is cleared */
rc = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_HOUR);
- if (rc < 0)
- goto ht_err;
- if (rc & M41T80_ALHOUR_HT) {
+ if (rc >= 0 && rc & M41T80_ALHOUR_HT) {
if (clientdata->features & M41T80_FEATURE_HT) {
m41t80_get_datetime(client, &tm);
dev_info(&client->dev, "HT bit was set!\n");
@@ -825,72 +669,56 @@ static int m41t80_probe(struct i2c_client *client,
tm.tm_mon + 1, tm.tm_mday, tm.tm_hour,
tm.tm_min, tm.tm_sec);
}
- if (i2c_smbus_write_byte_data(client,
- M41T80_REG_ALARM_HOUR,
- rc & ~M41T80_ALHOUR_HT) < 0)
- goto ht_err;
+ rc = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_HOUR,
+ rc & ~M41T80_ALHOUR_HT);
+ }
+
+ if (rc < 0) {
+ dev_err(&client->dev, "Can't clear HT bit\n");
+ return rc;
}
/* Make sure ST (stop) bit is cleared */
rc = i2c_smbus_read_byte_data(client, M41T80_REG_SEC);
- if (rc < 0)
- goto st_err;
- if (rc & M41T80_SEC_ST) {
- if (i2c_smbus_write_byte_data(client, M41T80_REG_SEC,
- rc & ~M41T80_SEC_ST) < 0)
- goto st_err;
+ if (rc >= 0 && rc & M41T80_SEC_ST)
+ rc = i2c_smbus_write_byte_data(client, M41T80_REG_SEC,
+ rc & ~M41T80_SEC_ST);
+ if (rc < 0) {
+ dev_err(&client->dev, "Can't clear ST bit\n");
+ return rc;
}
rc = m41t80_sysfs_register(&client->dev);
if (rc)
- goto exit;
+ return rc;
#ifdef CONFIG_RTC_DRV_M41T80_WDT
if (clientdata->features & M41T80_FEATURE_HT) {
save_client = client;
rc = misc_register(&wdt_dev);
if (rc)
- goto exit;
+ return rc;
rc = register_reboot_notifier(&wdt_notifier);
if (rc) {
misc_deregister(&wdt_dev);
- goto exit;
+ return rc;
}
}
#endif
return 0;
-
-st_err:
- rc = -EIO;
- dev_err(&client->dev, "Can't clear ST bit\n");
- goto exit;
-ht_err:
- rc = -EIO;
- dev_err(&client->dev, "Can't clear HT bit\n");
- goto exit;
-
-exit:
- if (rtc)
- rtc_device_unregister(rtc);
- kfree(clientdata);
- return rc;
}
static int m41t80_remove(struct i2c_client *client)
{
+#ifdef CONFIG_RTC_DRV_M41T80_WDT
struct m41t80_data *clientdata = i2c_get_clientdata(client);
- struct rtc_device *rtc = clientdata->rtc;
-#ifdef CONFIG_RTC_DRV_M41T80_WDT
if (clientdata->features & M41T80_FEATURE_HT) {
misc_deregister(&wdt_dev);
unregister_reboot_notifier(&wdt_notifier);
}
#endif
- if (rtc)
- rtc_device_unregister(rtc);
- kfree(clientdata);
return 0;
}
@@ -904,20 +732,8 @@ static struct i2c_driver m41t80_driver = {
.id_table = m41t80_id,
};
-static int __init m41t80_rtc_init(void)
-{
- return i2c_add_driver(&m41t80_driver);
-}
-
-static void __exit m41t80_rtc_exit(void)
-{
- i2c_del_driver(&m41t80_driver);
-}
+module_i2c_driver(m41t80_driver);
MODULE_AUTHOR("Alexander Bigga <ab@mycable.de>");
MODULE_DESCRIPTION("ST Microelectronics M41T80 series RTC I2C Client Driver");
MODULE_LICENSE("GPL");
-MODULE_VERSION(DRV_VERSION);
-
-module_init(m41t80_rtc_init);
-module_exit(m41t80_rtc_exit);