RTC for 4.15
Subsystem: - Fix setting the alarm to the next expiring timer New driver: - Mediatek MT7622 RTC - NXP PCF85363 - Spreadtrum SC27xx PMIC RTC Drivers: - Use generic nvmem to expose the Non volatile ram for ds1305, ds1511, m48t86 and omap - abx80x: solve possible race condition at probe - armada38x: support trimming the RTC oscillator - at91rm9200: fix reading the alarm value at boot - ds1511: allow waking platform - m41t80: rework square wave output - pcf8523: support trimming the RTC oscillator - pcf8563: fix clock output rate - pl031: make interrupt optional - xgene: fix suspend/resume -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEXx9Viay1+e7J/aM4AyWl4gNJNJIFAloTT70ACgkQAyWl4gNJ NJL7GhAAlHf/F+u4EfDyr7Rv4p0NlQrfmWVK021P4IuSyKispdQnCGWE0ZQJ2g6w 8gNICyBiHubGVVwH9cHV3EhdCQ9DFqmMFrBn/+qbmRW2/U2pT0DuVk4u3TUHxBot e16T//k6xlT3vPjm5WYN/VZGKa2lxTio+EfSI914+LxBqs38YmKvwppTpWSIggQJ alGizKce+LZ/maORb11+GF+fgeXXek6gll0aJZQZW+JLbt0huQOBrOup8WrfWsrO Zj9fK6Xzzu0mAPhbu9D+keInLl1vavDdqWit/xYBOOXjsSRwU83e2NMHsZcTxIO5 wU6qHZqK12fprOz+cl6xvXIy1jAAvPtpqGCTg/ssRsJTm52IuoqiCDuvJ84oAiNF TSFCxar0DHzAIQOP2rcWUdEEm7wPYfNn1XWR3KZ38pUKwxqa9PBKcT1Uro0naklL f7VHe/ZZJm/a7vgW8FunSXaSIvlbB8/ggC1aWpGpTXLJs1sTmg8vTv5Z+dSrkqa1 ZJQd+YQgjIsxHNpoLqOHhho/oSun5c/YKzoc2eN/LSwKGv1jgCEi1v4iWNrv3pnS i12wef6mOrzg4iDKHx8rRXlkrb6wRgGG6ZB+yxIKjwB0ndUeSF9fCBkRBfgkyd5Q N3tHcqg2kbZaV1xl8stVkMrxbwtKHb3jTDH90e1SpzpoFXdjGXY= =tRVi -----END PGP SIGNATURE----- Merge tag 'rtc-4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "There is nothing scary this cycle, mostly driver fixes and updates. The core fix has been in for a while and has been tested on multiple kernel revisions by multiple teams. Core: - Fix setting the alarm to the next expiring timer New drivers: - Mediatek MT7622 RTC - NXP PCF85363 - Spreadtrum SC27xx PMIC RTC Drivers updates: - Use generic nvmem to expose the Non volatile ram for ds1305, ds1511, m48t86 and omap - abx80x: solve possible race condition at probe - armada38x: support trimming the RTC oscillator - at91rm9200: fix reading the alarm value at boot - ds1511: allow waking platform - m41t80: rework square wave output - pcf8523: support trimming the RTC oscillator - pcf8563: fix clock output rate - pl031: make interrupt optional - xgene: fix suspend/resume" * tag 'rtc-4.15' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (50 commits) dt-bindings: rtc: imxdi: Improve the bindings text rtc: sc27xx: Add Spreadtrum SC27xx PMIC RTC driver dt-bindings: rtc: Add Spreadtrum SC27xx RTC documentation rtc: at91rm9200: fix reading alarm value rtc: at91rm9200: stop calculating yday in at91_rtc_readalarm rtc: sysfs: Use time64_t variables to set time/alarm rtc: xgene: mark PM functions as __maybe_unused rtc: xgene: Fix suspend/resume rtc: pcf8563: don't alway enable the alarm rtc: pcf8563: fix output clock rate rtc: rx8010: Fix for incorrect return value rtc: rx8010: Specify correct address for RX8010_RESV31 rtc: rx8010: Remove duplicate define rtc: m41t80: remove unneeded checks from m41t80_sqw_set_rate rtc: m41t80: avoid i2c read in m41t80_sqw_is_prepared rtc: m41t80: avoid i2c read in m41t80_sqw_recalc_rate rtc: m41t80: fix m41t80_sqw_round_rate return value rtc: m41t80: m41t80_sqw_set_rate should return 0 on success rtc: add support for NXP PCF85363 real-time clock rtc: omap: Support scratch registers ...
This commit is contained in:
commit
1b8c5cd890
|
@ -1,20 +1,20 @@
|
|||
* i.MX25 Real Time Clock controller
|
||||
|
||||
This binding supports the following chips: i.MX25, i.MX53
|
||||
|
||||
Required properties:
|
||||
- compatible: should be: "fsl,imx25-rtc"
|
||||
- reg: physical base address of the controller and length of memory mapped
|
||||
region.
|
||||
- clocks: should contain the phandle for the rtc clock
|
||||
- interrupts: rtc alarm interrupt
|
||||
|
||||
Optional properties:
|
||||
- interrupts: dryice security violation interrupt
|
||||
- interrupts: dryice security violation interrupt (second entry)
|
||||
|
||||
Example:
|
||||
|
||||
rtc@80056000 {
|
||||
compatible = "fsl,imx53-rtc", "fsl,imx25-rtc";
|
||||
reg = <0x80056000 2000>;
|
||||
interrupts = <29 56>;
|
||||
rtc@53ffc000 {
|
||||
compatible = "fsl,imx25-rtc";
|
||||
reg = <0x53ffc000 0x4000>;
|
||||
clocks = <&clks 81>;
|
||||
interrupts = <25 56>;
|
||||
};
|
||||
|
|
17
Documentation/devicetree/bindings/rtc/pcf85363.txt
Normal file
17
Documentation/devicetree/bindings/rtc/pcf85363.txt
Normal file
|
@ -0,0 +1,17 @@
|
|||
NXP PCF85363 Real Time Clock
|
||||
============================
|
||||
|
||||
Required properties:
|
||||
- compatible: Should contain "nxp,pcf85363".
|
||||
- reg: I2C address for chip.
|
||||
|
||||
Optional properties:
|
||||
- interrupts: IRQ line for the RTC (not implemented).
|
||||
|
||||
Example:
|
||||
|
||||
pcf85363: pcf85363@51 {
|
||||
compatible = "nxp,pcf85363";
|
||||
reg = <0x51>;
|
||||
};
|
||||
|
21
Documentation/devicetree/bindings/rtc/rtc-mt7622.txt
Normal file
21
Documentation/devicetree/bindings/rtc/rtc-mt7622.txt
Normal file
|
@ -0,0 +1,21 @@
|
|||
Device-Tree bindings for MediaTek SoC based RTC
|
||||
|
||||
Required properties:
|
||||
- compatible : Should be
|
||||
"mediatek,mt7622-rtc", "mediatek,soc-rtc" : for MT7622 SoC
|
||||
- reg : Specifies base physical address and size of the registers;
|
||||
- interrupts : Should contain the interrupt for RTC alarm;
|
||||
- clocks : Specifies list of clock specifiers, corresponding to
|
||||
entries in clock-names property;
|
||||
- clock-names : Should contain "rtc" entries
|
||||
|
||||
Example:
|
||||
|
||||
rtc: rtc@10212800 {
|
||||
compatible = "mediatek,mt7622-rtc",
|
||||
"mediatek,soc-rtc";
|
||||
reg = <0 0x10212800 0 0x200>;
|
||||
interrupts = <GIC_SPI 129 IRQ_TYPE_LEVEL_LOW>;
|
||||
clocks = <&topckgen CLK_TOP_RTC>;
|
||||
clock-names = "rtc";
|
||||
};
|
27
Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt
Normal file
27
Documentation/devicetree/bindings/rtc/sprd,sc27xx-rtc.txt
Normal file
|
@ -0,0 +1,27 @@
|
|||
Spreadtrum SC27xx Real Time Clock
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "sprd,sc2731-rtc".
|
||||
- reg: address offset of rtc register.
|
||||
- interrupt-parent: phandle for the interrupt controller.
|
||||
- interrupts: rtc alarm interrupt.
|
||||
|
||||
Example:
|
||||
|
||||
sc2731_pmic: pmic@0 {
|
||||
compatible = "sprd,sc2731";
|
||||
reg = <0>;
|
||||
spi-max-frequency = <26000000>;
|
||||
interrupts = <GIC_SPI 31 IRQ_TYPE_LEVEL_HIGH>;
|
||||
interrupt-controller;
|
||||
#interrupt-cells = <2>;
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@280 {
|
||||
compatible = "sprd,sc2731-rtc";
|
||||
reg = <0x280>;
|
||||
interrupt-parent = <&sc2731_pmic>;
|
||||
interrupts = <2 IRQ_TYPE_LEVEL_HIGH>;
|
||||
};
|
||||
};
|
|
@ -72,7 +72,6 @@ maxim,ds1050 5 Bit Programmable, Pulse-Width Modulator
|
|||
maxim,max1237 Low-Power, 4-/12-Channel, 2-Wire Serial, 12-Bit ADCs
|
||||
maxim,max6621 PECI-to-I2C translator for PECI-to-SMBus/I2C protocol conversion
|
||||
maxim,max6625 9-Bit/12-Bit Temperature Sensors with I²C-Compatible Serial Interface
|
||||
mc,rv3029c2 Real Time Clock Module with I2C-Bus
|
||||
mcube,mc3230 mCube 3-axis 8-bit digital accelerometer
|
||||
memsic,mxc6225 MEMSIC 2-axis 8-bit digital accelerometer
|
||||
microchip,mcp4531-502 Microchip 7-bit Single I2C Digital Potentiometer (5k)
|
||||
|
@ -141,6 +140,7 @@ microchip,mcp4662-503 Microchip 8-bit Dual I2C Digital Potentiometer with NV Mem
|
|||
microchip,mcp4662-104 Microchip 8-bit Dual I2C Digital Potentiometer with NV Memory (100k)
|
||||
microchip,tc654 PWM Fan Speed Controller With Fan Fault Detection
|
||||
microchip,tc655 PWM Fan Speed Controller With Fan Fault Detection
|
||||
microcrystal,rv3029 Real Time Clock Module with I2C-Bus
|
||||
miramems,da226 MiraMEMS DA226 2-axis 14-bit digital accelerometer
|
||||
miramems,da280 MiraMEMS DA280 3-axis 14-bit digital accelerometer
|
||||
miramems,da311 MiraMEMS DA311 3-axis 12-bit digital accelerometer
|
||||
|
|
|
@ -1590,10 +1590,13 @@ F: drivers/rtc/rtc-armada38x.c
|
|||
|
||||
ARM/Mediatek RTC DRIVER
|
||||
M: Eddie Huang <eddie.huang@mediatek.com>
|
||||
M: Sean Wang <sean.wang@mediatek.com>
|
||||
L: linux-arm-kernel@lists.infradead.org (moderated for non-subscribers)
|
||||
L: linux-mediatek@lists.infradead.org (moderated for non-subscribers)
|
||||
S: Maintained
|
||||
F: Documentation/devicetree/bindings/rtc/rtc-mt7622.txt
|
||||
F: drivers/rtc/rtc-mt6397.c
|
||||
F: drivers/rtc/rtc-mt7622.c
|
||||
|
||||
ARM/Mediatek SoC support
|
||||
M: Matthias Brugger <matthias.bgg@gmail.com>
|
||||
|
|
|
@ -433,6 +433,19 @@ 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_PCF85363
|
||||
tristate "NXP PCF85363"
|
||||
depends on I2C
|
||||
select REGMAP_I2C
|
||||
help
|
||||
If you say yes here you get support for the PCF85363 RTC chip.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-pcf85363.
|
||||
|
||||
The nvmem interface will be named pcf85363-#, where # is the
|
||||
zero-based instance number.
|
||||
|
||||
config RTC_DRV_PCF8563
|
||||
tristate "Philips PCF8563/Epson RTC8564"
|
||||
help
|
||||
|
@ -1174,6 +1187,17 @@ config RTC_DRV_WM8350
|
|||
This driver can also be built as a module. If so, the module
|
||||
will be called "rtc-wm8350".
|
||||
|
||||
config RTC_DRV_SC27XX
|
||||
tristate "Spreadtrum SC27xx RTC"
|
||||
depends on MFD_SC27XX_PMIC || COMPILE_TEST
|
||||
help
|
||||
If you say Y here you will get support for the RTC subsystem
|
||||
of the Spreadtrum SC27xx series PMICs. The SC27xx series PMICs
|
||||
includes the SC2720, SC2721, SC2723, SC2730 and SC2731 chips.
|
||||
|
||||
This driver can also be built as a module. If so, the module
|
||||
will be called rtc-sc27xx.
|
||||
|
||||
config RTC_DRV_SPEAR
|
||||
tristate "SPEAR ST RTC"
|
||||
depends on PLAT_SPEAR || COMPILE_TEST
|
||||
|
@ -1706,14 +1730,24 @@ config RTC_DRV_MOXART
|
|||
will be called rtc-moxart
|
||||
|
||||
config RTC_DRV_MT6397
|
||||
tristate "Mediatek Real Time Clock driver"
|
||||
tristate "MediaTek PMIC based RTC"
|
||||
depends on MFD_MT6397 || (COMPILE_TEST && IRQ_DOMAIN)
|
||||
help
|
||||
This selects the Mediatek(R) RTC driver. RTC is part of Mediatek
|
||||
This selects the MediaTek(R) RTC driver. RTC is part of MediaTek
|
||||
MT6397 PMIC. You should enable MT6397 PMIC MFD before select
|
||||
Mediatek(R) RTC driver.
|
||||
MediaTek(R) RTC driver.
|
||||
|
||||
If you want to use Mediatek(R) RTC interface, select Y or M here.
|
||||
If you want to use MediaTek(R) RTC interface, select Y or M here.
|
||||
|
||||
config RTC_DRV_MT7622
|
||||
tristate "MediaTek SoC based RTC"
|
||||
depends on ARCH_MEDIATEK || COMPILE_TEST
|
||||
help
|
||||
This enables support for the real time clock built in the MediaTek
|
||||
SoCs.
|
||||
|
||||
This drive can also be built as a module. If so, the module
|
||||
will be called rtc-mt7622.
|
||||
|
||||
config RTC_DRV_XGENE
|
||||
tristate "APM X-Gene RTC"
|
||||
|
|
|
@ -103,6 +103,7 @@ obj-$(CONFIG_RTC_DRV_MPC5121) += rtc-mpc5121.o
|
|||
obj-$(CONFIG_RTC_DRV_VRTC) += rtc-mrst.o
|
||||
obj-$(CONFIG_RTC_DRV_MSM6242) += rtc-msm6242.o
|
||||
obj-$(CONFIG_RTC_DRV_MT6397) += rtc-mt6397.o
|
||||
obj-$(CONFIG_RTC_DRV_MT7622) += rtc-mt7622.o
|
||||
obj-$(CONFIG_RTC_DRV_MV) += rtc-mv.o
|
||||
obj-$(CONFIG_RTC_DRV_MXC) += rtc-mxc.o
|
||||
obj-$(CONFIG_RTC_DRV_NUC900) += rtc-nuc900.o
|
||||
|
@ -114,6 +115,7 @@ obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
|
|||
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_PCF85363) += rtc-pcf85363.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
|
||||
|
@ -144,6 +146,7 @@ obj-$(CONFIG_RTC_DRV_S35390A) += rtc-s35390a.o
|
|||
obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o
|
||||
obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o
|
||||
obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o
|
||||
obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o
|
||||
obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o
|
||||
obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o
|
||||
obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o
|
||||
|
|
|
@ -779,7 +779,7 @@ static int rtc_timer_enqueue(struct rtc_device *rtc, struct rtc_timer *timer)
|
|||
}
|
||||
|
||||
timerqueue_add(&rtc->timerqueue, &timer->node);
|
||||
if (!next) {
|
||||
if (!next || ktime_before(timer->node.expires, next->expires)) {
|
||||
struct rtc_wkalrm alarm;
|
||||
int err;
|
||||
alarm.time = rtc_ktime_to_tm(timer->node.expires);
|
||||
|
@ -1004,6 +1004,10 @@ int rtc_read_offset(struct rtc_device *rtc, long *offset)
|
|||
* to compensate for differences in the actual clock rate due to temperature,
|
||||
* the crystal, capacitor, etc.
|
||||
*
|
||||
* The adjustment applied is as follows:
|
||||
* t = t0 * (1 + offset * 1e-9)
|
||||
* where t0 is the measured length of 1 RTC second with offset = 0
|
||||
*
|
||||
* Kernel interface to adjust an rtc clock offset.
|
||||
* Return 0 on success, or a negative number on error.
|
||||
* If the rtc offset is not setable (or not implemented), return -EINVAL
|
||||
|
|
|
@ -614,12 +614,12 @@ static int abx80x_probe(struct i2c_client *client,
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
rtc = devm_rtc_device_register(&client->dev, "abx8xx",
|
||||
&abx80x_rtc_ops, THIS_MODULE);
|
||||
|
||||
rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
|
||||
rtc->ops = &abx80x_rtc_ops;
|
||||
|
||||
i2c_set_clientdata(client, rtc);
|
||||
|
||||
if (client->irq > 0) {
|
||||
|
@ -646,10 +646,14 @@ static int abx80x_probe(struct i2c_client *client,
|
|||
err = devm_add_action_or_reset(&client->dev,
|
||||
rtc_calib_remove_sysfs_group,
|
||||
&client->dev);
|
||||
if (err)
|
||||
if (err) {
|
||||
dev_err(&client->dev,
|
||||
"Failed to add sysfs cleanup action: %d\n",
|
||||
err);
|
||||
return err;
|
||||
}
|
||||
|
||||
err = rtc_register_device(rtc);
|
||||
|
||||
return err;
|
||||
}
|
||||
|
|
|
@ -28,6 +28,8 @@
|
|||
#define RTC_IRQ_AL_EN BIT(0)
|
||||
#define RTC_IRQ_FREQ_EN BIT(1)
|
||||
#define RTC_IRQ_FREQ_1HZ BIT(2)
|
||||
#define RTC_CCR 0x18
|
||||
#define RTC_CCR_MODE BIT(15)
|
||||
|
||||
#define RTC_TIME 0xC
|
||||
#define RTC_ALARM1 0x10
|
||||
|
@ -343,18 +345,117 @@ static irqreturn_t armada38x_rtc_alarm_irq(int irq, void *data)
|
|||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
/*
|
||||
* The information given in the Armada 388 functional spec is complex.
|
||||
* They give two different formulas for calculating the offset value,
|
||||
* but when considering "Offset" as an 8-bit signed integer, they both
|
||||
* reduce down to (we shall rename "Offset" as "val" here):
|
||||
*
|
||||
* val = (f_ideal / f_measured - 1) / resolution where f_ideal = 32768
|
||||
*
|
||||
* Converting to time, f = 1/t:
|
||||
* val = (t_measured / t_ideal - 1) / resolution where t_ideal = 1/32768
|
||||
*
|
||||
* => t_measured / t_ideal = val * resolution + 1
|
||||
*
|
||||
* "offset" in the RTC interface is defined as:
|
||||
* t = t0 * (1 + offset * 1e-9)
|
||||
* where t is the desired period, t0 is the measured period with a zero
|
||||
* offset, which is t_measured above. With t0 = t_measured and t = t_ideal,
|
||||
* offset = (t_ideal / t_measured - 1) / 1e-9
|
||||
*
|
||||
* => t_ideal / t_measured = offset * 1e-9 + 1
|
||||
*
|
||||
* so:
|
||||
*
|
||||
* offset * 1e-9 + 1 = 1 / (val * resolution + 1)
|
||||
*
|
||||
* We want "resolution" to be an integer, so resolution = R * 1e-9, giving
|
||||
* offset = 1e18 / (val * R + 1e9) - 1e9
|
||||
* val = (1e18 / (offset + 1e9) - 1e9) / R
|
||||
* with a common transformation:
|
||||
* f(x) = 1e18 / (x + 1e9) - 1e9
|
||||
* offset = f(val * R)
|
||||
* val = f(offset) / R
|
||||
*
|
||||
* Armada 38x supports two modes, fine mode (954ppb) and coarse mode (3815ppb).
|
||||
*/
|
||||
static long armada38x_ppb_convert(long ppb)
|
||||
{
|
||||
long div = ppb + 1000000000L;
|
||||
|
||||
return div_s64(1000000000000000000LL + div / 2, div) - 1000000000L;
|
||||
}
|
||||
|
||||
static int armada38x_rtc_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
||||
unsigned long ccr, flags;
|
||||
long ppb_cor;
|
||||
|
||||
spin_lock_irqsave(&rtc->lock, flags);
|
||||
ccr = rtc->data->read_rtc_reg(rtc, RTC_CCR);
|
||||
spin_unlock_irqrestore(&rtc->lock, flags);
|
||||
|
||||
ppb_cor = (ccr & RTC_CCR_MODE ? 3815 : 954) * (s8)ccr;
|
||||
/* ppb_cor + 1000000000L can never be zero */
|
||||
*offset = armada38x_ppb_convert(ppb_cor);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int armada38x_rtc_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct armada38x_rtc *rtc = dev_get_drvdata(dev);
|
||||
unsigned long ccr = 0;
|
||||
long ppb_cor, off;
|
||||
|
||||
/*
|
||||
* The maximum ppb_cor is -128 * 3815 .. 127 * 3815, but we
|
||||
* need to clamp the input. This equates to -484270 .. 488558.
|
||||
* Not only is this to stop out of range "off" but also to
|
||||
* avoid the division by zero in armada38x_ppb_convert().
|
||||
*/
|
||||
offset = clamp(offset, -484270L, 488558L);
|
||||
|
||||
ppb_cor = armada38x_ppb_convert(offset);
|
||||
|
||||
/*
|
||||
* Use low update mode where possible, which gives a better
|
||||
* resolution of correction.
|
||||
*/
|
||||
off = DIV_ROUND_CLOSEST(ppb_cor, 954);
|
||||
if (off > 127 || off < -128) {
|
||||
ccr = RTC_CCR_MODE;
|
||||
off = DIV_ROUND_CLOSEST(ppb_cor, 3815);
|
||||
}
|
||||
|
||||
/*
|
||||
* Armada 388 requires a bit pattern in bits 14..8 depending on
|
||||
* the sign bit: { 0, ~S, S, S, S, S, S }
|
||||
*/
|
||||
ccr |= (off & 0x3fff) ^ 0x2000;
|
||||
rtc_delayed_write(ccr, rtc, RTC_CCR);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops armada38x_rtc_ops = {
|
||||
.read_time = armada38x_rtc_read_time,
|
||||
.set_time = armada38x_rtc_set_time,
|
||||
.read_alarm = armada38x_rtc_read_alarm,
|
||||
.set_alarm = armada38x_rtc_set_alarm,
|
||||
.alarm_irq_enable = armada38x_rtc_alarm_irq_enable,
|
||||
.read_offset = armada38x_rtc_read_offset,
|
||||
.set_offset = armada38x_rtc_set_offset,
|
||||
};
|
||||
|
||||
static const struct rtc_class_ops armada38x_rtc_ops_noirq = {
|
||||
.read_time = armada38x_rtc_read_time,
|
||||
.set_time = armada38x_rtc_set_time,
|
||||
.read_alarm = armada38x_rtc_read_alarm,
|
||||
.read_offset = armada38x_rtc_read_offset,
|
||||
.set_offset = armada38x_rtc_set_offset,
|
||||
};
|
||||
|
||||
static const struct armada38x_rtc_data armada38x_data = {
|
||||
|
|
|
@ -42,8 +42,6 @@
|
|||
#define at91_rtc_write(field, val) \
|
||||
writel_relaxed((val), at91_rtc_regs + field)
|
||||
|
||||
#define AT91_RTC_EPOCH 1900UL /* just like arch/arm/common/rtctime.c */
|
||||
|
||||
struct at91_rtc_config {
|
||||
bool use_shadow_imr;
|
||||
};
|
||||
|
@ -51,7 +49,6 @@ struct at91_rtc_config {
|
|||
static const struct at91_rtc_config *at91_rtc_config;
|
||||
static DECLARE_COMPLETION(at91_rtc_updated);
|
||||
static DECLARE_COMPLETION(at91_rtc_upd_rdy);
|
||||
static unsigned int at91_alarm_year = AT91_RTC_EPOCH;
|
||||
static void __iomem *at91_rtc_regs;
|
||||
static int irq;
|
||||
static DEFINE_SPINLOCK(at91_rtc_lock);
|
||||
|
@ -131,8 +128,7 @@ static void at91_rtc_decodetime(unsigned int timereg, unsigned int calreg,
|
|||
|
||||
/*
|
||||
* The Calendar Alarm register does not have a field for
|
||||
* the year - so these will return an invalid value. When an
|
||||
* alarm is set, at91_alarm_year will store the current year.
|
||||
* the year - so these will return an invalid value.
|
||||
*/
|
||||
tm->tm_year = bcd2bin(date & AT91_RTC_CENT) * 100; /* century */
|
||||
tm->tm_year += bcd2bin((date & AT91_RTC_YEAR) >> 8); /* year */
|
||||
|
@ -208,15 +204,14 @@ static int at91_rtc_readalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
struct rtc_time *tm = &alrm->time;
|
||||
|
||||
at91_rtc_decodetime(AT91_RTC_TIMALR, AT91_RTC_CALALR, tm);
|
||||
tm->tm_yday = rtc_year_days(tm->tm_mday, tm->tm_mon, tm->tm_year);
|
||||
tm->tm_year = at91_alarm_year - 1900;
|
||||
tm->tm_year = -1;
|
||||
|
||||
alrm->enabled = (at91_rtc_read_imr() & AT91_RTC_ALARM)
|
||||
? 1 : 0;
|
||||
|
||||
dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
|
||||
1900 + tm->tm_year, tm->tm_mon, tm->tm_mday,
|
||||
tm->tm_hour, tm->tm_min, tm->tm_sec);
|
||||
dev_dbg(dev, "%s(): %02d-%02d %02d:%02d:%02d %sabled\n", __func__,
|
||||
tm->tm_mon, tm->tm_mday, tm->tm_hour, tm->tm_min, tm->tm_sec,
|
||||
alrm->enabled ? "en" : "dis");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -230,8 +225,6 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
|
||||
at91_rtc_decodetime(AT91_RTC_TIMR, AT91_RTC_CALR, &tm);
|
||||
|
||||
at91_alarm_year = tm.tm_year;
|
||||
|
||||
tm.tm_mon = alrm->time.tm_mon;
|
||||
tm.tm_mday = alrm->time.tm_mday;
|
||||
tm.tm_hour = alrm->time.tm_hour;
|
||||
|
@ -255,7 +248,7 @@ static int at91_rtc_setalarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
}
|
||||
|
||||
dev_dbg(dev, "%s(): %4d-%02d-%02d %02d:%02d:%02d\n", __func__,
|
||||
at91_alarm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
|
||||
tm.tm_year, tm.tm_mon, tm.tm_mday, tm.tm_hour,
|
||||
tm.tm_min, tm.tm_sec);
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -514,56 +514,43 @@ static void msg_init(struct spi_message *m, struct spi_transfer *x,
|
|||
spi_message_add_tail(x, m);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ds1305_nvram_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
static int ds1305_nvram_read(void *priv, unsigned int off, void *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct spi_device *spi;
|
||||
struct ds1305 *ds1305 = priv;
|
||||
struct spi_device *spi = ds1305->spi;
|
||||
u8 addr;
|
||||
struct spi_message m;
|
||||
struct spi_transfer x[2];
|
||||
int status;
|
||||
|
||||
spi = to_spi_device(kobj_to_dev(kobj));
|
||||
|
||||
addr = DS1305_NVRAM + off;
|
||||
msg_init(&m, x, &addr, count, NULL, buf);
|
||||
|
||||
status = spi_sync(spi, &m);
|
||||
if (status < 0)
|
||||
dev_err(&spi->dev, "nvram %s error %d\n", "read", status);
|
||||
return (status < 0) ? status : count;
|
||||
return spi_sync(spi, &m);
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ds1305_nvram_write(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
static int ds1305_nvram_write(void *priv, unsigned int off, void *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct spi_device *spi;
|
||||
struct ds1305 *ds1305 = priv;
|
||||
struct spi_device *spi = ds1305->spi;
|
||||
u8 addr;
|
||||
struct spi_message m;
|
||||
struct spi_transfer x[2];
|
||||
int status;
|
||||
|
||||
spi = to_spi_device(kobj_to_dev(kobj));
|
||||
|
||||
addr = (DS1305_WRITE | DS1305_NVRAM) + off;
|
||||
msg_init(&m, x, &addr, count, buf, NULL);
|
||||
|
||||
status = spi_sync(spi, &m);
|
||||
if (status < 0)
|
||||
dev_err(&spi->dev, "nvram %s error %d\n", "write", status);
|
||||
return (status < 0) ? status : count;
|
||||
return spi_sync(spi, &m);
|
||||
}
|
||||
|
||||
static struct bin_attribute nvram = {
|
||||
.attr.name = "nvram",
|
||||
.attr.mode = S_IRUGO | S_IWUSR,
|
||||
.read = ds1305_nvram_read,
|
||||
.write = ds1305_nvram_write,
|
||||
.size = DS1305_NVRAM_LEN,
|
||||
static struct nvmem_config ds1305_nvmem_cfg = {
|
||||
.name = "ds1305_nvram",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = DS1305_NVRAM_LEN,
|
||||
.reg_read = ds1305_nvram_read,
|
||||
.reg_write = ds1305_nvram_write,
|
||||
};
|
||||
|
||||
/*----------------------------------------------------------------------*/
|
||||
|
@ -708,10 +695,19 @@ static int ds1305_probe(struct spi_device *spi)
|
|||
dev_dbg(&spi->dev, "AM/PM\n");
|
||||
|
||||
/* register RTC ... from here on, ds1305->ctrl needs locking */
|
||||
ds1305->rtc = devm_rtc_device_register(&spi->dev, "ds1305",
|
||||
&ds1305_ops, THIS_MODULE);
|
||||
ds1305->rtc = devm_rtc_allocate_device(&spi->dev);
|
||||
if (IS_ERR(ds1305->rtc)) {
|
||||
status = PTR_ERR(ds1305->rtc);
|
||||
return PTR_ERR(ds1305->rtc);
|
||||
}
|
||||
|
||||
ds1305->rtc->ops = &ds1305_ops;
|
||||
|
||||
ds1305_nvmem_cfg.priv = ds1305;
|
||||
ds1305->rtc->nvmem_config = &ds1305_nvmem_cfg;
|
||||
ds1305->rtc->nvram_old_abi = true;
|
||||
|
||||
status = rtc_register_device(ds1305->rtc);
|
||||
if (status) {
|
||||
dev_dbg(&spi->dev, "register rtc --> %d\n", status);
|
||||
return status;
|
||||
}
|
||||
|
@ -734,12 +730,6 @@ static int ds1305_probe(struct spi_device *spi)
|
|||
}
|
||||
}
|
||||
|
||||
/* export NVRAM */
|
||||
status = sysfs_create_bin_file(&spi->dev.kobj, &nvram);
|
||||
if (status < 0) {
|
||||
dev_err(&spi->dev, "register nvram --> %d\n", status);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -747,8 +737,6 @@ static int ds1305_remove(struct spi_device *spi)
|
|||
{
|
||||
struct ds1305 *ds1305 = spi_get_drvdata(spi);
|
||||
|
||||
sysfs_remove_bin_file(&spi->dev.kobj, &nvram);
|
||||
|
||||
/* carefully shut down irq and workqueue, if present */
|
||||
if (spi->irq) {
|
||||
set_bit(FLAG_EXITING, &ds1305->flags);
|
||||
|
|
|
@ -325,6 +325,10 @@ static const struct of_device_id ds1307_of_match[] = {
|
|||
.compatible = "isil,isl12057",
|
||||
.data = (void *)ds_1337
|
||||
},
|
||||
{
|
||||
.compatible = "epson,rx8130",
|
||||
.data = (void *)rx_8130
|
||||
},
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ds1307_of_match);
|
||||
|
@ -348,6 +352,7 @@ static const struct acpi_device_id ds1307_acpi_ids[] = {
|
|||
{ .id = "PT7C4338", .driver_data = ds_1307 },
|
||||
{ .id = "RX8025", .driver_data = rx_8025 },
|
||||
{ .id = "ISL12057", .driver_data = ds_1337 },
|
||||
{ .id = "RX8130", .driver_data = rx_8130 },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, ds1307_acpi_ids);
|
||||
|
@ -787,8 +792,6 @@ static int rx8130_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
|||
* Alarm support for mcp794xx devices.
|
||||
*/
|
||||
|
||||
#define MCP794XX_REG_WEEKDAY 0x3
|
||||
#define MCP794XX_REG_WEEKDAY_WDAY_MASK 0x7
|
||||
#define MCP794XX_REG_CONTROL 0x07
|
||||
# define MCP794XX_BIT_ALM0_EN 0x10
|
||||
# define MCP794XX_BIT_ALM1_EN 0x20
|
||||
|
@ -877,15 +880,38 @@ static int mcp794xx_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* We may have a random RTC weekday, therefore calculate alarm weekday based
|
||||
* on current weekday we read from the RTC timekeeping regs
|
||||
*/
|
||||
static int mcp794xx_alm_weekday(struct device *dev, struct rtc_time *tm_alarm)
|
||||
{
|
||||
struct rtc_time tm_now;
|
||||
int days_now, days_alarm, ret;
|
||||
|
||||
ret = ds1307_get_time(dev, &tm_now);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
days_now = div_s64(rtc_tm_to_time64(&tm_now), 24 * 60 * 60);
|
||||
days_alarm = div_s64(rtc_tm_to_time64(tm_alarm), 24 * 60 * 60);
|
||||
|
||||
return (tm_now.tm_wday + days_alarm - days_now) % 7 + 1;
|
||||
}
|
||||
|
||||
static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
{
|
||||
struct ds1307 *ds1307 = dev_get_drvdata(dev);
|
||||
unsigned char regs[10];
|
||||
int ret;
|
||||
int wday, ret;
|
||||
|
||||
if (!test_bit(HAS_ALARM, &ds1307->flags))
|
||||
return -EINVAL;
|
||||
|
||||
wday = mcp794xx_alm_weekday(dev, &t->time);
|
||||
if (wday < 0)
|
||||
return wday;
|
||||
|
||||
dev_dbg(dev, "%s, sec=%d min=%d hour=%d wday=%d mday=%d mon=%d "
|
||||
"enabled=%d pending=%d\n", __func__,
|
||||
t->time.tm_sec, t->time.tm_min, t->time.tm_hour,
|
||||
|
@ -902,7 +928,7 @@ static int mcp794xx_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|||
regs[3] = bin2bcd(t->time.tm_sec);
|
||||
regs[4] = bin2bcd(t->time.tm_min);
|
||||
regs[5] = bin2bcd(t->time.tm_hour);
|
||||
regs[6] = bin2bcd(t->time.tm_wday + 1);
|
||||
regs[6] = wday;
|
||||
regs[7] = bin2bcd(t->time.tm_mday);
|
||||
regs[8] = bin2bcd(t->time.tm_mon + 1);
|
||||
|
||||
|
@ -1354,14 +1380,12 @@ static int ds1307_probe(struct i2c_client *client,
|
|||
{
|
||||
struct ds1307 *ds1307;
|
||||
int err = -ENODEV;
|
||||
int tmp, wday;
|
||||
int tmp;
|
||||
const struct chip_desc *chip;
|
||||
bool want_irq;
|
||||
bool ds1307_can_wakeup_device = false;
|
||||
unsigned char regs[8];
|
||||
struct ds1307_platform_data *pdata = dev_get_platdata(&client->dev);
|
||||
struct rtc_time tm;
|
||||
unsigned long timestamp;
|
||||
u8 trickle_charger_setup = 0;
|
||||
|
||||
ds1307 = devm_kzalloc(&client->dev, sizeof(struct ds1307), GFP_KERNEL);
|
||||
|
@ -1641,25 +1665,6 @@ static int ds1307_probe(struct i2c_client *client,
|
|||
bin2bcd(tmp));
|
||||
}
|
||||
|
||||
/*
|
||||
* Some IPs have weekday reset value = 0x1 which might not correct
|
||||
* hence compute the wday using the current date/month/year values
|
||||
*/
|
||||
ds1307_get_time(ds1307->dev, &tm);
|
||||
wday = tm.tm_wday;
|
||||
timestamp = rtc_tm_to_time64(&tm);
|
||||
rtc_time64_to_tm(timestamp, &tm);
|
||||
|
||||
/*
|
||||
* Check if reset wday is different from the computed wday
|
||||
* If different then set the wday which we computed using
|
||||
* timestamp
|
||||
*/
|
||||
if (wday != tm.tm_wday)
|
||||
regmap_update_bits(ds1307->regmap, MCP794XX_REG_WEEKDAY,
|
||||
MCP794XX_REG_WEEKDAY_WDAY_MASK,
|
||||
tm.tm_wday + 1);
|
||||
|
||||
if (want_irq || ds1307_can_wakeup_device) {
|
||||
device_set_wakeup_capable(ds1307->dev, true);
|
||||
set_bit(HAS_ALARM, &ds1307->flags);
|
||||
|
|
|
@ -216,9 +216,16 @@ static int ds1390_probe(struct spi_device *spi)
|
|||
return res;
|
||||
}
|
||||
|
||||
static const struct of_device_id ds1390_of_match[] = {
|
||||
{ .compatible = "dallas,ds1390" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, ds1390_of_match);
|
||||
|
||||
static struct spi_driver ds1390_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-ds1390",
|
||||
.of_match_table = of_match_ptr(ds1390_of_match),
|
||||
},
|
||||
.probe = ds1390_probe,
|
||||
};
|
||||
|
|
|
@ -398,42 +398,37 @@ static const struct rtc_class_ops ds1511_rtc_ops = {
|
|||
.alarm_irq_enable = ds1511_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static ssize_t
|
||||
ds1511_nvram_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *ba,
|
||||
char *buf, loff_t pos, size_t size)
|
||||
static int ds1511_nvram_read(void *priv, unsigned int pos, void *buf,
|
||||
size_t size)
|
||||
{
|
||||
ssize_t count;
|
||||
int i;
|
||||
|
||||
rtc_write(pos, DS1511_RAMADDR_LSB);
|
||||
for (count = 0; count < size; count++)
|
||||
*buf++ = rtc_read(DS1511_RAMDATA);
|
||||
for (i = 0; i < size; i++)
|
||||
*(char *)buf++ = rtc_read(DS1511_RAMDATA);
|
||||
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t
|
||||
ds1511_nvram_write(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *bin_attr,
|
||||
char *buf, loff_t pos, size_t size)
|
||||
static int ds1511_nvram_write(void *priv, unsigned int pos, void *buf,
|
||||
size_t size)
|
||||
{
|
||||
ssize_t count;
|
||||
int i;
|
||||
|
||||
rtc_write(pos, DS1511_RAMADDR_LSB);
|
||||
for (count = 0; count < size; count++)
|
||||
rtc_write(*buf++, DS1511_RAMDATA);
|
||||
for (i = 0; i < size; i++)
|
||||
rtc_write(*(char *)buf++, DS1511_RAMDATA);
|
||||
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct bin_attribute ds1511_nvram_attr = {
|
||||
.attr = {
|
||||
.name = "nvram",
|
||||
.mode = S_IRUGO | S_IWUSR,
|
||||
},
|
||||
static struct nvmem_config ds1511_nvmem_cfg = {
|
||||
.name = "ds1511_nvram",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = DS1511_RAM_MAX,
|
||||
.read = ds1511_nvram_read,
|
||||
.write = ds1511_nvram_write,
|
||||
.reg_read = ds1511_nvram_read,
|
||||
.reg_write = ds1511_nvram_write,
|
||||
};
|
||||
|
||||
static int ds1511_rtc_probe(struct platform_device *pdev)
|
||||
|
@ -477,11 +472,20 @@ static int ds1511_rtc_probe(struct platform_device *pdev)
|
|||
spin_lock_init(&pdata->lock);
|
||||
platform_set_drvdata(pdev, pdata);
|
||||
|
||||
pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||
&ds1511_rtc_ops, THIS_MODULE);
|
||||
pdata->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(pdata->rtc))
|
||||
return PTR_ERR(pdata->rtc);
|
||||
|
||||
pdata->rtc->ops = &ds1511_rtc_ops;
|
||||
|
||||
ds1511_nvmem_cfg.priv = &pdev->dev;
|
||||
pdata->rtc->nvmem_config = &ds1511_nvmem_cfg;
|
||||
pdata->rtc->nvram_old_abi = true;
|
||||
|
||||
ret = rtc_register_device(pdata->rtc);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* if the platform has an interrupt in mind for this device,
|
||||
* then by all means, set it
|
||||
|
@ -496,26 +500,6 @@ static int ds1511_rtc_probe(struct platform_device *pdev)
|
|||
}
|
||||
}
|
||||
|
||||
ret = sysfs_create_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
|
||||
if (ret)
|
||||
dev_err(&pdev->dev, "Unable to create sysfs entry: %s\n",
|
||||
ds1511_nvram_attr.attr.name);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int ds1511_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct rtc_plat_data *pdata = platform_get_drvdata(pdev);
|
||||
|
||||
sysfs_remove_bin_file(&pdev->dev.kobj, &ds1511_nvram_attr);
|
||||
if (pdata->irq > 0) {
|
||||
/*
|
||||
* disable the alarm interrupt
|
||||
*/
|
||||
rtc_write(rtc_read(RTC_CMD) & ~RTC_TIE, RTC_CMD);
|
||||
rtc_read(RTC_CMD1);
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -524,7 +508,6 @@ MODULE_ALIAS("platform:ds1511");
|
|||
|
||||
static struct platform_driver ds1511_rtc_driver = {
|
||||
.probe = ds1511_rtc_probe,
|
||||
.remove = ds1511_rtc_remove,
|
||||
.driver = {
|
||||
.name = "ds1511",
|
||||
},
|
||||
|
|
|
@ -82,7 +82,7 @@ static inline uint32_t jz4740_rtc_reg_read(struct jz4740_rtc *rtc, size_t reg)
|
|||
static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc)
|
||||
{
|
||||
uint32_t ctrl;
|
||||
int timeout = 1000;
|
||||
int timeout = 10000;
|
||||
|
||||
do {
|
||||
ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
|
||||
|
@ -94,7 +94,7 @@ static int jz4740_rtc_wait_write_ready(struct jz4740_rtc *rtc)
|
|||
static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc)
|
||||
{
|
||||
uint32_t ctrl;
|
||||
int ret, timeout = 1000;
|
||||
int ret, timeout = 10000;
|
||||
|
||||
ret = jz4740_rtc_wait_write_ready(rtc);
|
||||
if (ret != 0)
|
||||
|
@ -368,7 +368,7 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
|
|||
ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SCRATCHPAD, 0x12345678);
|
||||
ret = jz4740_rtc_reg_write(rtc, JZ_REG_RTC_SEC, 0);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Could not write write to RTC registers\n");
|
||||
dev_err(&pdev->dev, "Could not write to RTC registers\n");
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -154,6 +154,8 @@ struct m41t80_data {
|
|||
struct rtc_device *rtc;
|
||||
#ifdef CONFIG_COMMON_CLK
|
||||
struct clk_hw sqw;
|
||||
unsigned long freq;
|
||||
unsigned int sqwe;
|
||||
#endif
|
||||
};
|
||||
|
||||
|
@ -443,43 +445,40 @@ static SIMPLE_DEV_PM_OPS(m41t80_pm, m41t80_suspend, m41t80_resume);
|
|||
#ifdef CONFIG_COMMON_CLK
|
||||
#define sqw_to_m41t80_data(_hw) container_of(_hw, struct m41t80_data, sqw)
|
||||
|
||||
static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
static unsigned long m41t80_decode_freq(int setting)
|
||||
{
|
||||
return (setting == 0) ? 0 : (setting == 1) ? M41T80_SQW_MAX_FREQ :
|
||||
M41T80_SQW_MAX_FREQ >> setting;
|
||||
}
|
||||
|
||||
static unsigned long m41t80_get_freq(struct m41t80_data *m41t80)
|
||||
{
|
||||
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
|
||||
struct i2c_client *client = m41t80->client;
|
||||
int reg_sqw = (m41t80->features & M41T80_FEATURE_SQ_ALT) ?
|
||||
M41T80_REG_WDAY : M41T80_REG_SQW;
|
||||
int ret = i2c_smbus_read_byte_data(client, reg_sqw);
|
||||
unsigned long val = M41T80_SQW_MAX_FREQ;
|
||||
|
||||
if (ret < 0)
|
||||
return 0;
|
||||
return m41t80_decode_freq(ret >> 4);
|
||||
}
|
||||
|
||||
ret >>= 4;
|
||||
if (ret == 0)
|
||||
val = 0;
|
||||
else if (ret > 1)
|
||||
val = val / (1 << ret);
|
||||
|
||||
return val;
|
||||
static unsigned long m41t80_sqw_recalc_rate(struct clk_hw *hw,
|
||||
unsigned long parent_rate)
|
||||
{
|
||||
return sqw_to_m41t80_data(hw)->freq;
|
||||
}
|
||||
|
||||
static long m41t80_sqw_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
unsigned long *prate)
|
||||
{
|
||||
int i, freq = M41T80_SQW_MAX_FREQ;
|
||||
|
||||
if (freq <= rate)
|
||||
return freq;
|
||||
|
||||
for (i = 2; i <= ilog2(M41T80_SQW_MAX_FREQ); i++) {
|
||||
freq /= 1 << i;
|
||||
if (freq <= rate)
|
||||
return freq;
|
||||
}
|
||||
|
||||
return 0;
|
||||
if (rate >= M41T80_SQW_MAX_FREQ)
|
||||
return M41T80_SQW_MAX_FREQ;
|
||||
if (rate >= M41T80_SQW_MAX_FREQ / 4)
|
||||
return M41T80_SQW_MAX_FREQ / 4;
|
||||
if (!rate)
|
||||
return 0;
|
||||
return 1 << ilog2(rate);
|
||||
}
|
||||
|
||||
static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
@ -491,17 +490,12 @@ static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
M41T80_REG_WDAY : M41T80_REG_SQW;
|
||||
int reg, ret, val = 0;
|
||||
|
||||
if (rate) {
|
||||
if (!is_power_of_2(rate))
|
||||
return -EINVAL;
|
||||
val = ilog2(rate);
|
||||
if (val == ilog2(M41T80_SQW_MAX_FREQ))
|
||||
val = 1;
|
||||
else if (val < (ilog2(M41T80_SQW_MAX_FREQ) - 1))
|
||||
val = ilog2(M41T80_SQW_MAX_FREQ) - val;
|
||||
else
|
||||
return -EINVAL;
|
||||
}
|
||||
if (rate >= M41T80_SQW_MAX_FREQ)
|
||||
val = 1;
|
||||
else if (rate >= M41T80_SQW_MAX_FREQ / 4)
|
||||
val = 2;
|
||||
else if (rate)
|
||||
val = 15 - ilog2(rate);
|
||||
|
||||
reg = i2c_smbus_read_byte_data(client, reg_sqw);
|
||||
if (reg < 0)
|
||||
|
@ -510,10 +504,9 @@ static int m41t80_sqw_set_rate(struct clk_hw *hw, unsigned long rate,
|
|||
reg = (reg & 0x0f) | (val << 4);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(client, reg_sqw, reg);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return -EINVAL;
|
||||
if (!ret)
|
||||
m41t80->freq = m41t80_decode_freq(val);
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int m41t80_sqw_control(struct clk_hw *hw, bool enable)
|
||||
|
@ -530,7 +523,10 @@ static int m41t80_sqw_control(struct clk_hw *hw, bool enable)
|
|||
else
|
||||
ret &= ~M41T80_ALMON_SQWE;
|
||||
|
||||
return i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret);
|
||||
ret = i2c_smbus_write_byte_data(client, M41T80_REG_ALARM_MON, ret);
|
||||
if (!ret)
|
||||
m41t80->sqwe = enable;
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int m41t80_sqw_prepare(struct clk_hw *hw)
|
||||
|
@ -545,14 +541,7 @@ static void m41t80_sqw_unprepare(struct clk_hw *hw)
|
|||
|
||||
static int m41t80_sqw_is_prepared(struct clk_hw *hw)
|
||||
{
|
||||
struct m41t80_data *m41t80 = sqw_to_m41t80_data(hw);
|
||||
struct i2c_client *client = m41t80->client;
|
||||
int ret = i2c_smbus_read_byte_data(client, M41T80_REG_ALARM_MON);
|
||||
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return !!(ret & M41T80_ALMON_SQWE);
|
||||
return sqw_to_m41t80_data(hw)->sqwe;
|
||||
}
|
||||
|
||||
static const struct clk_ops m41t80_sqw_ops = {
|
||||
|
@ -587,6 +576,7 @@ static struct clk *m41t80_sqw_register_clk(struct m41t80_data *m41t80)
|
|||
init.parent_names = NULL;
|
||||
init.num_parents = 0;
|
||||
m41t80->sqw.init = &init;
|
||||
m41t80->freq = m41t80_get_freq(m41t80);
|
||||
|
||||
/* optional override of the clockname */
|
||||
of_property_read_string(node, "clock-output-names", &init.name);
|
||||
|
|
|
@ -163,35 +163,30 @@ static const struct rtc_class_ops m48t86_rtc_ops = {
|
|||
.proc = m48t86_rtc_proc,
|
||||
};
|
||||
|
||||
static ssize_t m48t86_nvram_read(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
static int m48t86_nvram_read(void *priv, unsigned int off, void *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct device *dev = priv;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
buf[i] = m48t86_readb(dev, M48T86_NVRAM(off + i));
|
||||
((u8 *)buf)[i] = m48t86_readb(dev, M48T86_NVRAM(off + i));
|
||||
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static ssize_t m48t86_nvram_write(struct file *filp, struct kobject *kobj,
|
||||
struct bin_attribute *attr,
|
||||
char *buf, loff_t off, size_t count)
|
||||
static int m48t86_nvram_write(void *priv, unsigned int off, void *buf,
|
||||
size_t count)
|
||||
{
|
||||
struct device *dev = kobj_to_dev(kobj);
|
||||
struct device *dev = priv;
|
||||
unsigned int i;
|
||||
|
||||
for (i = 0; i < count; i++)
|
||||
m48t86_writeb(dev, buf[i], M48T86_NVRAM(off + i));
|
||||
m48t86_writeb(dev, ((u8 *)buf)[i], M48T86_NVRAM(off + i));
|
||||
|
||||
return count;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static BIN_ATTR(nvram, 0644, m48t86_nvram_read, m48t86_nvram_write,
|
||||
M48T86_NVRAM_LEN);
|
||||
|
||||
/*
|
||||
* The RTC is an optional feature at purchase time on some Technologic Systems
|
||||
* boards. Verify that it actually exists by checking if the last two bytes
|
||||
|
@ -223,11 +218,21 @@ static bool m48t86_verify_chip(struct platform_device *pdev)
|
|||
return false;
|
||||
}
|
||||
|
||||
static struct nvmem_config m48t86_nvmem_cfg = {
|
||||
.name = "m48t86_nvram",
|
||||
.word_size = 1,
|
||||
.stride = 1,
|
||||
.size = M48T86_NVRAM_LEN,
|
||||
.reg_read = m48t86_nvram_read,
|
||||
.reg_write = m48t86_nvram_write,
|
||||
};
|
||||
|
||||
static int m48t86_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct m48t86_rtc_info *info;
|
||||
struct resource *res;
|
||||
unsigned char reg;
|
||||
int err;
|
||||
|
||||
info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
|
||||
if (!info)
|
||||
|
@ -254,25 +259,25 @@ static int m48t86_rtc_probe(struct platform_device *pdev)
|
|||
return -ENODEV;
|
||||
}
|
||||
|
||||
info->rtc = devm_rtc_device_register(&pdev->dev, "m48t86",
|
||||
&m48t86_rtc_ops, THIS_MODULE);
|
||||
info->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(info->rtc))
|
||||
return PTR_ERR(info->rtc);
|
||||
|
||||
info->rtc->ops = &m48t86_rtc_ops;
|
||||
|
||||
m48t86_nvmem_cfg.priv = &pdev->dev;
|
||||
info->rtc->nvmem_config = &m48t86_nvmem_cfg;
|
||||
info->rtc->nvram_old_abi = true;
|
||||
|
||||
err = rtc_register_device(info->rtc);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
/* read battery status */
|
||||
reg = m48t86_readb(&pdev->dev, M48T86_D);
|
||||
dev_info(&pdev->dev, "battery %s\n",
|
||||
(reg & M48T86_D_VRT) ? "ok" : "exhausted");
|
||||
|
||||
if (device_create_bin_file(&pdev->dev, &bin_attr_nvram))
|
||||
dev_err(&pdev->dev, "failed to create nvram sysfs entry\n");
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int m48t86_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
device_remove_bin_file(&pdev->dev, &bin_attr_nvram);
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
@ -281,7 +286,6 @@ static struct platform_driver m48t86_rtc_platform_driver = {
|
|||
.name = "rtc-m48t86",
|
||||
},
|
||||
.probe = m48t86_rtc_probe,
|
||||
.remove = m48t86_rtc_remove,
|
||||
};
|
||||
|
||||
module_platform_driver(m48t86_rtc_platform_driver);
|
||||
|
|
422
drivers/rtc/rtc-mt7622.c
Normal file
422
drivers/rtc/rtc-mt7622.c
Normal file
|
@ -0,0 +1,422 @@
|
|||
/*
|
||||
* Driver for MediaTek SoC based RTC
|
||||
*
|
||||
* Copyright (C) 2017 Sean Wang <sean.wang@mediatek.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License as
|
||||
* published by the Free Software Foundation; either version 2 of
|
||||
* the License, or (at your option) any later version.
|
||||
*
|
||||
* This program is distributed in the hope that it will be useful,
|
||||
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
||||
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
||||
* GNU General Public License for more details.
|
||||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/interrupt.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_address.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define MTK_RTC_DEV KBUILD_MODNAME
|
||||
|
||||
#define MTK_RTC_PWRCHK1 0x4
|
||||
#define RTC_PWRCHK1_MAGIC 0xc6
|
||||
|
||||
#define MTK_RTC_PWRCHK2 0x8
|
||||
#define RTC_PWRCHK2_MAGIC 0x9a
|
||||
|
||||
#define MTK_RTC_KEY 0xc
|
||||
#define RTC_KEY_MAGIC 0x59
|
||||
|
||||
#define MTK_RTC_PROT1 0x10
|
||||
#define RTC_PROT1_MAGIC 0xa3
|
||||
|
||||
#define MTK_RTC_PROT2 0x14
|
||||
#define RTC_PROT2_MAGIC 0x57
|
||||
|
||||
#define MTK_RTC_PROT3 0x18
|
||||
#define RTC_PROT3_MAGIC 0x67
|
||||
|
||||
#define MTK_RTC_PROT4 0x1c
|
||||
#define RTC_PROT4_MAGIC 0xd2
|
||||
|
||||
#define MTK_RTC_CTL 0x20
|
||||
#define RTC_RC_STOP BIT(0)
|
||||
|
||||
#define MTK_RTC_DEBNCE 0x2c
|
||||
#define RTC_DEBNCE_MASK GENMASK(2, 0)
|
||||
|
||||
#define MTK_RTC_INT 0x30
|
||||
#define RTC_INT_AL_STA BIT(4)
|
||||
|
||||
/*
|
||||
* Ranges from 0x40 to 0x78 provide RTC time setup for year, month,
|
||||
* day of month, day of week, hour, minute and second.
|
||||
*/
|
||||
#define MTK_RTC_TREG(_t, _f) (0x40 + (0x4 * (_f)) + ((_t) * 0x20))
|
||||
|
||||
#define MTK_RTC_AL_CTL 0x7c
|
||||
#define RTC_AL_EN BIT(0)
|
||||
#define RTC_AL_ALL GENMASK(7, 0)
|
||||
|
||||
/*
|
||||
* The offset is used in the translation for the year between in struct
|
||||
* rtc_time and in hardware register MTK_RTC_TREG(x,MTK_YEA)
|
||||
*/
|
||||
#define MTK_RTC_TM_YR_OFFSET 100
|
||||
|
||||
/*
|
||||
* The lowest value for the valid tm_year. RTC hardware would take incorrectly
|
||||
* tm_year 100 as not a leap year and thus it is also required being excluded
|
||||
* from the valid options.
|
||||
*/
|
||||
#define MTK_RTC_TM_YR_L (MTK_RTC_TM_YR_OFFSET + 1)
|
||||
|
||||
/*
|
||||
* The most year the RTC can hold is 99 and the next to 99 in year register
|
||||
* would be wraparound to 0, for MT7622.
|
||||
*/
|
||||
#define MTK_RTC_HW_YR_LIMIT 99
|
||||
|
||||
/* The highest value for the valid tm_year */
|
||||
#define MTK_RTC_TM_YR_H (MTK_RTC_TM_YR_OFFSET + MTK_RTC_HW_YR_LIMIT)
|
||||
|
||||
/* Simple macro helps to check whether the hardware supports the tm_year */
|
||||
#define MTK_RTC_TM_YR_VALID(_y) ((_y) >= MTK_RTC_TM_YR_L && \
|
||||
(_y) <= MTK_RTC_TM_YR_H)
|
||||
|
||||
/* Types of the function the RTC provides are time counter and alarm. */
|
||||
enum {
|
||||
MTK_TC,
|
||||
MTK_AL,
|
||||
};
|
||||
|
||||
/* Indexes are used for the pointer to relevant registers in MTK_RTC_TREG */
|
||||
enum {
|
||||
MTK_YEA,
|
||||
MTK_MON,
|
||||
MTK_DOM,
|
||||
MTK_DOW,
|
||||
MTK_HOU,
|
||||
MTK_MIN,
|
||||
MTK_SEC
|
||||
};
|
||||
|
||||
struct mtk_rtc {
|
||||
struct rtc_device *rtc;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static void mtk_w32(struct mtk_rtc *rtc, u32 reg, u32 val)
|
||||
{
|
||||
writel_relaxed(val, rtc->base + reg);
|
||||
}
|
||||
|
||||
static u32 mtk_r32(struct mtk_rtc *rtc, u32 reg)
|
||||
{
|
||||
return readl_relaxed(rtc->base + reg);
|
||||
}
|
||||
|
||||
static void mtk_rmw(struct mtk_rtc *rtc, u32 reg, u32 mask, u32 set)
|
||||
{
|
||||
u32 val;
|
||||
|
||||
val = mtk_r32(rtc, reg);
|
||||
val &= ~mask;
|
||||
val |= set;
|
||||
mtk_w32(rtc, reg, val);
|
||||
}
|
||||
|
||||
static void mtk_set(struct mtk_rtc *rtc, u32 reg, u32 val)
|
||||
{
|
||||
mtk_rmw(rtc, reg, 0, val);
|
||||
}
|
||||
|
||||
static void mtk_clr(struct mtk_rtc *rtc, u32 reg, u32 val)
|
||||
{
|
||||
mtk_rmw(rtc, reg, val, 0);
|
||||
}
|
||||
|
||||
static void mtk_rtc_hw_init(struct mtk_rtc *hw)
|
||||
{
|
||||
/* The setup of the init sequence is for allowing RTC got to work */
|
||||
mtk_w32(hw, MTK_RTC_PWRCHK1, RTC_PWRCHK1_MAGIC);
|
||||
mtk_w32(hw, MTK_RTC_PWRCHK2, RTC_PWRCHK2_MAGIC);
|
||||
mtk_w32(hw, MTK_RTC_KEY, RTC_KEY_MAGIC);
|
||||
mtk_w32(hw, MTK_RTC_PROT1, RTC_PROT1_MAGIC);
|
||||
mtk_w32(hw, MTK_RTC_PROT2, RTC_PROT2_MAGIC);
|
||||
mtk_w32(hw, MTK_RTC_PROT3, RTC_PROT3_MAGIC);
|
||||
mtk_w32(hw, MTK_RTC_PROT4, RTC_PROT4_MAGIC);
|
||||
mtk_rmw(hw, MTK_RTC_DEBNCE, RTC_DEBNCE_MASK, 0);
|
||||
mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP);
|
||||
}
|
||||
|
||||
static void mtk_rtc_get_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm,
|
||||
int time_alarm)
|
||||
{
|
||||
u32 year, mon, mday, wday, hour, min, sec;
|
||||
|
||||
/*
|
||||
* Read again until the field of the second is not changed which
|
||||
* ensures all fields in the consistent state. Note that MTK_SEC must
|
||||
* be read first. In this way, it guarantees the others remain not
|
||||
* changed when the results for two MTK_SEC consecutive reads are same.
|
||||
*/
|
||||
do {
|
||||
sec = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC));
|
||||
min = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN));
|
||||
hour = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU));
|
||||
wday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW));
|
||||
mday = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM));
|
||||
mon = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_MON));
|
||||
year = mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA));
|
||||
} while (sec != mtk_r32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC)));
|
||||
|
||||
tm->tm_sec = sec;
|
||||
tm->tm_min = min;
|
||||
tm->tm_hour = hour;
|
||||
tm->tm_wday = wday;
|
||||
tm->tm_mday = mday;
|
||||
tm->tm_mon = mon - 1;
|
||||
|
||||
/* Rebase to the absolute year which userspace queries */
|
||||
tm->tm_year = year + MTK_RTC_TM_YR_OFFSET;
|
||||
}
|
||||
|
||||
static void mtk_rtc_set_alarm_or_time(struct mtk_rtc *hw, struct rtc_time *tm,
|
||||
int time_alarm)
|
||||
{
|
||||
u32 year;
|
||||
|
||||
/* Rebase to the relative year which RTC hardware requires */
|
||||
year = tm->tm_year - MTK_RTC_TM_YR_OFFSET;
|
||||
|
||||
mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_YEA), year);
|
||||
mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MON), tm->tm_mon + 1);
|
||||
mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOW), tm->tm_wday);
|
||||
mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_DOM), tm->tm_mday);
|
||||
mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_HOU), tm->tm_hour);
|
||||
mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_MIN), tm->tm_min);
|
||||
mtk_w32(hw, MTK_RTC_TREG(time_alarm, MTK_SEC), tm->tm_sec);
|
||||
}
|
||||
|
||||
static irqreturn_t mtk_rtc_alarmirq(int irq, void *id)
|
||||
{
|
||||
struct mtk_rtc *hw = (struct mtk_rtc *)id;
|
||||
u32 irq_sta;
|
||||
|
||||
irq_sta = mtk_r32(hw, MTK_RTC_INT);
|
||||
if (irq_sta & RTC_INT_AL_STA) {
|
||||
/* Stop alarm also implicitly disables the alarm interrupt */
|
||||
mtk_w32(hw, MTK_RTC_AL_CTL, 0);
|
||||
rtc_update_irq(hw->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
/* Ack alarm interrupt status */
|
||||
mtk_w32(hw, MTK_RTC_INT, RTC_INT_AL_STA);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int mtk_rtc_gettime(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct mtk_rtc *hw = dev_get_drvdata(dev);
|
||||
|
||||
mtk_rtc_get_alarm_or_time(hw, tm, MTK_TC);
|
||||
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int mtk_rtc_settime(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct mtk_rtc *hw = dev_get_drvdata(dev);
|
||||
|
||||
if (!MTK_RTC_TM_YR_VALID(tm->tm_year))
|
||||
return -EINVAL;
|
||||
|
||||
/* Stop time counter before setting a new one*/
|
||||
mtk_set(hw, MTK_RTC_CTL, RTC_RC_STOP);
|
||||
|
||||
mtk_rtc_set_alarm_or_time(hw, tm, MTK_TC);
|
||||
|
||||
/* Restart the time counter */
|
||||
mtk_clr(hw, MTK_RTC_CTL, RTC_RC_STOP);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_rtc_getalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||
{
|
||||
struct mtk_rtc *hw = dev_get_drvdata(dev);
|
||||
struct rtc_time *alrm_tm = &wkalrm->time;
|
||||
|
||||
mtk_rtc_get_alarm_or_time(hw, alrm_tm, MTK_AL);
|
||||
|
||||
wkalrm->enabled = !!(mtk_r32(hw, MTK_RTC_AL_CTL) & RTC_AL_EN);
|
||||
wkalrm->pending = !!(mtk_r32(hw, MTK_RTC_INT) & RTC_INT_AL_STA);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_rtc_setalarm(struct device *dev, struct rtc_wkalrm *wkalrm)
|
||||
{
|
||||
struct mtk_rtc *hw = dev_get_drvdata(dev);
|
||||
struct rtc_time *alrm_tm = &wkalrm->time;
|
||||
|
||||
if (!MTK_RTC_TM_YR_VALID(alrm_tm->tm_year))
|
||||
return -EINVAL;
|
||||
|
||||
/*
|
||||
* Stop the alarm also implicitly including disables interrupt before
|
||||
* setting a new one.
|
||||
*/
|
||||
mtk_clr(hw, MTK_RTC_AL_CTL, RTC_AL_EN);
|
||||
|
||||
/*
|
||||
* Avoid contention between mtk_rtc_setalarm and IRQ handler so that
|
||||
* disabling the interrupt and awaiting for pending IRQ handler to
|
||||
* complete.
|
||||
*/
|
||||
synchronize_irq(hw->irq);
|
||||
|
||||
mtk_rtc_set_alarm_or_time(hw, alrm_tm, MTK_AL);
|
||||
|
||||
/* Restart the alarm with the new setup */
|
||||
mtk_w32(hw, MTK_RTC_AL_CTL, RTC_AL_ALL);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops mtk_rtc_ops = {
|
||||
.read_time = mtk_rtc_gettime,
|
||||
.set_time = mtk_rtc_settime,
|
||||
.read_alarm = mtk_rtc_getalarm,
|
||||
.set_alarm = mtk_rtc_setalarm,
|
||||
};
|
||||
|
||||
static const struct of_device_id mtk_rtc_match[] = {
|
||||
{ .compatible = "mediatek,mt7622-rtc" },
|
||||
{ .compatible = "mediatek,soc-rtc" },
|
||||
{},
|
||||
};
|
||||
|
||||
static int mtk_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_rtc *hw;
|
||||
struct resource *res;
|
||||
int ret;
|
||||
|
||||
hw = devm_kzalloc(&pdev->dev, sizeof(*hw), GFP_KERNEL);
|
||||
if (!hw)
|
||||
return -ENOMEM;
|
||||
|
||||
platform_set_drvdata(pdev, hw);
|
||||
|
||||
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
|
||||
hw->base = devm_ioremap_resource(&pdev->dev, res);
|
||||
if (IS_ERR(hw->base))
|
||||
return PTR_ERR(hw->base);
|
||||
|
||||
hw->clk = devm_clk_get(&pdev->dev, "rtc");
|
||||
if (IS_ERR(hw->clk)) {
|
||||
dev_err(&pdev->dev, "No clock\n");
|
||||
return PTR_ERR(hw->clk);
|
||||
}
|
||||
|
||||
ret = clk_prepare_enable(hw->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hw->irq = platform_get_irq(pdev, 0);
|
||||
if (hw->irq < 0) {
|
||||
dev_err(&pdev->dev, "No IRQ resource\n");
|
||||
ret = hw->irq;
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, hw->irq, mtk_rtc_alarmirq,
|
||||
0, dev_name(&pdev->dev), hw);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "Can't request IRQ\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
mtk_rtc_hw_init(hw);
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
hw->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||
&mtk_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(hw->rtc)) {
|
||||
ret = PTR_ERR(hw->rtc);
|
||||
dev_err(&pdev->dev, "Unable to register device\n");
|
||||
goto err;
|
||||
}
|
||||
|
||||
return 0;
|
||||
err:
|
||||
clk_disable_unprepare(hw->clk);
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int mtk_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
struct mtk_rtc *hw = platform_get_drvdata(pdev);
|
||||
|
||||
clk_disable_unprepare(hw->clk);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int mtk_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct mtk_rtc *hw = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
enable_irq_wake(hw->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int mtk_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct mtk_rtc *hw = dev_get_drvdata(dev);
|
||||
|
||||
if (device_may_wakeup(dev))
|
||||
disable_irq_wake(hw->irq);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(mtk_rtc_pm_ops, mtk_rtc_suspend, mtk_rtc_resume);
|
||||
|
||||
#define MTK_RTC_PM_OPS (&mtk_rtc_pm_ops)
|
||||
#else /* CONFIG_PM */
|
||||
#define MTK_RTC_PM_OPS NULL
|
||||
#endif /* CONFIG_PM */
|
||||
|
||||
static struct platform_driver mtk_rtc_driver = {
|
||||
.probe = mtk_rtc_probe,
|
||||
.remove = mtk_rtc_remove,
|
||||
.driver = {
|
||||
.name = MTK_RTC_DEV,
|
||||
.of_match_table = mtk_rtc_match,
|
||||
.pm = MTK_RTC_PM_OPS,
|
||||
},
|
||||
};
|
||||
|
||||
module_platform_driver(mtk_rtc_driver);
|
||||
|
||||
MODULE_DESCRIPTION("MediaTek SoC based RTC Driver");
|
||||
MODULE_AUTHOR("Sean Wang <sean.wang@mediatek.com>");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -70,6 +70,10 @@
|
|||
#define OMAP_RTC_COMP_MSB_REG 0x50
|
||||
#define OMAP_RTC_OSC_REG 0x54
|
||||
|
||||
#define OMAP_RTC_SCRATCH0_REG 0x60
|
||||
#define OMAP_RTC_SCRATCH1_REG 0x64
|
||||
#define OMAP_RTC_SCRATCH2_REG 0x68
|
||||
|
||||
#define OMAP_RTC_KICK0_REG 0x6c
|
||||
#define OMAP_RTC_KICK1_REG 0x70
|
||||
|
||||
|
@ -667,6 +671,45 @@ static struct pinctrl_desc rtc_pinctrl_desc = {
|
|||
.owner = THIS_MODULE,
|
||||
};
|
||||
|
||||
static int omap_rtc_scratch_read(void *priv, unsigned int offset, void *_val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct omap_rtc *rtc = priv;
|
||||
u32 *val = _val;
|
||||
int i;
|
||||
|
||||
for (i = 0; i < bytes / 4; i++)
|
||||
val[i] = rtc_readl(rtc,
|
||||
OMAP_RTC_SCRATCH0_REG + offset + (i * 4));
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int omap_rtc_scratch_write(void *priv, unsigned int offset, void *_val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct omap_rtc *rtc = priv;
|
||||
u32 *val = _val;
|
||||
int i;
|
||||
|
||||
rtc->type->unlock(rtc);
|
||||
for (i = 0; i < bytes / 4; i++)
|
||||
rtc_writel(rtc,
|
||||
OMAP_RTC_SCRATCH0_REG + offset + (i * 4), val[i]);
|
||||
rtc->type->lock(rtc);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static struct nvmem_config omap_rtc_nvmem_config = {
|
||||
.name = "omap_rtc_scratch",
|
||||
.word_size = 4,
|
||||
.stride = 4,
|
||||
.size = OMAP_RTC_KICK0_REG - OMAP_RTC_SCRATCH0_REG,
|
||||
.reg_read = omap_rtc_scratch_read,
|
||||
.reg_write = omap_rtc_scratch_write,
|
||||
};
|
||||
|
||||
static int omap_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct omap_rtc *rtc;
|
||||
|
@ -797,13 +840,16 @@ static int omap_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
|
||||
rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||
&omap_rtc_ops, THIS_MODULE);
|
||||
rtc->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(rtc->rtc)) {
|
||||
ret = PTR_ERR(rtc->rtc);
|
||||
goto err;
|
||||
}
|
||||
|
||||
rtc->rtc->ops = &omap_rtc_ops;
|
||||
omap_rtc_nvmem_config.priv = rtc;
|
||||
rtc->rtc->nvmem_config = &omap_rtc_nvmem_config;
|
||||
|
||||
/* handle periodic and alarm irqs */
|
||||
ret = devm_request_irq(&pdev->dev, rtc->irq_timer, rtc_irq, 0,
|
||||
dev_name(&rtc->rtc->dev), rtc);
|
||||
|
@ -830,9 +876,14 @@ static int omap_rtc_probe(struct platform_device *pdev)
|
|||
rtc->pctldev = pinctrl_register(&rtc_pinctrl_desc, &pdev->dev, rtc);
|
||||
if (IS_ERR(rtc->pctldev)) {
|
||||
dev_err(&pdev->dev, "Couldn't register pinctrl driver\n");
|
||||
return PTR_ERR(rtc->pctldev);
|
||||
ret = PTR_ERR(rtc->pctldev);
|
||||
goto err;
|
||||
}
|
||||
|
||||
ret = rtc_register_device(rtc->rtc);
|
||||
if (ret)
|
||||
goto err;
|
||||
|
||||
return 0;
|
||||
|
||||
err:
|
||||
|
|
|
@ -35,6 +35,9 @@
|
|||
#define REG_MONTHS 0x08
|
||||
#define REG_YEARS 0x09
|
||||
|
||||
#define REG_OFFSET 0x0e
|
||||
#define REG_OFFSET_MODE BIT(7)
|
||||
|
||||
struct pcf8523 {
|
||||
struct rtc_device *rtc;
|
||||
};
|
||||
|
@ -272,10 +275,47 @@ static int pcf8523_rtc_ioctl(struct device *dev, unsigned int cmd,
|
|||
#define pcf8523_rtc_ioctl NULL
|
||||
#endif
|
||||
|
||||
static int pcf8523_rtc_read_offset(struct device *dev, long *offset)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
int err;
|
||||
u8 value;
|
||||
s8 val;
|
||||
|
||||
err = pcf8523_read(client, REG_OFFSET, &value);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
/* sign extend the 7-bit offset value */
|
||||
val = value << 1;
|
||||
*offset = (value & REG_OFFSET_MODE ? 4069 : 4340) * (val >> 1);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf8523_rtc_set_offset(struct device *dev, long offset)
|
||||
{
|
||||
struct i2c_client *client = to_i2c_client(dev);
|
||||
long reg_m0, reg_m1;
|
||||
u8 value;
|
||||
|
||||
reg_m0 = clamp(DIV_ROUND_CLOSEST(offset, 4340), -64L, 63L);
|
||||
reg_m1 = clamp(DIV_ROUND_CLOSEST(offset, 4069), -64L, 63L);
|
||||
|
||||
if (abs(reg_m0 * 4340 - offset) < abs(reg_m1 * 4069 - offset))
|
||||
value = reg_m0 & 0x7f;
|
||||
else
|
||||
value = (reg_m1 & 0x7f) | REG_OFFSET_MODE;
|
||||
|
||||
return pcf8523_write(client, REG_OFFSET, value);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops pcf8523_rtc_ops = {
|
||||
.read_time = pcf8523_rtc_read_time,
|
||||
.set_time = pcf8523_rtc_set_time,
|
||||
.ioctl = pcf8523_rtc_ioctl,
|
||||
.read_offset = pcf8523_rtc_read_offset,
|
||||
.set_offset = pcf8523_rtc_set_offset,
|
||||
};
|
||||
|
||||
static int pcf8523_probe(struct i2c_client *client,
|
||||
|
|
220
drivers/rtc/rtc-pcf85363.c
Normal file
220
drivers/rtc/rtc-pcf85363.c
Normal file
|
@ -0,0 +1,220 @@
|
|||
/*
|
||||
* drivers/rtc/rtc-pcf85363.c
|
||||
*
|
||||
* Driver for NXP PCF85363 real-time clock.
|
||||
*
|
||||
* Copyright (C) 2017 Eric Nelson
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* Based loosely on rtc-8583 by Russell King, Wolfram Sang and Juergen Beisert
|
||||
*/
|
||||
#include <linux/module.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/errno.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
|
||||
/*
|
||||
* Date/Time registers
|
||||
*/
|
||||
#define DT_100THS 0x00
|
||||
#define DT_SECS 0x01
|
||||
#define DT_MINUTES 0x02
|
||||
#define DT_HOURS 0x03
|
||||
#define DT_DAYS 0x04
|
||||
#define DT_WEEKDAYS 0x05
|
||||
#define DT_MONTHS 0x06
|
||||
#define DT_YEARS 0x07
|
||||
|
||||
/*
|
||||
* Alarm registers
|
||||
*/
|
||||
#define DT_SECOND_ALM1 0x08
|
||||
#define DT_MINUTE_ALM1 0x09
|
||||
#define DT_HOUR_ALM1 0x0a
|
||||
#define DT_DAY_ALM1 0x0b
|
||||
#define DT_MONTH_ALM1 0x0c
|
||||
#define DT_MINUTE_ALM2 0x0d
|
||||
#define DT_HOUR_ALM2 0x0e
|
||||
#define DT_WEEKDAY_ALM2 0x0f
|
||||
#define DT_ALARM_EN 0x10
|
||||
|
||||
/*
|
||||
* Time stamp registers
|
||||
*/
|
||||
#define DT_TIMESTAMP1 0x11
|
||||
#define DT_TIMESTAMP2 0x17
|
||||
#define DT_TIMESTAMP3 0x1d
|
||||
#define DT_TS_MODE 0x23
|
||||
|
||||
/*
|
||||
* control registers
|
||||
*/
|
||||
#define CTRL_OFFSET 0x24
|
||||
#define CTRL_OSCILLATOR 0x25
|
||||
#define CTRL_BATTERY 0x26
|
||||
#define CTRL_PIN_IO 0x27
|
||||
#define CTRL_FUNCTION 0x28
|
||||
#define CTRL_INTA_EN 0x29
|
||||
#define CTRL_INTB_EN 0x2a
|
||||
#define CTRL_FLAGS 0x2b
|
||||
#define CTRL_RAMBYTE 0x2c
|
||||
#define CTRL_WDOG 0x2d
|
||||
#define CTRL_STOP_EN 0x2e
|
||||
#define CTRL_RESETS 0x2f
|
||||
#define CTRL_RAM 0x40
|
||||
|
||||
#define NVRAM_SIZE 0x40
|
||||
|
||||
static struct i2c_driver pcf85363_driver;
|
||||
|
||||
struct pcf85363 {
|
||||
struct device *dev;
|
||||
struct rtc_device *rtc;
|
||||
struct nvmem_config nvmem_cfg;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
|
||||
unsigned char buf[DT_YEARS + 1];
|
||||
int ret, len = sizeof(buf);
|
||||
|
||||
/* read the RTC date and time registers all at once */
|
||||
ret = regmap_bulk_read(pcf85363->regmap, DT_100THS, buf, len);
|
||||
if (ret) {
|
||||
dev_err(dev, "%s: error %d\n", __func__, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
tm->tm_year = bcd2bin(buf[DT_YEARS]);
|
||||
/* adjust for 1900 base of rtc_time */
|
||||
tm->tm_year += 100;
|
||||
|
||||
tm->tm_wday = buf[DT_WEEKDAYS] & 7;
|
||||
buf[DT_SECS] &= 0x7F;
|
||||
tm->tm_sec = bcd2bin(buf[DT_SECS]);
|
||||
buf[DT_MINUTES] &= 0x7F;
|
||||
tm->tm_min = bcd2bin(buf[DT_MINUTES]);
|
||||
tm->tm_hour = bcd2bin(buf[DT_HOURS]);
|
||||
tm->tm_mday = bcd2bin(buf[DT_DAYS]);
|
||||
tm->tm_mon = bcd2bin(buf[DT_MONTHS]) - 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pcf85363_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
|
||||
unsigned char buf[DT_YEARS + 1];
|
||||
int len = sizeof(buf);
|
||||
|
||||
buf[DT_100THS] = 0;
|
||||
buf[DT_SECS] = bin2bcd(tm->tm_sec);
|
||||
buf[DT_MINUTES] = bin2bcd(tm->tm_min);
|
||||
buf[DT_HOURS] = bin2bcd(tm->tm_hour);
|
||||
buf[DT_DAYS] = bin2bcd(tm->tm_mday);
|
||||
buf[DT_WEEKDAYS] = tm->tm_wday;
|
||||
buf[DT_MONTHS] = bin2bcd(tm->tm_mon + 1);
|
||||
buf[DT_YEARS] = bin2bcd(tm->tm_year % 100);
|
||||
|
||||
return regmap_bulk_write(pcf85363->regmap, DT_100THS,
|
||||
buf, len);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops rtc_ops = {
|
||||
.read_time = pcf85363_rtc_read_time,
|
||||
.set_time = pcf85363_rtc_set_time,
|
||||
};
|
||||
|
||||
static int pcf85363_nvram_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct pcf85363 *pcf85363 = priv;
|
||||
|
||||
return regmap_bulk_read(pcf85363->regmap, CTRL_RAM + offset,
|
||||
val, bytes);
|
||||
}
|
||||
|
||||
static int pcf85363_nvram_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
struct pcf85363 *pcf85363 = priv;
|
||||
|
||||
return regmap_bulk_write(pcf85363->regmap, CTRL_RAM + offset,
|
||||
val, bytes);
|
||||
}
|
||||
|
||||
static const struct regmap_config regmap_config = {
|
||||
.reg_bits = 8,
|
||||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int pcf85363_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
{
|
||||
struct pcf85363 *pcf85363;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
pcf85363 = devm_kzalloc(&client->dev, sizeof(struct pcf85363),
|
||||
GFP_KERNEL);
|
||||
if (!pcf85363)
|
||||
return -ENOMEM;
|
||||
|
||||
pcf85363->regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(pcf85363->regmap)) {
|
||||
dev_err(&client->dev, "regmap allocation failed\n");
|
||||
return PTR_ERR(pcf85363->regmap);
|
||||
}
|
||||
|
||||
pcf85363->dev = &client->dev;
|
||||
i2c_set_clientdata(client, pcf85363);
|
||||
|
||||
pcf85363->rtc = devm_rtc_allocate_device(pcf85363->dev);
|
||||
if (IS_ERR(pcf85363->rtc))
|
||||
return PTR_ERR(pcf85363->rtc);
|
||||
|
||||
pcf85363->nvmem_cfg.name = "pcf85363-";
|
||||
pcf85363->nvmem_cfg.word_size = 1;
|
||||
pcf85363->nvmem_cfg.stride = 1;
|
||||
pcf85363->nvmem_cfg.size = NVRAM_SIZE;
|
||||
pcf85363->nvmem_cfg.reg_read = pcf85363_nvram_read;
|
||||
pcf85363->nvmem_cfg.reg_write = pcf85363_nvram_write;
|
||||
pcf85363->nvmem_cfg.priv = pcf85363;
|
||||
pcf85363->rtc->nvmem_config = &pcf85363->nvmem_cfg;
|
||||
pcf85363->rtc->ops = &rtc_ops;
|
||||
|
||||
return rtc_register_device(pcf85363->rtc);
|
||||
}
|
||||
|
||||
static const struct of_device_id dev_ids[] = {
|
||||
{ .compatible = "nxp,pcf85363" },
|
||||
{}
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, dev_ids);
|
||||
|
||||
static struct i2c_driver pcf85363_driver = {
|
||||
.driver = {
|
||||
.name = "pcf85363",
|
||||
.of_match_table = of_match_ptr(dev_ids),
|
||||
},
|
||||
.probe = pcf85363_probe,
|
||||
};
|
||||
|
||||
module_i2c_driver(pcf85363_driver);
|
||||
|
||||
MODULE_AUTHOR("Eric Nelson");
|
||||
MODULE_DESCRIPTION("pcf85363 I2C RTC driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -387,7 +387,7 @@ static int pcf8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *tm)
|
|||
if (err)
|
||||
return err;
|
||||
|
||||
return pcf8563_set_alarm_mode(client, 1);
|
||||
return pcf8563_set_alarm_mode(client, !!tm->enabled);
|
||||
}
|
||||
|
||||
static int pcf8563_irq_enable(struct device *dev, unsigned int enabled)
|
||||
|
@ -422,7 +422,7 @@ static unsigned long pcf8563_clkout_recalc_rate(struct clk_hw *hw,
|
|||
return 0;
|
||||
|
||||
buf &= PCF8563_REG_CLKO_F_MASK;
|
||||
return clkout_rates[ret];
|
||||
return clkout_rates[buf];
|
||||
}
|
||||
|
||||
static long pcf8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
|
||||
|
|
|
@ -308,10 +308,9 @@ static int pl031_remove(struct amba_device *adev)
|
|||
|
||||
dev_pm_clear_wake_irq(&adev->dev);
|
||||
device_init_wakeup(&adev->dev, false);
|
||||
free_irq(adev->irq[0], ldata);
|
||||
if (adev->irq[0])
|
||||
free_irq(adev->irq[0], ldata);
|
||||
rtc_device_unregister(ldata->rtc);
|
||||
iounmap(ldata->base);
|
||||
kfree(ldata);
|
||||
amba_release_regions(adev);
|
||||
|
||||
return 0;
|
||||
|
@ -322,25 +321,28 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
int ret;
|
||||
struct pl031_local *ldata;
|
||||
struct pl031_vendor_data *vendor = id->data;
|
||||
struct rtc_class_ops *ops = &vendor->ops;
|
||||
struct rtc_class_ops *ops;
|
||||
unsigned long time, data;
|
||||
|
||||
ret = amba_request_regions(adev, NULL);
|
||||
if (ret)
|
||||
goto err_req;
|
||||
|
||||
ldata = kzalloc(sizeof(struct pl031_local), GFP_KERNEL);
|
||||
if (!ldata) {
|
||||
ldata = devm_kzalloc(&adev->dev, sizeof(struct pl031_local),
|
||||
GFP_KERNEL);
|
||||
ops = devm_kmemdup(&adev->dev, &vendor->ops, sizeof(vendor->ops),
|
||||
GFP_KERNEL);
|
||||
if (!ldata || !ops) {
|
||||
ret = -ENOMEM;
|
||||
goto out;
|
||||
}
|
||||
|
||||
ldata->vendor = vendor;
|
||||
|
||||
ldata->base = ioremap(adev->res.start, resource_size(&adev->res));
|
||||
|
||||
ldata->base = devm_ioremap(&adev->dev, adev->res.start,
|
||||
resource_size(&adev->res));
|
||||
if (!ldata->base) {
|
||||
ret = -ENOMEM;
|
||||
goto out_no_remap;
|
||||
goto out;
|
||||
}
|
||||
|
||||
amba_set_drvdata(adev, ldata);
|
||||
|
@ -373,28 +375,32 @@ static int pl031_probe(struct amba_device *adev, const struct amba_id *id)
|
|||
}
|
||||
}
|
||||
|
||||
if (!adev->irq[0]) {
|
||||
/* When there's no interrupt, no point in exposing the alarm */
|
||||
ops->read_alarm = NULL;
|
||||
ops->set_alarm = NULL;
|
||||
ops->alarm_irq_enable = NULL;
|
||||
}
|
||||
|
||||
device_init_wakeup(&adev->dev, true);
|
||||
ldata->rtc = rtc_device_register("pl031", &adev->dev, ops,
|
||||
THIS_MODULE);
|
||||
if (IS_ERR(ldata->rtc)) {
|
||||
ret = PTR_ERR(ldata->rtc);
|
||||
goto out_no_rtc;
|
||||
goto out;
|
||||
}
|
||||
|
||||
if (request_irq(adev->irq[0], pl031_interrupt,
|
||||
vendor->irqflags, "rtc-pl031", ldata)) {
|
||||
ret = -EIO;
|
||||
goto out_no_irq;
|
||||
if (adev->irq[0]) {
|
||||
ret = request_irq(adev->irq[0], pl031_interrupt,
|
||||
vendor->irqflags, "rtc-pl031", ldata);
|
||||
if (ret)
|
||||
goto out_no_irq;
|
||||
dev_pm_set_wake_irq(&adev->dev, adev->irq[0]);
|
||||
}
|
||||
dev_pm_set_wake_irq(&adev->dev, adev->irq[0]);
|
||||
return 0;
|
||||
|
||||
out_no_irq:
|
||||
rtc_device_unregister(ldata->rtc);
|
||||
out_no_rtc:
|
||||
iounmap(ldata->base);
|
||||
out_no_remap:
|
||||
kfree(ldata);
|
||||
out:
|
||||
amba_release_regions(adev);
|
||||
err_req:
|
||||
|
@ -446,7 +452,7 @@ static struct pl031_vendor_data stv2_pl031 = {
|
|||
.irqflags = IRQF_SHARED | IRQF_COND_SUSPEND,
|
||||
};
|
||||
|
||||
static struct amba_id pl031_ids[] = {
|
||||
static const struct amba_id pl031_ids[] = {
|
||||
{
|
||||
.id = 0x00041031,
|
||||
.mask = 0x000fffff,
|
||||
|
|
|
@ -282,13 +282,13 @@ static int rv3029_eeprom_read(struct device *dev, u8 reg,
|
|||
static int rv3029_eeprom_write(struct device *dev, u8 reg,
|
||||
u8 const buf[], size_t len)
|
||||
{
|
||||
int ret, err;
|
||||
int ret;
|
||||
size_t i;
|
||||
u8 tmp;
|
||||
|
||||
err = rv3029_eeprom_enter(dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ret = rv3029_eeprom_enter(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
for (i = 0; i < len; i++, reg++) {
|
||||
ret = rv3029_read_regs(dev, reg, &tmp, 1);
|
||||
|
@ -304,11 +304,11 @@ static int rv3029_eeprom_write(struct device *dev, u8 reg,
|
|||
break;
|
||||
}
|
||||
|
||||
err = rv3029_eeprom_exit(dev);
|
||||
if (err < 0)
|
||||
return err;
|
||||
ret = rv3029_eeprom_exit(dev);
|
||||
if (ret < 0)
|
||||
return ret;
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rv3029_eeprom_update_bits(struct device *dev,
|
||||
|
@ -876,6 +876,8 @@ static const struct i2c_device_id rv3029_id[] = {
|
|||
MODULE_DEVICE_TABLE(i2c, rv3029_id);
|
||||
|
||||
static const struct of_device_id rv3029_of_match[] = {
|
||||
{ .compatible = "microcrystal,rv3029" },
|
||||
/* Backward compatibility only, do not use compatibles below: */
|
||||
{ .compatible = "rv3029" },
|
||||
{ .compatible = "rv3029c2" },
|
||||
{ .compatible = "mc,rv3029c2" },
|
||||
|
|
|
@ -24,7 +24,6 @@
|
|||
#define RX8010_MDAY 0x14
|
||||
#define RX8010_MONTH 0x15
|
||||
#define RX8010_YEAR 0x16
|
||||
#define RX8010_YEAR 0x16
|
||||
#define RX8010_RESV17 0x17
|
||||
#define RX8010_ALMIN 0x18
|
||||
#define RX8010_ALHOUR 0x19
|
||||
|
@ -36,7 +35,7 @@
|
|||
#define RX8010_CTRL 0x1F
|
||||
/* 0x20 to 0x2F are user registers */
|
||||
#define RX8010_RESV30 0x30
|
||||
#define RX8010_RESV31 0x32
|
||||
#define RX8010_RESV31 0x31
|
||||
#define RX8010_IRQ 0x32
|
||||
|
||||
#define RX8010_EXT_WADA BIT(3)
|
||||
|
@ -248,7 +247,7 @@ static int rx8010_init_client(struct i2c_client *client)
|
|||
|
||||
rx8010->ctrlreg = (ctrl[1] & ~RX8010_CTRL_TEST);
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
|
@ -277,7 +276,7 @@ static int rx8010_read_alarm(struct device *dev, struct rtc_wkalrm *t)
|
|||
t->enabled = !!(rx8010->ctrlreg & RX8010_CTRL_AIE);
|
||||
t->pending = (flagreg & RX8010_FLAG_AF) && t->enabled;
|
||||
|
||||
return err;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int rx8010_set_alarm(struct device *dev, struct rtc_wkalrm *t)
|
||||
|
|
662
drivers/rtc/rtc-sc27xx.c
Normal file
662
drivers/rtc/rtc-sc27xx.c
Normal file
|
@ -0,0 +1,662 @@
|
|||
/*
|
||||
* Copyright (C) 2017 Spreadtrum Communications Inc.
|
||||
*
|
||||
* SPDX-License-Identifier: GPL-2.0
|
||||
*/
|
||||
|
||||
#include <linux/bitops.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define SPRD_RTC_SEC_CNT_VALUE 0x0
|
||||
#define SPRD_RTC_MIN_CNT_VALUE 0x4
|
||||
#define SPRD_RTC_HOUR_CNT_VALUE 0x8
|
||||
#define SPRD_RTC_DAY_CNT_VALUE 0xc
|
||||
#define SPRD_RTC_SEC_CNT_UPD 0x10
|
||||
#define SPRD_RTC_MIN_CNT_UPD 0x14
|
||||
#define SPRD_RTC_HOUR_CNT_UPD 0x18
|
||||
#define SPRD_RTC_DAY_CNT_UPD 0x1c
|
||||
#define SPRD_RTC_SEC_ALM_UPD 0x20
|
||||
#define SPRD_RTC_MIN_ALM_UPD 0x24
|
||||
#define SPRD_RTC_HOUR_ALM_UPD 0x28
|
||||
#define SPRD_RTC_DAY_ALM_UPD 0x2c
|
||||
#define SPRD_RTC_INT_EN 0x30
|
||||
#define SPRD_RTC_INT_RAW_STS 0x34
|
||||
#define SPRD_RTC_INT_CLR 0x38
|
||||
#define SPRD_RTC_INT_MASK_STS 0x3C
|
||||
#define SPRD_RTC_SEC_ALM_VALUE 0x40
|
||||
#define SPRD_RTC_MIN_ALM_VALUE 0x44
|
||||
#define SPRD_RTC_HOUR_ALM_VALUE 0x48
|
||||
#define SPRD_RTC_DAY_ALM_VALUE 0x4c
|
||||
#define SPRD_RTC_SPG_VALUE 0x50
|
||||
#define SPRD_RTC_SPG_UPD 0x54
|
||||
#define SPRD_RTC_SEC_AUXALM_UPD 0x60
|
||||
#define SPRD_RTC_MIN_AUXALM_UPD 0x64
|
||||
#define SPRD_RTC_HOUR_AUXALM_UPD 0x68
|
||||
#define SPRD_RTC_DAY_AUXALM_UPD 0x6c
|
||||
|
||||
/* BIT & MASK definition for SPRD_RTC_INT_* registers */
|
||||
#define SPRD_RTC_SEC_EN BIT(0)
|
||||
#define SPRD_RTC_MIN_EN BIT(1)
|
||||
#define SPRD_RTC_HOUR_EN BIT(2)
|
||||
#define SPRD_RTC_DAY_EN BIT(3)
|
||||
#define SPRD_RTC_ALARM_EN BIT(4)
|
||||
#define SPRD_RTC_HRS_FORMAT_EN BIT(5)
|
||||
#define SPRD_RTC_AUXALM_EN BIT(6)
|
||||
#define SPRD_RTC_SPG_UPD_EN BIT(7)
|
||||
#define SPRD_RTC_SEC_UPD_EN BIT(8)
|
||||
#define SPRD_RTC_MIN_UPD_EN BIT(9)
|
||||
#define SPRD_RTC_HOUR_UPD_EN BIT(10)
|
||||
#define SPRD_RTC_DAY_UPD_EN BIT(11)
|
||||
#define SPRD_RTC_ALMSEC_UPD_EN BIT(12)
|
||||
#define SPRD_RTC_ALMMIN_UPD_EN BIT(13)
|
||||
#define SPRD_RTC_ALMHOUR_UPD_EN BIT(14)
|
||||
#define SPRD_RTC_ALMDAY_UPD_EN BIT(15)
|
||||
#define SPRD_RTC_INT_MASK GENMASK(15, 0)
|
||||
|
||||
#define SPRD_RTC_TIME_INT_MASK \
|
||||
(SPRD_RTC_SEC_UPD_EN | SPRD_RTC_MIN_UPD_EN | \
|
||||
SPRD_RTC_HOUR_UPD_EN | SPRD_RTC_DAY_UPD_EN)
|
||||
|
||||
#define SPRD_RTC_ALMTIME_INT_MASK \
|
||||
(SPRD_RTC_ALMSEC_UPD_EN | SPRD_RTC_ALMMIN_UPD_EN | \
|
||||
SPRD_RTC_ALMHOUR_UPD_EN | SPRD_RTC_ALMDAY_UPD_EN)
|
||||
|
||||
#define SPRD_RTC_ALM_INT_MASK \
|
||||
(SPRD_RTC_SEC_EN | SPRD_RTC_MIN_EN | \
|
||||
SPRD_RTC_HOUR_EN | SPRD_RTC_DAY_EN | \
|
||||
SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN)
|
||||
|
||||
/* second/minute/hour/day values mask definition */
|
||||
#define SPRD_RTC_SEC_MASK GENMASK(5, 0)
|
||||
#define SPRD_RTC_MIN_MASK GENMASK(5, 0)
|
||||
#define SPRD_RTC_HOUR_MASK GENMASK(4, 0)
|
||||
#define SPRD_RTC_DAY_MASK GENMASK(15, 0)
|
||||
|
||||
/* alarm lock definition for SPRD_RTC_SPG_UPD register */
|
||||
#define SPRD_RTC_ALMLOCK_MASK GENMASK(7, 0)
|
||||
#define SPRD_RTC_ALM_UNLOCK 0xa5
|
||||
#define SPRD_RTC_ALM_LOCK (~SPRD_RTC_ALM_UNLOCK & \
|
||||
SPRD_RTC_ALMLOCK_MASK)
|
||||
|
||||
/* SPG values definition for SPRD_RTC_SPG_UPD register */
|
||||
#define SPRD_RTC_POWEROFF_ALM_FLAG BIT(8)
|
||||
#define SPRD_RTC_POWER_RESET_FLAG BIT(9)
|
||||
|
||||
/* timeout of synchronizing time and alarm registers (us) */
|
||||
#define SPRD_RTC_POLL_TIMEOUT 200000
|
||||
#define SPRD_RTC_POLL_DELAY_US 20000
|
||||
|
||||
struct sprd_rtc {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
struct device *dev;
|
||||
u32 base;
|
||||
int irq;
|
||||
bool valid;
|
||||
};
|
||||
|
||||
/*
|
||||
* The Spreadtrum RTC controller has 3 groups registers, including time, normal
|
||||
* alarm and auxiliary alarm. The time group registers are used to set RTC time,
|
||||
* the normal alarm registers are used to set normal alarm, and the auxiliary
|
||||
* alarm registers are used to set auxiliary alarm. Both alarm event and
|
||||
* auxiliary alarm event can wake up system from deep sleep, but only alarm
|
||||
* event can power up system from power down status.
|
||||
*/
|
||||
enum sprd_rtc_reg_types {
|
||||
SPRD_RTC_TIME,
|
||||
SPRD_RTC_ALARM,
|
||||
SPRD_RTC_AUX_ALARM,
|
||||
};
|
||||
|
||||
static int sprd_rtc_clear_alarm_ints(struct sprd_rtc *rtc)
|
||||
{
|
||||
return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
|
||||
SPRD_RTC_ALM_INT_MASK);
|
||||
}
|
||||
|
||||
static int sprd_rtc_disable_ints(struct sprd_rtc *rtc)
|
||||
{
|
||||
int ret;
|
||||
|
||||
ret = regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN,
|
||||
SPRD_RTC_INT_MASK, 0);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
|
||||
SPRD_RTC_INT_MASK);
|
||||
}
|
||||
|
||||
static int sprd_rtc_lock_alarm(struct sprd_rtc *rtc, bool lock)
|
||||
{
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
val &= ~(SPRD_RTC_ALMLOCK_MASK | SPRD_RTC_POWEROFF_ALM_FLAG);
|
||||
if (lock)
|
||||
val |= SPRD_RTC_ALM_LOCK;
|
||||
else
|
||||
val |= SPRD_RTC_ALM_UNLOCK | SPRD_RTC_POWEROFF_ALM_FLAG;
|
||||
|
||||
ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_SPG_UPD, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* wait until the SPG value is updated successfully */
|
||||
ret = regmap_read_poll_timeout(rtc->regmap,
|
||||
rtc->base + SPRD_RTC_INT_RAW_STS, val,
|
||||
(val & SPRD_RTC_SPG_UPD_EN),
|
||||
SPRD_RTC_POLL_DELAY_US,
|
||||
SPRD_RTC_POLL_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(rtc->dev, "failed to update SPG value:%d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_rtc_get_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type,
|
||||
time64_t *secs)
|
||||
{
|
||||
u32 sec_reg, min_reg, hour_reg, day_reg;
|
||||
u32 val, sec, min, hour, day;
|
||||
int ret;
|
||||
|
||||
switch (type) {
|
||||
case SPRD_RTC_TIME:
|
||||
sec_reg = SPRD_RTC_SEC_CNT_VALUE;
|
||||
min_reg = SPRD_RTC_MIN_CNT_VALUE;
|
||||
hour_reg = SPRD_RTC_HOUR_CNT_VALUE;
|
||||
day_reg = SPRD_RTC_DAY_CNT_VALUE;
|
||||
break;
|
||||
case SPRD_RTC_ALARM:
|
||||
sec_reg = SPRD_RTC_SEC_ALM_VALUE;
|
||||
min_reg = SPRD_RTC_MIN_ALM_VALUE;
|
||||
hour_reg = SPRD_RTC_HOUR_ALM_VALUE;
|
||||
day_reg = SPRD_RTC_DAY_ALM_VALUE;
|
||||
break;
|
||||
case SPRD_RTC_AUX_ALARM:
|
||||
sec_reg = SPRD_RTC_SEC_AUXALM_UPD;
|
||||
min_reg = SPRD_RTC_MIN_AUXALM_UPD;
|
||||
hour_reg = SPRD_RTC_HOUR_AUXALM_UPD;
|
||||
day_reg = SPRD_RTC_DAY_AUXALM_UPD;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + sec_reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
sec = val & SPRD_RTC_SEC_MASK;
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + min_reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
min = val & SPRD_RTC_MIN_MASK;
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + hour_reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
hour = val & SPRD_RTC_HOUR_MASK;
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + day_reg, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
day = val & SPRD_RTC_DAY_MASK;
|
||||
*secs = (((time64_t)(day * 24) + hour) * 60 + min) * 60 + sec;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_rtc_set_secs(struct sprd_rtc *rtc, enum sprd_rtc_reg_types type,
|
||||
time64_t secs)
|
||||
{
|
||||
u32 sec_reg, min_reg, hour_reg, day_reg, sts_mask;
|
||||
u32 sec, min, hour, day, val;
|
||||
int ret, rem;
|
||||
|
||||
/* convert seconds to RTC time format */
|
||||
day = div_s64_rem(secs, 86400, &rem);
|
||||
hour = rem / 3600;
|
||||
rem -= hour * 3600;
|
||||
min = rem / 60;
|
||||
sec = rem - min * 60;
|
||||
|
||||
switch (type) {
|
||||
case SPRD_RTC_TIME:
|
||||
sec_reg = SPRD_RTC_SEC_CNT_UPD;
|
||||
min_reg = SPRD_RTC_MIN_CNT_UPD;
|
||||
hour_reg = SPRD_RTC_HOUR_CNT_UPD;
|
||||
day_reg = SPRD_RTC_DAY_CNT_UPD;
|
||||
sts_mask = SPRD_RTC_TIME_INT_MASK;
|
||||
break;
|
||||
case SPRD_RTC_ALARM:
|
||||
sec_reg = SPRD_RTC_SEC_ALM_UPD;
|
||||
min_reg = SPRD_RTC_MIN_ALM_UPD;
|
||||
hour_reg = SPRD_RTC_HOUR_ALM_UPD;
|
||||
day_reg = SPRD_RTC_DAY_ALM_UPD;
|
||||
sts_mask = SPRD_RTC_ALMTIME_INT_MASK;
|
||||
break;
|
||||
case SPRD_RTC_AUX_ALARM:
|
||||
sec_reg = SPRD_RTC_SEC_AUXALM_UPD;
|
||||
min_reg = SPRD_RTC_MIN_AUXALM_UPD;
|
||||
hour_reg = SPRD_RTC_HOUR_AUXALM_UPD;
|
||||
day_reg = SPRD_RTC_DAY_AUXALM_UPD;
|
||||
sts_mask = 0;
|
||||
break;
|
||||
default:
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = regmap_write(rtc->regmap, rtc->base + sec_reg, sec);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(rtc->regmap, rtc->base + min_reg, min);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(rtc->regmap, rtc->base + hour_reg, hour);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_write(rtc->regmap, rtc->base + day_reg, day);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (type == SPRD_RTC_AUX_ALARM)
|
||||
return 0;
|
||||
|
||||
/*
|
||||
* Since the time and normal alarm registers are put in always-power-on
|
||||
* region supplied by VDDRTC, then these registers changing time will
|
||||
* be very long, about 125ms. Thus here we should wait until all
|
||||
* values are updated successfully.
|
||||
*/
|
||||
ret = regmap_read_poll_timeout(rtc->regmap,
|
||||
rtc->base + SPRD_RTC_INT_RAW_STS, val,
|
||||
((val & sts_mask) == sts_mask),
|
||||
SPRD_RTC_POLL_DELAY_US,
|
||||
SPRD_RTC_POLL_TIMEOUT);
|
||||
if (ret < 0) {
|
||||
dev_err(rtc->dev, "set time/alarm values timeout\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
return regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
|
||||
sts_mask);
|
||||
}
|
||||
|
||||
static int sprd_rtc_read_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct sprd_rtc *rtc = dev_get_drvdata(dev);
|
||||
time64_t secs;
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = sprd_rtc_get_secs(rtc, SPRD_RTC_AUX_ALARM, &secs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtc_time64_to_tm(secs, &alrm->time);
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alrm->enabled = !!(val & SPRD_RTC_AUXALM_EN);
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alrm->pending = !!(val & SPRD_RTC_AUXALM_EN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_rtc_set_aux_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct sprd_rtc *rtc = dev_get_drvdata(dev);
|
||||
time64_t secs = rtc_tm_to_time64(&alrm->time);
|
||||
int ret;
|
||||
|
||||
/* clear the auxiliary alarm interrupt status */
|
||||
ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
|
||||
SPRD_RTC_AUXALM_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprd_rtc_set_secs(rtc, SPRD_RTC_AUX_ALARM, secs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (alrm->enabled) {
|
||||
ret = regmap_update_bits(rtc->regmap,
|
||||
rtc->base + SPRD_RTC_INT_EN,
|
||||
SPRD_RTC_AUXALM_EN,
|
||||
SPRD_RTC_AUXALM_EN);
|
||||
} else {
|
||||
ret = regmap_update_bits(rtc->regmap,
|
||||
rtc->base + SPRD_RTC_INT_EN,
|
||||
SPRD_RTC_AUXALM_EN, 0);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sprd_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct sprd_rtc *rtc = dev_get_drvdata(dev);
|
||||
time64_t secs;
|
||||
int ret;
|
||||
|
||||
if (!rtc->valid) {
|
||||
dev_warn(dev, "RTC values are invalid\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
ret = sprd_rtc_get_secs(rtc, SPRD_RTC_TIME, &secs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtc_time64_to_tm(secs, tm);
|
||||
return rtc_valid_tm(tm);
|
||||
}
|
||||
|
||||
static int sprd_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct sprd_rtc *rtc = dev_get_drvdata(dev);
|
||||
time64_t secs = rtc_tm_to_time64(tm);
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = sprd_rtc_set_secs(rtc, SPRD_RTC_TIME, secs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (!rtc->valid) {
|
||||
/*
|
||||
* Set SPRD_RTC_POWER_RESET_FLAG to indicate now RTC has valid
|
||||
* time values.
|
||||
*/
|
||||
ret = regmap_update_bits(rtc->regmap,
|
||||
rtc->base + SPRD_RTC_SPG_UPD,
|
||||
SPRD_RTC_POWER_RESET_FLAG,
|
||||
SPRD_RTC_POWER_RESET_FLAG);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = regmap_read_poll_timeout(rtc->regmap,
|
||||
rtc->base + SPRD_RTC_INT_RAW_STS,
|
||||
val, (val & SPRD_RTC_SPG_UPD_EN),
|
||||
SPRD_RTC_POLL_DELAY_US,
|
||||
SPRD_RTC_POLL_TIMEOUT);
|
||||
if (ret) {
|
||||
dev_err(rtc->dev, "failed to update SPG value:%d\n",
|
||||
ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rtc->valid = true;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct sprd_rtc *rtc = dev_get_drvdata(dev);
|
||||
time64_t secs;
|
||||
int ret;
|
||||
u32 val;
|
||||
|
||||
/*
|
||||
* If aie_timer is enabled, we should get the normal alarm time.
|
||||
* Otherwise we should get auxiliary alarm time.
|
||||
*/
|
||||
if (rtc->rtc && rtc->rtc->aie_timer.enabled == 0)
|
||||
return sprd_rtc_read_aux_alarm(dev, alrm);
|
||||
|
||||
ret = sprd_rtc_get_secs(rtc, SPRD_RTC_ALARM, &secs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
rtc_time64_to_tm(secs, &alrm->time);
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_EN, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alrm->enabled = !!(val & SPRD_RTC_ALARM_EN);
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_INT_RAW_STS, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
alrm->pending = !!(val & SPRD_RTC_ALARM_EN);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct sprd_rtc *rtc = dev_get_drvdata(dev);
|
||||
time64_t secs = rtc_tm_to_time64(&alrm->time);
|
||||
struct rtc_time aie_time =
|
||||
rtc_ktime_to_tm(rtc->rtc->aie_timer.node.expires);
|
||||
int ret;
|
||||
|
||||
/*
|
||||
* We have 2 groups alarms: normal alarm and auxiliary alarm. Since
|
||||
* both normal alarm event and auxiliary alarm event can wake up system
|
||||
* from deep sleep, but only alarm event can power up system from power
|
||||
* down status. Moreover we do not need to poll about 125ms when
|
||||
* updating auxiliary alarm registers. Thus we usually set auxiliary
|
||||
* alarm when wake up system from deep sleep, and for other scenarios,
|
||||
* we should set normal alarm with polling status.
|
||||
*
|
||||
* So here we check if the alarm time is set by aie_timer, if yes, we
|
||||
* should set normal alarm, if not, we should set auxiliary alarm which
|
||||
* means it is just a wake event.
|
||||
*/
|
||||
if (!rtc->rtc->aie_timer.enabled || rtc_tm_sub(&aie_time, &alrm->time))
|
||||
return sprd_rtc_set_aux_alarm(dev, alrm);
|
||||
|
||||
/* clear the alarm interrupt status firstly */
|
||||
ret = regmap_write(rtc->regmap, rtc->base + SPRD_RTC_INT_CLR,
|
||||
SPRD_RTC_ALARM_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprd_rtc_set_secs(rtc, SPRD_RTC_ALARM, secs);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (alrm->enabled) {
|
||||
ret = regmap_update_bits(rtc->regmap,
|
||||
rtc->base + SPRD_RTC_INT_EN,
|
||||
SPRD_RTC_ALARM_EN,
|
||||
SPRD_RTC_ALARM_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* unlock the alarm to enable the alarm function. */
|
||||
ret = sprd_rtc_lock_alarm(rtc, false);
|
||||
} else {
|
||||
regmap_update_bits(rtc->regmap,
|
||||
rtc->base + SPRD_RTC_INT_EN,
|
||||
SPRD_RTC_ALARM_EN, 0);
|
||||
|
||||
/*
|
||||
* Lock the alarm function in case fake alarm event will power
|
||||
* up systems.
|
||||
*/
|
||||
ret = sprd_rtc_lock_alarm(rtc, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static int sprd_rtc_alarm_irq_enable(struct device *dev, unsigned int enabled)
|
||||
{
|
||||
struct sprd_rtc *rtc = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
|
||||
if (enabled) {
|
||||
ret = regmap_update_bits(rtc->regmap,
|
||||
rtc->base + SPRD_RTC_INT_EN,
|
||||
SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN,
|
||||
SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
ret = sprd_rtc_lock_alarm(rtc, false);
|
||||
} else {
|
||||
regmap_update_bits(rtc->regmap, rtc->base + SPRD_RTC_INT_EN,
|
||||
SPRD_RTC_ALARM_EN | SPRD_RTC_AUXALM_EN, 0);
|
||||
|
||||
ret = sprd_rtc_lock_alarm(rtc, true);
|
||||
}
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops sprd_rtc_ops = {
|
||||
.read_time = sprd_rtc_read_time,
|
||||
.set_time = sprd_rtc_set_time,
|
||||
.read_alarm = sprd_rtc_read_alarm,
|
||||
.set_alarm = sprd_rtc_set_alarm,
|
||||
.alarm_irq_enable = sprd_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static irqreturn_t sprd_rtc_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct sprd_rtc *rtc = dev_id;
|
||||
int ret;
|
||||
|
||||
ret = sprd_rtc_clear_alarm_ints(rtc);
|
||||
if (ret)
|
||||
return IRQ_RETVAL(ret);
|
||||
|
||||
rtc_update_irq(rtc->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int sprd_rtc_check_power_down(struct sprd_rtc *rtc)
|
||||
{
|
||||
u32 val;
|
||||
int ret;
|
||||
|
||||
ret = regmap_read(rtc->regmap, rtc->base + SPRD_RTC_SPG_VALUE, &val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/*
|
||||
* If the SPRD_RTC_POWER_RESET_FLAG was not set, which means the RTC has
|
||||
* been powered down, so the RTC time values are invalid.
|
||||
*/
|
||||
rtc->valid = (val & SPRD_RTC_POWER_RESET_FLAG) ? true : false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *node = pdev->dev.of_node;
|
||||
struct sprd_rtc *rtc;
|
||||
int ret;
|
||||
|
||||
rtc = devm_kzalloc(&pdev->dev, sizeof(*rtc), GFP_KERNEL);
|
||||
if (!rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
rtc->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!rtc->regmap)
|
||||
return -ENODEV;
|
||||
|
||||
ret = of_property_read_u32(node, "reg", &rtc->base);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to get RTC base address\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
rtc->irq = platform_get_irq(pdev, 0);
|
||||
if (rtc->irq < 0) {
|
||||
dev_err(&pdev->dev, "failed to get RTC irq number\n");
|
||||
return rtc->irq;
|
||||
}
|
||||
|
||||
rtc->dev = &pdev->dev;
|
||||
platform_set_drvdata(pdev, rtc);
|
||||
|
||||
/* clear all RTC interrupts and disable all RTC interrupts */
|
||||
ret = sprd_rtc_disable_ints(rtc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to disable RTC interrupts\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
/* check if RTC time values are valid */
|
||||
ret = sprd_rtc_check_power_down(rtc);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to check RTC time values\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_request_threaded_irq(&pdev->dev, rtc->irq, NULL,
|
||||
sprd_rtc_handler,
|
||||
IRQF_ONESHOT | IRQF_EARLY_RESUME,
|
||||
pdev->name, rtc);
|
||||
if (ret < 0) {
|
||||
dev_err(&pdev->dev, "failed to request RTC irq\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||
&sprd_rtc_ops, THIS_MODULE);
|
||||
if (IS_ERR(rtc->rtc))
|
||||
return PTR_ERR(rtc->rtc);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int sprd_rtc_remove(struct platform_device *pdev)
|
||||
{
|
||||
device_init_wakeup(&pdev->dev, 0);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct of_device_id sprd_rtc_of_match[] = {
|
||||
{ .compatible = "sprd,sc2731-rtc", },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, sprd_rtc_of_match);
|
||||
|
||||
static struct platform_driver sprd_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "sprd-rtc",
|
||||
.of_match_table = sprd_rtc_of_match,
|
||||
},
|
||||
.probe = sprd_rtc_probe,
|
||||
.remove = sprd_rtc_remove,
|
||||
};
|
||||
module_platform_driver(sprd_rtc_driver);
|
||||
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_DESCRIPTION("Spreadtrum RTC Device Driver");
|
||||
MODULE_AUTHOR("Baolin Wang <baolin.wang@spreadtrum.com>");
|
|
@ -72,9 +72,10 @@ since_epoch_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
|
||||
retval = rtc_read_time(to_rtc_device(dev), &tm);
|
||||
if (retval == 0) {
|
||||
unsigned long time;
|
||||
rtc_tm_to_time(&tm, &time);
|
||||
retval = sprintf(buf, "%lu\n", time);
|
||||
time64_t time;
|
||||
|
||||
time = rtc_tm_to_time64(&tm);
|
||||
retval = sprintf(buf, "%lld\n", time);
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
@ -132,7 +133,7 @@ static ssize_t
|
|||
wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf)
|
||||
{
|
||||
ssize_t retval;
|
||||
unsigned long alarm;
|
||||
time64_t alarm;
|
||||
struct rtc_wkalrm alm;
|
||||
|
||||
/* Don't show disabled alarms. For uniformity, RTC alarms are
|
||||
|
@ -145,8 +146,8 @@ wakealarm_show(struct device *dev, struct device_attribute *attr, char *buf)
|
|||
*/
|
||||
retval = rtc_read_alarm(to_rtc_device(dev), &alm);
|
||||
if (retval == 0 && alm.enabled) {
|
||||
rtc_tm_to_time(&alm.time, &alarm);
|
||||
retval = sprintf(buf, "%lu\n", alarm);
|
||||
alarm = rtc_tm_to_time64(&alm.time);
|
||||
retval = sprintf(buf, "%lld\n", alarm);
|
||||
}
|
||||
|
||||
return retval;
|
||||
|
@ -157,8 +158,8 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
|
|||
const char *buf, size_t n)
|
||||
{
|
||||
ssize_t retval;
|
||||
unsigned long now, alarm;
|
||||
unsigned long push = 0;
|
||||
time64_t now, alarm;
|
||||
time64_t push = 0;
|
||||
struct rtc_wkalrm alm;
|
||||
struct rtc_device *rtc = to_rtc_device(dev);
|
||||
const char *buf_ptr;
|
||||
|
@ -170,7 +171,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
|
|||
retval = rtc_read_time(rtc, &alm.time);
|
||||
if (retval < 0)
|
||||
return retval;
|
||||
rtc_tm_to_time(&alm.time, &now);
|
||||
now = rtc_tm_to_time64(&alm.time);
|
||||
|
||||
buf_ptr = buf;
|
||||
if (*buf_ptr == '+') {
|
||||
|
@ -181,7 +182,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
|
|||
} else
|
||||
adjust = 1;
|
||||
}
|
||||
retval = kstrtoul(buf_ptr, 0, &alarm);
|
||||
retval = kstrtos64(buf_ptr, 0, &alarm);
|
||||
if (retval)
|
||||
return retval;
|
||||
if (adjust) {
|
||||
|
@ -197,7 +198,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
|
|||
return retval;
|
||||
if (alm.enabled) {
|
||||
if (push) {
|
||||
rtc_tm_to_time(&alm.time, &push);
|
||||
push = rtc_tm_to_time64(&alm.time);
|
||||
alarm += push;
|
||||
} else
|
||||
return -EBUSY;
|
||||
|
@ -212,7 +213,7 @@ wakealarm_store(struct device *dev, struct device_attribute *attr,
|
|||
*/
|
||||
alarm = now + 300;
|
||||
}
|
||||
rtc_time_to_tm(alarm, &alm.time);
|
||||
rtc_time64_to_tm(alarm, &alm.time);
|
||||
|
||||
retval = rtc_set_alarm(rtc, &alm);
|
||||
return (retval < 0) ? retval : n;
|
||||
|
|
|
@ -52,6 +52,7 @@ struct xgene_rtc_dev {
|
|||
void __iomem *csr_base;
|
||||
struct clk *clk;
|
||||
unsigned int irq_wake;
|
||||
unsigned int irq_enabled;
|
||||
};
|
||||
|
||||
static int xgene_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
|
@ -104,15 +105,19 @@ static int xgene_rtc_alarm_irq_enable(struct device *dev, u32 enabled)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_rtc_alarm_irq_enabled(struct device *dev)
|
||||
{
|
||||
struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
|
||||
|
||||
return readl(pdata->csr_base + RTC_CCR) & RTC_CCR_IE ? 1 : 0;
|
||||
}
|
||||
|
||||
static int xgene_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct xgene_rtc_dev *pdata = dev_get_drvdata(dev);
|
||||
unsigned long rtc_time;
|
||||
unsigned long alarm_time;
|
||||
|
||||
rtc_time = readl(pdata->csr_base + RTC_CCVR);
|
||||
rtc_tm_to_time(&alrm->time, &alarm_time);
|
||||
|
||||
pdata->alarm_time = alarm_time;
|
||||
writel((u32) pdata->alarm_time, pdata->csr_base + RTC_CMR);
|
||||
|
||||
|
@ -180,12 +185,18 @@ static int xgene_rtc_probe(struct platform_device *pdev)
|
|||
dev_err(&pdev->dev, "Couldn't get the clock for RTC\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
clk_prepare_enable(pdata->clk);
|
||||
ret = clk_prepare_enable(pdata->clk);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
/* Turn on the clock and the crystal */
|
||||
writel(RTC_CCR_EN, pdata->csr_base + RTC_CCR);
|
||||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
ret = device_init_wakeup(&pdev->dev, 1);
|
||||
if (ret) {
|
||||
clk_disable_unprepare(pdata->clk);
|
||||
return ret;
|
||||
}
|
||||
|
||||
pdata->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||
&xgene_rtc_ops, THIS_MODULE);
|
||||
|
@ -210,45 +221,55 @@ static int xgene_rtc_remove(struct platform_device *pdev)
|
|||
return 0;
|
||||
}
|
||||
|
||||
#ifdef CONFIG_PM_SLEEP
|
||||
static int xgene_rtc_suspend(struct device *dev)
|
||||
static int __maybe_unused xgene_rtc_suspend(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
|
||||
int irq;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
||||
/*
|
||||
* If this RTC alarm will be used for waking the system up,
|
||||
* don't disable it of course. Else we just disable the alarm
|
||||
* and await suspension.
|
||||
*/
|
||||
if (device_may_wakeup(&pdev->dev)) {
|
||||
if (!enable_irq_wake(irq))
|
||||
pdata->irq_wake = 1;
|
||||
} else {
|
||||
pdata->irq_enabled = xgene_rtc_alarm_irq_enabled(dev);
|
||||
xgene_rtc_alarm_irq_enable(dev, 0);
|
||||
clk_disable(pdata->clk);
|
||||
clk_disable_unprepare(pdata->clk);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int xgene_rtc_resume(struct device *dev)
|
||||
static int __maybe_unused xgene_rtc_resume(struct device *dev)
|
||||
{
|
||||
struct platform_device *pdev = to_platform_device(dev);
|
||||
struct xgene_rtc_dev *pdata = platform_get_drvdata(pdev);
|
||||
int irq;
|
||||
int rc;
|
||||
|
||||
irq = platform_get_irq(pdev, 0);
|
||||
|
||||
if (device_may_wakeup(&pdev->dev)) {
|
||||
if (pdata->irq_wake) {
|
||||
disable_irq_wake(irq);
|
||||
pdata->irq_wake = 0;
|
||||
}
|
||||
} else {
|
||||
clk_enable(pdata->clk);
|
||||
xgene_rtc_alarm_irq_enable(dev, 1);
|
||||
rc = clk_prepare_enable(pdata->clk);
|
||||
if (rc) {
|
||||
dev_err(dev, "Unable to enable clock error %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
xgene_rtc_alarm_irq_enable(dev, pdata->irq_enabled);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
#endif
|
||||
|
||||
static SIMPLE_DEV_PM_OPS(xgene_rtc_pm_ops, xgene_rtc_suspend, xgene_rtc_resume);
|
||||
|
||||
|
|
Loading…
Reference in New Issue
Block a user