mirror of
				git://git.openwrt.org/openwrt/openwrt.git
				synced 2025-10-31 05:54:26 -04:00 
			
		
		
		
	This patch is to upgrade kernel to 4.14 for layerscape. patches-4.14 for layerscape included two categories. - NXP Layerscape SDK kernel-4.14 patches All patches on tag LSDK-18.09-V4.14 were ported to OpenWrt kernel. Since there were hundreds patches, we had to make an all-in-one patch for each IP/feature. See below links for LSDK kernel. https://lsdk.github.io/components.html https://source.codeaurora.org/external/qoriq/qoriq-components/linux - Non-LSDK kernel patches Other patches which were not in LSDK were just put in patches-4.14. Kept below patches from patches-4.9. 303-dts-layerscape-add-traverse-ls1043.patch 821-add-esdhc-vsel-to-ls1043.patch 822-rgmii-fixed-link.patch Renamed and rebase them as below in patches-4.14, 303-add-DTS-for-Traverse-LS1043-Boards.patch 712-sdk-dpaa-rgmii-fixed-link.patch 824-mmc-sdhci-of-esdhc-add-voltage-switch-support-for-ls.patch Signed-off-by: Yangbo Lu <yangbo.lu@nxp.com> Signed-off-by: Biwen Li <biwen.li@nxp.com>
		
			
				
	
	
		
			777 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
			
		
		
	
	
			777 lines
		
	
	
		
			21 KiB
		
	
	
	
		
			Diff
		
	
	
	
	
	
| 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, ®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 <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 */
 |