Thread (8 messages) 8 messages, 3 authors, 2017-03-15

[rtc-linux] Re: [PATCH 2/3] rtc: pcf85263: Basic driver

From: <hidden>
Date: 2017-03-15 17:28:34

Hello, 
I wanted to inform you that we are using this kernel driver on a beaglebone 
black and we are not having any kind of issue. Thanks for your work


Il giorno mercoledì 21 settembre 2016 23:35:42 UTC+2, alexandre.belloni ha 
scritto:
Hi, 

Sorry for the very late review. 

The driver seems good to me but I'd like a review from the DT 
maintainers before taking it. 

On 01/08/2016 at 17:50:34 +0200, Martin Fuzzey wrote : 
quoted
Add basic support for NXP PCF85263 I2C RTC chip. 

Only RTC and Alarm functions are supported. 

Signed-off-by: Martin Fuzzey <mfu...@parkeon.com <javascript:>> 
--- 
 drivers/rtc/Kconfig        |    8 + 
 drivers/rtc/Makefile       |    1 
 drivers/rtc/rtc-pcf85263.c |  655 
++++++++++++++++++++++++++++++++++++++++++++ 
quoted
 3 files changed, 664 insertions(+) 
 create mode 100644 drivers/rtc/rtc-pcf85263.c 
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig 
index 18639e0..63ec0fc 100644 
--- a/drivers/rtc/Kconfig 
+++ b/drivers/rtc/Kconfig 
@@ -396,6 +396,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 
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile 
index ea28337..04f48fd 100644 
--- a/drivers/rtc/Makefile 
+++ b/drivers/rtc/Makefile 
@@ -108,6 +108,7 @@ obj-$(CONFIG_RTC_DRV_PCF2123)        += 
rtc-pcf2123.o 
quoted
 obj-$(CONFIG_RTC_DRV_PCF2127)        += rtc-pcf2127.o 
 obj-$(CONFIG_RTC_DRV_PCF50633)        += rtc-pcf50633.o 
 obj-$(CONFIG_RTC_DRV_PCF85063)        += rtc-pcf85063.o 
+obj-$(CONFIG_RTC_DRV_PCF85263)        += rtc-pcf85263.o 
 obj-$(CONFIG_RTC_DRV_PCF8523)        += rtc-pcf8523.o 
 obj-$(CONFIG_RTC_DRV_PCF8563)        += rtc-pcf8563.o 
 obj-$(CONFIG_RTC_DRV_PCF8583)        += rtc-pcf8583.o 
diff --git a/drivers/rtc/rtc-pcf85263.c b/drivers/rtc/rtc-pcf85263.c 
new file mode 100644 
index 0000000..997742d 
--- /dev/null 
+++ b/drivers/rtc/rtc-pcf85263.c 
@@ -0,0 +1,655 @@ 
+/* 
+ * 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> 
+ 
+#include <dt-bindings/rtc/nxp,pcf85263.h> 
+ 
+#define DRV_NAME "rtc-pcf85263" 
+ 
+#define PCF85263_REG_RTC_SC        0x01        /* Seconds */ 
+#define PCF85263_REG_RTC_SC_OS                BIT(7)        /* 
Oscilator stopped flag */ 
quoted
+ 
+#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 */ 
quoted
+ 
+#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) 
quoted
+#define PCF85263_INTAPM_HIGHZ        (0x3 << 
PCF85263_REG_PINIO_INTAPM_SHIFT) 
quoted
+#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) 
quoted
+#define PCF85263_TSPM_INTB                (0x1 << 
PCF85263_REG_PINIO_TSPM_SHIFT) 
quoted
+#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 */ 
quoted
+ 
+#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 */ 
quoted
+ 
+#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 */ 
quoted
+ 
+#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) 
{ 
quoted
+                dev_warn(dev, "Oscillator stop detected, date/time is 
not reliable.\n"); 
quoted
+                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 
quoted
+         * 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 
*/ 
quoted
+                /* Wrap around to register 0 (1/100s) */ 
+                0,                                /* 1/100s always 
zero. */ 
quoted
+                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, 
quoted
+                                regs, sizeof(regs)); 
+        if (ret) 
+                return ret; 
+ 
+        /* As we have set the time in 24H update the hardware for that 
*/ 
quoted
+        if (pcf85263->mode_12h) { 
+                pcf85263->mode_12h = false; 
+                ret = regmap_update_bits(pcf85263->regmap, 
PCF85263_REG_OSC, 
quoted
+                                         PCF85263_REG_OSC_12H, 0); 
+                if (ret) 
+                        return ret; 
+        } 
+ 
+        /* Start it again */ 
+        return regmap_write(pcf85263->regmap, PCF85263_REG_STOPENABLE, 
0); 
quoted
+} 
+ 
+static int pcf85263_enable_alarm(struct pcf85263 *pcf85263, bool 
enable) 
quoted
+{ 
+        int reg; 
+        int ret; 
+ 
+        ret = regmap_update_bits(pcf85263->regmap, 
PCF85263_REG_ALM_CTL, 
quoted
+                                 PCF85263_REG_ALM_CTL_ALL_A1E, 
+                                 enable ? PCF85263_REG_ALM_CTL_ALL_A1E 
: 0); 
quoted
+        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); 
quoted
+} 
+ 
+static int pcf85263_read_alarm(struct device *dev, struct rtc_wkalrm 
*alarm) 
quoted
+{ 
+        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); 
quoted
+        tm->tm_min = bcd2bin(regs[PCF85263_REG_ALM1_MN - first] & 
0x7f); 
quoted
+ 
+        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); 
quoted
+        if (ret) 
+                return ret; 
+        alarm->enabled = !!(regval & PCF85263_REG_ALM_CTL_ALL_A1E); 
+ 
+        ret = regmap_read(pcf85263->regmap, PCF85263_REG_FLAGS, 
&regval); 
quoted
+        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) 
quoted
+{ 
+        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, 
+                           ~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)); 
quoted
+        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) 
quoted
+{ 
+        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); 
quoted
+        if (ret) 
+                return IRQ_NONE; 
+ 
+        if (regval & PCF85263_REG_FLAGS_A1F) { 
+                regmap_write(pcf85263->regmap, PCF85263_REG_FLAGS, 
+                             ~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); 
quoted
+        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"); 
quoted
+ 
+        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))) 
quoted
+                        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) 
*/ 
quoted
+        ret = pcf85263_check_osc_stopped(pcf85263); 
+        if (ret < 0) { 
+                /* Log here since this is the first hw access on probe 
*/ 
quoted
+                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 */ 
quoted
+ 
+        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) */ 
quoted
+        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); 
quoted
+                break; 
+        case PCF85263_IRQPIN_INTB: 
+                regval |= (PCF85263_INTAPM_HIGHZ | PCF85263_TSPM_INTB); 
+                break; 
+        case PCF85263_IRQPIN_NONE: 
+                regval |= (PCF85263_INTAPM_HIGHZ | 
PCF85263_TSPM_DISABLED); 
quoted
+                break; 
+        } 
+        ret = regmap_write(pcf85263->regmap, PCF85263_REG_PINIO, 
regval); 
quoted
+ 
+        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 
quoted
+ * a power supply circuit. 
+ * In that case no interrupt will be specified in the device tree but 
the 
quoted
+ * wakeup-source DT property may be used to enable wakeup programming 
in 
quoted
+ * sysfs 
+ */ 
+static bool pcf85263_can_wakeup_machine(struct pcf85263 *pcf85263) 
+{ 
+        return pcf85263->irq || 
+                of_property_read_bool(pcf85263->dev->of_node, 
"wakeup-source"); 
quoted
+} 
+ 
+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); 
quoted
+        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, 
quoted
+                                                pcf85263_irq, 
+                                                IRQF_ONESHOT, 
+                                                dev->driver->name, 
pcf85263); 
quoted
+                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, 
quoted
+                                                 &rtc_ops, 
THIS_MODULE); 
quoted
+        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); 
quoted
+ 
+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 <mfu...@parkeon.com <javascript:>>"); 
+MODULE_DESCRIPTION("PCF85263 RTC Driver"); 
+MODULE_LICENSE("GPL"); 
-- 
Alexandre Belloni, Free Electrons 
Embedded Linux and Kernel engineering 
http://free-electrons.com 
-- 
You received this message because you are subscribed to "rtc-linux".
Membership options at http://groups.google.com/group/rtc-linux .
Please read http://groups.google.com/group/rtc-linux/web/checklist
before submitting a driver.
--- 
You received this message because you are subscribed to the Google Groups "rtc-linux" group.
To unsubscribe from this group and stop receiving emails from it, send an email to rtc-linux+unsubscribe@googlegroups.com.
For more options, visit https://groups.google.com/d/optout.
Keyboard shortcuts
hback out one level
jnext message in thread
kprevious message in thread
ldrill in
Escclose help / fold thread tree
?toggle this help