Commit 05cb3a56 authored by Alexandre Belloni's avatar Alexandre Belloni

rtc: pcf85063: add alarm support

Add support for the alarms. The match on the weekday is not used as it it
not necessarily properly set.

The tested RTC shows a behaviour where setting an alarm on the second right
after an alarm that fired is not working, probably because of the circuit
that ensures an alarm only fires once. This is why uie_unsupported is set.
Signed-off-by: default avatarAlexandre Belloni <alexandre.belloni@bootlin.com>
parent 0e2e8777
...@@ -11,6 +11,7 @@ ...@@ -11,6 +11,7 @@
#include <linux/rtc.h> #include <linux/rtc.h>
#include <linux/module.h> #include <linux/module.h>
#include <linux/of_device.h> #include <linux/of_device.h>
#include <linux/pm_wakeirq.h>
#include <linux/regmap.h> #include <linux/regmap.h>
/* /*
...@@ -27,11 +28,19 @@ ...@@ -27,11 +28,19 @@
#define PCF85063_REG_CTRL1_CAP_SEL BIT(0) #define PCF85063_REG_CTRL1_CAP_SEL BIT(0)
#define PCF85063_REG_CTRL1_STOP BIT(5) #define PCF85063_REG_CTRL1_STOP BIT(5)
#define PCF85063_REG_CTRL2 0x01
#define PCF85063_CTRL2_AF BIT(6)
#define PCF85063_CTRL2_AIE BIT(7)
#define PCF85063_REG_SC 0x04 /* datetime */ #define PCF85063_REG_SC 0x04 /* datetime */
#define PCF85063_REG_SC_OS 0x80 #define PCF85063_REG_SC_OS 0x80
#define PCF85063_REG_ALM_S 0x0b
#define PCF85063_AEN BIT(7)
struct pcf85063_config { struct pcf85063_config {
struct regmap_config regmap; struct regmap_config regmap;
unsigned has_alarms:1;
}; };
struct pcf85063 { struct pcf85063 {
...@@ -123,11 +132,103 @@ static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm) ...@@ -123,11 +132,103 @@ static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
PCF85063_REG_CTRL1_STOP, 0); PCF85063_REG_CTRL1_STOP, 0);
} }
static int pcf85063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
u8 buf[4];
unsigned int val;
int ret;
ret = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_ALM_S,
buf, sizeof(buf));
if (ret)
return ret;
alrm->time.tm_sec = bcd2bin(buf[0]);
alrm->time.tm_min = bcd2bin(buf[1]);
alrm->time.tm_hour = bcd2bin(buf[2]);
alrm->time.tm_mday = bcd2bin(buf[3]);
ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
if (ret)
return ret;
alrm->enabled = !!(val & PCF85063_CTRL2_AIE);
return 0;
}
static int pcf85063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
u8 buf[5];
int ret;
buf[0] = bin2bcd(alrm->time.tm_sec);
buf[1] = bin2bcd(alrm->time.tm_min);
buf[2] = bin2bcd(alrm->time.tm_hour);
buf[3] = bin2bcd(alrm->time.tm_mday);
buf[4] = PCF85063_AEN; /* Do not match on week day */
ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, 0);
if (ret)
return ret;
ret = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_ALM_S,
buf, sizeof(buf));
if (ret)
return ret;
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
alrm->enabled ? PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF : PCF85063_CTRL2_AF);
}
static int pcf85063_rtc_alarm_irq_enable(struct device *dev,
unsigned int enabled)
{
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
PCF85063_CTRL2_AIE,
enabled ? PCF85063_CTRL2_AIE : 0);
}
static irqreturn_t pcf85063_rtc_handle_irq(int irq, void *dev_id)
{
struct pcf85063 *pcf85063 = dev_id;
unsigned int val;
int err;
err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
if (err)
return IRQ_NONE;
if (val & PCF85063_CTRL2_AF) {
rtc_update_irq(pcf85063->rtc, 1, RTC_IRQF | RTC_AF);
regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
0);
return IRQ_HANDLED;
}
return IRQ_NONE;
}
static const struct rtc_class_ops pcf85063_rtc_ops = { static const struct rtc_class_ops pcf85063_rtc_ops = {
.read_time = pcf85063_rtc_read_time, .read_time = pcf85063_rtc_read_time,
.set_time = pcf85063_rtc_set_time .set_time = pcf85063_rtc_set_time
}; };
static const struct rtc_class_ops pcf85063_rtc_ops_alarm = {
.read_time = pcf85063_rtc_read_time,
.set_time = pcf85063_rtc_set_time,
.read_alarm = pcf85063_rtc_read_alarm,
.set_alarm = pcf85063_rtc_set_alarm,
.alarm_irq_enable = pcf85063_rtc_alarm_irq_enable,
};
static int pcf85063_load_capacitance(struct pcf85063 *pcf85063, static int pcf85063_load_capacitance(struct pcf85063 *pcf85063,
const struct device_node *np) const struct device_node *np)
{ {
...@@ -157,6 +258,7 @@ static const struct pcf85063_config pcf85063a_config = { ...@@ -157,6 +258,7 @@ static const struct pcf85063_config pcf85063a_config = {
.val_bits = 8, .val_bits = 8,
.max_register = 0x11, .max_register = 0x11,
}, },
.has_alarms = 1,
}; };
static const struct pcf85063_config pcf85063tp_config = { static const struct pcf85063_config pcf85063tp_config = {
...@@ -209,6 +311,25 @@ static int pcf85063_probe(struct i2c_client *client) ...@@ -209,6 +311,25 @@ static int pcf85063_probe(struct i2c_client *client)
pcf85063->rtc->ops = &pcf85063_rtc_ops; pcf85063->rtc->ops = &pcf85063_rtc_ops;
pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099; pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
pcf85063->rtc->uie_unsupported = 1;
if (config->has_alarms && client->irq > 0) {
err = devm_request_threaded_irq(&client->dev, client->irq,
NULL, pcf85063_rtc_handle_irq,
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"pcf85063", pcf85063);
if (err) {
dev_warn(&pcf85063->rtc->dev,
"unable to request IRQ, alarms disabled\n");
} else {
pcf85063->rtc->ops = &pcf85063_rtc_ops_alarm;
device_init_wakeup(&client->dev, true);
err = dev_pm_set_wake_irq(&client->dev, client->irq);
if (err)
dev_err(&pcf85063->rtc->dev,
"failed to enable irq wake\n");
}
}
return rtc_register_device(pcf85063->rtc); return rtc_register_device(pcf85063->rtc);
} }
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment