OpenWrt – Rev 1

Subversion Repositories:
Rev:
From f8d89482075e2a4a62fc5cbacf6bea6baf4dc65f Mon Sep 17 00:00:00 2001
From: Biwen Li <biwen.li@nxp.com>
Date: Tue, 30 Oct 2018 18:27:31 +0800
Subject: [PATCH 23/40] rtc: support layerscape
This is an integrated patch of rtc for layerscape

Signed-off-by: Martin Fuzzey <mfuzzey@parkeon.com>
Signed-off-by: Zhang Ying-22455 <ying.zhang22455@nxp.com>
Signed-off-by: Biwen Li <biwen.li@nxp.com>
---
 .../devicetree/bindings/rtc/nxp,pcf85263.txt  |  42 ++
 drivers/rtc/Kconfig                           |   8 +
 drivers/rtc/Makefile                          |   1 +
 drivers/rtc/rtc-pcf85263.c                    | 664 ++++++++++++++++++
 include/dt-bindings/rtc/nxp,pcf85263.h        |  14 +
 5 files changed, 729 insertions(+)
 create mode 100644 Documentation/devicetree/bindings/rtc/nxp,pcf85263.txt
 create mode 100644 drivers/rtc/rtc-pcf85263.c
 create mode 100644 include/dt-bindings/rtc/nxp,pcf85263.h

--- /dev/null
+++ b/Documentation/devicetree/bindings/rtc/nxp,pcf85263.txt
@@ -0,0 +1,42 @@
+NXP PCF85263 I2C Real Time Clock
+
+Required properties:
+- compatible: must be: "nxp,rtc-pcf85263"
+- reg: must be the I2C address
+
+Optional properties:
+- interrupt-names: Which interrupt signal is used must be "INTA" or "INTB"
+    Defaults to "INTA"
+
+- quartz-load-capacitance: The internal capacitor to select for the quartz:
+       PCF85263_QUARTZCAP_7pF          [0]
+       PCF85263_QUARTZCAP_6pF          [1]
+       PCF85263_QUARTZCAP_12p5pF       [2] DEFAULT
+
+- quartz-drive-strength: Drive strength for the quartz:
+       PCF85263_QUARTZDRIVE_NORMAL     [0] DEFAULT
+       PCF85263_QUARTZDRIVE_LOW        [1]
+       PCF85263_QUARTZDRIVE_HIGH       [2]
+
+- quartz-low-jitter: Boolean property, if present enables low jitter mode
+which
+    reduces jitter at the cost of increased power consumption.
+
+- wakeup-source: mark the chip as a wakeup source, independently of
+    the availability of an IRQ line connected to the SoC.
+    This is useful if the IRQ line is connected to a PMIC or other circuit
+    that can power up the device rather than to a normal SOC interrupt.
+
+Example:
+
+rtc@51 {
+       compatible = "nxp,pcf85263";
+       reg = <0x51>;
+
+       interrupt-parent = <&gpio4>;
+       interrupts = <5 IRQ_TYPE_LEVEL_LOW>;
+       interrupt-names = "INTB";
+
+       quartz-load-capacitance = <PCF85263_QUARTZCAP_12p5pF>;
+       quartz-drive-strength = <PCF85263_QUARTZDRIVE_LOW>;
+};
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -433,6 +433,14 @@ config RTC_DRV_PCF85063
          This driver can also be built as a module. If so, the module
          will be called rtc-pcf85063.
 
+config RTC_DRV_PCF85263
+       tristate "NXP PCF85263"
+       help
+         If you say yes here you get support for the PCF85263 RTC chip
+
+         This driver can also be built as a module. If so, the module
+         will be called rtc-pcf85263.
+
 config RTC_DRV_PCF8563
        tristate "Philips PCF8563/Epson RTC8564"
        help
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -115,6 +115,7 @@ obj-$(CONFIG_RTC_DRV_PCF2127)       += rtc-pcf
 obj-$(CONFIG_RTC_DRV_PCF50633) += rtc-pcf50633.o
 obj-$(CONFIG_RTC_DRV_PCF85063) += rtc-pcf85063.o
 obj-$(CONFIG_RTC_DRV_PCF8523)  += rtc-pcf8523.o
+obj-$(CONFIG_RTC_DRV_PCF85263) += rtc-pcf85263.o
 obj-$(CONFIG_RTC_DRV_PCF8563)  += rtc-pcf8563.o
 obj-$(CONFIG_RTC_DRV_PCF8583)  += rtc-pcf8583.o
 obj-$(CONFIG_RTC_DRV_PIC32)    += rtc-pic32.o
--- /dev/null
+++ b/drivers/rtc/rtc-pcf85263.c
@@ -0,0 +1,664 @@
+/*
+ * rtc-pcf85263 Driver for the NXP PCF85263 RTC
+ * Copyright 2016 Parkeon
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/mutex.h>
+#include <linux/rtc.h>
+#include <linux/i2c.h>
+#include <linux/bcd.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+
+
+#define DRV_NAME "rtc-pcf85263"
+
+/* Quartz capacitance */
+#define PCF85263_QUARTZCAP_7pF         0
+#define PCF85263_QUARTZCAP_6pF         1
+#define PCF85263_QUARTZCAP_12p5pF      2
+
+/* Quartz drive strength */
+#define PCF85263_QUARTZDRIVE_NORMAL    0
+#define PCF85263_QUARTZDRIVE_LOW       1
+#define PCF85263_QUARTZDRIVE_HIGH      2
+
+
+#define PCF85263_REG_RTC_SC    0x01    /* Seconds */
+#define PCF85263_REG_RTC_SC_OS         BIT(7)  /* Oscilator stopped flag */
+
+#define PCF85263_REG_RTC_MN    0x02    /* Minutes */
+#define PCF85263_REG_RTC_HR    0x03    /* Hours */
+#define PCF85263_REG_RTC_DT    0x04    /* Day of month 1-31 */
+#define PCF85263_REG_RTC_DW    0x05    /* Day of week 0-6 */
+#define PCF85263_REG_RTC_MO    0x06    /* Month 1-12 */
+#define PCF85263_REG_RTC_YR    0x07    /* Year 0-99 */
+
+#define PCF85263_REG_ALM1_SC   0x08    /* Seconds */
+#define PCF85263_REG_ALM1_MN   0x09    /* Minutes */
+#define PCF85263_REG_ALM1_HR   0x0a    /* Hours */
+#define PCF85263_REG_ALM1_DT   0x0b    /* Day of month 1-31 */
+#define PCF85263_REG_ALM1_MO   0x0c    /* Month 1-12 */
+
+#define PCF85263_REG_ALM_CTL   0x10
+#define PCF85263_REG_ALM_CTL_ALL_A1E   0x1f /* sec,min,hr,day,mon alarm 1 */
+
+#define PCF85263_REG_OSC       0x25
+#define PCF85263_REG_OSC_CL_MASK       (BIT(0) | BIT(1))
+#define PCF85263_REG_OSC_CL_SHIFT      0
+#define PCF85263_REG_OSC_OSCD_MASK     (BIT(2) | BIT(3))
+#define PCF85263_REG_OSC_OSCD_SHIFT    2
+#define PCF85263_REG_OSC_LOWJ          BIT(4)
+#define PCF85263_REG_OSC_12H           BIT(5)
+
+#define PCF85263_REG_PINIO     0x27
+#define PCF85263_REG_PINIO_INTAPM_MASK (BIT(0) | BIT(1))
+#define PCF85263_REG_PINIO_INTAPM_SHIFT        0
+#define PCF85263_INTAPM_INTA   (0x2 << PCF85263_REG_PINIO_INTAPM_SHIFT)
+#define PCF85263_INTAPM_HIGHZ  (0x3 << PCF85263_REG_PINIO_INTAPM_SHIFT)
+#define PCF85263_REG_PINIO_TSPM_MASK   (BIT(2) | BIT(3))
+#define PCF85263_REG_PINIO_TSPM_SHIFT  2
+#define PCF85263_TSPM_DISABLED         (0x0 << PCF85263_REG_PINIO_TSPM_SHIFT)
+#define PCF85263_TSPM_INTB             (0x1 << PCF85263_REG_PINIO_TSPM_SHIFT)
+#define PCF85263_REG_PINIO_CLKDISABLE  BIT(7)
+
+#define PCF85263_REG_FUNCTION  0x28
+#define PCF85263_REG_FUNCTION_COF_MASK 0x7
+#define PCF85263_REG_FUNCTION_COF_OFF  0x7     /* No clock output */
+
+#define PCF85263_REG_INTA_CTL  0x29
+#define PCF85263_REG_INTB_CTL  0x2A
+#define PCF85263_REG_INTx_CTL_A1E      BIT(4)  /* Alarm 1 */
+#define PCF85263_REG_INTx_CTL_ILP      BIT(7)  /* 0=pulse, 1=level */
+
+#define PCF85263_REG_FLAGS     0x2B
+#define PCF85263_REG_FLAGS_A1F         BIT(5)
+
+#define PCF85263_REG_RAM_BYTE  0x2c
+
+#define PCF85263_REG_STOPENABLE 0x2e
+#define PCF85263_REG_STOPENABLE_STOP   BIT(0)
+
+#define PCF85263_REG_RESET     0x2f    /* Reset command */
+#define PCF85263_REG_RESET_CMD_CPR     0xa4    /* Clear prescaler */
+
+#define PCF85263_MAX_REG 0x2f
+
+#define PCF85263_HR_PM         BIT(5)
+
+enum pcf85263_irqpin {
+       PCF85263_IRQPIN_NONE,
+       PCF85263_IRQPIN_INTA,
+       PCF85263_IRQPIN_INTB
+};
+
+static const char *const pcf85263_irqpin_names[] = {
+       [PCF85263_IRQPIN_NONE] = "None",
+       [PCF85263_IRQPIN_INTA] = "INTA",
+       [PCF85263_IRQPIN_INTB] = "INTB"
+};
+
+struct pcf85263 {
+       struct device *dev;
+       struct rtc_device *rtc;
+       struct regmap *regmap;
+       enum pcf85263_irqpin irq_pin;
+       int irq;
+       bool mode_12h;
+};
+
+/*
+ * Helpers to convert 12h to 24h and vice versa.
+ * Values in register are stored in BCD with a PM flag in bit 5
+ *
+ * 23:00 <=> 11PM <=> 0x31
+ * 00:00 <=> 12AM <=> 0x12
+ * 01:00 <=> 1AM <=> 0x01
+ * 12:00 <=> 12PM <=> 0x32
+ * 13:00 <=> 1PM <=> 0x21
+ */
+static int pcf85263_bcd12h_to_bin24h(int regval)
+{
+       int hr = bcd2bin(regval & 0x1f);
+       bool pm = regval & PCF85263_HR_PM;
+
+       if (hr == 12)
+               return pm ? 12 : 0;
+
+       return pm ? hr + 12 : hr;
+}
+
+static int pcf85263_bin24h_to_bcd12h(int hr24)
+{
+       bool pm = hr24 >= 12;
+       int hr12 = hr24 % 12;
+
+       if (!hr12)
+               hr12++;
+
+       return bin2bcd(hr12) | pm ? 0 : PCF85263_HR_PM;
+}
+
+static int pcf85263_read_time(struct device *dev, struct rtc_time *tm)
+{
+       struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
+       const int first = PCF85263_REG_RTC_SC;
+       const int last = PCF85263_REG_RTC_YR;
+       const int len = last - first + 1;
+       u8 regs[len];
+       u8 hr_reg;
+       int ret;
+
+       ret = regmap_bulk_read(pcf85263->regmap, first, regs, len);
+       if (ret)
+               return ret;
+
+       if (regs[PCF85263_REG_RTC_SC - first] & PCF85263_REG_RTC_SC_OS) {
+               dev_warn(dev, "Oscillator stop detected, date/time is not reliable.\n");
+               return -EINVAL;
+       }
+
+       tm->tm_sec = bcd2bin(regs[PCF85263_REG_RTC_SC - first] & 0x7f);
+       tm->tm_min = bcd2bin(regs[PCF85263_REG_RTC_MN - first] & 0x7f);
+
+       hr_reg = regs[PCF85263_REG_RTC_HR - first];
+       if (pcf85263->mode_12h)
+               tm->tm_hour = pcf85263_bcd12h_to_bin24h(hr_reg);
+       else
+               tm->tm_hour = bcd2bin(hr_reg & 0x3f);
+
+       tm->tm_mday = bcd2bin(regs[PCF85263_REG_RTC_DT - first]);
+       tm->tm_wday = bcd2bin(regs[PCF85263_REG_RTC_DW - first]);
+       tm->tm_mon  = bcd2bin(regs[PCF85263_REG_RTC_MO - first]) - 1;
+       tm->tm_year = bcd2bin(regs[PCF85263_REG_RTC_YR - first]);
+
+       tm->tm_year += 100;  /* Assume 21st century */
+
+       return 0;
+}
+
+static int pcf85263_set_time(struct device *dev, struct rtc_time *tm)
+{
+       struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
+
+       /*
+        * Before setting time need to stop RTC and disable prescaler
+        * Do this all in a single I2C transaction exploiting wraparound
+        * as described in data sheet.
+        * This means that the array below must be in register order
+        */
+       u8 regs[] = {
+               PCF85263_REG_STOPENABLE_STOP,   /* STOP */
+               PCF85263_REG_RESET_CMD_CPR,     /* Disable prescaler */
+               /* Wrap around to register 0 (1/100s) */
+               0,                              /* 1/100s always zero. */
+               bin2bcd(tm->tm_sec),
+               bin2bcd(tm->tm_min),
+               bin2bcd(tm->tm_hour),           /* 24-hour */
+               bin2bcd(tm->tm_mday),
+               bin2bcd(tm->tm_wday + 1),
+               bin2bcd(tm->tm_mon + 1),
+               bin2bcd(tm->tm_year % 100)
+       };
+       int ret;
+
+       ret = regmap_bulk_write(pcf85263->regmap, PCF85263_REG_STOPENABLE,
+                               regs, sizeof(regs));
+       if (ret)
+               return ret;
+
+       /* As we have set the time in 24H update the hardware for that */
+       if (pcf85263->mode_12h) {
+               pcf85263->mode_12h = false;
+               ret = regmap_update_bits(pcf85263->regmap, PCF85263_REG_OSC,
+                                        PCF85263_REG_OSC_12H, 0);
+               if (ret)
+                       return ret;
+       }
+
+       /* Start it again */
+       return regmap_write(pcf85263->regmap, PCF85263_REG_STOPENABLE, 0);
+}
+
+static int pcf85263_enable_alarm(struct pcf85263 *pcf85263, bool enable)
+{
+       int reg;
+       int ret;
+
+       ret = regmap_update_bits(pcf85263->regmap, PCF85263_REG_ALM_CTL,
+                                PCF85263_REG_ALM_CTL_ALL_A1E,
+                                enable ? PCF85263_REG_ALM_CTL_ALL_A1E : 0);
+       if (ret)
+               return ret;
+
+       switch (pcf85263->irq_pin) {
+       case PCF85263_IRQPIN_NONE:
+               return 0;
+
+       case PCF85263_IRQPIN_INTA:
+               reg = PCF85263_REG_INTA_CTL;
+               break;
+
+       case PCF85263_IRQPIN_INTB:
+               reg = PCF85263_REG_INTB_CTL;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return regmap_update_bits(pcf85263->regmap, reg,
+                                 PCF85263_REG_INTx_CTL_A1E,
+                                 enable ? PCF85263_REG_INTx_CTL_A1E : 0);
+}
+
+static int pcf85263_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+       struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
+       struct rtc_time *tm = &alarm->time;
+       const int first = PCF85263_REG_ALM1_SC;
+       const int last = PCF85263_REG_ALM1_MO;
+       const int len = last - first + 1;
+       u8 regs[len];
+       u8 hr_reg;
+       unsigned int regval;
+       int ret;
+
+       ret = regmap_bulk_read(pcf85263->regmap, first, regs, len);
+       if (ret)
+               return ret;
+
+       tm->tm_sec = bcd2bin(regs[PCF85263_REG_ALM1_SC - first] & 0x7f);
+       tm->tm_min = bcd2bin(regs[PCF85263_REG_ALM1_MN - first] & 0x7f);
+
+       hr_reg = regs[PCF85263_REG_ALM1_HR - first];
+       if (pcf85263->mode_12h)
+               tm->tm_hour = pcf85263_bcd12h_to_bin24h(hr_reg);
+       else
+               tm->tm_hour = bcd2bin(hr_reg & 0x3f);
+
+       tm->tm_mday = bcd2bin(regs[PCF85263_REG_ALM1_DT - first]);
+       tm->tm_mon  = bcd2bin(regs[PCF85263_REG_ALM1_MO - first]) - 1;
+       tm->tm_year = -1;
+       tm->tm_wday = -1;
+
+       ret = regmap_read(pcf85263->regmap, PCF85263_REG_ALM_CTL, &regval);
+       if (ret)
+               return ret;
+       alarm->enabled = !!(regval & PCF85263_REG_ALM_CTL_ALL_A1E);
+
+       ret = regmap_read(pcf85263->regmap, PCF85263_REG_FLAGS, &regval);
+       if (ret)
+               return ret;
+       alarm->pending = !!(regval & PCF85263_REG_FLAGS_A1F);
+
+       return 0;
+}
+
+static int pcf85263_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
+{
+       struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
+       struct rtc_time *tm = &alarm->time;
+       const int first = PCF85263_REG_ALM1_SC;
+       const int last = PCF85263_REG_ALM1_MO;
+       const int len = last - first + 1;
+       u8 regs[len];
+       int ret;
+
+       /* Disable alarm comparison during update */
+       ret = pcf85263_enable_alarm(pcf85263, false);
+       if (ret)
+               return ret;
+
+       /* Clear any pending alarm (write 0=>clr, 1=>no change) */
+       ret = regmap_write(pcf85263->regmap, PCF85263_REG_FLAGS,
+                          (unsigned int)(~PCF85263_REG_FLAGS_A1F));
+       if (ret)
+               return ret;
+
+       /* Set the alarm time registers */
+       regs[PCF85263_REG_ALM1_SC - first] = bin2bcd(tm->tm_sec);
+       regs[PCF85263_REG_ALM1_MN - first] = bin2bcd(tm->tm_min);
+       regs[PCF85263_REG_ALM1_HR - first] = pcf85263->mode_12h ?
+                       pcf85263_bin24h_to_bcd12h(tm->tm_hour) :
+                       bin2bcd(tm->tm_hour);
+       regs[PCF85263_REG_ALM1_DT - first] = bin2bcd(tm->tm_mday);
+       regs[PCF85263_REG_ALM1_MO - first] = bin2bcd(tm->tm_mon + 1);
+
+       ret = regmap_bulk_write(pcf85263->regmap, first, regs, sizeof(regs));
+       if (ret)
+               return ret;
+
+       if (alarm->enabled)
+               ret = pcf85263_enable_alarm(pcf85263, true);
+
+       return ret;
+}
+
+static int pcf85263_alarm_irq_enable(struct device *dev, unsigned int enable)
+{
+       struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
+
+       return pcf85263_enable_alarm(pcf85263, !!enable);
+}
+
+static irqreturn_t pcf85263_irq(int irq, void *data)
+{
+       struct pcf85263 *pcf85263 = data;
+       unsigned int regval;
+       int ret;
+
+       ret = regmap_read(pcf85263->regmap, PCF85263_REG_FLAGS, &regval);
+       if (ret)
+               return IRQ_NONE;
+
+       if (regval & PCF85263_REG_FLAGS_A1F) {
+               regmap_write(pcf85263->regmap, PCF85263_REG_FLAGS,
+                            (unsigned int)(~PCF85263_REG_FLAGS_A1F));
+
+               rtc_update_irq(pcf85263->rtc, 1, RTC_IRQF | RTC_AF);
+
+               return IRQ_HANDLED;
+       }
+
+       return IRQ_NONE;
+}
+
+static int pcf85263_check_osc_stopped(struct pcf85263 *pcf85263)
+{
+       unsigned int regval;
+       int ret;
+
+       ret = regmap_read(pcf85263->regmap, PCF85263_REG_RTC_SC, &regval);
+       if (ret)
+               return ret;
+
+       ret = regval & PCF85263_REG_RTC_SC_OS ? 1 : 0;
+       if (ret)
+               dev_warn(pcf85263->dev, "Oscillator stop detected, date/time is not reliable.\n");
+
+       return ret;
+}
+
+#ifdef CONFIG_RTC_INTF_DEV
+static int pcf85263_ioctl(struct device *dev,
+                         unsigned int cmd, unsigned long arg)
+{
+       struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
+       int ret;
+
+       switch (cmd) {
+       case RTC_VL_READ:
+               ret = pcf85263_check_osc_stopped(pcf85263);
+               if (ret < 0)
+                       return ret;
+
+               if (copy_to_user((void __user *)arg, &ret, sizeof(int)))
+                       return -EFAULT;
+               return 0;
+
+       case RTC_VL_CLR:
+               return regmap_update_bits(pcf85263->regmap,
+                                         PCF85263_REG_RTC_SC,
+                                         PCF85263_REG_RTC_SC_OS, 0);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+#else
+#define pcf85263_ioctl NULL
+#endif
+
+static int pcf85263_init_hw(struct pcf85263 *pcf85263)
+{
+       struct device_node *np = pcf85263->dev->of_node;
+       unsigned int regval;
+       u32 propval;
+       int ret;
+
+       /* Determine if oscilator has been stopped (probably low power) */
+       ret = pcf85263_check_osc_stopped(pcf85263);
+       if (ret < 0) {
+               /* Log here since this is the first hw access on probe */
+               dev_err(pcf85263->dev, "Unable to read register\n");
+
+               return ret;
+       }
+
+       /* Determine 12/24H mode */
+       ret = regmap_read(pcf85263->regmap, PCF85263_REG_OSC, &regval);
+       if (ret)
+               return ret;
+       pcf85263->mode_12h = !!(regval & PCF85263_REG_OSC_12H);
+
+       /* Set oscilator register */
+       regval &= ~PCF85263_REG_OSC_12H; /* keep current 12/24 h setting */
+
+       propval = PCF85263_QUARTZCAP_12p5pF;
+       of_property_read_u32(np, "quartz-load-capacitance", &propval);
+       regval |= ((propval << PCF85263_REG_OSC_CL_SHIFT)
+                   & PCF85263_REG_OSC_CL_MASK);
+
+       propval = PCF85263_QUARTZDRIVE_NORMAL;
+       of_property_read_u32(np, "quartz-drive-strength", &propval);
+       regval |= ((propval << PCF85263_REG_OSC_OSCD_SHIFT)
+                   & PCF85263_REG_OSC_OSCD_MASK);
+
+       if (of_property_read_bool(np, "quartz-low-jitter"))
+               regval |= PCF85263_REG_OSC_LOWJ;
+
+       ret = regmap_write(pcf85263->regmap, PCF85263_REG_OSC, regval);
+       if (ret)
+               return ret;
+
+       /* Set function register (RTC mode, 1s tick, clock output static) */
+       ret = regmap_write(pcf85263->regmap, PCF85263_REG_FUNCTION,
+                          PCF85263_REG_FUNCTION_COF_OFF);
+       if (ret)
+               return ret;
+
+       /* Set all interrupts to disabled, level mode */
+       ret = regmap_write(pcf85263->regmap, PCF85263_REG_INTA_CTL,
+                          PCF85263_REG_INTx_CTL_ILP);
+       if (ret)
+               return ret;
+       ret = regmap_write(pcf85263->regmap, PCF85263_REG_INTB_CTL,
+                          PCF85263_REG_INTx_CTL_ILP);
+       if (ret)
+               return ret;
+
+       /* Setup IO pin config register */
+       regval = PCF85263_REG_PINIO_CLKDISABLE;
+       switch (pcf85263->irq_pin) {
+       case PCF85263_IRQPIN_INTA:
+               regval |= (PCF85263_INTAPM_INTA | PCF85263_TSPM_DISABLED);
+               break;
+       case PCF85263_IRQPIN_INTB:
+               regval |= (PCF85263_INTAPM_HIGHZ | PCF85263_TSPM_INTB);
+               break;
+       case PCF85263_IRQPIN_NONE:
+               regval |= (PCF85263_INTAPM_HIGHZ | PCF85263_TSPM_DISABLED);
+               break;
+       }
+       ret = regmap_write(pcf85263->regmap, PCF85263_REG_PINIO, regval);
+
+       return ret;
+}
+
+static const struct rtc_class_ops rtc_ops = {
+       .ioctl = pcf85263_ioctl,
+       .read_time = pcf85263_read_time,
+       .set_time = pcf85263_set_time,
+       .read_alarm = pcf85263_read_alarm,
+       .set_alarm = pcf85263_set_alarm,
+       .alarm_irq_enable = pcf85263_alarm_irq_enable,
+};
+
+static const struct regmap_config pcf85263_regmap_cfg = {
+       .reg_bits = 8,
+       .val_bits = 8,
+       .max_register = PCF85263_MAX_REG,
+};
+
+/*
+ * On some boards the interrupt line may not be wired to the CPU but only to
+ * a power supply circuit.
+ * In that case no interrupt will be specified in the device tree but the
+ * wakeup-source DT property may be used to enable wakeup programming in
+ * sysfs
+ */
+static bool pcf85263_can_wakeup_machine(struct pcf85263 *pcf85263)
+{
+       return pcf85263->irq ||
+               of_property_read_bool(pcf85263->dev->of_node, "wakeup-source");
+}
+
+static int pcf85263_probe(struct i2c_client *client,
+                               const struct i2c_device_id *id)
+{
+       struct device *dev = &client->dev;
+       struct pcf85263 *pcf85263;
+       int ret;
+
+       if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C |
+                                    I2C_FUNC_SMBUS_BYTE_DATA |
+                                    I2C_FUNC_SMBUS_I2C_BLOCK))
+               return -ENODEV;
+
+       pcf85263 = devm_kzalloc(dev, sizeof(*pcf85263), GFP_KERNEL);
+       if (!pcf85263)
+               return -ENOMEM;
+
+       pcf85263->dev = dev;
+       pcf85263->irq = client->irq;
+       dev_set_drvdata(dev, pcf85263);
+
+       pcf85263->regmap = devm_regmap_init_i2c(client, &pcf85263_regmap_cfg);
+       if (IS_ERR(pcf85263->regmap)) {
+               ret = PTR_ERR(pcf85263->regmap);
+               dev_err(dev, "regmap allocation failed (%d)\n", ret);
+
+               return ret;
+       }
+
+       /* Determine which interrupt pin the board uses */
+       if (pcf85263_can_wakeup_machine(pcf85263)) {
+               if (of_property_match_string(dev->of_node,
+                                            "interrupt-names", "INTB") >= 0)
+                       pcf85263->irq_pin = PCF85263_IRQPIN_INTB;
+               else
+                       pcf85263->irq_pin = PCF85263_IRQPIN_INTA;
+       } else {
+               pcf85263->irq_pin = PCF85263_IRQPIN_NONE;
+       }
+
+       ret = pcf85263_init_hw(pcf85263);
+       if (ret)
+               return ret;
+
+       if (pcf85263->irq) {
+               ret = devm_request_threaded_irq(dev, pcf85263->irq, NULL,
+                                               pcf85263_irq,
+                                               IRQF_ONESHOT,
+                                               dev->driver->name, pcf85263);
+               if (ret) {
+                       dev_err(dev, "irq %d unavailable (%d)\n",
+                               pcf85263->irq, ret);
+                       pcf85263->irq = 0;
+               }
+       }
+
+       if (pcf85263_can_wakeup_machine(pcf85263))
+               device_init_wakeup(dev, true);
+
+       pcf85263->rtc = devm_rtc_device_register(dev, dev->driver->name,
+                                                &rtc_ops, THIS_MODULE);
+       ret = PTR_ERR_OR_ZERO(pcf85263->rtc);
+       if (ret)
+               return ret;
+
+       /* We cannot support UIE mode if we do not have an IRQ line */
+       if (!pcf85263->irq)
+               pcf85263->rtc->uie_unsupported = 1;
+
+       dev_info(pcf85263->dev,
+                "PCF85263 RTC (irqpin=%s irq=%d)\n",
+                pcf85263_irqpin_names[pcf85263->irq_pin],
+                pcf85263->irq);
+
+       return 0;
+}
+
+static int pcf85263_remove(struct i2c_client *client)
+{
+       struct pcf85263 *pcf85263 = i2c_get_clientdata(client);
+
+       if (pcf85263_can_wakeup_machine(pcf85263))
+               device_init_wakeup(pcf85263->dev, false);
+
+       return 0;
+}
+
+#ifdef CONFIG_PM_SLEEP
+static int pcf85263_suspend(struct device *dev)
+{
+       struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (device_may_wakeup(dev))
+               ret = enable_irq_wake(pcf85263->irq);
+
+       return ret;
+}
+
+static int pcf85263_resume(struct device *dev)
+{
+       struct pcf85263 *pcf85263 = dev_get_drvdata(dev);
+       int ret = 0;
+
+       if (device_may_wakeup(dev))
+               ret = disable_irq_wake(pcf85263->irq);
+
+       return ret;
+}
+
+#endif
+
+static const struct i2c_device_id pcf85263_id[] = {
+       { "pcf85263", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, pcf85263_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcf85263_of_match[] = {
+       { .compatible = "nxp,pcf85263" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, pcf85263_of_match);
+#endif
+
+static SIMPLE_DEV_PM_OPS(pcf85263_pm_ops, pcf85263_suspend,  pcf85263_resume);
+
+static struct i2c_driver pcf85263_driver = {
+       .driver         = {
+               .name   = "rtc-pcf85263",
+               .of_match_table = of_match_ptr(pcf85263_of_match),
+               .pm = &pcf85263_pm_ops,
+       },
+       .probe          = pcf85263_probe,
+       .remove         = pcf85263_remove,
+       .id_table       = pcf85263_id,
+};
+
+module_i2c_driver(pcf85263_driver);
+
+MODULE_AUTHOR("Martin Fuzzey <mfuzzey@parkeon.com>");
+MODULE_DESCRIPTION("PCF85263 RTC Driver");
+MODULE_LICENSE("GPL");
--- /dev/null
+++ b/include/dt-bindings/rtc/nxp,pcf85263.h
@@ -0,0 +1,14 @@
+#ifndef _DT_BINDINGS_RTC_NXP_PCF85263_H
+#define _DT_BINDINGS_RTC_NXP_PCF85263_H
+
+/* Quartz capacitance */
+#define PCF85263_QUARTZCAP_7pF         0
+#define PCF85263_QUARTZCAP_6pF         1
+#define PCF85263_QUARTZCAP_12p5pF      2
+
+/* Quartz drive strength */
+#define PCF85263_QUARTZDRIVE_NORMAL    0
+#define PCF85263_QUARTZDRIVE_LOW       1
+#define PCF85263_QUARTZDRIVE_HIGH      2
+
+#endif /* _DT_BINDINGS_RTC_NXP_PCF85263_H */

Generated by GNU Enscript 1.6.5.90.