diff options
Diffstat (limited to 'drivers/rtc/rtc-isl1208.c')
-rw-r--r-- | drivers/rtc/rtc-isl1208.c | 192 |
1 files changed, 169 insertions, 23 deletions
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c index 1a2c38cc0178..ea18a8f4bce0 100644 --- a/drivers/rtc/rtc-isl1208.c +++ b/drivers/rtc/rtc-isl1208.c @@ -14,6 +14,8 @@ #include <linux/i2c.h> #include <linux/bcd.h> #include <linux/rtc.h> +#include "rtc-core.h" +#include <linux/of_irq.h> /* Register map */ /* rtc section */ @@ -33,13 +35,16 @@ #define ISL1208_REG_SR_ARST (1<<7) /* auto reset */ #define ISL1208_REG_SR_XTOSCB (1<<6) /* crystal oscillator */ #define ISL1208_REG_SR_WRTC (1<<4) /* write rtc */ +#define ISL1208_REG_SR_EVT (1<<3) /* event */ #define ISL1208_REG_SR_ALM (1<<2) /* alarm */ #define ISL1208_REG_SR_BAT (1<<1) /* battery */ #define ISL1208_REG_SR_RTCF (1<<0) /* rtc fail */ #define ISL1208_REG_INT 0x08 #define ISL1208_REG_INT_ALME (1<<6) /* alarm enable */ #define ISL1208_REG_INT_IM (1<<7) /* interrupt/alarm mode */ -#define ISL1208_REG_09 0x09 /* reserved */ +#define ISL1219_REG_EV 0x09 +#define ISL1219_REG_EV_EVEN (1<<4) /* event detection enable */ +#define ISL1219_REG_EV_EVIENB (1<<7) /* event in pull-up disable */ #define ISL1208_REG_ATR 0x0a #define ISL1208_REG_DTR 0x0b @@ -57,8 +62,24 @@ #define ISL1208_REG_USR2 0x13 #define ISL1208_USR_SECTION_LEN 2 +/* event section */ +#define ISL1219_REG_SCT 0x14 +#define ISL1219_REG_MNT 0x15 +#define ISL1219_REG_HRT 0x16 +#define ISL1219_REG_DTT 0x17 +#define ISL1219_REG_MOT 0x18 +#define ISL1219_REG_YRT 0x19 +#define ISL1219_EVT_SECTION_LEN 6 + static struct i2c_driver isl1208_driver; +/* ISL1208 various variants */ +enum { + TYPE_ISL1208 = 0, + TYPE_ISL1218, + TYPE_ISL1219, +}; + /* block read */ static int isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], @@ -80,8 +101,8 @@ isl1208_i2c_read_regs(struct i2c_client *client, u8 reg, u8 buf[], }; int ret; - BUG_ON(reg > ISL1208_REG_USR2); - BUG_ON(reg + len > ISL1208_REG_USR2 + 1); + WARN_ON(reg > ISL1219_REG_YRT); + WARN_ON(reg + len > ISL1219_REG_YRT + 1); ret = i2c_transfer(client->adapter, msgs, 2); if (ret > 0) @@ -104,8 +125,8 @@ isl1208_i2c_set_regs(struct i2c_client *client, u8 reg, u8 const buf[], }; int ret; - BUG_ON(reg > ISL1208_REG_USR2); - BUG_ON(reg + len > ISL1208_REG_USR2 + 1); + WARN_ON(reg > ISL1219_REG_YRT); + WARN_ON(reg + len > ISL1219_REG_YRT + 1); i2c_buf[0] = reg; memcpy(&i2c_buf[1], &buf[0], len); @@ -493,6 +514,73 @@ isl1208_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm) return isl1208_i2c_set_alarm(to_i2c_client(dev), alarm); } +static ssize_t timestamp0_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t count) +{ + struct i2c_client *client = dev_get_drvdata(dev); + int sr; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return sr; + } + + sr &= ~ISL1208_REG_SR_EVT; + + sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr); + if (sr < 0) + dev_err(dev, "%s: writing SR failed\n", + __func__); + + return count; +}; + +static ssize_t timestamp0_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct i2c_client *client = dev_get_drvdata(dev); + u8 regs[ISL1219_EVT_SECTION_LEN] = { 0, }; + struct rtc_time tm; + int sr; + + sr = isl1208_i2c_get_sr(client); + if (sr < 0) { + dev_err(dev, "%s: reading SR failed\n", __func__); + return sr; + } + + if (!(sr & ISL1208_REG_SR_EVT)) + return 0; + + sr = isl1208_i2c_read_regs(client, ISL1219_REG_SCT, regs, + ISL1219_EVT_SECTION_LEN); + if (sr < 0) { + dev_err(dev, "%s: reading event section failed\n", + __func__); + return 0; + } + + /* MSB of each alarm register is an enable bit */ + tm.tm_sec = bcd2bin(regs[ISL1219_REG_SCT - ISL1219_REG_SCT] & 0x7f); + tm.tm_min = bcd2bin(regs[ISL1219_REG_MNT - ISL1219_REG_SCT] & 0x7f); + tm.tm_hour = bcd2bin(regs[ISL1219_REG_HRT - ISL1219_REG_SCT] & 0x3f); + tm.tm_mday = bcd2bin(regs[ISL1219_REG_DTT - ISL1219_REG_SCT] & 0x3f); + tm.tm_mon = + bcd2bin(regs[ISL1219_REG_MOT - ISL1219_REG_SCT] & 0x1f) - 1; + tm.tm_year = bcd2bin(regs[ISL1219_REG_YRT - ISL1219_REG_SCT]) + 100; + + sr = rtc_valid_tm(&tm); + if (sr) + return sr; + + return sprintf(buf, "%llu\n", + (unsigned long long)rtc_tm_to_time64(&tm)); +}; + +static DEVICE_ATTR_RW(timestamp0); + static irqreturn_t isl1208_rtc_interrupt(int irq, void *data) { @@ -538,6 +626,13 @@ isl1208_rtc_interrupt(int irq, void *data) return err; } + if (sr & ISL1208_REG_SR_EVT) { + sysfs_notify(&rtc->dev.kobj, NULL, + dev_attr_timestamp0.attr.name); + dev_warn(&client->dev, "event detected"); + handled = 1; + } + return handled ? IRQ_HANDLED : IRQ_NONE; } @@ -623,11 +718,39 @@ static const struct attribute_group isl1208_rtc_sysfs_files = { .attrs = isl1208_rtc_attrs, }; +static struct attribute *isl1219_rtc_attrs[] = { + &dev_attr_timestamp0.attr, + NULL +}; + +static const struct attribute_group isl1219_rtc_sysfs_files = { + .attrs = isl1219_rtc_attrs, +}; + +static int isl1208_setup_irq(struct i2c_client *client, int irq) +{ + int rc = devm_request_threaded_irq(&client->dev, irq, NULL, + isl1208_rtc_interrupt, + IRQF_SHARED | IRQF_ONESHOT, + isl1208_driver.driver.name, + client); + if (!rc) { + device_init_wakeup(&client->dev, 1); + enable_irq_wake(irq); + } else { + dev_err(&client->dev, + "Unable to request irq %d, no alarm support\n", + irq); + } + return rc; +} + static int isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) { int rc = 0; struct rtc_device *rtc; + int evdet_irq = -1; if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) return -ENODEV; @@ -642,6 +765,7 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) rtc->ops = &isl1208_rtc_ops; i2c_set_clientdata(client, rtc); + dev_set_drvdata(&rtc->dev, client); rc = isl1208_i2c_get_sr(client); if (rc < 0) { @@ -653,26 +777,46 @@ isl1208_probe(struct i2c_client *client, const struct i2c_device_id *id) dev_warn(&client->dev, "rtc power failure detected, " "please set clock.\n"); + if (id->driver_data == TYPE_ISL1219) { + struct device_node *np = client->dev.of_node; + u32 evienb; + + rc = i2c_smbus_read_byte_data(client, ISL1219_REG_EV); + if (rc < 0) { + dev_err(&client->dev, "failed to read EV reg\n"); + return rc; + } + rc |= ISL1219_REG_EV_EVEN; + if (!of_property_read_u32(np, "isil,ev-evienb", &evienb)) { + if (evienb) + rc |= ISL1219_REG_EV_EVIENB; + else + rc &= ~ISL1219_REG_EV_EVIENB; + } + rc = i2c_smbus_write_byte_data(client, ISL1219_REG_EV, rc); + if (rc < 0) { + dev_err(&client->dev, "could not enable tamper detection\n"); + return rc; + } + rc = rtc_add_group(rtc, &isl1219_rtc_sysfs_files); + if (rc) + return rc; + evdet_irq = of_irq_get_byname(np, "evdet"); + } + rc = sysfs_create_group(&client->dev.kobj, &isl1208_rtc_sysfs_files); if (rc) return rc; - if (client->irq > 0) { - rc = devm_request_threaded_irq(&client->dev, client->irq, NULL, - isl1208_rtc_interrupt, - IRQF_SHARED | IRQF_ONESHOT, - isl1208_driver.driver.name, - client); - if (!rc) { - device_init_wakeup(&client->dev, 1); - enable_irq_wake(client->irq); - } else { - dev_err(&client->dev, - "Unable to request irq %d, no alarm support\n", - client->irq); - client->irq = 0; - } - } + if (client->irq > 0) + rc = isl1208_setup_irq(client, client->irq); + if (rc) + return rc; + + if (evdet_irq > 0 && evdet_irq != client->irq) + rc = isl1208_setup_irq(client, evdet_irq); + if (rc) + return rc; return rtc_register_device(rtc); } @@ -686,8 +830,9 @@ isl1208_remove(struct i2c_client *client) } static const struct i2c_device_id isl1208_id[] = { - { "isl1208", 0 }, - { "isl1218", 0 }, + { "isl1208", TYPE_ISL1208 }, + { "isl1218", TYPE_ISL1218 }, + { "isl1219", TYPE_ISL1219 }, { } }; MODULE_DEVICE_TABLE(i2c, isl1208_id); @@ -695,6 +840,7 @@ MODULE_DEVICE_TABLE(i2c, isl1208_id); static const struct of_device_id isl1208_of_match[] = { { .compatible = "isil,isl1208" }, { .compatible = "isil,isl1218" }, + { .compatible = "isil,isl1219" }, { } }; MODULE_DEVICE_TABLE(of, isl1208_of_match); |