123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682 |
- From 7e7944c484954ff7b5d53047194e59bfffd1540a Mon Sep 17 00:00:00 2001
- From: Yangbo Lu <[email protected]>
- Date: Mon, 25 Sep 2017 12:20:55 +0800
- Subject: [PATCH] rtc: support layerscape
- This is a integrated patch for layerscape rtc support.
- Signed-off-by: Zhang Ying-22455 <[email protected]>
- Signed-off-by: Yangbo Lu <[email protected]>
- ---
- drivers/rtc/rtc-pcf85263.c | 665 +++++++++++++++++++++++++++++++++++++++++++++
- 1 file changed, 665 insertions(+)
- create mode 100644 drivers/rtc/rtc-pcf85263.c
- --- /dev/null
- +++ b/drivers/rtc/rtc-pcf85263.c
- @@ -0,0 +1,665 @@
- +/*
- + * 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, ®val);
- + if (ret)
- + return ret;
- + alarm->enabled = !!(regval & PCF85263_REG_ALM_CTL_ALL_A1E);
- +
- + ret = regmap_read(pcf85263->regmap, PCF85263_REG_FLAGS, ®val);
- + 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, ®val);
- + 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, ®val);
- + 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, ®val);
- + 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 <[email protected]>");
- +MODULE_DESCRIPTION("PCF85263 RTC Driver");
- +MODULE_LICENSE("GPL");
- +
|