#include <linux/rtc.h>
#include <linux/module.h>
#include <linux/of_device.h>
+#include <linux/pm_wakeirq.h>
#include <linux/regmap.h>
/*
#define PCF85063_REG_CTRL1_CAP_SEL BIT(0)
#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_OS 0x80
+#define PCF85063_REG_ALM_S 0x0b
+#define PCF85063_AEN BIT(7)
+
struct pcf85063_config {
struct regmap_config regmap;
+ unsigned has_alarms:1;
};
struct pcf85063 {
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 = {
.read_time = pcf85063_rtc_read_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,
const struct device_node *np)
{
.val_bits = 8,
.max_register = 0x11,
},
+ .has_alarms = 1,
};
static const struct pcf85063_config pcf85063tp_config = {
pcf85063->rtc->ops = &pcf85063_rtc_ops;
pcf85063->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
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);
}