Skip to content

Commit 05cb3a5

Browse files
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: Alexandre Belloni <[email protected]>
1 parent 0e2e877 commit 05cb3a5

File tree

1 file changed

+121
-0
lines changed

1 file changed

+121
-0
lines changed

drivers/rtc/rtc-pcf85063.c

Lines changed: 121 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,7 @@
1111
#include <linux/rtc.h>
1212
#include <linux/module.h>
1313
#include <linux/of_device.h>
14+
#include <linux/pm_wakeirq.h>
1415
#include <linux/regmap.h>
1516

1617
/*
@@ -27,11 +28,19 @@
2728
#define PCF85063_REG_CTRL1_CAP_SEL BIT(0)
2829
#define PCF85063_REG_CTRL1_STOP BIT(5)
2930

31+
#define PCF85063_REG_CTRL2 0x01
32+
#define PCF85063_CTRL2_AF BIT(6)
33+
#define PCF85063_CTRL2_AIE BIT(7)
34+
3035
#define PCF85063_REG_SC 0x04 /* datetime */
3136
#define PCF85063_REG_SC_OS 0x80
3237

38+
#define PCF85063_REG_ALM_S 0x0b
39+
#define PCF85063_AEN BIT(7)
40+
3341
struct pcf85063_config {
3442
struct regmap_config regmap;
43+
unsigned has_alarms:1;
3544
};
3645

3746
struct pcf85063 {
@@ -123,11 +132,103 @@ static int pcf85063_rtc_set_time(struct device *dev, struct rtc_time *tm)
123132
PCF85063_REG_CTRL1_STOP, 0);
124133
}
125134

135+
static int pcf85063_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
136+
{
137+
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
138+
u8 buf[4];
139+
unsigned int val;
140+
int ret;
141+
142+
ret = regmap_bulk_read(pcf85063->regmap, PCF85063_REG_ALM_S,
143+
buf, sizeof(buf));
144+
if (ret)
145+
return ret;
146+
147+
alrm->time.tm_sec = bcd2bin(buf[0]);
148+
alrm->time.tm_min = bcd2bin(buf[1]);
149+
alrm->time.tm_hour = bcd2bin(buf[2]);
150+
alrm->time.tm_mday = bcd2bin(buf[3]);
151+
152+
ret = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
153+
if (ret)
154+
return ret;
155+
156+
alrm->enabled = !!(val & PCF85063_CTRL2_AIE);
157+
158+
return 0;
159+
}
160+
161+
static int pcf85063_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
162+
{
163+
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
164+
u8 buf[5];
165+
int ret;
166+
167+
buf[0] = bin2bcd(alrm->time.tm_sec);
168+
buf[1] = bin2bcd(alrm->time.tm_min);
169+
buf[2] = bin2bcd(alrm->time.tm_hour);
170+
buf[3] = bin2bcd(alrm->time.tm_mday);
171+
buf[4] = PCF85063_AEN; /* Do not match on week day */
172+
173+
ret = regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
174+
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF, 0);
175+
if (ret)
176+
return ret;
177+
178+
ret = regmap_bulk_write(pcf85063->regmap, PCF85063_REG_ALM_S,
179+
buf, sizeof(buf));
180+
if (ret)
181+
return ret;
182+
183+
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
184+
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
185+
alrm->enabled ? PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF : PCF85063_CTRL2_AF);
186+
}
187+
188+
static int pcf85063_rtc_alarm_irq_enable(struct device *dev,
189+
unsigned int enabled)
190+
{
191+
struct pcf85063 *pcf85063 = dev_get_drvdata(dev);
192+
193+
return regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
194+
PCF85063_CTRL2_AIE,
195+
enabled ? PCF85063_CTRL2_AIE : 0);
196+
}
197+
198+
static irqreturn_t pcf85063_rtc_handle_irq(int irq, void *dev_id)
199+
{
200+
struct pcf85063 *pcf85063 = dev_id;
201+
unsigned int val;
202+
int err;
203+
204+
err = regmap_read(pcf85063->regmap, PCF85063_REG_CTRL2, &val);
205+
if (err)
206+
return IRQ_NONE;
207+
208+
if (val & PCF85063_CTRL2_AF) {
209+
rtc_update_irq(pcf85063->rtc, 1, RTC_IRQF | RTC_AF);
210+
regmap_update_bits(pcf85063->regmap, PCF85063_REG_CTRL2,
211+
PCF85063_CTRL2_AIE | PCF85063_CTRL2_AF,
212+
0);
213+
return IRQ_HANDLED;
214+
}
215+
216+
return IRQ_NONE;
217+
}
218+
126219
static const struct rtc_class_ops pcf85063_rtc_ops = {
127220
.read_time = pcf85063_rtc_read_time,
128221
.set_time = pcf85063_rtc_set_time
129222
};
130223

224+
static const struct rtc_class_ops pcf85063_rtc_ops_alarm = {
225+
.read_time = pcf85063_rtc_read_time,
226+
.set_time = pcf85063_rtc_set_time,
227+
.read_alarm = pcf85063_rtc_read_alarm,
228+
.set_alarm = pcf85063_rtc_set_alarm,
229+
.alarm_irq_enable = pcf85063_rtc_alarm_irq_enable,
230+
};
231+
131232
static int pcf85063_load_capacitance(struct pcf85063 *pcf85063,
132233
const struct device_node *np)
133234
{
@@ -157,6 +258,7 @@ static const struct pcf85063_config pcf85063a_config = {
157258
.val_bits = 8,
158259
.max_register = 0x11,
159260
},
261+
.has_alarms = 1,
160262
};
161263

162264
static const struct pcf85063_config pcf85063tp_config = {
@@ -209,6 +311,25 @@ static int pcf85063_probe(struct i2c_client *client)
209311
pcf85063->rtc->ops = &pcf85063_rtc_ops;
210312
pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
211313
pcf85063->rtc->range_max = RTC_TIMESTAMP_END_2099;
314+
pcf85063->rtc->uie_unsupported = 1;
315+
316+
if (config->has_alarms && client->irq > 0) {
317+
err = devm_request_threaded_irq(&client->dev, client->irq,
318+
NULL, pcf85063_rtc_handle_irq,
319+
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
320+
"pcf85063", pcf85063);
321+
if (err) {
322+
dev_warn(&pcf85063->rtc->dev,
323+
"unable to request IRQ, alarms disabled\n");
324+
} else {
325+
pcf85063->rtc->ops = &pcf85063_rtc_ops_alarm;
326+
device_init_wakeup(&client->dev, true);
327+
err = dev_pm_set_wake_irq(&client->dev, client->irq);
328+
if (err)
329+
dev_err(&pcf85063->rtc->dev,
330+
"failed to enable irq wake\n");
331+
}
332+
}
212333

213334
return rtc_register_device(pcf85063->rtc);
214335
}

0 commit comments

Comments
 (0)