summaryrefslogtreecommitdiff
path: root/drivers/rtc/rtc-isl1208.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/rtc/rtc-isl1208.c')
-rw-r--r--drivers/rtc/rtc-isl1208.c179
1 files changed, 124 insertions, 55 deletions
diff --git a/drivers/rtc/rtc-isl1208.c b/drivers/rtc/rtc-isl1208.c
index 73cc6aaf9b8b..f71a6bb77b2a 100644
--- a/drivers/rtc/rtc-isl1208.c
+++ b/drivers/rtc/rtc-isl1208.c
@@ -6,9 +6,11 @@
*/
#include <linux/bcd.h>
+#include <linux/clk.h>
+#include <linux/delay.h>
#include <linux/i2c.h>
#include <linux/module.h>
-#include <linux/of_device.h>
+#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/rtc.h>
@@ -68,42 +70,60 @@
static struct i2c_driver isl1208_driver;
-/* ISL1208 various variants */
-enum isl1208_id {
- TYPE_ISL1208 = 0,
- TYPE_ISL1209,
- TYPE_ISL1218,
- TYPE_ISL1219,
- ISL_LAST_ID
-};
-
/* Chip capabilities table */
-static const struct isl1208_config {
- const char name[8];
+struct isl1208_config {
unsigned int nvmem_length;
unsigned has_tamper:1;
unsigned has_timestamp:1;
-} isl1208_configs[] = {
- [TYPE_ISL1208] = { "isl1208", 2, false, false },
- [TYPE_ISL1209] = { "isl1209", 2, true, false },
- [TYPE_ISL1218] = { "isl1218", 8, false, false },
- [TYPE_ISL1219] = { "isl1219", 2, true, true },
+ unsigned has_inverted_osc_bit:1;
+};
+
+static const struct isl1208_config config_isl1208 = {
+ .nvmem_length = 2,
+ .has_tamper = false,
+ .has_timestamp = false
+};
+
+static const struct isl1208_config config_isl1209 = {
+ .nvmem_length = 2,
+ .has_tamper = true,
+ .has_timestamp = false
+};
+
+static const struct isl1208_config config_isl1218 = {
+ .nvmem_length = 8,
+ .has_tamper = false,
+ .has_timestamp = false
+};
+
+static const struct isl1208_config config_isl1219 = {
+ .nvmem_length = 2,
+ .has_tamper = true,
+ .has_timestamp = true
+};
+
+static const struct isl1208_config config_raa215300_a0 = {
+ .nvmem_length = 2,
+ .has_tamper = false,
+ .has_timestamp = false,
+ .has_inverted_osc_bit = true
};
static const struct i2c_device_id isl1208_id[] = {
- { "isl1208", TYPE_ISL1208 },
- { "isl1209", TYPE_ISL1209 },
- { "isl1218", TYPE_ISL1218 },
- { "isl1219", TYPE_ISL1219 },
+ { "isl1208", .driver_data = (kernel_ulong_t)&config_isl1208 },
+ { "isl1209", .driver_data = (kernel_ulong_t)&config_isl1209 },
+ { "isl1218", .driver_data = (kernel_ulong_t)&config_isl1218 },
+ { "isl1219", .driver_data = (kernel_ulong_t)&config_isl1219 },
+ { "raa215300_a0", .driver_data = (kernel_ulong_t)&config_raa215300_a0 },
{ }
};
MODULE_DEVICE_TABLE(i2c, isl1208_id);
static const __maybe_unused struct of_device_id isl1208_of_match[] = {
- { .compatible = "isil,isl1208", .data = &isl1208_configs[TYPE_ISL1208] },
- { .compatible = "isil,isl1209", .data = &isl1208_configs[TYPE_ISL1209] },
- { .compatible = "isil,isl1218", .data = &isl1208_configs[TYPE_ISL1218] },
- { .compatible = "isil,isl1219", .data = &isl1208_configs[TYPE_ISL1219] },
+ { .compatible = "isil,isl1208", .data = &config_isl1208 },
+ { .compatible = "isil,isl1209", .data = &config_isl1209 },
+ { .compatible = "isil,isl1218", .data = &config_isl1218 },
+ { .compatible = "isil,isl1219", .data = &config_isl1219 },
{ }
};
MODULE_DEVICE_TABLE(of, isl1208_of_match);
@@ -166,6 +186,20 @@ isl1208_i2c_validate_client(struct i2c_client *client)
return 0;
}
+static int isl1208_set_xtoscb(struct i2c_client *client, int sr, int xtosb_val)
+{
+ /* Do nothing if bit is already set to desired value */
+ if (!!(sr & ISL1208_REG_SR_XTOSCB) == xtosb_val)
+ return 0;
+
+ if (xtosb_val)
+ sr |= ISL1208_REG_SR_XTOSCB;
+ else
+ sr &= ~ISL1208_REG_SR_XTOSCB;
+
+ return i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);
+}
+
static int
isl1208_i2c_get_sr(struct i2c_client *client)
{
@@ -502,7 +536,6 @@ isl1208_i2c_set_time(struct i2c_client *client, struct rtc_time const *tm)
return 0;
}
-
static int
isl1208_rtc_set_time(struct device *dev, struct rtc_time *tm)
{
@@ -596,6 +629,18 @@ isl1208_rtc_interrupt(int irq, void *data)
struct isl1208_state *isl1208 = i2c_get_clientdata(client);
int handled = 0, sr, err;
+ if (!isl1208->config->has_tamper) {
+ /*
+ * The INT# output is pulled low 250ms after the alarm is
+ * triggered. After the INT# output is pulled low, it is low for
+ * at least 250ms, even if the correct action is taken to clear
+ * it. It is impossible to clear ALM if it is still active. The
+ * host must wait for the RTC to progress past the alarm time
+ * plus the 250ms delay before clearing ALM.
+ */
+ msleep(250);
+ }
+
/*
* I2C reads get NAK'ed if we read straight away after an interrupt?
* Using a mdelay/msleep didn't seem to help either, so we work around
@@ -618,6 +663,13 @@ isl1208_rtc_interrupt(int irq, void *data)
rtc_update_irq(isl1208->rtc, 1, RTC_IRQF | RTC_AF);
+ /* Disable the alarm */
+ err = isl1208_rtc_toggle_alarm(client, 0);
+ if (err)
+ return err;
+
+ fsleep(275);
+
/* Clear the alarm */
sr &= ~ISL1208_REG_SR_ALM;
sr = i2c_smbus_write_byte_data(client, ISL1208_REG_SR, sr);
@@ -626,11 +678,6 @@ isl1208_rtc_interrupt(int irq, void *data)
__func__);
else
handled = 1;
-
- /* Disable the alarm */
- err = isl1208_rtc_toggle_alarm(client, 0);
- if (err)
- return err;
}
if (isl1208->config->has_tamper && (sr & ISL1208_REG_SR_EVT)) {
@@ -743,14 +790,13 @@ static int isl1208_nvmem_read(void *priv, unsigned int off, void *buf,
{
struct isl1208_state *isl1208 = priv;
struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent);
- int ret;
/* nvmem sanitizes offset/count for us, but count==0 is possible */
if (!count)
return count;
- ret = isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf,
+
+ return isl1208_i2c_read_regs(client, ISL1208_REG_USR1 + off, buf,
count);
- return ret == 0 ? count : ret;
}
static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf,
@@ -758,15 +804,13 @@ static int isl1208_nvmem_write(void *priv, unsigned int off, void *buf,
{
struct isl1208_state *isl1208 = priv;
struct i2c_client *client = to_i2c_client(isl1208->rtc->dev.parent);
- int ret;
/* nvmem sanitizes off/count for us, but count==0 is possible */
if (!count)
return count;
- ret = isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf,
- count);
- return ret == 0 ? count : ret;
+ return isl1208_i2c_set_regs(client, ISL1208_REG_USR1 + off, buf,
+ count);
}
static const struct nvmem_config isl1208_nvmem_config = {
@@ -786,7 +830,7 @@ static int isl1208_setup_irq(struct i2c_client *client, int irq)
isl1208_driver.driver.name,
client);
if (!rc) {
- device_init_wakeup(&client->dev, 1);
+ device_init_wakeup(&client->dev, true);
enable_irq_wake(irq);
} else {
dev_err(&client->dev,
@@ -797,11 +841,25 @@ static int isl1208_setup_irq(struct i2c_client *client, int irq)
}
static int
+isl1208_clk_present(struct i2c_client *client, const char *name)
+{
+ struct clk *clk;
+
+ clk = devm_clk_get_optional(&client->dev, name);
+ if (IS_ERR(clk))
+ return PTR_ERR(clk);
+
+ return !!clk;
+}
+
+static int
isl1208_probe(struct i2c_client *client)
{
- int rc = 0;
struct isl1208_state *isl1208;
int evdet_irq = -1;
+ int xtosb_val = 0;
+ int rc = 0;
+ int sr;
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
return -ENODEV;
@@ -816,16 +874,21 @@ isl1208_probe(struct i2c_client *client)
i2c_set_clientdata(client, isl1208);
/* Determine which chip we have */
- if (client->dev.of_node) {
- isl1208->config = of_device_get_match_data(&client->dev);
- if (!isl1208->config)
- return -ENODEV;
- } else {
- const struct i2c_device_id *id = i2c_match_id(isl1208_id, client);
+ isl1208->config = i2c_get_match_data(client);
+ if (!isl1208->config)
+ return -ENODEV;
- if (id->driver_data >= ISL_LAST_ID)
- return -ENODEV;
- isl1208->config = &isl1208_configs[id->driver_data];
+ rc = isl1208_clk_present(client, "xin");
+ if (rc < 0)
+ return rc;
+
+ if (!rc) {
+ rc = isl1208_clk_present(client, "clkin");
+ if (rc < 0)
+ return rc;
+
+ if (rc)
+ xtosb_val = 1;
}
isl1208->rtc = devm_rtc_allocate_device(&client->dev);
@@ -839,13 +902,20 @@ isl1208_probe(struct i2c_client *client)
isl1208->nvmem_config.size = isl1208->config->nvmem_length;
isl1208->nvmem_config.priv = isl1208;
- rc = isl1208_i2c_get_sr(client);
- if (rc < 0) {
+ sr = isl1208_i2c_get_sr(client);
+ if (sr < 0) {
dev_err(&client->dev, "reading status failed\n");
- return rc;
+ return sr;
}
- if (rc & ISL1208_REG_SR_RTCF)
+ if (isl1208->config->has_inverted_osc_bit)
+ xtosb_val = !xtosb_val;
+
+ rc = isl1208_set_xtoscb(client, sr, xtosb_val);
+ if (rc)
+ return rc;
+
+ if (sr & ISL1208_REG_SR_RTCF)
dev_warn(&client->dev, "rtc power failure detected, "
"please set clock.\n");
@@ -886,7 +956,6 @@ isl1208_probe(struct i2c_client *client)
rc = isl1208_setup_irq(client, client->irq);
if (rc)
return rc;
-
} else {
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, isl1208->rtc->features);
}
@@ -908,7 +977,7 @@ static struct i2c_driver isl1208_driver = {
.name = "rtc-isl1208",
.of_match_table = of_match_ptr(isl1208_of_match),
},
- .probe_new = isl1208_probe,
+ .probe = isl1208_probe,
.id_table = isl1208_id,
};