RTC for 6.3
Subsystem: - allow rtc_read_alarm without read_alarm callback New driver: - NXP BBNSM module RTC Drivers: - use IRQ flags from fwnode when available - abx80x: nvmem support - brcmstb-waketimer: add non-wake alarm support - ingenic: provide CLK32K clock - isl12022: cleanups - moxart: switch to using gpiod API - pcf85363: allow setting quartz load - pm8xxx: cleanups and support for setting time - rv3028, rv3032: add ACPI support -----BEGIN PGP SIGNATURE----- iQIzBAABCgAdFiEEBqsFVZXh8s/0O5JiY6TcMGxwOjIFAmQBNu0ACgkQY6TcMGxw OjLXoA//S8YQQLqAOHPdmEmiVHEoYQv+VYwYrnK8of3QTDOJizboDRgRSSMgNNBj ViqDCLUUDnmPd/91BiTVgKdfmjY2gqR3coyu5XcEKDNRoLCeLjrKENtrmrjwOvKW gdbvs3oXsUtWC6JY0vuEABCn0DNxg7I7pvT57Ykaun2mhASo6LeTwGU5Jwo27GtY /MEY7Av+VMOvNWXsjRi9qaIms9DtX8LbXSDgkYoVy8QH97iW9iTVZu2tCgFAR9Fl HrLzVslnOg+ae8sBJ7rAY57xD+ZRT/P8ZnVlgUs/oQ9DxJ15rmjifzJA6SurcVea HmNrVzdyiNmTY4Vk9/M/pAADwwz0L5sxXMnbemLcl97MHIZulhAz/4zWMEwZixCi Of+ROqt1tahk69d3QRry4zlWp5dGfn07+DDD9aVFV2YqmAmBSlUkru65tse10gem QWVcrmURMQoBjrgXkWki3+6NjvqlBTrJa7qY8KAB3iGpDF/yAUwYm8rBVYxGAiUz NaoiFKgwbVxE67SSP6OFURTWi+lGyS+kmaTVbzfTfWDI8Tf++U4xbMkDzhyvL4A2 HZD3HbTghdnbVfysTI9Fxtk1mFbO3EDuMRK5YpLcx7gZAn+S7vUNRU0oFPxAR/aX BhZAWSQvZJWewPuTBSfoSSPFCEEiCSqOVupHmMr5J4H1lgtZAXo= =aZkd -----END PGP SIGNATURE----- Merge tag 'rtc-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux Pull RTC updates from Alexandre Belloni: "A few drivers got some nice cleanups and a new driver are making the bulk of the changes. Subsystem: - allow rtc_read_alarm without read_alarm callback New driver: - NXP BBNSM module RTC Drivers: - use IRQ flags from fwnode when available - abx80x: nvmem support - brcmstb-waketimer: add non-wake alarm support - ingenic: provide CLK32K clock - isl12022: cleanups - moxart: switch to using gpiod API - pcf85363: allow setting quartz load - pm8xxx: cleanups and support for setting time - rv3028, rv3032: add ACPI support" * tag 'rtc-6.3' of git://git.kernel.org/pub/scm/linux/kernel/git/abelloni/linux: (64 commits) rtc: pm8xxx: add support for nvmem offset dt-bindings: rtc: qcom-pm8xxx: add nvmem-cell offset rtc: abx80x: Add nvmem support rtc: rx6110: Remove unused of_gpio,h rtc: efi: Avoid spamming the log on RTC read failure rtc: isl12022: sort header inclusion alphabetically rtc: isl12022: Join string literals back rtc: isl12022: Drop unneeded OF guards and of_match_ptr() rtc: isl12022: Explicitly use __le16 type for ISL12022_REG_TEMP_L rtc: isl12022: Get rid of unneeded private struct isl12022 rtc: pcf85363: add support for the quartz-load-femtofarads property dt-bindings: rtc: nxp,pcf8563: move pcf85263/pcf85363 to a dedicated binding rtc: allow rtc_read_alarm without read_alarm callback rtc: rv3032: add ACPI support rtc: rv3028: add ACPI support rtc: bbnsm: Add the bbnsm rtc support rtc: jz4740: Register clock provider for the CLK32K pin rtc: jz4740: Use dev_err_probe() rtc: jz4740: Use readl_poll_timeout dt-bindings: rtc: Add #clock-cells property ...
This commit is contained in:
commit
271d89394e
|
@ -0,0 +1,44 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/amlogic,meson-vrtc.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Amlogic Virtual RTC (VRTC)
|
||||
|
||||
maintainers:
|
||||
- Neil Armstrong <neil.armstrong@linaro.org>
|
||||
|
||||
description: |
|
||||
This is a Linux interface to an RTC managed by firmware, hence it's
|
||||
virtual from a Linux perspective. The interface is 1 register where
|
||||
an alarm time (in seconds) is to be written.
|
||||
The alarm register is a simple scratch register shared between the
|
||||
application processors (AP) and the secure co-processor (SCP.) When
|
||||
the AP suspends, the SCP will use the value of this register to
|
||||
program an always-on timer before going sleep. When the timer expires,
|
||||
the SCP will wake up and will then wake the AP.
|
||||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- amlogic,meson-vrtc
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
rtc@a8 {
|
||||
compatible = "amlogic,meson-vrtc";
|
||||
reg = <0x000a8 0x4>;
|
||||
};
|
|
@ -11,7 +11,8 @@ maintainers:
|
|||
|
||||
description:
|
||||
The Broadcom STB wake-up timer provides a 27Mhz resolution timer, with the
|
||||
ability to wake up the system from low-power suspend/standby modes.
|
||||
ability to wake up the system from low-power suspend/standby modes and
|
||||
optionally generate RTC alarm interrupts.
|
||||
|
||||
allOf:
|
||||
- $ref: "rtc.yaml#"
|
||||
|
@ -24,8 +25,14 @@ properties:
|
|||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
description: the TIMER interrupt
|
||||
maxItems: 1
|
||||
minItems: 1
|
||||
items:
|
||||
- description: the TIMER interrupt
|
||||
- description: the ALARM interrupt
|
||||
description:
|
||||
The TIMER interrupt wakes the system from low-power suspend/standby modes.
|
||||
An ALARM interrupt may be specified to interrupt the CPU when an RTC alarm
|
||||
is enabled.
|
||||
|
||||
clocks:
|
||||
description: clock reference in the 27MHz domain
|
||||
|
@ -35,10 +42,10 @@ additionalProperties: false
|
|||
|
||||
examples:
|
||||
- |
|
||||
rtc@f0411580 {
|
||||
rtc@f041a080 {
|
||||
compatible = "brcm,brcmstb-waketimer";
|
||||
reg = <0xf0411580 0x14>;
|
||||
interrupts = <0x3>;
|
||||
interrupt-parent = <&aon_pm_l2_intc>;
|
||||
reg = <0xf041a080 0x14>;
|
||||
interrupts-extended = <&aon_pm_l2_intc 0x04>,
|
||||
<&upg_aux_aon_intr2_intc 0x08>;
|
||||
clocks = <&upg_fixed>;
|
||||
};
|
||||
|
|
|
@ -11,6 +11,17 @@ maintainers:
|
|||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
- if:
|
||||
not:
|
||||
properties:
|
||||
compatible:
|
||||
contains:
|
||||
enum:
|
||||
- ingenic,jz4770-rtc
|
||||
- ingenic,jz4780-rtc
|
||||
then:
|
||||
properties:
|
||||
"#clock-cells": false
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
|
@ -39,6 +50,9 @@ properties:
|
|||
clock-names:
|
||||
const: rtc
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
system-power-controller:
|
||||
description: |
|
||||
Indicates that the RTC is responsible for powering OFF
|
||||
|
@ -83,3 +97,18 @@ examples:
|
|||
clocks = <&cgu JZ4740_CLK_RTC>;
|
||||
clock-names = "rtc";
|
||||
};
|
||||
|
||||
- |
|
||||
#include <dt-bindings/clock/ingenic,jz4780-cgu.h>
|
||||
rtc: rtc@10003000 {
|
||||
compatible = "ingenic,jz4780-rtc", "ingenic,jz4760-rtc";
|
||||
reg = <0x10003000 0x4c>;
|
||||
|
||||
interrupt-parent = <&intc>;
|
||||
interrupts = <32>;
|
||||
|
||||
clocks = <&cgu JZ4780_CLK_RTCLK>;
|
||||
clock-names = "rtc";
|
||||
|
||||
#clock-cells = <0>;
|
||||
};
|
||||
|
|
|
@ -0,0 +1,54 @@
|
|||
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/microcrystal,rv3028.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Microchip RV-3028 RTC
|
||||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
|
||||
maintainers:
|
||||
- Alexandre Belloni <alexandre.belloni@bootlin.com>
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
const: microcrystal,rv3028
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
trickle-resistor-ohms:
|
||||
enum:
|
||||
- 3000
|
||||
- 5000
|
||||
- 9000
|
||||
- 15000
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
unevaluatedProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
#include <dt-bindings/interrupt-controller/irq.h>
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@51 {
|
||||
compatible = "microcrystal,rv3028";
|
||||
reg = <0x51>;
|
||||
pinctrl-0 = <&rtc_nint_pins>;
|
||||
interrupts-extended = <&gpio1 16 IRQ_TYPE_LEVEL_HIGH>;
|
||||
trickle-resistor-ohms = <3000>;
|
||||
};
|
||||
};
|
||||
|
||||
...
|
|
@ -3,15 +3,15 @@ MOXA ART real-time clock
|
|||
Required properties:
|
||||
|
||||
- compatible : Should be "moxa,moxart-rtc"
|
||||
- gpio-rtc-sclk : RTC sclk gpio, with zero flags
|
||||
- gpio-rtc-data : RTC data gpio, with zero flags
|
||||
- gpio-rtc-reset : RTC reset gpio, with zero flags
|
||||
- rtc-sclk-gpios : RTC sclk gpio, with zero flags
|
||||
- rtc-data-gpios : RTC data gpio, with zero flags
|
||||
- rtc-reset-gpios : RTC reset gpio, with zero flags
|
||||
|
||||
Example:
|
||||
|
||||
rtc: rtc {
|
||||
compatible = "moxa,moxart-rtc";
|
||||
gpio-rtc-sclk = <&gpio 5 0>;
|
||||
gpio-rtc-data = <&gpio 6 0>;
|
||||
gpio-rtc-reset = <&gpio 7 0>;
|
||||
rtc-sclk-gpios = <&gpio 5 0>;
|
||||
rtc-data-gpios = <&gpio 6 0>;
|
||||
rtc-reset-gpios = <&gpio 7 0>;
|
||||
};
|
||||
|
|
|
@ -14,7 +14,10 @@ maintainers:
|
|||
|
||||
properties:
|
||||
compatible:
|
||||
const: nxp,pcf2127
|
||||
enum:
|
||||
- nxp,pca2129
|
||||
- nxp,pcf2127
|
||||
- nxp,pcf2129
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
# SPDX-License-Identifier: (GPL-2.0-only OR BSD-2-Clause)
|
||||
%YAML 1.2
|
||||
---
|
||||
$id: http://devicetree.org/schemas/rtc/nxp,pcf85363.yaml#
|
||||
$schema: http://devicetree.org/meta-schemas/core.yaml#
|
||||
|
||||
title: Philips PCF85263/PCF85363 Real Time Clock
|
||||
|
||||
maintainers:
|
||||
- Alexandre Belloni <alexandre.belloni@bootlin.com>
|
||||
|
||||
allOf:
|
||||
- $ref: rtc.yaml#
|
||||
|
||||
properties:
|
||||
compatible:
|
||||
enum:
|
||||
- nxp,pcf85263
|
||||
- nxp,pcf85363
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
||||
"#clock-cells":
|
||||
const: 0
|
||||
|
||||
clock-output-names:
|
||||
maxItems: 1
|
||||
|
||||
interrupts:
|
||||
maxItems: 1
|
||||
|
||||
quartz-load-femtofarads:
|
||||
description:
|
||||
The capacitive load of the quartz(x-tal).
|
||||
enum: [6000, 7000, 12500]
|
||||
default: 7000
|
||||
|
||||
start-year: true
|
||||
wakeup-source: true
|
||||
|
||||
required:
|
||||
- compatible
|
||||
- reg
|
||||
|
||||
additionalProperties: false
|
||||
|
||||
examples:
|
||||
- |
|
||||
i2c {
|
||||
#address-cells = <1>;
|
||||
#size-cells = <0>;
|
||||
|
||||
rtc@51 {
|
||||
compatible = "nxp,pcf85363";
|
||||
reg = <0x51>;
|
||||
#clock-cells = <0>;
|
||||
quartz-load-femtofarads = <12500>;
|
||||
};
|
||||
};
|
|
@ -19,8 +19,6 @@ properties:
|
|||
- microcrystal,rv8564
|
||||
- nxp,pca8565
|
||||
- nxp,pcf8563
|
||||
- nxp,pcf85263
|
||||
- nxp,pcf85363
|
||||
|
||||
reg:
|
||||
maxItems: 1
|
||||
|
|
|
@ -40,6 +40,16 @@ properties:
|
|||
description:
|
||||
Indicates that the setting of RTC time is allowed by the host CPU.
|
||||
|
||||
nvmem-cells:
|
||||
items:
|
||||
- description:
|
||||
four-byte nvmem cell holding a little-endian offset from the Unix
|
||||
epoch representing the time when the RTC timer was last reset
|
||||
|
||||
nvmem-cell-names:
|
||||
items:
|
||||
- const: offset
|
||||
|
||||
wakeup-source: true
|
||||
|
||||
required:
|
||||
|
@ -69,6 +79,8 @@ examples:
|
|||
compatible = "qcom,pm8921-rtc";
|
||||
reg = <0x11d>;
|
||||
interrupts = <0x27 0>;
|
||||
nvmem-cells = <&rtc_offset>;
|
||||
nvmem-cell-names = "offset";
|
||||
};
|
||||
};
|
||||
};
|
||||
|
|
|
@ -1,22 +0,0 @@
|
|||
* Amlogic Virtual RTC (VRTC)
|
||||
|
||||
This is a Linux interface to an RTC managed by firmware, hence it's
|
||||
virtual from a Linux perspective. The interface is 1 register where
|
||||
an alarm time (in seconds) is to be written.
|
||||
|
||||
Required properties:
|
||||
- compatible: should be "amlogic,meson-vrtc"
|
||||
- reg: physical address for the alarm register
|
||||
|
||||
The alarm register is a simple scratch register shared between the
|
||||
application processors (AP) and the secure co-processor (SCP.) When
|
||||
the AP suspends, the SCP will use the value of this register to
|
||||
program an always-on timer before going sleep. When the timer expires,
|
||||
the SCP will wake up and will then wake the AP.
|
||||
|
||||
Example:
|
||||
|
||||
vrtc: rtc@0a8 {
|
||||
compatible = "amlogic,meson-vrtc";
|
||||
reg = <0x0 0x000a8 0x0 0x4>;
|
||||
};
|
|
@ -47,14 +47,12 @@ properties:
|
|||
- isil,isl1218
|
||||
# Intersil ISL12022 Real-time Clock
|
||||
- isil,isl12022
|
||||
# Real Time Clock Module with I2C-Bus
|
||||
- microcrystal,rv3028
|
||||
# Loongson-2K Socs/LS7A bridge Real-time Clock
|
||||
- loongson,ls2x-rtc
|
||||
# Real Time Clock Module with I2C-Bus
|
||||
- microcrystal,rv3029
|
||||
# Real Time Clock
|
||||
- microcrystal,rv8523
|
||||
- nxp,pca2129
|
||||
- nxp,pcf2129
|
||||
# Real-time Clock Module
|
||||
- pericom,pt7c4338
|
||||
# I2C bus SERIAL INTERFACE REAL-TIME CLOCK IC
|
||||
|
|
|
@ -1677,7 +1677,7 @@ config RTC_DRV_MPC5121
|
|||
config RTC_DRV_JZ4740
|
||||
tristate "Ingenic JZ4740 SoC"
|
||||
depends on MIPS || COMPILE_TEST
|
||||
depends on OF
|
||||
depends on OF && COMMON_CLK
|
||||
help
|
||||
If you say yes here you get support for the Ingenic JZ47xx SoCs RTC
|
||||
controllers.
|
||||
|
@ -1773,6 +1773,18 @@ config RTC_DRV_SNVS
|
|||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-snvs".
|
||||
|
||||
config RTC_DRV_BBNSM
|
||||
tristate "NXP BBNSM RTC support"
|
||||
select REGMAP_MMIO
|
||||
depends on ARCH_MXC || COMPILE_TEST
|
||||
depends on HAS_IOMEM
|
||||
depends on OF
|
||||
help
|
||||
If you say yes here you get support for the NXP BBNSM RTC module.
|
||||
|
||||
This driver can also be built as a module, if so, the module
|
||||
will be called "rtc-bbnsm".
|
||||
|
||||
config RTC_DRV_IMX_SC
|
||||
depends on IMX_SCU
|
||||
depends on HAVE_ARM_SMCCC
|
||||
|
|
|
@ -33,6 +33,7 @@ obj-$(CONFIG_RTC_DRV_ASPEED) += rtc-aspeed.o
|
|||
obj-$(CONFIG_RTC_DRV_AT91RM9200)+= rtc-at91rm9200.o
|
||||
obj-$(CONFIG_RTC_DRV_AT91SAM9) += rtc-at91sam9.o
|
||||
obj-$(CONFIG_RTC_DRV_AU1XXX) += rtc-au1xxx.o
|
||||
obj-$(CONFIG_RTC_DRV_BBNSM) += rtc-nxp-bbnsm.o
|
||||
obj-$(CONFIG_RTC_DRV_BD70528) += rtc-bd70528.o
|
||||
obj-$(CONFIG_RTC_DRV_BQ32K) += rtc-bq32k.o
|
||||
obj-$(CONFIG_RTC_DRV_BQ4802) += rtc-bq4802.o
|
||||
|
|
|
@ -392,7 +392,7 @@ int rtc_read_alarm(struct rtc_device *rtc, struct rtc_wkalrm *alarm)
|
|||
return err;
|
||||
if (!rtc->ops) {
|
||||
err = -ENODEV;
|
||||
} else if (!test_bit(RTC_FEATURE_ALARM, rtc->features) || !rtc->ops->read_alarm) {
|
||||
} else if (!test_bit(RTC_FEATURE_ALARM, rtc->features)) {
|
||||
err = -EINVAL;
|
||||
} else {
|
||||
memset(alarm, 0, sizeof(struct rtc_wkalrm));
|
||||
|
|
|
@ -536,9 +536,14 @@ static int abeoz9_probe(struct i2c_client *client)
|
|||
clear_bit(RTC_FEATURE_ALARM, data->rtc->features);
|
||||
|
||||
if (client->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
ret = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
abeoz9_rtc_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
dev_name(dev), dev);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to request alarm irq\n");
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
*/
|
||||
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/bitfield.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/kstrtox.h>
|
||||
#include <linux/module.h>
|
||||
|
@ -88,6 +89,16 @@
|
|||
#define ABX8XX_TRICKLE_STANDARD_DIODE 0x8
|
||||
#define ABX8XX_TRICKLE_SCHOTTKY_DIODE 0x4
|
||||
|
||||
#define ABX8XX_REG_EXTRAM 0x3f
|
||||
#define ABX8XX_EXTRAM_XADS GENMASK(1, 0)
|
||||
|
||||
#define ABX8XX_SRAM_BASE 0x40
|
||||
#define ABX8XX_SRAM_WIN_SIZE 0x40
|
||||
#define ABX8XX_RAM_SIZE 256
|
||||
|
||||
#define NVMEM_ADDR_LOWER GENMASK(5, 0)
|
||||
#define NVMEM_ADDR_UPPER GENMASK(7, 6)
|
||||
|
||||
static u8 trickle_resistors[] = {0, 3, 6, 11};
|
||||
|
||||
enum abx80x_chip {AB0801, AB0803, AB0804, AB0805,
|
||||
|
@ -674,6 +685,68 @@ static int abx80x_setup_watchdog(struct abx80x_priv *priv)
|
|||
}
|
||||
#endif
|
||||
|
||||
static int abx80x_nvmem_xfer(struct abx80x_priv *priv, unsigned int offset,
|
||||
void *val, size_t bytes, bool write)
|
||||
{
|
||||
int ret;
|
||||
|
||||
while (bytes) {
|
||||
u8 extram, reg, len, lower, upper;
|
||||
|
||||
lower = FIELD_GET(NVMEM_ADDR_LOWER, offset);
|
||||
upper = FIELD_GET(NVMEM_ADDR_UPPER, offset);
|
||||
extram = FIELD_PREP(ABX8XX_EXTRAM_XADS, upper);
|
||||
reg = ABX8XX_SRAM_BASE + lower;
|
||||
len = min(lower + bytes, (size_t)ABX8XX_SRAM_WIN_SIZE) - lower;
|
||||
len = min_t(u8, len, I2C_SMBUS_BLOCK_MAX);
|
||||
|
||||
ret = i2c_smbus_write_byte_data(priv->client, ABX8XX_REG_EXTRAM,
|
||||
extram);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
if (write)
|
||||
ret = i2c_smbus_write_i2c_block_data(priv->client, reg,
|
||||
len, val);
|
||||
else
|
||||
ret = i2c_smbus_read_i2c_block_data(priv->client, reg,
|
||||
len, val);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
||||
offset += len;
|
||||
val += len;
|
||||
bytes -= len;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int abx80x_nvmem_read(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
return abx80x_nvmem_xfer(priv, offset, val, bytes, false);
|
||||
}
|
||||
|
||||
static int abx80x_nvmem_write(void *priv, unsigned int offset, void *val,
|
||||
size_t bytes)
|
||||
{
|
||||
return abx80x_nvmem_xfer(priv, offset, val, bytes, true);
|
||||
}
|
||||
|
||||
static int abx80x_setup_nvmem(struct abx80x_priv *priv)
|
||||
{
|
||||
struct nvmem_config config = {
|
||||
.type = NVMEM_TYPE_BATTERY_BACKED,
|
||||
.reg_read = abx80x_nvmem_read,
|
||||
.reg_write = abx80x_nvmem_write,
|
||||
.size = ABX8XX_RAM_SIZE,
|
||||
.priv = priv,
|
||||
};
|
||||
|
||||
return devm_rtc_nvmem_register(priv->rtc, &config);
|
||||
}
|
||||
|
||||
static const struct i2c_device_id abx80x_id[] = {
|
||||
{ "abx80x", ABX80X },
|
||||
{ "ab0801", AB0801 },
|
||||
|
@ -840,6 +913,10 @@ static int abx80x_probe(struct i2c_client *client)
|
|||
return err;
|
||||
}
|
||||
|
||||
err = abx80x_setup_nvmem(priv);
|
||||
if (err)
|
||||
return err;
|
||||
|
||||
if (client->irq > 0) {
|
||||
dev_info(&client->dev, "IRQ %d supplied\n", client->irq);
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq, NULL,
|
||||
|
|
|
@ -27,13 +27,17 @@ struct brcmstb_waketmr {
|
|||
struct rtc_device *rtc;
|
||||
struct device *dev;
|
||||
void __iomem *base;
|
||||
int irq;
|
||||
unsigned int wake_irq;
|
||||
unsigned int alarm_irq;
|
||||
struct notifier_block reboot_notifier;
|
||||
struct clk *clk;
|
||||
u32 rate;
|
||||
unsigned long rtc_alarm;
|
||||
bool alarm_en;
|
||||
};
|
||||
|
||||
#define BRCMSTB_WKTMR_EVENT 0x00
|
||||
#define WKTMR_ALARM_EVENT BIT(0)
|
||||
#define BRCMSTB_WKTMR_COUNTER 0x04
|
||||
#define BRCMSTB_WKTMR_ALARM 0x08
|
||||
#define BRCMSTB_WKTMR_PRESCALER 0x0C
|
||||
|
@ -41,28 +45,71 @@ struct brcmstb_waketmr {
|
|||
|
||||
#define BRCMSTB_WKTMR_DEFAULT_FREQ 27000000
|
||||
|
||||
static inline bool brcmstb_waketmr_is_pending(struct brcmstb_waketmr *timer)
|
||||
{
|
||||
u32 reg;
|
||||
|
||||
reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
|
||||
return !!(reg & WKTMR_ALARM_EVENT);
|
||||
}
|
||||
|
||||
static inline void brcmstb_waketmr_clear_alarm(struct brcmstb_waketmr *timer)
|
||||
{
|
||||
writel_relaxed(1, timer->base + BRCMSTB_WKTMR_EVENT);
|
||||
u32 reg;
|
||||
|
||||
if (timer->alarm_en && timer->alarm_irq)
|
||||
disable_irq(timer->alarm_irq);
|
||||
timer->alarm_en = false;
|
||||
reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);
|
||||
writel_relaxed(reg - 1, timer->base + BRCMSTB_WKTMR_ALARM);
|
||||
writel_relaxed(WKTMR_ALARM_EVENT, timer->base + BRCMSTB_WKTMR_EVENT);
|
||||
(void)readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
|
||||
}
|
||||
|
||||
static void brcmstb_waketmr_set_alarm(struct brcmstb_waketmr *timer,
|
||||
unsigned int secs)
|
||||
{
|
||||
unsigned int now;
|
||||
|
||||
brcmstb_waketmr_clear_alarm(timer);
|
||||
|
||||
/* Make sure we are actually counting in seconds */
|
||||
writel_relaxed(timer->rate, timer->base + BRCMSTB_WKTMR_PRESCALER);
|
||||
|
||||
writel_relaxed(secs + 1, timer->base + BRCMSTB_WKTMR_ALARM);
|
||||
writel_relaxed(secs, timer->base + BRCMSTB_WKTMR_ALARM);
|
||||
now = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);
|
||||
|
||||
while ((int)(secs - now) <= 0 &&
|
||||
!brcmstb_waketmr_is_pending(timer)) {
|
||||
secs = now + 1;
|
||||
writel_relaxed(secs, timer->base + BRCMSTB_WKTMR_ALARM);
|
||||
now = readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER);
|
||||
}
|
||||
}
|
||||
|
||||
static irqreturn_t brcmstb_waketmr_irq(int irq, void *data)
|
||||
{
|
||||
struct brcmstb_waketmr *timer = data;
|
||||
|
||||
if (!timer->alarm_irq)
|
||||
pm_wakeup_event(timer->dev, 0);
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static irqreturn_t brcmstb_alarm_irq(int irq, void *data)
|
||||
{
|
||||
struct brcmstb_waketmr *timer = data;
|
||||
|
||||
/* Ignore spurious interrupts */
|
||||
if (!brcmstb_waketmr_is_pending(timer))
|
||||
return IRQ_HANDLED;
|
||||
|
||||
if (timer->alarm_en) {
|
||||
if (!device_may_wakeup(timer->dev))
|
||||
writel_relaxed(WKTMR_ALARM_EVENT,
|
||||
timer->base + BRCMSTB_WKTMR_EVENT);
|
||||
rtc_update_irq(timer->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
}
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
@ -88,17 +135,25 @@ static void wktmr_read(struct brcmstb_waketmr *timer,
|
|||
static int brcmstb_waketmr_prepare_suspend(struct brcmstb_waketmr *timer)
|
||||
{
|
||||
struct device *dev = timer->dev;
|
||||
int ret = 0;
|
||||
int ret;
|
||||
|
||||
if (device_may_wakeup(dev)) {
|
||||
ret = enable_irq_wake(timer->irq);
|
||||
ret = enable_irq_wake(timer->wake_irq);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable wake-up interrupt\n");
|
||||
return ret;
|
||||
}
|
||||
if (timer->alarm_en && timer->alarm_irq) {
|
||||
ret = enable_irq_wake(timer->alarm_irq);
|
||||
if (ret) {
|
||||
dev_err(dev, "failed to enable rtc interrupt\n");
|
||||
disable_irq_wake(timer->wake_irq);
|
||||
return ret;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ret;
|
||||
return 0;
|
||||
}
|
||||
|
||||
/* If enabled as a wakeup-source, arm the timer when powering off */
|
||||
|
@ -146,18 +201,33 @@ static int brcmstb_waketmr_getalarm(struct device *dev,
|
|||
struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
|
||||
time64_t sec;
|
||||
u32 reg;
|
||||
|
||||
sec = readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM);
|
||||
if (sec != 0) {
|
||||
/* Alarm is enabled */
|
||||
alarm->enabled = 1;
|
||||
rtc_time64_to_tm(sec, &alarm->time);
|
||||
alarm->enabled = timer->alarm_en;
|
||||
rtc_time64_to_tm(timer->rtc_alarm, &alarm->time);
|
||||
|
||||
alarm->pending = brcmstb_waketmr_is_pending(timer);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
reg = readl_relaxed(timer->base + BRCMSTB_WKTMR_EVENT);
|
||||
alarm->pending = !!(reg & 1);
|
||||
static int brcmstb_waketmr_alarm_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
|
||||
|
||||
if (enabled && !timer->alarm_en) {
|
||||
if ((int)(readl_relaxed(timer->base + BRCMSTB_WKTMR_COUNTER) -
|
||||
readl_relaxed(timer->base + BRCMSTB_WKTMR_ALARM)) >= 0 &&
|
||||
!brcmstb_waketmr_is_pending(timer))
|
||||
return -EINVAL;
|
||||
timer->alarm_en = true;
|
||||
if (timer->alarm_irq)
|
||||
enable_irq(timer->alarm_irq);
|
||||
} else if (!enabled && timer->alarm_en) {
|
||||
if (timer->alarm_irq)
|
||||
disable_irq(timer->alarm_irq);
|
||||
timer->alarm_en = false;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
@ -166,26 +236,12 @@ static int brcmstb_waketmr_setalarm(struct device *dev,
|
|||
struct rtc_wkalrm *alarm)
|
||||
{
|
||||
struct brcmstb_waketmr *timer = dev_get_drvdata(dev);
|
||||
time64_t sec;
|
||||
|
||||
if (alarm->enabled)
|
||||
sec = rtc_tm_to_time64(&alarm->time);
|
||||
else
|
||||
sec = 0;
|
||||
timer->rtc_alarm = rtc_tm_to_time64(&alarm->time);
|
||||
|
||||
brcmstb_waketmr_set_alarm(timer, sec);
|
||||
brcmstb_waketmr_set_alarm(timer, timer->rtc_alarm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Does not do much but keep the RTC class happy. We always support
|
||||
* alarms.
|
||||
*/
|
||||
static int brcmstb_waketmr_alarm_enable(struct device *dev,
|
||||
unsigned int enabled)
|
||||
{
|
||||
return 0;
|
||||
return brcmstb_waketmr_alarm_enable(dev, alarm->enabled);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops brcmstb_waketmr_ops = {
|
||||
|
@ -221,12 +277,12 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev)
|
|||
* Set wakeup capability before requesting wakeup interrupt, so we can
|
||||
* process boot-time "wakeups" (e.g., from S5 soft-off)
|
||||
*/
|
||||
device_set_wakeup_capable(dev, true);
|
||||
device_wakeup_enable(dev);
|
||||
device_init_wakeup(dev, true);
|
||||
|
||||
timer->irq = platform_get_irq(pdev, 0);
|
||||
if (timer->irq < 0)
|
||||
ret = platform_get_irq(pdev, 0);
|
||||
if (ret < 0)
|
||||
return -ENODEV;
|
||||
timer->wake_irq = (unsigned int)ret;
|
||||
|
||||
timer->clk = devm_clk_get(dev, NULL);
|
||||
if (!IS_ERR(timer->clk)) {
|
||||
|
@ -241,11 +297,24 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev)
|
|||
timer->clk = NULL;
|
||||
}
|
||||
|
||||
ret = devm_request_irq(dev, timer->irq, brcmstb_waketmr_irq, 0,
|
||||
ret = devm_request_irq(dev, timer->wake_irq, brcmstb_waketmr_irq, 0,
|
||||
"brcmstb-waketimer", timer);
|
||||
if (ret < 0)
|
||||
goto err_clk;
|
||||
|
||||
brcmstb_waketmr_clear_alarm(timer);
|
||||
|
||||
/* Attempt to initialize non-wake irq */
|
||||
ret = platform_get_irq(pdev, 1);
|
||||
if (ret > 0) {
|
||||
timer->alarm_irq = (unsigned int)ret;
|
||||
ret = devm_request_irq(dev, timer->alarm_irq, brcmstb_alarm_irq,
|
||||
IRQF_NO_AUTOEN, "brcmstb-waketimer-rtc",
|
||||
timer);
|
||||
if (ret < 0)
|
||||
timer->alarm_irq = 0;
|
||||
}
|
||||
|
||||
timer->reboot_notifier.notifier_call = brcmstb_waketmr_reboot;
|
||||
register_reboot_notifier(&timer->reboot_notifier);
|
||||
|
||||
|
@ -256,8 +325,6 @@ static int brcmstb_waketmr_probe(struct platform_device *pdev)
|
|||
if (ret)
|
||||
goto err_notifier;
|
||||
|
||||
dev_info(dev, "registered, with irq %d\n", timer->irq);
|
||||
|
||||
return 0;
|
||||
|
||||
err_notifier:
|
||||
|
@ -295,7 +362,9 @@ static int brcmstb_waketmr_resume(struct device *dev)
|
|||
if (!device_may_wakeup(dev))
|
||||
return 0;
|
||||
|
||||
ret = disable_irq_wake(timer->irq);
|
||||
ret = disable_irq_wake(timer->wake_irq);
|
||||
if (timer->alarm_en && timer->alarm_irq)
|
||||
disable_irq_wake(timer->alarm_irq);
|
||||
|
||||
brcmstb_waketmr_clear_alarm(timer);
|
||||
|
||||
|
@ -325,4 +394,5 @@ module_platform_driver(brcmstb_waketmr_driver);
|
|||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Brian Norris");
|
||||
MODULE_AUTHOR("Markus Mayer");
|
||||
MODULE_AUTHOR("Doug Berger");
|
||||
MODULE_DESCRIPTION("Wake-up timer driver for STB chips");
|
||||
|
|
|
@ -1712,9 +1712,9 @@ static const struct regmap_config regmap_config = {
|
|||
.val_bits = 8,
|
||||
};
|
||||
|
||||
static int ds1307_probe(struct i2c_client *client,
|
||||
const struct i2c_device_id *id)
|
||||
static int ds1307_probe(struct i2c_client *client)
|
||||
{
|
||||
const struct i2c_device_id *id = i2c_client_get_device_id(client);
|
||||
struct ds1307 *ds1307;
|
||||
const void *match;
|
||||
int err = -ENODEV;
|
||||
|
@ -2011,7 +2011,7 @@ static struct i2c_driver ds1307_driver = {
|
|||
.name = "rtc-ds1307",
|
||||
.of_match_table = ds1307_of_match,
|
||||
},
|
||||
.probe = ds1307_probe,
|
||||
.probe_new = ds1307_probe,
|
||||
.id_table = ds1307_id,
|
||||
};
|
||||
|
||||
|
|
|
@ -164,7 +164,7 @@ static int efi_read_time(struct device *dev, struct rtc_time *tm)
|
|||
|
||||
if (status != EFI_SUCCESS) {
|
||||
/* should never happen */
|
||||
dev_err(dev, "can't read time\n");
|
||||
dev_err_once(dev, "can't read time\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
|
|
|
@ -518,9 +518,14 @@ static int hym8563_probe(struct i2c_client *client)
|
|||
}
|
||||
|
||||
if (client->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, hym8563_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
client->name, hym8563);
|
||||
if (ret < 0) {
|
||||
dev_err(&client->dev, "irq %d request failed, %d\n",
|
||||
|
|
|
@ -8,16 +8,16 @@
|
|||
* by Alessandro Zummo <a.zummo@towertech.it>.
|
||||
*/
|
||||
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/hwmon.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/err.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/hwmon.h>
|
||||
|
||||
#include <asm/byteorder.h>
|
||||
|
||||
/* ISL register offsets */
|
||||
#define ISL12022_REG_SC 0x00
|
||||
|
@ -44,13 +44,6 @@
|
|||
|
||||
#define ISL12022_BETA_TSE (1 << 7)
|
||||
|
||||
static struct i2c_driver isl12022_driver;
|
||||
|
||||
struct isl12022 {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
};
|
||||
|
||||
static umode_t isl12022_hwmon_is_visible(const void *data,
|
||||
enum hwmon_sensor_types type,
|
||||
u32 attr, int channel)
|
||||
|
@ -67,19 +60,17 @@ static umode_t isl12022_hwmon_is_visible(const void *data,
|
|||
*/
|
||||
static int isl12022_hwmon_read_temp(struct device *dev, long *mC)
|
||||
{
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
u8 temp_buf[2];
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
int temp, ret;
|
||||
__le16 buf;
|
||||
|
||||
ret = regmap_bulk_read(regmap, ISL12022_REG_TEMP_L,
|
||||
temp_buf, sizeof(temp_buf));
|
||||
ret = regmap_bulk_read(regmap, ISL12022_REG_TEMP_L, &buf, sizeof(buf));
|
||||
if (ret)
|
||||
return ret;
|
||||
/*
|
||||
* Temperature is represented as a 10-bit number, unit half-Kelvins.
|
||||
*/
|
||||
temp = (temp_buf[1] << 8) | temp_buf[0];
|
||||
temp = le16_to_cpu(buf);
|
||||
temp *= 500;
|
||||
temp -= 273000;
|
||||
|
||||
|
@ -115,23 +106,21 @@ static const struct hwmon_chip_info isl12022_hwmon_chip_info = {
|
|||
|
||||
static void isl12022_hwmon_register(struct device *dev)
|
||||
{
|
||||
struct isl12022 *isl12022;
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
struct device *hwmon;
|
||||
int ret;
|
||||
|
||||
if (!IS_REACHABLE(CONFIG_HWMON))
|
||||
return;
|
||||
|
||||
isl12022 = dev_get_drvdata(dev);
|
||||
|
||||
ret = regmap_update_bits(isl12022->regmap, ISL12022_REG_BETA,
|
||||
ret = regmap_update_bits(regmap, ISL12022_REG_BETA,
|
||||
ISL12022_BETA_TSE, ISL12022_BETA_TSE);
|
||||
if (ret) {
|
||||
dev_warn(dev, "unable to enable temperature sensor\n");
|
||||
return;
|
||||
}
|
||||
|
||||
hwmon = devm_hwmon_device_register_with_info(dev, "isl12022", isl12022,
|
||||
hwmon = devm_hwmon_device_register_with_info(dev, "isl12022", regmap,
|
||||
&isl12022_hwmon_chip_info,
|
||||
NULL);
|
||||
if (IS_ERR(hwmon))
|
||||
|
@ -144,8 +133,7 @@ static void isl12022_hwmon_register(struct device *dev)
|
|||
*/
|
||||
static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
uint8_t buf[ISL12022_REG_INT + 1];
|
||||
int ret;
|
||||
|
||||
|
@ -155,16 +143,12 @@ static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
|
||||
if (buf[ISL12022_REG_SR] & (ISL12022_SR_LBAT85 | ISL12022_SR_LBAT75)) {
|
||||
dev_warn(dev,
|
||||
"voltage dropped below %u%%, "
|
||||
"date and time is not reliable.\n",
|
||||
"voltage dropped below %u%%, date and time is not reliable.\n",
|
||||
buf[ISL12022_REG_SR] & ISL12022_SR_LBAT85 ? 85 : 75);
|
||||
}
|
||||
|
||||
dev_dbg(dev,
|
||||
"%s: raw data is sec=%02x, min=%02x, hr=%02x, "
|
||||
"mday=%02x, mon=%02x, year=%02x, wday=%02x, "
|
||||
"sr=%02x, int=%02x",
|
||||
__func__,
|
||||
"raw data is sec=%02x, min=%02x, hr=%02x, mday=%02x, mon=%02x, year=%02x, wday=%02x, sr=%02x, int=%02x",
|
||||
buf[ISL12022_REG_SC],
|
||||
buf[ISL12022_REG_MN],
|
||||
buf[ISL12022_REG_HR],
|
||||
|
@ -190,8 +174,7 @@ static int isl12022_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
|||
|
||||
static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct isl12022 *isl12022 = dev_get_drvdata(dev);
|
||||
struct regmap *regmap = isl12022->regmap;
|
||||
struct regmap *regmap = dev_get_drvdata(dev);
|
||||
int ret;
|
||||
uint8_t buf[ISL12022_REG_DW + 1];
|
||||
|
||||
|
@ -218,8 +201,7 @@ static int isl12022_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
|||
|
||||
buf[ISL12022_REG_DW] = tm->tm_wday & 0x07;
|
||||
|
||||
return regmap_bulk_write(isl12022->regmap, ISL12022_REG_SC,
|
||||
buf, sizeof(buf));
|
||||
return regmap_bulk_write(regmap, ISL12022_REG_SC, buf, sizeof(buf));
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops isl12022_rtc_ops = {
|
||||
|
@ -235,44 +217,39 @@ static const struct regmap_config regmap_config = {
|
|||
|
||||
static int isl12022_probe(struct i2c_client *client)
|
||||
{
|
||||
struct isl12022 *isl12022;
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
|
||||
if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C))
|
||||
return -ENODEV;
|
||||
|
||||
isl12022 = devm_kzalloc(&client->dev, sizeof(struct isl12022),
|
||||
GFP_KERNEL);
|
||||
if (!isl12022)
|
||||
return -ENOMEM;
|
||||
dev_set_drvdata(&client->dev, isl12022);
|
||||
|
||||
isl12022->regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(isl12022->regmap)) {
|
||||
regmap = devm_regmap_init_i2c(client, ®map_config);
|
||||
if (IS_ERR(regmap)) {
|
||||
dev_err(&client->dev, "regmap allocation failed\n");
|
||||
return PTR_ERR(isl12022->regmap);
|
||||
return PTR_ERR(regmap);
|
||||
}
|
||||
|
||||
dev_set_drvdata(&client->dev, regmap);
|
||||
|
||||
isl12022_hwmon_register(&client->dev);
|
||||
|
||||
isl12022->rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(isl12022->rtc))
|
||||
return PTR_ERR(isl12022->rtc);
|
||||
rtc = devm_rtc_allocate_device(&client->dev);
|
||||
if (IS_ERR(rtc))
|
||||
return PTR_ERR(rtc);
|
||||
|
||||
isl12022->rtc->ops = &isl12022_rtc_ops;
|
||||
isl12022->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
isl12022->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
rtc->ops = &isl12022_rtc_ops;
|
||||
rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
|
||||
return devm_rtc_register_device(isl12022->rtc);
|
||||
return devm_rtc_register_device(rtc);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_OF
|
||||
static const struct of_device_id isl12022_dt_match[] = {
|
||||
{ .compatible = "isl,isl12022" }, /* for backward compat., don't use */
|
||||
{ .compatible = "isil,isl12022" },
|
||||
{ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, isl12022_dt_match);
|
||||
#endif
|
||||
|
||||
static const struct i2c_device_id isl12022_id[] = {
|
||||
{ "isl12022", 0 },
|
||||
|
@ -283,9 +260,7 @@ MODULE_DEVICE_TABLE(i2c, isl12022_id);
|
|||
static struct i2c_driver isl12022_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-isl12022",
|
||||
#ifdef CONFIG_OF
|
||||
.of_match_table = of_match_ptr(isl12022_dt_match),
|
||||
#endif
|
||||
.of_match_table = isl12022_dt_match,
|
||||
},
|
||||
.probe_new = isl12022_probe,
|
||||
.id_table = isl12022_id,
|
||||
|
|
|
@ -6,12 +6,15 @@
|
|||
*/
|
||||
|
||||
#include <linux/clk.h>
|
||||
#include <linux/clk-provider.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/iopoll.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_device.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/property.h>
|
||||
#include <linux/reboot.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/slab.h>
|
||||
|
@ -25,6 +28,7 @@
|
|||
#define JZ_REG_RTC_WAKEUP_FILTER 0x24
|
||||
#define JZ_REG_RTC_RESET_COUNTER 0x28
|
||||
#define JZ_REG_RTC_SCRATCHPAD 0x34
|
||||
#define JZ_REG_RTC_CKPCR 0x40
|
||||
|
||||
/* The following are present on the jz4780 */
|
||||
#define JZ_REG_RTC_WENR 0x3C
|
||||
|
@ -44,6 +48,9 @@
|
|||
#define JZ_RTC_WAKEUP_FILTER_MASK 0x0000FFE0
|
||||
#define JZ_RTC_RESET_COUNTER_MASK 0x00000FE0
|
||||
|
||||
#define JZ_RTC_CKPCR_CK32PULL_DIS BIT(4)
|
||||
#define JZ_RTC_CKPCR_CK32CTL_EN (BIT(2) | BIT(1))
|
||||
|
||||
enum jz4740_rtc_type {
|
||||
ID_JZ4740,
|
||||
ID_JZ4760,
|
||||
|
@ -56,6 +63,8 @@ struct jz4740_rtc {
|
|||
|
||||
struct rtc_device *rtc;
|
||||
|
||||
struct clk_hw clk32k;
|
||||
|
||||
spinlock_t lock;
|
||||
};
|
||||
|
||||
|
@ -69,19 +78,15 @@ 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 = 10000;
|
||||
|
||||
do {
|
||||
ctrl = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CTRL);
|
||||
} while (!(ctrl & JZ_RTC_CTRL_WRDY) && --timeout);
|
||||
|
||||
return timeout ? 0 : -EIO;
|
||||
return readl_poll_timeout(rtc->base + JZ_REG_RTC_CTRL, ctrl,
|
||||
ctrl & JZ_RTC_CTRL_WRDY, 0, 1000);
|
||||
}
|
||||
|
||||
static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc)
|
||||
{
|
||||
uint32_t ctrl;
|
||||
int ret, timeout = 10000;
|
||||
int ret;
|
||||
|
||||
ret = jz4740_rtc_wait_write_ready(rtc);
|
||||
if (ret != 0)
|
||||
|
@ -89,11 +94,8 @@ static inline int jz4780_rtc_enable_write(struct jz4740_rtc *rtc)
|
|||
|
||||
writel(JZ_RTC_WENR_MAGIC, rtc->base + JZ_REG_RTC_WENR);
|
||||
|
||||
do {
|
||||
ctrl = readl(rtc->base + JZ_REG_RTC_WENR);
|
||||
} while (!(ctrl & JZ_RTC_WENR_WEN) && --timeout);
|
||||
|
||||
return timeout ? 0 : -EIO;
|
||||
return readl_poll_timeout(rtc->base + JZ_REG_RTC_WENR, ctrl,
|
||||
ctrl & JZ_RTC_WENR_WEN, 0, 1000);
|
||||
}
|
||||
|
||||
static inline int jz4740_rtc_reg_write(struct jz4740_rtc *rtc, size_t reg,
|
||||
|
@ -260,6 +262,7 @@ static void jz4740_rtc_power_off(void)
|
|||
static const struct of_device_id jz4740_rtc_of_match[] = {
|
||||
{ .compatible = "ingenic,jz4740-rtc", .data = (void *)ID_JZ4740 },
|
||||
{ .compatible = "ingenic,jz4760-rtc", .data = (void *)ID_JZ4760 },
|
||||
{ .compatible = "ingenic,jz4770-rtc", .data = (void *)ID_JZ4780 },
|
||||
{ .compatible = "ingenic,jz4780-rtc", .data = (void *)ID_JZ4780 },
|
||||
{},
|
||||
};
|
||||
|
@ -301,6 +304,38 @@ static void jz4740_rtc_set_wakeup_params(struct jz4740_rtc *rtc,
|
|||
jz4740_rtc_reg_write(rtc, JZ_REG_RTC_RESET_COUNTER, reset_ticks);
|
||||
}
|
||||
|
||||
static int jz4740_rtc_clk32k_enable(struct clk_hw *hw)
|
||||
{
|
||||
struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k);
|
||||
|
||||
return jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR,
|
||||
JZ_RTC_CKPCR_CK32PULL_DIS |
|
||||
JZ_RTC_CKPCR_CK32CTL_EN);
|
||||
}
|
||||
|
||||
static void jz4740_rtc_clk32k_disable(struct clk_hw *hw)
|
||||
{
|
||||
struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k);
|
||||
|
||||
jz4740_rtc_reg_write(rtc, JZ_REG_RTC_CKPCR, 0);
|
||||
}
|
||||
|
||||
static int jz4740_rtc_clk32k_is_enabled(struct clk_hw *hw)
|
||||
{
|
||||
struct jz4740_rtc *rtc = container_of(hw, struct jz4740_rtc, clk32k);
|
||||
u32 ckpcr;
|
||||
|
||||
ckpcr = jz4740_rtc_reg_read(rtc, JZ_REG_RTC_CKPCR);
|
||||
|
||||
return !!(ckpcr & JZ_RTC_CKPCR_CK32CTL_EN);
|
||||
}
|
||||
|
||||
static const struct clk_ops jz4740_rtc_clk32k_ops = {
|
||||
.enable = jz4740_rtc_clk32k_enable,
|
||||
.disable = jz4740_rtc_clk32k_disable,
|
||||
.is_enabled = jz4740_rtc_clk32k_is_enabled,
|
||||
};
|
||||
|
||||
static int jz4740_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device *dev = &pdev->dev;
|
||||
|
@ -335,17 +370,13 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
|
|||
device_init_wakeup(dev, 1);
|
||||
|
||||
ret = dev_pm_set_wake_irq(dev, irq);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to set wake irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to set wake irq\n");
|
||||
|
||||
rtc->rtc = devm_rtc_allocate_device(dev);
|
||||
if (IS_ERR(rtc->rtc)) {
|
||||
ret = PTR_ERR(rtc->rtc);
|
||||
dev_err(dev, "Failed to allocate rtc device: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (IS_ERR(rtc->rtc))
|
||||
return dev_err_probe(dev, PTR_ERR(rtc->rtc),
|
||||
"Failed to allocate rtc device\n");
|
||||
|
||||
rtc->rtc->ops = &jz4740_rtc_ops;
|
||||
rtc->rtc->range_max = U32_MAX;
|
||||
|
@ -362,10 +393,8 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
ret = devm_request_irq(dev, irq, jz4740_rtc_irq, 0,
|
||||
pdev->name, rtc);
|
||||
if (ret) {
|
||||
dev_err(dev, "Failed to request rtc irq: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret, "Failed to request rtc irq\n");
|
||||
|
||||
if (of_device_is_system_power_controller(np)) {
|
||||
dev_for_power_off = dev;
|
||||
|
@ -376,6 +405,21 @@ static int jz4740_rtc_probe(struct platform_device *pdev)
|
|||
dev_warn(dev, "Poweroff handler already present!\n");
|
||||
}
|
||||
|
||||
if (device_property_present(dev, "#clock-cells")) {
|
||||
rtc->clk32k.init = CLK_HW_INIT_HW("clk32k", __clk_get_hw(clk),
|
||||
&jz4740_rtc_clk32k_ops, 0);
|
||||
|
||||
ret = devm_clk_hw_register(dev, &rtc->clk32k);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Unable to register clk32k clock\n");
|
||||
|
||||
ret = of_clk_add_hw_provider(np, of_clk_hw_simple_get, &rtc->clk32k);
|
||||
if (ret)
|
||||
return dev_err_probe(dev, ret,
|
||||
"Unable to register clk32k clock provider\n");
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
|
|
@ -914,9 +914,14 @@ static int m41t80_probe(struct i2c_client *client)
|
|||
"wakeup-source");
|
||||
#endif
|
||||
if (client->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
rc = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, m41t80_handle_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
"m41t80", client);
|
||||
if (rc) {
|
||||
dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
|
||||
|
|
|
@ -9,7 +9,6 @@
|
|||
*/
|
||||
|
||||
#include <linux/bcd.h>
|
||||
#include <linux/i2c.h>
|
||||
#include <linux/mfd/max8907.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
|
|
@ -10,14 +10,15 @@
|
|||
* Moxa Technology Co., Ltd. <www.moxa.com>
|
||||
*/
|
||||
|
||||
#include <linux/err.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/gpio.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/mod_devicetable.h>
|
||||
#include <linux/gpio/consumer.h>
|
||||
|
||||
#define GPIO_RTC_RESERVED 0x0C
|
||||
#define GPIO_RTC_DATA_SET 0x10
|
||||
|
@ -55,7 +56,9 @@
|
|||
struct moxart_rtc {
|
||||
struct rtc_device *rtc;
|
||||
spinlock_t rtc_lock;
|
||||
int gpio_data, gpio_sclk, gpio_reset;
|
||||
struct gpio_desc *gpio_data;
|
||||
struct gpio_desc *gpio_sclk;
|
||||
struct gpio_desc *gpio_reset;
|
||||
};
|
||||
|
||||
static int day_of_year[12] = { 0, 31, 59, 90, 120, 151, 181,
|
||||
|
@ -67,10 +70,10 @@ static void moxart_rtc_write_byte(struct device *dev, u8 data)
|
|||
int i;
|
||||
|
||||
for (i = 0; i < 8; i++, data >>= 1) {
|
||||
gpio_set_value(moxart_rtc->gpio_sclk, 0);
|
||||
gpio_set_value(moxart_rtc->gpio_data, ((data & 1) == 1));
|
||||
gpiod_set_value(moxart_rtc->gpio_sclk, 0);
|
||||
gpiod_set_value(moxart_rtc->gpio_data, ((data & 1) == 1));
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
gpio_set_value(moxart_rtc->gpio_sclk, 1);
|
||||
gpiod_set_value(moxart_rtc->gpio_sclk, 1);
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
}
|
||||
}
|
||||
|
@ -82,11 +85,11 @@ static u8 moxart_rtc_read_byte(struct device *dev)
|
|||
u8 data = 0;
|
||||
|
||||
for (i = 0; i < 8; i++) {
|
||||
gpio_set_value(moxart_rtc->gpio_sclk, 0);
|
||||
gpiod_set_value(moxart_rtc->gpio_sclk, 0);
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
gpio_set_value(moxart_rtc->gpio_sclk, 1);
|
||||
gpiod_set_value(moxart_rtc->gpio_sclk, 1);
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
if (gpio_get_value(moxart_rtc->gpio_data))
|
||||
if (gpiod_get_value(moxart_rtc->gpio_data))
|
||||
data |= (1 << i);
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
}
|
||||
|
@ -101,15 +104,15 @@ static u8 moxart_rtc_read_register(struct device *dev, u8 cmd)
|
|||
|
||||
local_irq_save(flags);
|
||||
|
||||
gpio_direction_output(moxart_rtc->gpio_data, 0);
|
||||
gpio_set_value(moxart_rtc->gpio_reset, 1);
|
||||
gpiod_direction_output(moxart_rtc->gpio_data, 0);
|
||||
gpiod_set_value(moxart_rtc->gpio_reset, 1);
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
moxart_rtc_write_byte(dev, cmd);
|
||||
gpio_direction_input(moxart_rtc->gpio_data);
|
||||
gpiod_direction_input(moxart_rtc->gpio_data);
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
data = moxart_rtc_read_byte(dev);
|
||||
gpio_set_value(moxart_rtc->gpio_sclk, 0);
|
||||
gpio_set_value(moxart_rtc->gpio_reset, 0);
|
||||
gpiod_set_value(moxart_rtc->gpio_sclk, 0);
|
||||
gpiod_set_value(moxart_rtc->gpio_reset, 0);
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
@ -124,13 +127,13 @@ static void moxart_rtc_write_register(struct device *dev, u8 cmd, u8 data)
|
|||
|
||||
local_irq_save(flags);
|
||||
|
||||
gpio_direction_output(moxart_rtc->gpio_data, 0);
|
||||
gpio_set_value(moxart_rtc->gpio_reset, 1);
|
||||
gpiod_direction_output(moxart_rtc->gpio_data, 0);
|
||||
gpiod_set_value(moxart_rtc->gpio_reset, 1);
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
moxart_rtc_write_byte(dev, cmd);
|
||||
moxart_rtc_write_byte(dev, data);
|
||||
gpio_set_value(moxart_rtc->gpio_sclk, 0);
|
||||
gpio_set_value(moxart_rtc->gpio_reset, 0);
|
||||
gpiod_set_value(moxart_rtc->gpio_sclk, 0);
|
||||
gpiod_set_value(moxart_rtc->gpio_reset, 0);
|
||||
udelay(GPIO_RTC_DELAY_TIME);
|
||||
|
||||
local_irq_restore(flags);
|
||||
|
@ -247,53 +250,33 @@ static int moxart_rtc_probe(struct platform_device *pdev)
|
|||
if (!moxart_rtc)
|
||||
return -ENOMEM;
|
||||
|
||||
moxart_rtc->gpio_data = of_get_named_gpio(pdev->dev.of_node,
|
||||
"gpio-rtc-data", 0);
|
||||
if (!gpio_is_valid(moxart_rtc->gpio_data)) {
|
||||
dev_err(&pdev->dev, "invalid gpio (data): %d\n",
|
||||
moxart_rtc->gpio_data);
|
||||
return moxart_rtc->gpio_data;
|
||||
moxart_rtc->gpio_data = devm_gpiod_get(&pdev->dev, "rtc-data",
|
||||
GPIOD_IN);
|
||||
ret = PTR_ERR_OR_ZERO(moxart_rtc->gpio_data);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get rtc data gpio: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
moxart_rtc->gpio_sclk = of_get_named_gpio(pdev->dev.of_node,
|
||||
"gpio-rtc-sclk", 0);
|
||||
if (!gpio_is_valid(moxart_rtc->gpio_sclk)) {
|
||||
dev_err(&pdev->dev, "invalid gpio (sclk): %d\n",
|
||||
moxart_rtc->gpio_sclk);
|
||||
return moxart_rtc->gpio_sclk;
|
||||
moxart_rtc->gpio_sclk = devm_gpiod_get(&pdev->dev, "rtc-sclk",
|
||||
GPIOD_ASIS);
|
||||
ret = PTR_ERR_OR_ZERO(moxart_rtc->gpio_sclk);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get rtc sclk gpio: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
moxart_rtc->gpio_reset = of_get_named_gpio(pdev->dev.of_node,
|
||||
"gpio-rtc-reset", 0);
|
||||
if (!gpio_is_valid(moxart_rtc->gpio_reset)) {
|
||||
dev_err(&pdev->dev, "invalid gpio (reset): %d\n",
|
||||
moxart_rtc->gpio_reset);
|
||||
return moxart_rtc->gpio_reset;
|
||||
moxart_rtc->gpio_reset = devm_gpiod_get(&pdev->dev, "rtc-reset",
|
||||
GPIOD_ASIS);
|
||||
ret = PTR_ERR_OR_ZERO(moxart_rtc->gpio_reset);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get rtc reset gpio: %d\n", ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
spin_lock_init(&moxart_rtc->rtc_lock);
|
||||
platform_set_drvdata(pdev, moxart_rtc);
|
||||
|
||||
ret = devm_gpio_request(&pdev->dev, moxart_rtc->gpio_data, "rtc_data");
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get rtc_data gpio\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request_one(&pdev->dev, moxart_rtc->gpio_sclk,
|
||||
GPIOF_DIR_OUT, "rtc_sclk");
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get rtc_sclk gpio\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
ret = devm_gpio_request_one(&pdev->dev, moxart_rtc->gpio_reset,
|
||||
GPIOF_DIR_OUT, "rtc_reset");
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "can't get rtc_reset gpio\n");
|
||||
return ret;
|
||||
}
|
||||
|
||||
moxart_rtc->rtc = devm_rtc_device_register(&pdev->dev, pdev->name,
|
||||
&moxart_rtc_ops,
|
||||
THIS_MODULE);
|
||||
|
|
|
@ -0,0 +1,226 @@
|
|||
// SPDX-License-Identifier: GPL-2.0+
|
||||
//
|
||||
// Copyright 2022 NXP.
|
||||
|
||||
#include <linux/init.h>
|
||||
#include <linux/io.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/mfd/syscon.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of.h>
|
||||
#include <linux/platform_device.h>
|
||||
#include <linux/pm_wakeirq.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
|
||||
#define BBNSM_CTRL 0x8
|
||||
#define BBNSM_INT_EN 0x10
|
||||
#define BBNSM_EVENTS 0x14
|
||||
#define BBNSM_RTC_LS 0x40
|
||||
#define BBNSM_RTC_MS 0x44
|
||||
#define BBNSM_TA 0x50
|
||||
|
||||
#define RTC_EN 0x2
|
||||
#define RTC_EN_MSK 0x3
|
||||
#define TA_EN (0x2 << 2)
|
||||
#define TA_DIS (0x1 << 2)
|
||||
#define TA_EN_MSK (0x3 << 2)
|
||||
#define RTC_INT_EN 0x2
|
||||
#define TA_INT_EN (0x2 << 2)
|
||||
|
||||
#define BBNSM_EVENT_TA (0x2 << 2)
|
||||
|
||||
#define CNTR_TO_SECS_SH 15
|
||||
|
||||
struct bbnsm_rtc {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
int irq;
|
||||
struct clk *clk;
|
||||
};
|
||||
|
||||
static u32 bbnsm_read_counter(struct bbnsm_rtc *bbnsm)
|
||||
{
|
||||
u32 rtc_msb, rtc_lsb;
|
||||
unsigned int timeout = 100;
|
||||
u32 time;
|
||||
u32 tmp = 0;
|
||||
|
||||
do {
|
||||
time = tmp;
|
||||
/* read the msb */
|
||||
regmap_read(bbnsm->regmap, BBNSM_RTC_MS, &rtc_msb);
|
||||
/* read the lsb */
|
||||
regmap_read(bbnsm->regmap, BBNSM_RTC_LS, &rtc_lsb);
|
||||
/* convert to seconds */
|
||||
tmp = (rtc_msb << 17) | (rtc_lsb >> 15);
|
||||
} while (tmp != time && --timeout);
|
||||
|
||||
return time;
|
||||
}
|
||||
|
||||
static int bbnsm_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev);
|
||||
unsigned long time;
|
||||
u32 val;
|
||||
|
||||
regmap_read(bbnsm->regmap, BBNSM_CTRL, &val);
|
||||
if ((val & RTC_EN_MSK) != RTC_EN)
|
||||
return -EINVAL;
|
||||
|
||||
time = bbnsm_read_counter(bbnsm);
|
||||
rtc_time64_to_tm(time, tm);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bbnsm_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev);
|
||||
unsigned long time = rtc_tm_to_time64(tm);
|
||||
|
||||
/* disable the RTC first */
|
||||
regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK, 0);
|
||||
|
||||
/* write the 32bit sec time to 47 bit timer counter, leaving 15 LSBs blank */
|
||||
regmap_write(bbnsm->regmap, BBNSM_RTC_LS, time << CNTR_TO_SECS_SH);
|
||||
regmap_write(bbnsm->regmap, BBNSM_RTC_MS, time >> (32 - CNTR_TO_SECS_SH));
|
||||
|
||||
/* Enable the RTC again */
|
||||
regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, RTC_EN_MSK, RTC_EN);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bbnsm_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev);
|
||||
u32 bbnsm_events, bbnsm_ta;
|
||||
|
||||
regmap_read(bbnsm->regmap, BBNSM_TA, &bbnsm_ta);
|
||||
rtc_time64_to_tm(bbnsm_ta, &alrm->time);
|
||||
|
||||
regmap_read(bbnsm->regmap, BBNSM_EVENTS, &bbnsm_events);
|
||||
alrm->pending = (bbnsm_events & BBNSM_EVENT_TA) ? 1 : 0;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bbnsm_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
|
||||
{
|
||||
struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev);
|
||||
|
||||
/* enable the alarm event */
|
||||
regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, TA_EN_MSK, enable ? TA_EN : TA_DIS);
|
||||
/* enable the alarm interrupt */
|
||||
regmap_update_bits(bbnsm->regmap, BBNSM_INT_EN, TA_EN_MSK, enable ? TA_EN : TA_DIS);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int bbnsm_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
{
|
||||
struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev);
|
||||
unsigned long time = rtc_tm_to_time64(&alrm->time);
|
||||
|
||||
/* disable the alarm */
|
||||
regmap_update_bits(bbnsm->regmap, BBNSM_CTRL, TA_EN, TA_EN);
|
||||
|
||||
/* write the seconds to TA */
|
||||
regmap_write(bbnsm->regmap, BBNSM_TA, time);
|
||||
|
||||
return bbnsm_rtc_alarm_irq_enable(dev, alrm->enabled);
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops bbnsm_rtc_ops = {
|
||||
.read_time = bbnsm_rtc_read_time,
|
||||
.set_time = bbnsm_rtc_set_time,
|
||||
.read_alarm = bbnsm_rtc_read_alarm,
|
||||
.set_alarm = bbnsm_rtc_set_alarm,
|
||||
.alarm_irq_enable = bbnsm_rtc_alarm_irq_enable,
|
||||
};
|
||||
|
||||
static irqreturn_t bbnsm_rtc_irq_handler(int irq, void *dev_id)
|
||||
{
|
||||
struct device *dev = dev_id;
|
||||
struct bbnsm_rtc *bbnsm = dev_get_drvdata(dev);
|
||||
u32 val;
|
||||
|
||||
regmap_read(bbnsm->regmap, BBNSM_EVENTS, &val);
|
||||
if (val & BBNSM_EVENT_TA) {
|
||||
bbnsm_rtc_alarm_irq_enable(dev, false);
|
||||
/* clear the alarm event */
|
||||
regmap_write_bits(bbnsm->regmap, BBNSM_EVENTS, TA_EN_MSK, BBNSM_EVENT_TA);
|
||||
rtc_update_irq(bbnsm->rtc, 1, RTC_AF | RTC_IRQF);
|
||||
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
return IRQ_NONE;
|
||||
}
|
||||
|
||||
static int bbnsm_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
struct device_node *np = pdev->dev.of_node;
|
||||
struct bbnsm_rtc *bbnsm;
|
||||
int ret;
|
||||
|
||||
bbnsm = devm_kzalloc(&pdev->dev, sizeof(*bbnsm), GFP_KERNEL);
|
||||
if (!bbnsm)
|
||||
return -ENOMEM;
|
||||
|
||||
bbnsm->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(bbnsm->rtc))
|
||||
return PTR_ERR(bbnsm->rtc);
|
||||
|
||||
bbnsm->regmap = syscon_node_to_regmap(np->parent);
|
||||
if (IS_ERR(bbnsm->regmap)) {
|
||||
dev_dbg(&pdev->dev, "bbnsm get regmap failed\n");
|
||||
return PTR_ERR(bbnsm->regmap);
|
||||
}
|
||||
|
||||
bbnsm->irq = platform_get_irq(pdev, 0);
|
||||
if (bbnsm->irq < 0)
|
||||
return bbnsm->irq;
|
||||
|
||||
platform_set_drvdata(pdev, bbnsm);
|
||||
|
||||
/* clear all the pending events */
|
||||
regmap_write(bbnsm->regmap, BBNSM_EVENTS, 0x7A);
|
||||
|
||||
device_init_wakeup(&pdev->dev, true);
|
||||
dev_pm_set_wake_irq(&pdev->dev, bbnsm->irq);
|
||||
|
||||
ret = devm_request_irq(&pdev->dev, bbnsm->irq, bbnsm_rtc_irq_handler,
|
||||
IRQF_SHARED, "rtc alarm", &pdev->dev);
|
||||
if (ret) {
|
||||
dev_err(&pdev->dev, "failed to request irq %d: %d\n",
|
||||
bbnsm->irq, ret);
|
||||
return ret;
|
||||
}
|
||||
|
||||
bbnsm->rtc->ops = &bbnsm_rtc_ops;
|
||||
bbnsm->rtc->range_max = U32_MAX;
|
||||
|
||||
return devm_rtc_register_device(bbnsm->rtc);
|
||||
}
|
||||
|
||||
static const struct of_device_id bbnsm_dt_ids[] = {
|
||||
{ .compatible = "nxp,imx93-bbnsm-rtc" },
|
||||
{ /* sentinel */ },
|
||||
};
|
||||
MODULE_DEVICE_TABLE(of, bbnsm_dt_ids);
|
||||
|
||||
static struct platform_driver bbnsm_rtc_driver = {
|
||||
.driver = {
|
||||
.name = "bbnsm_rtc",
|
||||
.of_match_table = bbnsm_dt_ids,
|
||||
},
|
||||
.probe = bbnsm_rtc_probe,
|
||||
};
|
||||
module_platform_driver(bbnsm_rtc_driver);
|
||||
|
||||
MODULE_AUTHOR("Jacky Bai <ping.bai@nxp.com>");
|
||||
MODULE_DESCRIPTION("NXP BBNSM RTC Driver");
|
||||
MODULE_LICENSE("GPL");
|
|
@ -413,9 +413,14 @@ static int pcf2123_probe(struct spi_device *spi)
|
|||
|
||||
/* Register alarm irq */
|
||||
if (spi->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&spi->dev))
|
||||
irqflags = 0;
|
||||
|
||||
ret = devm_request_threaded_irq(&spi->dev, spi->irq, NULL,
|
||||
pcf2123_rtc_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
pcf2123_driver.driver.name, &spi->dev);
|
||||
if (!ret)
|
||||
device_init_wakeup(&spi->dev, true);
|
||||
|
|
|
@ -621,9 +621,14 @@ static int pcf85063_probe(struct i2c_client *client)
|
|||
clear_bit(RTC_FEATURE_ALARM, pcf85063->rtc->features);
|
||||
|
||||
if (config->has_alarms && client->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, pcf85063_rtc_handle_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
"pcf85063", pcf85063);
|
||||
if (err) {
|
||||
dev_warn(&pcf85063->rtc->dev,
|
||||
|
|
|
@ -445,13 +445,18 @@ static int pcf8523_probe(struct i2c_client *client)
|
|||
clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, rtc->features);
|
||||
|
||||
if (client->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
err = regmap_write(pcf8523->regmap, PCF8523_TMR_CLKOUT_CTRL, 0x38);
|
||||
if (err < 0)
|
||||
return err;
|
||||
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, pcf8523_irq,
|
||||
IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW,
|
||||
IRQF_SHARED | IRQF_ONESHOT | irqflags,
|
||||
dev_name(&rtc->dev), pcf8523);
|
||||
if (err)
|
||||
return err;
|
||||
|
|
|
@ -101,6 +101,10 @@
|
|||
#define PIN_IO_INTA_OUT 2
|
||||
#define PIN_IO_INTA_HIZ 3
|
||||
|
||||
#define OSC_CAP_SEL GENMASK(1, 0)
|
||||
#define OSC_CAP_6000 0x01
|
||||
#define OSC_CAP_12500 0x02
|
||||
|
||||
#define STOP_EN_STOP BIT(0)
|
||||
|
||||
#define RESET_CPR 0xa4
|
||||
|
@ -117,6 +121,32 @@ struct pcf85x63_config {
|
|||
unsigned int num_nvram;
|
||||
};
|
||||
|
||||
static int pcf85363_load_capacitance(struct pcf85363 *pcf85363, struct device_node *node)
|
||||
{
|
||||
u32 load = 7000;
|
||||
u8 value = 0;
|
||||
|
||||
of_property_read_u32(node, "quartz-load-femtofarads", &load);
|
||||
|
||||
switch (load) {
|
||||
default:
|
||||
dev_warn(&pcf85363->rtc->dev, "Unknown quartz-load-femtofarads value: %d. Assuming 7000",
|
||||
load);
|
||||
fallthrough;
|
||||
case 7000:
|
||||
break;
|
||||
case 6000:
|
||||
value = OSC_CAP_6000;
|
||||
break;
|
||||
case 12500:
|
||||
value = OSC_CAP_12500;
|
||||
break;
|
||||
}
|
||||
|
||||
return regmap_update_bits(pcf85363->regmap, CTRL_OSCILLATOR,
|
||||
OSC_CAP_SEL, value);
|
||||
}
|
||||
|
||||
static int pcf85363_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct pcf85363 *pcf85363 = dev_get_drvdata(dev);
|
||||
|
@ -372,7 +402,7 @@ static int pcf85363_probe(struct i2c_client *client)
|
|||
.reg_write = pcf85363_nvram_write,
|
||||
},
|
||||
};
|
||||
int ret, i;
|
||||
int ret, i, err;
|
||||
|
||||
if (data)
|
||||
config = data;
|
||||
|
@ -394,18 +424,28 @@ static int pcf85363_probe(struct i2c_client *client)
|
|||
if (IS_ERR(pcf85363->rtc))
|
||||
return PTR_ERR(pcf85363->rtc);
|
||||
|
||||
err = pcf85363_load_capacitance(pcf85363, client->dev.of_node);
|
||||
if (err < 0)
|
||||
dev_warn(&client->dev, "failed to set xtal load capacitance: %d",
|
||||
err);
|
||||
|
||||
pcf85363->rtc->ops = &rtc_ops;
|
||||
pcf85363->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
|
||||
pcf85363->rtc->range_max = RTC_TIMESTAMP_END_2099;
|
||||
clear_bit(RTC_FEATURE_ALARM, pcf85363->rtc->features);
|
||||
|
||||
if (client->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
regmap_write(pcf85363->regmap, CTRL_FLAGS, 0);
|
||||
regmap_update_bits(pcf85363->regmap, CTRL_PIN_IO,
|
||||
PIN_IO_INTA_OUT, PIN_IO_INTAPM);
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, pcf85363_rtc_handle_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
"pcf85363", client);
|
||||
if (ret)
|
||||
dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
|
||||
|
|
|
@ -558,9 +558,14 @@ static int pcf8563_probe(struct i2c_client *client)
|
|||
pcf8563->rtc->set_start_time = true;
|
||||
|
||||
if (client->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, pcf8563_irq,
|
||||
IRQF_SHARED | IRQF_ONESHOT | IRQF_TRIGGER_LOW,
|
||||
IRQF_SHARED | IRQF_ONESHOT | irqflags,
|
||||
pcf8563_driver.driver.name, client);
|
||||
if (err) {
|
||||
dev_err(&client->dev, "unable to request IRQ %d\n",
|
||||
|
|
|
@ -1,8 +1,13 @@
|
|||
// SPDX-License-Identifier: GPL-2.0-only
|
||||
/* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
|
||||
/*
|
||||
* pm8xxx RTC driver
|
||||
*
|
||||
* Copyright (c) 2010-2011, Code Aurora Forum. All rights reserved.
|
||||
* Copyright (c) 2023, Linaro Limited
|
||||
*/
|
||||
#include <linux/of.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/nvmem-consumer.h>
|
||||
#include <linux/init.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/platform_device.h>
|
||||
|
@ -12,11 +17,7 @@
|
|||
#include <linux/slab.h>
|
||||
#include <linux/spinlock.h>
|
||||
|
||||
/* RTC Register offsets from RTC CTRL REG */
|
||||
#define PM8XXX_ALARM_CTRL_OFFSET 0x01
|
||||
#define PM8XXX_RTC_WRITE_OFFSET 0x02
|
||||
#define PM8XXX_RTC_READ_OFFSET 0x06
|
||||
#define PM8XXX_ALARM_RW_OFFSET 0x0A
|
||||
#include <asm/unaligned.h>
|
||||
|
||||
/* RTC_CTRL register bit fields */
|
||||
#define PM8xxx_RTC_ENABLE BIT(7)
|
||||
|
@ -27,12 +28,12 @@
|
|||
|
||||
/**
|
||||
* struct pm8xxx_rtc_regs - describe RTC registers per PMIC versions
|
||||
* @ctrl: base address of control register
|
||||
* @write: base address of write register
|
||||
* @read: base address of read register
|
||||
* @alarm_ctrl: base address of alarm control register
|
||||
* @alarm_ctrl2: base address of alarm control2 register
|
||||
* @alarm_rw: base address of alarm read-write register
|
||||
* @ctrl: address of control register
|
||||
* @write: base address of write registers
|
||||
* @read: base address of read registers
|
||||
* @alarm_ctrl: address of alarm control register
|
||||
* @alarm_ctrl2: address of alarm control2 register
|
||||
* @alarm_rw: base address of alarm read-write registers
|
||||
* @alarm_en: alarm enable mask
|
||||
*/
|
||||
struct pm8xxx_rtc_regs {
|
||||
|
@ -46,25 +47,135 @@ struct pm8xxx_rtc_regs {
|
|||
};
|
||||
|
||||
/**
|
||||
* struct pm8xxx_rtc - rtc driver internal structure
|
||||
* @rtc: rtc device for this driver.
|
||||
* @regmap: regmap used to access RTC registers
|
||||
* @allow_set_time: indicates whether writing to the RTC is allowed
|
||||
* @rtc_alarm_irq: rtc alarm irq number.
|
||||
* @regs: rtc registers description.
|
||||
* @rtc_dev: device structure.
|
||||
* @ctrl_reg_lock: spinlock protecting access to ctrl_reg.
|
||||
* struct pm8xxx_rtc - RTC driver internal structure
|
||||
* @rtc: RTC device
|
||||
* @regmap: regmap used to access registers
|
||||
* @allow_set_time: whether the time can be set
|
||||
* @alarm_irq: alarm irq number
|
||||
* @regs: register description
|
||||
* @dev: device structure
|
||||
* @nvmem_cell: nvmem cell for offset
|
||||
* @offset: offset from epoch in seconds
|
||||
*/
|
||||
struct pm8xxx_rtc {
|
||||
struct rtc_device *rtc;
|
||||
struct regmap *regmap;
|
||||
bool allow_set_time;
|
||||
int rtc_alarm_irq;
|
||||
int alarm_irq;
|
||||
const struct pm8xxx_rtc_regs *regs;
|
||||
struct device *rtc_dev;
|
||||
spinlock_t ctrl_reg_lock;
|
||||
struct device *dev;
|
||||
struct nvmem_cell *nvmem_cell;
|
||||
u32 offset;
|
||||
};
|
||||
|
||||
static int pm8xxx_rtc_read_nvmem_offset(struct pm8xxx_rtc *rtc_dd)
|
||||
{
|
||||
size_t len;
|
||||
void *buf;
|
||||
int rc;
|
||||
|
||||
buf = nvmem_cell_read(rtc_dd->nvmem_cell, &len);
|
||||
if (IS_ERR(buf)) {
|
||||
rc = PTR_ERR(buf);
|
||||
dev_dbg(rtc_dd->dev, "failed to read nvmem offset: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (len != sizeof(u32)) {
|
||||
dev_dbg(rtc_dd->dev, "unexpected nvmem cell size %zu\n", len);
|
||||
kfree(buf);
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
rtc_dd->offset = get_unaligned_le32(buf);
|
||||
|
||||
kfree(buf);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_write_nvmem_offset(struct pm8xxx_rtc *rtc_dd, u32 offset)
|
||||
{
|
||||
u8 buf[sizeof(u32)];
|
||||
int rc;
|
||||
|
||||
put_unaligned_le32(offset, buf);
|
||||
|
||||
rc = nvmem_cell_write(rtc_dd->nvmem_cell, buf, sizeof(buf));
|
||||
if (rc < 0) {
|
||||
dev_dbg(rtc_dd->dev, "failed to write nvmem offset: %d\n", rc);
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_read_offset(struct pm8xxx_rtc *rtc_dd)
|
||||
{
|
||||
if (!rtc_dd->nvmem_cell)
|
||||
return 0;
|
||||
|
||||
return pm8xxx_rtc_read_nvmem_offset(rtc_dd);
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_read_raw(struct pm8xxx_rtc *rtc_dd, u32 *secs)
|
||||
{
|
||||
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
|
||||
u8 value[NUM_8_BIT_RTC_REGS];
|
||||
unsigned int reg;
|
||||
int rc;
|
||||
|
||||
rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value));
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/*
|
||||
* Read the LSB again and check if there has been a carry over.
|
||||
* If there has, redo the read operation.
|
||||
*/
|
||||
rc = regmap_read(rtc_dd->regmap, regs->read, ®);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
|
||||
if (reg < value[0]) {
|
||||
rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value,
|
||||
sizeof(value));
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
*secs = get_unaligned_le32(value);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_update_offset(struct pm8xxx_rtc *rtc_dd, u32 secs)
|
||||
{
|
||||
u32 raw_secs;
|
||||
u32 offset;
|
||||
int rc;
|
||||
|
||||
if (!rtc_dd->nvmem_cell)
|
||||
return -ENODEV;
|
||||
|
||||
rc = pm8xxx_rtc_read_raw(rtc_dd, &raw_secs);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
offset = secs - raw_secs;
|
||||
|
||||
if (offset == rtc_dd->offset)
|
||||
return 0;
|
||||
|
||||
rc = pm8xxx_rtc_write_nvmem_offset(rtc_dd, offset);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rtc_dd->offset = offset;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
/*
|
||||
* Steps to write the RTC registers.
|
||||
* 1. Disable alarm if enabled.
|
||||
|
@ -74,269 +185,186 @@ struct pm8xxx_rtc {
|
|||
* 5. Enable rtc if disabled in step 2.
|
||||
* 6. Enable alarm if disabled in step 1.
|
||||
*/
|
||||
static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
static int __pm8xxx_rtc_set_time(struct pm8xxx_rtc *rtc_dd, u32 secs)
|
||||
{
|
||||
int rc, i;
|
||||
unsigned long secs, irq_flags;
|
||||
u8 value[NUM_8_BIT_RTC_REGS], alarm_enabled = 0, rtc_disabled = 0;
|
||||
unsigned int ctrl_reg, rtc_ctrl_reg;
|
||||
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
|
||||
u8 value[NUM_8_BIT_RTC_REGS];
|
||||
bool alarm_enabled;
|
||||
int rc;
|
||||
|
||||
if (!rtc_dd->allow_set_time)
|
||||
return -ENODEV;
|
||||
put_unaligned_le32(secs, value);
|
||||
|
||||
secs = rtc_tm_to_time64(tm);
|
||||
|
||||
dev_dbg(dev, "Seconds value to be written to RTC = %lu\n", secs);
|
||||
|
||||
for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
|
||||
value[i] = secs & 0xFF;
|
||||
secs >>= 8;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||
|
||||
rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
|
||||
rc = regmap_update_bits_check(rtc_dd->regmap, regs->alarm_ctrl,
|
||||
regs->alarm_en, 0, &alarm_enabled);
|
||||
if (rc)
|
||||
goto rtc_rw_fail;
|
||||
return rc;
|
||||
|
||||
if (ctrl_reg & regs->alarm_en) {
|
||||
alarm_enabled = 1;
|
||||
ctrl_reg &= ~regs->alarm_en;
|
||||
rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC Alarm control register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
}
|
||||
|
||||
/* Disable RTC H/w before writing on RTC register */
|
||||
rc = regmap_read(rtc_dd->regmap, regs->ctrl, &rtc_ctrl_reg);
|
||||
/* Disable RTC */
|
||||
rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE, 0);
|
||||
if (rc)
|
||||
goto rtc_rw_fail;
|
||||
|
||||
if (rtc_ctrl_reg & PM8xxx_RTC_ENABLE) {
|
||||
rtc_disabled = 1;
|
||||
rtc_ctrl_reg &= ~PM8xxx_RTC_ENABLE;
|
||||
rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_ctrl_reg);
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC control register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
}
|
||||
return rc;
|
||||
|
||||
/* Write 0 to Byte[0] */
|
||||
rc = regmap_write(rtc_dd->regmap, regs->write, 0);
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC write data register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Write Byte[1], Byte[2], Byte[3] */
|
||||
rc = regmap_bulk_write(rtc_dd->regmap, regs->write + 1,
|
||||
&value[1], sizeof(value) - 1);
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC write data register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Write Byte[0] */
|
||||
rc = regmap_write(rtc_dd->regmap, regs->write, value[0]);
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC write data register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Enable RTC H/w after writing on RTC register */
|
||||
if (rtc_disabled) {
|
||||
rtc_ctrl_reg |= PM8xxx_RTC_ENABLE;
|
||||
rc = regmap_write(rtc_dd->regmap, regs->ctrl, rtc_ctrl_reg);
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC control register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
}
|
||||
/* Enable RTC */
|
||||
rc = regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE,
|
||||
PM8xxx_RTC_ENABLE);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (alarm_enabled) {
|
||||
ctrl_reg |= regs->alarm_en;
|
||||
rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC Alarm control register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
}
|
||||
|
||||
rtc_rw_fail:
|
||||
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||
|
||||
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
|
||||
regs->alarm_en, regs->alarm_en);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_set_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||
u32 secs;
|
||||
int rc;
|
||||
|
||||
secs = rtc_tm_to_time64(tm);
|
||||
|
||||
if (rtc_dd->allow_set_time)
|
||||
rc = __pm8xxx_rtc_set_time(rtc_dd, secs);
|
||||
else
|
||||
rc = pm8xxx_rtc_update_offset(rtc_dd, secs);
|
||||
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
dev_dbg(dev, "set time: %ptRd %ptRt (%u + %u)\n", tm, tm,
|
||||
secs - rtc_dd->offset, rtc_dd->offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_read_time(struct device *dev, struct rtc_time *tm)
|
||||
{
|
||||
int rc;
|
||||
u8 value[NUM_8_BIT_RTC_REGS];
|
||||
unsigned long secs;
|
||||
unsigned int reg;
|
||||
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
|
||||
u32 secs;
|
||||
int rc;
|
||||
|
||||
rc = regmap_bulk_read(rtc_dd->regmap, regs->read, value, sizeof(value));
|
||||
if (rc) {
|
||||
dev_err(dev, "RTC read data register failed\n");
|
||||
rc = pm8xxx_rtc_read_raw(rtc_dd, &secs);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
/*
|
||||
* Read the LSB again and check if there has been a carry over.
|
||||
* If there is, redo the read operation.
|
||||
*/
|
||||
rc = regmap_read(rtc_dd->regmap, regs->read, ®);
|
||||
if (rc < 0) {
|
||||
dev_err(dev, "RTC read data register failed\n");
|
||||
return rc;
|
||||
}
|
||||
|
||||
if (unlikely(reg < value[0])) {
|
||||
rc = regmap_bulk_read(rtc_dd->regmap, regs->read,
|
||||
value, sizeof(value));
|
||||
if (rc) {
|
||||
dev_err(dev, "RTC read data register failed\n");
|
||||
return rc;
|
||||
}
|
||||
}
|
||||
|
||||
secs = value[0] | (value[1] << 8) | (value[2] << 16) |
|
||||
((unsigned long)value[3] << 24);
|
||||
|
||||
secs += rtc_dd->offset;
|
||||
rtc_time64_to_tm(secs, tm);
|
||||
|
||||
dev_dbg(dev, "secs = %lu, h:m:s == %ptRt, y-m-d = %ptRdr\n", secs, tm, tm);
|
||||
|
||||
dev_dbg(dev, "read time: %ptRd %ptRt (%u + %u)\n", tm, tm,
|
||||
secs - rtc_dd->offset, rtc_dd->offset);
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
int rc, i;
|
||||
u8 value[NUM_8_BIT_RTC_REGS];
|
||||
unsigned int ctrl_reg;
|
||||
unsigned long secs, irq_flags;
|
||||
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
|
||||
u8 value[NUM_8_BIT_RTC_REGS];
|
||||
u32 secs;
|
||||
int rc;
|
||||
|
||||
secs = rtc_tm_to_time64(&alarm->time);
|
||||
secs -= rtc_dd->offset;
|
||||
put_unaligned_le32(secs, value);
|
||||
|
||||
for (i = 0; i < NUM_8_BIT_RTC_REGS; i++) {
|
||||
value[i] = secs & 0xFF;
|
||||
secs >>= 8;
|
||||
}
|
||||
|
||||
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
|
||||
regs->alarm_en, 0);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
|
||||
sizeof(value));
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC ALARM register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
|
||||
rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
|
||||
if (rc)
|
||||
goto rtc_rw_fail;
|
||||
return rc;
|
||||
|
||||
if (alarm->enabled)
|
||||
ctrl_reg |= regs->alarm_en;
|
||||
else
|
||||
ctrl_reg &= ~regs->alarm_en;
|
||||
|
||||
rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC alarm control register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
if (alarm->enabled) {
|
||||
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
|
||||
regs->alarm_en, regs->alarm_en);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
dev_dbg(dev, "Alarm Set for h:m:s=%ptRt, y-m-d=%ptRdr\n",
|
||||
&alarm->time, &alarm->time);
|
||||
rtc_rw_fail:
|
||||
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||
return rc;
|
||||
dev_dbg(dev, "set alarm: %ptRd %ptRt\n", &alarm->time, &alarm->time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alarm)
|
||||
{
|
||||
int rc;
|
||||
unsigned int ctrl_reg;
|
||||
u8 value[NUM_8_BIT_RTC_REGS];
|
||||
unsigned long secs;
|
||||
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
|
||||
u8 value[NUM_8_BIT_RTC_REGS];
|
||||
unsigned int ctrl_reg;
|
||||
u32 secs;
|
||||
int rc;
|
||||
|
||||
rc = regmap_bulk_read(rtc_dd->regmap, regs->alarm_rw, value,
|
||||
sizeof(value));
|
||||
if (rc) {
|
||||
dev_err(dev, "RTC alarm time read failed\n");
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
secs = value[0] | (value[1] << 8) | (value[2] << 16) |
|
||||
((unsigned long)value[3] << 24);
|
||||
|
||||
secs = get_unaligned_le32(value);
|
||||
secs += rtc_dd->offset;
|
||||
rtc_time64_to_tm(secs, &alarm->time);
|
||||
|
||||
rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
|
||||
if (rc) {
|
||||
dev_err(dev, "Read from RTC alarm control register failed\n");
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
alarm->enabled = !!(ctrl_reg & PM8xxx_RTC_ALARM_ENABLE);
|
||||
|
||||
dev_dbg(dev, "Alarm set for - h:m:s=%ptRt, y-m-d=%ptRdr\n",
|
||||
&alarm->time, &alarm->time);
|
||||
dev_dbg(dev, "read alarm: %ptRd %ptRt\n", &alarm->time, &alarm->time);
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_alarm_irq_enable(struct device *dev, unsigned int enable)
|
||||
{
|
||||
int rc;
|
||||
unsigned long irq_flags;
|
||||
struct pm8xxx_rtc *rtc_dd = dev_get_drvdata(dev);
|
||||
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
|
||||
unsigned int ctrl_reg;
|
||||
u8 value[NUM_8_BIT_RTC_REGS] = {0};
|
||||
|
||||
spin_lock_irqsave(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||
|
||||
rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
|
||||
if (rc)
|
||||
goto rtc_rw_fail;
|
||||
unsigned int val;
|
||||
int rc;
|
||||
|
||||
if (enable)
|
||||
ctrl_reg |= regs->alarm_en;
|
||||
val = regs->alarm_en;
|
||||
else
|
||||
ctrl_reg &= ~regs->alarm_en;
|
||||
val = 0;
|
||||
|
||||
rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
|
||||
if (rc) {
|
||||
dev_err(dev, "Write to RTC control register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
|
||||
regs->alarm_en, val);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
/* Clear Alarm register */
|
||||
/* Clear alarm register */
|
||||
if (!enable) {
|
||||
rc = regmap_bulk_write(rtc_dd->regmap, regs->alarm_rw, value,
|
||||
sizeof(value));
|
||||
if (rc) {
|
||||
dev_err(dev, "Clear RTC ALARM register failed\n");
|
||||
goto rtc_rw_fail;
|
||||
}
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rtc_rw_fail:
|
||||
spin_unlock_irqrestore(&rtc_dd->ctrl_reg_lock, irq_flags);
|
||||
return rc;
|
||||
return 0;
|
||||
}
|
||||
|
||||
static const struct rtc_class_ops pm8xxx_rtc_ops = {
|
||||
|
@ -351,69 +379,31 @@ static irqreturn_t pm8xxx_alarm_trigger(int irq, void *dev_id)
|
|||
{
|
||||
struct pm8xxx_rtc *rtc_dd = dev_id;
|
||||
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
|
||||
unsigned int ctrl_reg;
|
||||
int rc;
|
||||
|
||||
rtc_update_irq(rtc_dd->rtc, 1, RTC_IRQF | RTC_AF);
|
||||
|
||||
spin_lock(&rtc_dd->ctrl_reg_lock);
|
||||
|
||||
/* Clear the alarm enable bit */
|
||||
rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl, &ctrl_reg);
|
||||
if (rc) {
|
||||
spin_unlock(&rtc_dd->ctrl_reg_lock);
|
||||
goto rtc_alarm_handled;
|
||||
}
|
||||
|
||||
ctrl_reg &= ~regs->alarm_en;
|
||||
|
||||
rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl, ctrl_reg);
|
||||
if (rc) {
|
||||
spin_unlock(&rtc_dd->ctrl_reg_lock);
|
||||
dev_err(rtc_dd->rtc_dev,
|
||||
"Write to alarm control register failed\n");
|
||||
goto rtc_alarm_handled;
|
||||
}
|
||||
|
||||
spin_unlock(&rtc_dd->ctrl_reg_lock);
|
||||
|
||||
/* Clear RTC alarm register */
|
||||
rc = regmap_read(rtc_dd->regmap, regs->alarm_ctrl2, &ctrl_reg);
|
||||
if (rc) {
|
||||
dev_err(rtc_dd->rtc_dev,
|
||||
"RTC Alarm control2 register read failed\n");
|
||||
goto rtc_alarm_handled;
|
||||
}
|
||||
|
||||
ctrl_reg |= PM8xxx_RTC_ALARM_CLEAR;
|
||||
rc = regmap_write(rtc_dd->regmap, regs->alarm_ctrl2, ctrl_reg);
|
||||
/* Disable alarm */
|
||||
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl,
|
||||
regs->alarm_en, 0);
|
||||
if (rc)
|
||||
dev_err(rtc_dd->rtc_dev,
|
||||
"Write to RTC Alarm control2 register failed\n");
|
||||
return IRQ_NONE;
|
||||
|
||||
/* Clear alarm status */
|
||||
rc = regmap_update_bits(rtc_dd->regmap, regs->alarm_ctrl2,
|
||||
PM8xxx_RTC_ALARM_CLEAR, 0);
|
||||
if (rc)
|
||||
return IRQ_NONE;
|
||||
|
||||
rtc_alarm_handled:
|
||||
return IRQ_HANDLED;
|
||||
}
|
||||
|
||||
static int pm8xxx_rtc_enable(struct pm8xxx_rtc *rtc_dd)
|
||||
{
|
||||
const struct pm8xxx_rtc_regs *regs = rtc_dd->regs;
|
||||
unsigned int ctrl_reg;
|
||||
int rc;
|
||||
|
||||
/* Check if the RTC is on, else turn it on */
|
||||
rc = regmap_read(rtc_dd->regmap, regs->ctrl, &ctrl_reg);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
if (!(ctrl_reg & PM8xxx_RTC_ENABLE)) {
|
||||
ctrl_reg |= PM8xxx_RTC_ENABLE;
|
||||
rc = regmap_write(rtc_dd->regmap, regs->ctrl, ctrl_reg);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
return 0;
|
||||
return regmap_update_bits(rtc_dd->regmap, regs->ctrl, PM8xxx_RTC_ENABLE,
|
||||
PM8xxx_RTC_ENABLE);
|
||||
}
|
||||
|
||||
static const struct pm8xxx_rtc_regs pm8921_regs = {
|
||||
|
@ -456,9 +446,6 @@ static const struct pm8xxx_rtc_regs pmk8350_regs = {
|
|||
.alarm_en = BIT(7),
|
||||
};
|
||||
|
||||
/*
|
||||
* Hardcoded RTC bases until IORESOURCE_REG mapping is figured out
|
||||
*/
|
||||
static const struct of_device_id pm8xxx_id_table[] = {
|
||||
{ .compatible = "qcom,pm8921-rtc", .data = &pm8921_regs },
|
||||
{ .compatible = "qcom,pm8058-rtc", .data = &pm8058_regs },
|
||||
|
@ -470,9 +457,9 @@ MODULE_DEVICE_TABLE(of, pm8xxx_id_table);
|
|||
|
||||
static int pm8xxx_rtc_probe(struct platform_device *pdev)
|
||||
{
|
||||
int rc;
|
||||
struct pm8xxx_rtc *rtc_dd;
|
||||
const struct of_device_id *match;
|
||||
struct pm8xxx_rtc *rtc_dd;
|
||||
int rc;
|
||||
|
||||
match = of_match_node(pm8xxx_id_table, pdev->dev.of_node);
|
||||
if (!match)
|
||||
|
@ -482,24 +469,33 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
|
|||
if (rtc_dd == NULL)
|
||||
return -ENOMEM;
|
||||
|
||||
/* Initialise spinlock to protect RTC control register */
|
||||
spin_lock_init(&rtc_dd->ctrl_reg_lock);
|
||||
|
||||
rtc_dd->regmap = dev_get_regmap(pdev->dev.parent, NULL);
|
||||
if (!rtc_dd->regmap) {
|
||||
dev_err(&pdev->dev, "Parent regmap unavailable.\n");
|
||||
if (!rtc_dd->regmap)
|
||||
return -ENXIO;
|
||||
}
|
||||
|
||||
rtc_dd->rtc_alarm_irq = platform_get_irq(pdev, 0);
|
||||
if (rtc_dd->rtc_alarm_irq < 0)
|
||||
rtc_dd->alarm_irq = platform_get_irq(pdev, 0);
|
||||
if (rtc_dd->alarm_irq < 0)
|
||||
return -ENXIO;
|
||||
|
||||
rtc_dd->allow_set_time = of_property_read_bool(pdev->dev.of_node,
|
||||
"allow-set-time");
|
||||
|
||||
rtc_dd->nvmem_cell = devm_nvmem_cell_get(&pdev->dev, "offset");
|
||||
if (IS_ERR(rtc_dd->nvmem_cell)) {
|
||||
rc = PTR_ERR(rtc_dd->nvmem_cell);
|
||||
if (rc != -ENOENT)
|
||||
return rc;
|
||||
rtc_dd->nvmem_cell = NULL;
|
||||
}
|
||||
|
||||
rtc_dd->regs = match->data;
|
||||
rtc_dd->rtc_dev = &pdev->dev;
|
||||
rtc_dd->dev = &pdev->dev;
|
||||
|
||||
if (!rtc_dd->allow_set_time) {
|
||||
rc = pm8xxx_rtc_read_offset(rtc_dd);
|
||||
if (rc)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = pm8xxx_rtc_enable(rtc_dd);
|
||||
if (rc)
|
||||
|
@ -509,7 +505,6 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
|
|||
|
||||
device_init_wakeup(&pdev->dev, 1);
|
||||
|
||||
/* Register the RTC device */
|
||||
rtc_dd->rtc = devm_rtc_allocate_device(&pdev->dev);
|
||||
if (IS_ERR(rtc_dd->rtc))
|
||||
return PTR_ERR(rtc_dd->rtc);
|
||||
|
@ -517,21 +512,18 @@ static int pm8xxx_rtc_probe(struct platform_device *pdev)
|
|||
rtc_dd->rtc->ops = &pm8xxx_rtc_ops;
|
||||
rtc_dd->rtc->range_max = U32_MAX;
|
||||
|
||||
/* Request the alarm IRQ */
|
||||
rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->rtc_alarm_irq,
|
||||
rc = devm_request_any_context_irq(&pdev->dev, rtc_dd->alarm_irq,
|
||||
pm8xxx_alarm_trigger,
|
||||
IRQF_TRIGGER_RISING,
|
||||
"pm8xxx_rtc_alarm", rtc_dd);
|
||||
if (rc < 0) {
|
||||
dev_err(&pdev->dev, "Request IRQ failed (%d)\n", rc);
|
||||
if (rc < 0)
|
||||
return rc;
|
||||
}
|
||||
|
||||
rc = devm_rtc_register_device(rtc_dd->rtc);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
rc = dev_pm_set_wake_irq(&pdev->dev, rtc_dd->rtc_alarm_irq);
|
||||
rc = dev_pm_set_wake_irq(&pdev->dev, rtc_dd->alarm_irq);
|
||||
if (rc)
|
||||
return rc;
|
||||
|
||||
|
@ -559,3 +551,4 @@ MODULE_ALIAS("platform:rtc-pm8xxx");
|
|||
MODULE_DESCRIPTION("PMIC8xxx RTC driver");
|
||||
MODULE_LICENSE("GPL v2");
|
||||
MODULE_AUTHOR("Anirudh Ghayal <aghayal@codeaurora.org>");
|
||||
MODULE_AUTHOR("Johan Hovold <johan@kernel.org>");
|
||||
|
|
|
@ -982,6 +982,12 @@ static int rv3028_probe(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id rv3028_i2c_acpi_match[] = {
|
||||
{ "MCRY3028" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, rv3028_i2c_acpi_match);
|
||||
|
||||
static const __maybe_unused struct of_device_id rv3028_of_match[] = {
|
||||
{ .compatible = "microcrystal,rv3028", },
|
||||
{ }
|
||||
|
@ -991,6 +997,7 @@ MODULE_DEVICE_TABLE(of, rv3028_of_match);
|
|||
static struct i2c_driver rv3028_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-rv3028",
|
||||
.acpi_match_table = rv3028_i2c_acpi_match,
|
||||
.of_match_table = of_match_ptr(rv3028_of_match),
|
||||
},
|
||||
.probe_new = rv3028_probe,
|
||||
|
|
|
@ -735,9 +735,14 @@ static int rv3029_probe(struct device *dev, struct regmap *regmap, int irq,
|
|||
return PTR_ERR(rv3029->rtc);
|
||||
|
||||
if (rv3029->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(dev))
|
||||
irqflags = 0;
|
||||
|
||||
rc = devm_request_threaded_irq(dev, rv3029->irq,
|
||||
NULL, rv3029_handle_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
"rv3029", dev);
|
||||
if (rc) {
|
||||
dev_warn(dev, "unable to request IRQ, alarms disabled\n");
|
||||
|
|
|
@ -930,9 +930,14 @@ static int rv3032_probe(struct i2c_client *client)
|
|||
return PTR_ERR(rv3032->rtc);
|
||||
|
||||
if (client->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
ret = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, rv3032_handle_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
"rv3032", rv3032);
|
||||
if (ret) {
|
||||
dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
|
||||
|
@ -975,6 +980,12 @@ static int rv3032_probe(struct i2c_client *client)
|
|||
return 0;
|
||||
}
|
||||
|
||||
static const struct acpi_device_id rv3032_i2c_acpi_match[] = {
|
||||
{ "MCRY3032" },
|
||||
{ }
|
||||
};
|
||||
MODULE_DEVICE_TABLE(acpi, rv3032_i2c_acpi_match);
|
||||
|
||||
static const __maybe_unused struct of_device_id rv3032_of_match[] = {
|
||||
{ .compatible = "microcrystal,rv3032", },
|
||||
{ }
|
||||
|
@ -984,6 +995,7 @@ MODULE_DEVICE_TABLE(of, rv3032_of_match);
|
|||
static struct i2c_driver rv3032_driver = {
|
||||
.driver = {
|
||||
.name = "rtc-rv3032",
|
||||
.acpi_match_table = rv3032_i2c_acpi_match,
|
||||
.of_match_table = of_match_ptr(rv3032_of_match),
|
||||
},
|
||||
.probe_new = rv3032_probe,
|
||||
|
|
|
@ -70,6 +70,7 @@ struct rv8803_data {
|
|||
struct mutex flags_lock;
|
||||
u8 ctrl;
|
||||
u8 backup;
|
||||
u8 alarm_invalid:1;
|
||||
enum rv8803_type type;
|
||||
};
|
||||
|
||||
|
@ -165,13 +166,13 @@ static int rv8803_regs_init(struct rv8803_data *rv8803)
|
|||
|
||||
static int rv8803_regs_configure(struct rv8803_data *rv8803);
|
||||
|
||||
static int rv8803_regs_reset(struct rv8803_data *rv8803)
|
||||
static int rv8803_regs_reset(struct rv8803_data *rv8803, bool full)
|
||||
{
|
||||
/*
|
||||
* The RV-8803 resets all registers to POR defaults after voltage-loss,
|
||||
* the Epson RTCs don't, so we manually reset the remainder here.
|
||||
*/
|
||||
if (rv8803->type == rx_8803 || rv8803->type == rx_8900) {
|
||||
if (full || rv8803->type == rx_8803 || rv8803->type == rx_8900) {
|
||||
int ret = rv8803_regs_init(rv8803);
|
||||
if (ret)
|
||||
return ret;
|
||||
|
@ -238,6 +239,11 @@ static int rv8803_get_time(struct device *dev, struct rtc_time *tm)
|
|||
u8 *date = date1;
|
||||
int ret, flags;
|
||||
|
||||
if (rv8803->alarm_invalid) {
|
||||
dev_warn(dev, "Corruption detected, data may be invalid.\n");
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
flags = rv8803_read_reg(rv8803->client, RV8803_FLAG);
|
||||
if (flags < 0)
|
||||
return flags;
|
||||
|
@ -313,12 +319,19 @@ static int rv8803_set_time(struct device *dev, struct rtc_time *tm)
|
|||
return flags;
|
||||
}
|
||||
|
||||
if (flags & RV8803_FLAG_V2F) {
|
||||
ret = rv8803_regs_reset(rv8803);
|
||||
if ((flags & RV8803_FLAG_V2F) || rv8803->alarm_invalid) {
|
||||
/*
|
||||
* If we sense corruption in the alarm registers, but see no
|
||||
* voltage loss flag, we can't rely on other registers having
|
||||
* sensible values. Reset them fully.
|
||||
*/
|
||||
ret = rv8803_regs_reset(rv8803, rv8803->alarm_invalid);
|
||||
if (ret) {
|
||||
mutex_unlock(&rv8803->flags_lock);
|
||||
return ret;
|
||||
}
|
||||
|
||||
rv8803->alarm_invalid = false;
|
||||
}
|
||||
|
||||
ret = rv8803_write_reg(rv8803->client, RV8803_FLAG,
|
||||
|
@ -344,15 +357,33 @@ static int rv8803_get_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
|||
if (flags < 0)
|
||||
return flags;
|
||||
|
||||
alarmvals[0] &= 0x7f;
|
||||
alarmvals[1] &= 0x3f;
|
||||
alarmvals[2] &= 0x3f;
|
||||
|
||||
if (!bcd_is_valid(alarmvals[0]) ||
|
||||
!bcd_is_valid(alarmvals[1]) ||
|
||||
!bcd_is_valid(alarmvals[2]))
|
||||
goto err_invalid;
|
||||
|
||||
alrm->time.tm_sec = 0;
|
||||
alrm->time.tm_min = bcd2bin(alarmvals[0] & 0x7f);
|
||||
alrm->time.tm_hour = bcd2bin(alarmvals[1] & 0x3f);
|
||||
alrm->time.tm_mday = bcd2bin(alarmvals[2] & 0x3f);
|
||||
alrm->time.tm_min = bcd2bin(alarmvals[0]);
|
||||
alrm->time.tm_hour = bcd2bin(alarmvals[1]);
|
||||
alrm->time.tm_mday = bcd2bin(alarmvals[2]);
|
||||
|
||||
alrm->enabled = !!(rv8803->ctrl & RV8803_CTRL_AIE);
|
||||
alrm->pending = (flags & RV8803_FLAG_AF) && alrm->enabled;
|
||||
|
||||
if ((unsigned int)alrm->time.tm_mday > 31 ||
|
||||
(unsigned int)alrm->time.tm_hour >= 24 ||
|
||||
(unsigned int)alrm->time.tm_min >= 60)
|
||||
goto err_invalid;
|
||||
|
||||
return 0;
|
||||
|
||||
err_invalid:
|
||||
rv8803->alarm_invalid = true;
|
||||
return -EINVAL;
|
||||
}
|
||||
|
||||
static int rv8803_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
|
||||
|
@ -641,9 +672,14 @@ static int rv8803_probe(struct i2c_client *client)
|
|||
return PTR_ERR(rv8803->rtc);
|
||||
|
||||
if (client->irq > 0) {
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
err = devm_request_threaded_irq(&client->dev, client->irq,
|
||||
NULL, rv8803_handle_irq,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
"rv8803", client);
|
||||
if (err) {
|
||||
dev_warn(&client->dev, "unable to request IRQ, alarms disabled\n");
|
||||
|
|
|
@ -10,7 +10,6 @@
|
|||
#include <linux/init.h>
|
||||
#include <linux/kernel.h>
|
||||
#include <linux/module.h>
|
||||
#include <linux/of_gpio.h>
|
||||
#include <linux/regmap.h>
|
||||
#include <linux/rtc.h>
|
||||
#include <linux/of.h>
|
||||
|
|
|
@ -394,10 +394,14 @@ static int rx8010_probe(struct i2c_client *client)
|
|||
return PTR_ERR(rx8010->rtc);
|
||||
|
||||
if (client->irq > 0) {
|
||||
dev_info(dev, "IRQ %d supplied\n", client->irq);
|
||||
unsigned long irqflags = IRQF_TRIGGER_LOW;
|
||||
|
||||
if (dev_fwnode(&client->dev))
|
||||
irqflags = 0;
|
||||
|
||||
err = devm_request_threaded_irq(dev, client->irq, NULL,
|
||||
rx8010_irq_1_handler,
|
||||
IRQF_TRIGGER_LOW | IRQF_ONESHOT,
|
||||
irqflags | IRQF_ONESHOT,
|
||||
"rx8010", client);
|
||||
if (err) {
|
||||
dev_err(dev, "unable to request IRQ\n");
|
||||
|
|
|
@ -136,7 +136,6 @@ struct sun6i_rtc_clk_data {
|
|||
unsigned int fixed_prescaler : 16;
|
||||
unsigned int has_prescaler : 1;
|
||||
unsigned int has_out_clk : 1;
|
||||
unsigned int export_iosc : 1;
|
||||
unsigned int has_losc_en : 1;
|
||||
unsigned int has_auto_swt : 1;
|
||||
};
|
||||
|
@ -271,8 +270,6 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
|
|||
/* Yes, I know, this is ugly. */
|
||||
sun6i_rtc = rtc;
|
||||
|
||||
/* Only read IOSC name from device tree if it is exported */
|
||||
if (rtc->data->export_iosc)
|
||||
of_property_read_string_index(node, "clock-output-names", 2,
|
||||
&iosc_name);
|
||||
|
||||
|
@ -315,13 +312,10 @@ static void __init sun6i_rtc_clk_init(struct device_node *node,
|
|||
goto err_register;
|
||||
}
|
||||
|
||||
clk_data->num = 2;
|
||||
clk_data->num = 3;
|
||||
clk_data->hws[0] = &rtc->hw;
|
||||
clk_data->hws[1] = __clk_get_hw(rtc->ext_losc);
|
||||
if (rtc->data->export_iosc) {
|
||||
clk_data->hws[2] = rtc->int_osc;
|
||||
clk_data->num = 3;
|
||||
}
|
||||
of_clk_add_hw_provider(node, of_clk_hw_onecell_get, clk_data);
|
||||
return;
|
||||
|
||||
|
@ -361,7 +355,6 @@ static const struct sun6i_rtc_clk_data sun8i_h3_rtc_data = {
|
|||
.fixed_prescaler = 32,
|
||||
.has_prescaler = 1,
|
||||
.has_out_clk = 1,
|
||||
.export_iosc = 1,
|
||||
};
|
||||
|
||||
static void __init sun8i_h3_rtc_clk_init(struct device_node *node)
|
||||
|
@ -379,7 +372,6 @@ static const struct sun6i_rtc_clk_data sun50i_h6_rtc_data = {
|
|||
.fixed_prescaler = 32,
|
||||
.has_prescaler = 1,
|
||||
.has_out_clk = 1,
|
||||
.export_iosc = 1,
|
||||
.has_losc_en = 1,
|
||||
.has_auto_swt = 1,
|
||||
};
|
||||
|
|
|
@ -14,8 +14,12 @@
|
|||
const_bin2bcd(x) : \
|
||||
_bin2bcd(x))
|
||||
|
||||
#define bcd_is_valid(x) \
|
||||
const_bcd_is_valid(x)
|
||||
|
||||
#define const_bcd2bin(x) (((x) & 0x0f) + ((x) >> 4) * 10)
|
||||
#define const_bin2bcd(x) ((((x) / 10) << 4) + (x) % 10)
|
||||
#define const_bcd_is_valid(x) (((x) & 0x0f) < 10 && ((x) >> 4) < 10)
|
||||
|
||||
unsigned _bcd2bin(unsigned char val) __attribute_const__;
|
||||
unsigned char _bin2bcd(unsigned val) __attribute_const__;
|
||||
|
|
Loading…
Reference in New Issue