diff options
Diffstat (limited to 'drivers/rtc/rtc-m41t80.c')
| -rw-r--r-- | drivers/rtc/rtc-m41t80.c | 502 |
1 files changed, 162 insertions, 340 deletions
diff --git a/drivers/rtc/rtc-m41t80.c b/drivers/rtc/rtc-m41t80.c index 1cb33cac123..7ff7427c2e6 100644 --- a/drivers/rtc/rtc-m41t80.c +++ b/drivers/rtc/rtc-m41t80.c @@ -13,19 +13,21 @@ * */ -#include <linux/module.h> +#include <linux/bcd.h> +#include <linux/i2c.h> #include <linux/init.h> +#include <linux/kernel.h> +#include <linux/module.h> +#include <linux/rtc.h> #include <linux/slab.h> +#include <linux/mutex.h> #include <linux/string.h> -#include <linux/i2c.h> -#include <linux/rtc.h> -#include <linux/bcd.h> #ifdef CONFIG_RTC_DRV_M41T80_WDT -#include <linux/miscdevice.h> -#include <linux/watchdog.h> -#include <linux/reboot.h> #include <linux/fs.h> #include <linux/ioctl.h> +#include <linux/miscdevice.h> +#include <linux/reboot.h> +#include <linux/watchdog.h> #endif #define M41T80_REG_SSEC 0 @@ -54,54 +56,35 @@ #define M41T80_ALHOUR_HT (1 << 6) /* HT: Halt Update Bit */ #define M41T80_FLAGS_AF (1 << 6) /* AF: Alarm Flag Bit */ #define M41T80_FLAGS_BATT_LOW (1 << 4) /* BL: Battery Low Bit */ - -#define M41T80_FEATURE_HT (1 << 0) -#define M41T80_FEATURE_BL (1 << 1) - -#define DRV_VERSION "0.05" - -struct m41t80_chip_info { - const char *name; - u8 features; -}; - -static const struct m41t80_chip_info m41t80_chip_info_tbl[] = { - { - .name = "m41t80", - .features = 0, - }, - { - .name = "m41t81", - .features = M41T80_FEATURE_HT, - }, - { - .name = "m41t81s", - .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, - }, - { - .name = "m41t82", - .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, - }, - { - .name = "m41t83", - .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, - }, - { - .name = "m41st84", - .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, - }, - { - .name = "m41st85", - .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, - }, - { - .name = "m41st87", - .features = M41T80_FEATURE_HT | M41T80_FEATURE_BL, - }, +#define M41T80_WATCHDOG_RB2 (1 << 7) /* RB: Watchdog resolution */ +#define M41T80_WATCHDOG_RB1 (1 << 1) /* RB: Watchdog resolution */ +#define M41T80_WATCHDOG_RB0 (1 << 0) /* RB: Watchdog resolution */ + +#define M41T80_FEATURE_HT (1 << 0) /* Halt feature */ +#define M41T80_FEATURE_BL (1 << 1) /* Battery low indicator */ +#define M41T80_FEATURE_SQ (1 << 2) /* Squarewave feature */ +#define M41T80_FEATURE_WD (1 << 3) /* Extra watchdog resolution */ +#define M41T80_FEATURE_SQ_ALT (1 << 4) /* RSx bits are in reg 4 */ + +static DEFINE_MUTEX(m41t80_rtc_mutex); +static const struct i2c_device_id m41t80_id[] = { + { "m41t62", M41T80_FEATURE_SQ | M41T80_FEATURE_SQ_ALT }, + { "m41t65", M41T80_FEATURE_HT | M41T80_FEATURE_WD }, + { "m41t80", M41T80_FEATURE_SQ }, + { "m41t81", M41T80_FEATURE_HT | M41T80_FEATURE_SQ}, + { "m41t81s", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { "m41t82", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { "m41t83", M41T80_FEATURE_HT | M41T80_FEATURE_BL | M41T80_FEATURE_SQ }, + { "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); struct m41t80_data { - const struct m41t80_chip_info *chip; + u8 features; struct rtc_device *rtc; }; @@ -129,16 +112,16 @@ static int m41t80_get_datetime(struct i2c_client *client, return -EIO; } - tm->tm_sec = BCD2BIN(buf[M41T80_REG_SEC] & 0x7f); - tm->tm_min = BCD2BIN(buf[M41T80_REG_MIN] & 0x7f); - tm->tm_hour = BCD2BIN(buf[M41T80_REG_HOUR] & 0x3f); - tm->tm_mday = BCD2BIN(buf[M41T80_REG_DAY] & 0x3f); + tm->tm_sec = bcd2bin(buf[M41T80_REG_SEC] & 0x7f); + tm->tm_min = bcd2bin(buf[M41T80_REG_MIN] & 0x7f); + tm->tm_hour = bcd2bin(buf[M41T80_REG_HOUR] & 0x3f); + tm->tm_mday = bcd2bin(buf[M41T80_REG_DAY] & 0x3f); tm->tm_wday = buf[M41T80_REG_WDAY] & 0x07; - tm->tm_mon = BCD2BIN(buf[M41T80_REG_MON] & 0x1f) - 1; + tm->tm_mon = bcd2bin(buf[M41T80_REG_MON] & 0x1f) - 1; /* assume 20YY not 19YY, and ignore the Century Bit */ - tm->tm_year = BCD2BIN(buf[M41T80_REG_YEAR]) + 100; - return 0; + tm->tm_year = bcd2bin(buf[M41T80_REG_YEAR]) + 100; + return rtc_valid_tm(tm); } /* Sets the given date and time to the real time clock. */ @@ -180,19 +163,19 @@ static int m41t80_set_datetime(struct i2c_client *client, struct rtc_time *tm) /* Merge time-data and register flags into buf[0..7] */ buf[M41T80_REG_SSEC] = 0; buf[M41T80_REG_SEC] = - BIN2BCD(tm->tm_sec) | (buf[M41T80_REG_SEC] & ~0x7f); + bin2bcd(tm->tm_sec) | (buf[M41T80_REG_SEC] & ~0x7f); buf[M41T80_REG_MIN] = - BIN2BCD(tm->tm_min) | (buf[M41T80_REG_MIN] & ~0x7f); + 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] = - BIN2BCD(tm->tm_mday) | (buf[M41T80_REG_DAY] & ~0x3f); + bin2bcd(tm->tm_mday) | (buf[M41T80_REG_DAY] & ~0x3f); buf[M41T80_REG_MON] = - BIN2BCD(tm->tm_mon + 1) | (buf[M41T80_REG_MON] & ~0x1f); + bin2bcd(tm->tm_mon + 1) | (buf[M41T80_REG_MON] & ~0x1f); /* assume 20YY not 19YY */ - buf[M41T80_REG_YEAR] = BIN2BCD(tm->tm_year % 100); + buf[M41T80_REG_YEAR] = bin2bcd(tm->tm_year % 100); if (i2c_transfer(client->adapter, msgs, 1) != 1) { dev_err(&client->dev, "write error\n"); @@ -208,7 +191,7 @@ static int m41t80_rtc_proc(struct device *dev, struct seq_file *seq) struct m41t80_data *clientdata = i2c_get_clientdata(client); u8 reg; - if (clientdata->chip->features & M41T80_FEATURE_BL) { + if (clientdata->features & M41T80_FEATURE_BL) { reg = i2c_smbus_read_byte_data(client, M41T80_REG_FLAGS); seq_printf(seq, "battery\t\t: %s\n", (reg & M41T80_FLAGS_BATT_LOW) ? "exhausted" : "ok"); @@ -229,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) @@ -402,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); @@ -411,11 +240,18 @@ static ssize_t m41t80_sysfs_show_sqwfreq(struct device *dev, struct device_attribute *attr, char *buf) { struct i2c_client *client = to_i2c_client(dev); - int val; + struct m41t80_data *clientdata = i2c_get_clientdata(client); + int val, reg_sqw; - val = i2c_smbus_read_byte_data(client, M41T80_REG_SQW); + if (!(clientdata->features & M41T80_FEATURE_SQ)) + return -EINVAL; + + reg_sqw = M41T80_REG_SQW; + if (clientdata->features & M41T80_FEATURE_SQ_ALT) + 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: @@ -433,9 +269,13 @@ static ssize_t m41t80_sysfs_set_sqwfreq(struct device *dev, const char *buf, size_t count) { struct i2c_client *client = to_i2c_client(dev); - int almon, sqw; + struct m41t80_data *clientdata = i2c_get_clientdata(client); + int almon, sqw, reg_sqw, rc; int val = simple_strtoul(buf, NULL, 0); + if (!(clientdata->features & M41T80_FEATURE_SQ)) + return -EINVAL; + if (val) { if (!is_power_of_2(val)) return -EINVAL; @@ -450,18 +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; - sqw = i2c_smbus_read_byte_data(client, M41T80_REG_SQW); + 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, M41T80_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, @@ -524,6 +376,8 @@ static void wdt_ping(void) .buf = i2c_data, }, }; + struct m41t80_data *clientdata = i2c_get_clientdata(save_client); + i2c_data[0] = 0x09; /* watchdog register */ if (wdt_margin > 31) @@ -534,6 +388,13 @@ static void wdt_ping(void) */ i2c_data[1] = wdt_margin<<2 | 0x82; + /* + * M41T65 has three bits for watchdog resolution. Don't set bit 7, as + * that would be an invalid resolution. + */ + if (clientdata->features & M41T80_FEATURE_WD) + i2c_data[1] &= ~M41T80_WATCHDOG_RB2; + i2c_transfer(save_client->adapter, msgs1, 1); } @@ -589,10 +450,6 @@ static void wdt_disable(void) static ssize_t wdt_write(struct file *file, const char __user *buf, size_t count, loff_t *ppos) { - /* Can't seek (pwrite) on this device - if (ppos != &file->f_pos) - return -ESPIPE; - */ if (count) { wdt_ping(); return 1; @@ -617,7 +474,7 @@ static ssize_t wdt_read(struct file *file, char __user *buf, * according to their available features. We only actually usefully support * querying capabilities and current status. */ -static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, +static int wdt_ioctl(struct file *file, unsigned int cmd, unsigned long arg) { int new_margin, rv; @@ -656,14 +513,12 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -EFAULT; if (rv & WDIOS_DISABLECARD) { - printk(KERN_INFO - "rtc-m41t80: disable watchdog\n"); + pr_info("rtc-m41t80: disable watchdog\n"); wdt_disable(); } if (rv & WDIOS_ENABLECARD) { - printk(KERN_INFO - "rtc-m41t80: enable watchdog\n"); + pr_info("rtc-m41t80: enable watchdog\n"); wdt_ping(); } @@ -672,6 +527,18 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, return -ENOTTY; } +static long wdt_unlocked_ioctl(struct file *file, unsigned int cmd, + unsigned long arg) +{ + int ret; + + mutex_lock(&m41t80_rtc_mutex); + ret = wdt_ioctl(file, cmd, arg); + mutex_unlock(&m41t80_rtc_mutex); + + return ret; +} + /** * wdt_open: * @inode: inode of device @@ -681,13 +548,17 @@ static int wdt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, static int wdt_open(struct inode *inode, struct file *file) { if (MINOR(inode->i_rdev) == WATCHDOG_MINOR) { - if (test_and_set_bit(0, &wdt_is_open)) + mutex_lock(&m41t80_rtc_mutex); + if (test_and_set_bit(0, &wdt_is_open)) { + mutex_unlock(&m41t80_rtc_mutex); return -EBUSY; + } /* * Activate */ wdt_is_open = 1; - return 0; + mutex_unlock(&m41t80_rtc_mutex); + return nonseekable_open(inode, file); } return -ENODEV; } @@ -728,10 +599,11 @@ static int wdt_notify_sys(struct notifier_block *this, unsigned long code, static const struct file_operations wdt_fops = { .owner = THIS_MODULE, .read = wdt_read, - .ioctl = wdt_ioctl, + .unlocked_ioctl = wdt_unlocked_ioctl, .write = wdt_write, .open = wdt_open, .release = wdt_release, + .llseek = no_llseek, }; static struct miscdevice wdt_dev = { @@ -756,61 +628,38 @@ static struct notifier_block wdt_notifier = { * ***************************************************************************** */ -static int m41t80_probe(struct i2c_client *client) +static int m41t80_probe(struct i2c_client *client, + const struct i2c_device_id *id) { - int i, rc = 0; + int rc = 0; struct rtc_device *rtc = NULL; struct rtc_time tm; - const struct m41t80_chip_info *chip; struct m41t80_data *clientdata = NULL; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C - | I2C_FUNC_SMBUS_BYTE_DATA)) { - rc = -ENODEV; - goto exit; - } - - dev_info(&client->dev, - "chip found, driver version " DRV_VERSION "\n"); + | I2C_FUNC_SMBUS_BYTE_DATA)) + return -ENODEV; - chip = NULL; - for (i = 0; i < ARRAY_SIZE(m41t80_chip_info_tbl); i++) { - if (!strcmp(m41t80_chip_info_tbl[i].name, client->name)) { - chip = &m41t80_chip_info_tbl[i]; - break; - } - } - if (!chip) { - dev_err(&client->dev, "%s is not supported\n", client->name); - rc = -ENODEV; - goto exit; - } + 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->chip = chip; - 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 (chip->features & M41T80_FEATURE_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"); dev_info(&client->dev, @@ -820,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 (chip->features & M41T80_FEATURE_HT) { + 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; } - save_client = client; } #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->chip->features & M41T80_FEATURE_HT) { + 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; } @@ -896,22 +729,11 @@ static struct i2c_driver m41t80_driver = { }, .probe = m41t80_probe, .remove = m41t80_remove, + .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); |
