Datasheets:
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31328.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31329.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31331.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31334.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31341B-MAX31341C.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31342.pdf
https://www.analog.com/media/en/technical-documentation/data-sheets/MAX31343.pdf
changelog:
since v7:
- Rename variables and functions to reduce delta with existing max31335
driver
- clean up some sparse warnings
- Keep old dt-binding name
since v6:
- Roll changes into max31335 driver that was landed while this was
in-flight.
- Adjusted code order and variable names to reduce the delta with
the max31335
since v5:
- change of maintainer
- use adi,ti-diode property
- deal with oscillator fail and releasing SWRST
since v4:
- dt-binding: add enum value "2" to aux-voltage-chargable
- dt-binding: remove adi,trickle-diode-enable
- dt-binding: change description of trickle-resistor-ohms
- dt-binding: reorder as in example schema
- parse "wakeup-source" when irq not requested
- remove limitation on max31328 irq and clockout
- remove error and warning messages during trickle charger setup
since v3:
- dt-binding: remove interrupt names.
- dt-binding: add description for "interrupts" property
- dt-binding: replace deprecated property "trickle-diode-disable"
by "aux-voltage-chargeable"
- dt-binding: add new property "adi,trickle-diode-enable"
- dt-binding: remove "wakeup-source"
- use clear_bit instead of __clear_bit
- use devm_of_clk_add_hw_provider instead of of_clk_add_provider
- use chip_desc pointer as driver data instead of enum.
since v2:
- add "break" to fix warning: unannotated fall-through
Reported-by: kernel test robot <[email protected]>
since v1:
- dt-binding: update title and description
- dt-binding: remove last example
- drop watchdog support
- support reading 12Hr format instead of forcing 24hr at probe time
- use "tm_year % 100" instead of range check
- refactor max313xx_init for readability
Ibrahim Tilki (2):
rtc: max31335: Add support for additional chips
dt-bindings: rtc: add max313xx RTCs
.../devicetree/bindings/rtc/adi,max31335.yaml | 88 +-
drivers/rtc/Kconfig | 2 +-
drivers/rtc/rtc-max31335.c | 969 +++++++++++++-----
3 files changed, 779 insertions(+), 280 deletions(-)
--
2.43.2
From: Ibrahim Tilki <[email protected]>
Add devicetree binding documentation for Analog Devices MAX313XX RTCs.
This combines the new models with the existing max31335 binding.
Signed-off-by: Ibrahim Tilki <[email protected]>
Signed-off-by: Zeynep Arslanbenzer <[email protected]>
Signed-off-by: Chris Packham <[email protected]>
---
.../devicetree/bindings/rtc/adi,max31335.yaml | 88 ++++++++++++++++---
1 file changed, 78 insertions(+), 10 deletions(-)
diff --git a/Documentation/devicetree/bindings/rtc/adi,max31335.yaml b/Documentation/devicetree/bindings/rtc/adi,max31335.yaml
index 0125cf6727cc..ad8d6fec9a2b 100644
--- a/Documentation/devicetree/bindings/rtc/adi,max31335.yaml
+++ b/Documentation/devicetree/bindings/rtc/adi,max31335.yaml
@@ -1,36 +1,54 @@
# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+# Copyright 2022 Analog Devices Inc.
%YAML 1.2
---
$id: http://devicetree.org/schemas/rtc/adi,max31335.yaml#
$schema: http://devicetree.org/meta-schemas/core.yaml#
-title: Analog Devices MAX31335 RTC
+title: Analog Devices MAX313XX series I2C RTCs
maintainers:
- Antoniu Miclaus <[email protected]>
+ - Chris Packham <[email protected]>
-description:
- Analog Devices MAX31335 I2C RTC ±2ppm Automotive Real-Time Clock with
- Integrated MEMS Resonator.
-
-allOf:
- - $ref: rtc.yaml#
+description: Analog Devices MAX313XX series I2C RTCs.
properties:
compatible:
- const: adi,max31335
+ enum:
+ - adi,max31328
+ - adi,max31329
+ - adi,max31331
+ - adi,max31334
+ - adi,max31335
+ - adi,max31341
+ - adi,max31342
+ - adi,max31343
reg:
- maxItems: 1
+ description: I2C address of the RTC
+ items:
+ - enum: [0x68, 0x69]
interrupts:
+ description:
+ Alarm1 interrupt line of the RTC. Some of the RTCs have two interrupt
+ lines and alarm1 interrupt muxing depends on the clockin/clockout
+ configuration.
maxItems: 1
"#clock-cells":
description:
- RTC can be used as a clock source through its clock output pin.
+ RTC can be used as a clock source through its clock output pin when
+ supplied.
const: 0
+ clocks:
+ description:
+ RTC uses this clock for clock input when supplied. Clock has to provide
+ one of these four frequencies - 1Hz, 50Hz, 60Hz or 32.768kHz.
+ maxItems: 1
+
adi,tc-diode:
description:
Select the diode configuration for the trickle charger.
@@ -48,6 +66,56 @@ required:
- compatible
- reg
+allOf:
+ - $ref: rtc.yaml#
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - adi,max31328
+ - adi,max31342
+
+ then:
+ properties:
+ aux-voltage-chargeable: false
+ trickle-resistor-ohms: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - adi,max31328
+ - adi,max31331
+ - adi,max31334
+ - adi,max31335
+ - adi,max31343
+
+ then:
+ properties:
+ clocks: false
+
+ - if:
+ properties:
+ compatible:
+ contains:
+ enum:
+ - adi,max31341
+ - adi,max31342
+
+ then:
+ properties:
+ reg:
+ items:
+ - const: 0x69
+
+ else:
+ properties:
+ reg:
+ items:
+ - const: 0x68
+
unevaluatedProperties: false
examples:
--
2.43.2
From: Ibrahim Tilki <[email protected]>
Add support for more members of the Analog Devices MAX313XX series RTCs.
Signed-off-by: Ibrahim Tilki <[email protected]>
Signed-off-by: Zeynep Arslanbenzer <[email protected]>
Signed-off-by: Chris Packham <[email protected]>
---
drivers/rtc/Kconfig | 2 +-
drivers/rtc/rtc-max31335.c | 969 +++++++++++++++++++++++++++----------
2 files changed, 701 insertions(+), 270 deletions(-)
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index e37a4341f442..b30e384d0d3c 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -381,7 +381,7 @@ config RTC_DRV_MAX31335
select REGMAP_I2C
help
If you say yes here you get support for the Analog Devices
- MAX31335.
+ MAX31335/31328/31329/31331/31334/31341/31342/31343.
This driver can also be built as a module. If so, the module
will be called rtc-max31335.
diff --git a/drivers/rtc/rtc-max31335.c b/drivers/rtc/rtc-max31335.c
index 402fda8fd548..9ced05231791 100644
--- a/drivers/rtc/rtc-max31335.c
+++ b/drivers/rtc/rtc-max31335.c
@@ -1,6 +1,6 @@
// SPDX-License-Identifier: GPL-2.0
/*
- * RTC driver for the MAX31335
+ * RTC driver for the MAX313XX series
*
* Copyright (C) 2023 Analog Devices
*
@@ -24,232 +24,398 @@
#include <linux/rtc.h>
#include <linux/util_macros.h>
-/* MAX31335 Register Map */
-#define MAX31335_STATUS1 0x00
-#define MAX31335_INT_EN1 0x01
-#define MAX31335_STATUS2 0x02
-#define MAX31335_INT_EN2 0x03
-#define MAX31335_RTC_RESET 0x04
-#define MAX31335_RTC_CONFIG 0x05
-#define MAX31335_RTC_CONFIG2 0x06
-#define MAX31335_TIMESTAMP_CONFIG 0x07
-#define MAX31335_TIMER_CONFIG 0x08
-#define MAX31335_SECONDS_1_128 0x09
-#define MAX31335_SECONDS 0x0A
-#define MAX31335_MINUTES 0x0B
-#define MAX31335_HOURS 0x0C
-#define MAX31335_DAY 0x0D
-#define MAX31335_DATE 0x0E
-#define MAX31335_MONTH 0x0F
-#define MAX31335_YEAR 0x0F
-#define MAX31335_ALM1_SEC 0x11
-#define MAX31335_ALM1_MIN 0x12
-#define MAX31335_ALM1_HRS 0x13
-#define MAX31335_ALM1_DAY_DATE 0x14
-#define MAX31335_ALM1_MON 0x15
-#define MAX31335_ALM1_YEAR 0x16
-#define MAX31335_ALM2_MIN 0x17
-#define MAX31335_ALM2_HRS 0x18
-#define MAX31335_ALM2_DAY_DATE 0x19
-#define MAX31335_TIMER_COUNT 0x1A
-#define MAX31335_TIMER_INIT 0x1B
-#define MAX31335_PWR_MGMT 0x1C
-#define MAX31335_TRICKLE_REG 0x1D
-#define MAX31335_AGING_OFFSET 0x1E
-#define MAX31335_TS_CONFIG 0x30
-#define MAX31335_TEMP_ALARM_HIGH_MSB 0x31
-#define MAX31335_TEMP_ALARM_HIGH_LSB 0x32
-#define MAX31335_TEMP_ALARM_LOW_MSB 0x33
-#define MAX31335_TEMP_ALARM_LOW_LSB 0x34
-#define MAX31335_TEMP_DATA_MSB 0x35
-#define MAX31335_TEMP_DATA_LSB 0x36
-#define MAX31335_TS0_SEC_1_128 0x40
-#define MAX31335_TS0_SEC 0x41
-#define MAX31335_TS0_MIN 0x42
-#define MAX31335_TS0_HOUR 0x43
-#define MAX31335_TS0_DATE 0x44
-#define MAX31335_TS0_MONTH 0x45
-#define MAX31335_TS0_YEAR 0x46
-#define MAX31335_TS0_FLAGS 0x47
-#define MAX31335_TS1_SEC_1_128 0x48
-#define MAX31335_TS1_SEC 0x49
-#define MAX31335_TS1_MIN 0x4A
-#define MAX31335_TS1_HOUR 0x4B
-#define MAX31335_TS1_DATE 0x4C
-#define MAX31335_TS1_MONTH 0x4D
-#define MAX31335_TS1_YEAR 0x4E
-#define MAX31335_TS1_FLAGS 0x4F
-#define MAX31335_TS2_SEC_1_128 0x50
-#define MAX31335_TS2_SEC 0x51
-#define MAX31335_TS2_MIN 0x52
-#define MAX31335_TS2_HOUR 0x53
-#define MAX31335_TS2_DATE 0x54
-#define MAX31335_TS2_MONTH 0x55
-#define MAX31335_TS2_YEAR 0x56
-#define MAX31335_TS2_FLAGS 0x57
-#define MAX31335_TS3_SEC_1_128 0x58
-#define MAX31335_TS3_SEC 0x59
-#define MAX31335_TS3_MIN 0x5A
-#define MAX31335_TS3_HOUR 0x5B
-#define MAX31335_TS3_DATE 0x5C
-#define MAX31335_TS3_MONTH 0x5D
-#define MAX31335_TS3_YEAR 0x5E
-#define MAX31335_TS3_FLAGS 0x5F
+/* common registers */
+#define MAX313XX_INT_ALARM1 BIT(0)
+#define MAX313XX_HRS_F_AM_PM BIT(5)
+#define MAX313XX_HRS_F_12_24 BIT(6)
+#define MAX313XX_MONTH_CENTURY BIT(7)
-/* MAX31335_STATUS1 Bit Definitions */
-#define MAX31335_STATUS1_PSDECT BIT(7)
-#define MAX31335_STATUS1_OSF BIT(6)
-#define MAX31335_STATUS1_PFAIL BIT(5)
-#define MAX31335_STATUS1_VBATLOW BIT(4)
-#define MAX31335_STATUS1_DIF BIT(3)
-#define MAX31335_STATUS1_TIF BIT(2)
-#define MAX31335_STATUS1_A2F BIT(1)
-#define MAX31335_STATUS1_A1F BIT(0)
+#define MAX313XX_TIME_SIZE 0x07
-/* MAX31335_INT_EN1 Bit Definitions */
-#define MAX31335_INT_EN1_DOSF BIT(6)
-#define MAX31335_INT_EN1_PFAILE BIT(5)
-#define MAX31335_INT_EN1_VBATLOWE BIT(4)
-#define MAX31335_INT_EN1_DIE BIT(3)
-#define MAX31335_INT_EN1_TIE BIT(2)
-#define MAX31335_INT_EN1_A2IE BIT(1)
-#define MAX31335_INT_EN1_A1IE BIT(0)
+/* device specific registers */
+#define MAX3134X_CFG2_REG 0x01
+#define MAX3134X_CFG2_SET_RTC BIT(1)
-/* MAX31335_STATUS2 Bit Definitions */
-#define MAX31335_STATUS2_TEMP_RDY BIT(2)
-#define MAX31335_STATUS2_OTF BIT(1)
-#define MAX31335_STATUS2_UTF BIT(0)
+#define MAX31341_TRICKLE_RES_MASK GENMASK(1, 0)
+#define MAX31341_TRICKLE_DIODE_EN BIT(2)
+#define MAX31341_TRICKLE_ENABLE_BIT BIT(3)
+#define MAX31341_POWER_MGMT_REG 0x56
+#define MAX31341_POWER_MGMT_TRICKLE_BIT BIT(0)
-/* MAX31335_INT_EN2 Bit Definitions */
-#define MAX31335_INT_EN2_TEMP_RDY_EN BIT(2)
-#define MAX31335_INT_EN2_OTIE BIT(1)
-#define MAX31335_INT_EN2_UTIE BIT(0)
+#define MAX3133X_TRICKLE_RES_MASK GENMASK(2, 1)
+#define MAX3133X_TRICKLE_DIODE_EN BIT(3)
+#define MAX3133X_TRICKLE_ENABLE_BIT BIT(0)
-/* MAX31335_RTC_RESET Bit Definitions */
-#define MAX31335_RTC_RESET_SWRST BIT(0)
+#define MAX31329_TRICKLE_ENABLE_BIT BIT(7)
+#define MAX31343_TRICKLE_ENABLE_MASK GENMASK(7, 4)
+#define MAX31343_TRICKLE_ENABLE_CODE 5
+#define MAX31329_43_TRICKLE_RES_MASK GENMASK(1, 0)
+#define MAX31329_43_TRICKLE_DIODE_EN BIT(2)
-/* MAX31335_RTC_CONFIG1 Bit Definitions */
-#define MAX31335_RTC_CONFIG1_EN_IO BIT(6)
-#define MAX31335_RTC_CONFIG1_A1AC GENMASK(5, 4)
-#define MAX31335_RTC_CONFIG1_DIP BIT(3)
-#define MAX31335_RTC_CONFIG1_I2C_TIMEOUT BIT(1)
-#define MAX31335_RTC_CONFIG1_EN_OSC BIT(0)
+#define MAX31329_CONFIG2_REG 0x04
+#define MAX31329_CONFIG2_CLKIN_EN BIT(2)
+#define MAX31329_CONFIG2_CLKIN_FREQ GENMASK(1, 0)
-/* MAX31335_RTC_CONFIG2 Bit Definitions */
-#define MAX31335_RTC_CONFIG2_ENCLKO BIT(2)
-#define MAX31335_RTC_CONFIG2_CLKO_HZ GENMASK(1, 0)
+#define MAX31341_42_CONFIG1_REG 0x00
+#define MAX31341_42_CONFIG1_CLKIN_EN BIT(7)
+#define MAX31341_42_CONFIG1_CLKIN_FREQ GENMASK(5, 4)
+#define MAX31341_42_CONFIG1_OSC_DISABLE BIT(3)
+#define MAX31341_42_CONFIG1_SWRST BIT(0)
-/* MAX31335_TIMESTAMP_CONFIG Bit Definitions */
-#define MAX31335_TIMESTAMP_CONFIG_TSVLOW BIT(5)
-#define MAX31335_TIMESTAMP_CONFIG_TSPWM BIT(4)
-#define MAX31335_TIMESTAMP_CONFIG_TSDIN BIT(3)
-#define MAX31335_TIMESTAMP_CONFIG_TSOW BIT(2)
-#define MAX31335_TIMESTAMP_CONFIG_TSR BIT(1)
-#define MAX31335_TIMESTAMP_CONFIG_TSE BIT(0)
+enum max313xx_ids {
+ ID_MAX31328,
+ ID_MAX31329,
+ ID_MAX31331,
+ ID_MAX31334,
+ ID_MAX31335,
+ ID_MAX31341,
+ ID_MAX31342,
+ ID_MAX31343,
+ MAX313XX_ID_NR
+};
-/* MAX31335_TIMER_CONFIG Bit Definitions */
-#define MAX31335_TIMER_CONFIG_TE BIT(4)
-#define MAX31335_TIMER_CONFIG_TPAUSE BIT(3)
-#define MAX31335_TIMER_CONFIG_TRPT BIT(2)
-#define MAX31335_TIMER_CONFIG_TFS GENMASK(1, 0)
+struct clkout_cfg {
+ const int *freq_avail;
+ u8 freq_size;
+ u8 freq_pos;
+ u8 reg;
+ u8 en_bit;
+ bool en_invert;
+};
-/* MAX31335_HOURS Bit Definitions */
-#define MAX31335_HOURS_F_24_12 BIT(6)
-#define MAX31335_HOURS_HR_20_AM_PM BIT(5)
+struct chip_desc {
+ struct clkout_cfg *clkout;
+ const char *clkout_name;
+ u8 sec_reg;
+ u8 alarm1_sec_reg;
-/* MAX31335_MONTH Bit Definitions */
-#define MAX31335_MONTH_CENTURY BIT(7)
+ u8 int_en_reg;
+ u8 int_status_reg;
+ u8 osf_bit;
-/* MAX31335_PWR_MGMT Bit Definitions */
-#define MAX31335_PWR_MGMT_PFVT BIT(0)
+ u8 ram_reg;
+ u8 ram_size;
-/* MAX31335_TRICKLE_REG Bit Definitions */
-#define MAX31335_TRICKLE_REG_TRICKLE GENMASK(3, 1)
-#define MAX31335_TRICKLE_REG_EN_TRICKLE BIT(0)
+ u8 temp_reg;
-/* MAX31335_TS_CONFIG Bit Definitions */
-#define MAX31335_TS_CONFIG_AUTO BIT(4)
-#define MAX31335_TS_CONFIG_CONVERT_T BIT(3)
-#define MAX31335_TS_CONFIG_TSINT GENMASK(2, 0)
+ u8 trickle_reg;
-/* MAX31335_TS_FLAGS Bit Definitions */
-#define MAX31335_TS_FLAGS_VLOWF BIT(3)
-#define MAX31335_TS_FLAGS_VBATF BIT(2)
-#define MAX31335_TS_FLAGS_VCCF BIT(1)
-#define MAX31335_TS_FLAGS_DINF BIT(0)
-
-/* MAX31335 Miscellaneous Definitions */
-#define MAX31335_TRICKLE_SCHOTTKY_DIODE 1
-#define MAX31335_TRICKLE_STANDARD_DIODE 4
-#define MAX31335_RAM_SIZE 32
-#define MAX31335_TIME_SIZE 0x07
+ u8 rst_reg;
+ u8 rst_bit;
+};
#define clk_hw_to_max31335(_hw) container_of(_hw, struct max31335_data, clkout)
struct max31335_data {
+ enum max313xx_ids id;
struct regmap *regmap;
struct rtc_device *rtc;
struct clk_hw clkout;
+ struct clk *clkin;
+ const struct chip_desc *chip;
+ int irq;
};
-static const int max31335_clkout_freq[] = { 1, 64, 1024, 32768 };
+static const int max313xx_clkin_freq[] = { 1, 50, 60, 32768 };
-static const u16 max31335_trickle_resistors[] = {3000, 6000, 11000};
+static const int max31328_clkout_freq[] = { 1, 1024, 4096, 8192 };
+static const int max31329_clkout_freq[] = { 1, 4096, 8192, 32768 };
+static const int max3133x_clkout_freq[] = { 1, 64, 1024, 32768 };
+static const int max31341_42_clkout_freq[] = { 1, 50, 60, 32768 };
+static const int max31343_clkout_freq[] = { 1, 2, 4, 8, 16, 32, 64, 128, 32875 };
+
+static struct clkout_cfg max31328_clkout = {
+ .freq_avail = max31328_clkout_freq,
+ .freq_size = ARRAY_SIZE(max31328_clkout_freq),
+ .freq_pos = 3,
+ .reg = 0x0E,
+ .en_bit = BIT(3),
+ .en_invert = true,
+};
+
+static struct clkout_cfg max31329_clkout = {
+ .freq_avail = max31329_clkout_freq,
+ .freq_size = ARRAY_SIZE(max31329_clkout_freq),
+ .freq_pos = 5,
+ .reg = 0x04,
+ .en_bit = BIT(7),
+};
+
+static struct clkout_cfg max3133x_clkout = {
+ .freq_avail = max3133x_clkout_freq,
+ .freq_size = ARRAY_SIZE(max3133x_clkout_freq),
+ .freq_pos = 0,
+ .reg = 0x04,
+ .en_bit = BIT(2),
+};
+
+static struct clkout_cfg max31335_clkout = {
+ .freq_avail = max3133x_clkout_freq,
+ .freq_size = ARRAY_SIZE(max3133x_clkout_freq),
+ .freq_pos = 0,
+ .reg = 0x06,
+ .en_bit = BIT(2),
+};
+
+static struct clkout_cfg max31341_42_clkout = {
+ .freq_avail = max31341_42_clkout_freq,
+ .freq_size = ARRAY_SIZE(max31341_42_clkout_freq),
+ .freq_pos = 1,
+ .reg = 0x00,
+ .en_bit = BIT(6),
+ .en_invert = true,
+};
+
+static struct clkout_cfg max31343_clkout = {
+ .freq_avail = max31343_clkout_freq,
+ .freq_size = ARRAY_SIZE(max31343_clkout_freq),
+ .freq_pos = 3,
+ .reg = 0x04,
+ .en_bit = BIT(7),
+};
+
+static const struct chip_desc chip[MAX313XX_ID_NR] = {
+ [ID_MAX31328] = {
+ .int_en_reg = 0x0E,
+ .int_status_reg = 0x0F,
+ .osf_bit = BIT(7),
+ .sec_reg = 0x00,
+ .alarm1_sec_reg = 0x07,
+ .temp_reg = 0x11,
+ .clkout = &max31328_clkout,
+ .clkout_name = "max31328-sqw",
+ },
+ [ID_MAX31329] = {
+ .int_en_reg = 0x01,
+ .int_status_reg = 0x00,
+ .osf_bit = BIT(6),
+ .sec_reg = 0x06,
+ .alarm1_sec_reg = 0x0D,
+ .ram_reg = 0x22,
+ .ram_size = 64,
+ .trickle_reg = 0x19,
+ .clkout = &max31329_clkout,
+ .clkout_name = "max31329-clkout",
+ .rst_reg = 0x02,
+ .rst_bit = BIT(0),
+ },
+ [ID_MAX31331] = {
+ .int_en_reg = 0x01,
+ .int_status_reg = 0x00,
+ .osf_bit = BIT(6),
+ .sec_reg = 0x08,
+ .alarm1_sec_reg = 0x0F,
+ .ram_reg = 0x20,
+ .ram_size = 32,
+ .trickle_reg = 0x1B,
+ .clkout = &max3133x_clkout,
+ .clkout_name = "max31331-clkout",
+ .rst_reg = 0x02,
+ .rst_bit = BIT(0),
+ },
+ [ID_MAX31334] = {
+ .int_en_reg = 0x01,
+ .int_status_reg = 0x00,
+ .osf_bit = BIT(6),
+ .sec_reg = 0x09,
+ .alarm1_sec_reg = 0x10,
+ .ram_reg = 0x30,
+ .ram_size = 32,
+ .trickle_reg = 0x1E,
+ .clkout = &max3133x_clkout,
+ .clkout_name = "max31334-clkout",
+ .rst_reg = 0x02,
+ .rst_bit = BIT(0),
+ },
+ [ID_MAX31335] = {
+ .int_en_reg = 0x01,
+ .int_status_reg = 0x00,
+ .osf_bit = BIT(6),
+ .sec_reg = 0x0a,
+ .alarm1_sec_reg = 0x11,
+ .ram_reg = 0x40,
+ .ram_size = 32,
+ .temp_reg = 0x35,
+ .trickle_reg = 0x1d,
+ .clkout = &max31335_clkout,
+ .clkout_name = "max31335-clkout",
+ .rst_reg = 0x04,
+ .rst_bit = BIT(0),
+ },
+ [ID_MAX31341] = {
+ .int_en_reg = 0x04,
+ .int_status_reg = 0x05,
+ .osf_bit = BIT(6),
+ .sec_reg = 0x06,
+ .alarm1_sec_reg = 0x0D,
+ .ram_reg = 0x16,
+ .ram_size = 64,
+ .trickle_reg = 0x57,
+ .clkout = &max31341_42_clkout,
+ .clkout_name = "max31341-clkout",
+ .rst_reg = 0x00,
+ .rst_bit = BIT(0),
+ },
+ [ID_MAX31342] = {
+ .int_en_reg = 0x04,
+ .int_status_reg = 0x05,
+ .osf_bit = BIT(6),
+ .sec_reg = 0x06,
+ .alarm1_sec_reg = 0x0D,
+ .clkout = &max31341_42_clkout,
+ .clkout_name = "max31342-clkout",
+ .rst_reg = 0x00,
+ .rst_bit = BIT(0),
+ },
+ [ID_MAX31343] = {
+ .int_en_reg = 0x01,
+ .int_status_reg = 0x00,
+ .osf_bit = BIT(6),
+ .sec_reg = 0x06,
+ .alarm1_sec_reg = 0x0D,
+ .ram_reg = 0x22,
+ .ram_size = 64,
+ .temp_reg = 0x1A,
+ .trickle_reg = 0x19,
+ .clkout = &max31343_clkout,
+ .clkout_name = "max31343-clko",
+ .rst_reg = 0x02,
+ .rst_bit = BIT(0),
+ },
+};
+
+static const u32 max313xx_trickle_ohms[] = { 3000, 6000, 11000 };
static bool max31335_volatile_reg(struct device *dev, unsigned int reg)
{
+ struct max31335_data *max31335 = dev_get_drvdata(dev);
+ const struct chip_desc *chip = max31335->chip;
+
/* time keeping registers */
- if (reg >= MAX31335_SECONDS &&
- reg < MAX31335_SECONDS + MAX31335_TIME_SIZE)
+ if (reg >= chip->sec_reg && reg < chip->sec_reg + MAX313XX_TIME_SIZE)
return true;
/* interrupt status register */
- if (reg == MAX31335_INT_EN1_A1IE)
+ if (reg == chip->int_status_reg)
return true;
/* temperature registers */
- if (reg == MAX31335_TEMP_DATA_MSB || reg == MAX31335_TEMP_DATA_LSB)
- return true;
-
- return false;
+ return chip->temp_reg &&
+ (reg == chip->temp_reg || reg == chip->temp_reg + 1);
}
static const struct regmap_config regmap_config = {
.reg_bits = 8,
.val_bits = 8,
- .max_register = 0x5F,
+ .cache_type = REGCACHE_RBTREE,
.volatile_reg = max31335_volatile_reg,
};
+static int max31335_get_hour(u8 hour_reg)
+{
+ int hour;
+
+ /* 24Hr mode */
+ if (!FIELD_GET(MAX313XX_HRS_F_12_24, hour_reg))
+ return bcd2bin(hour_reg & 0x3f);
+
+ /* 12Hr mode */
+ hour = bcd2bin(hour_reg & 0x1f);
+ if (hour == 12)
+ hour = 0;
+
+ if (FIELD_GET(MAX313XX_HRS_F_AM_PM, hour_reg))
+ hour += 12;
+
+ return hour;
+}
+
static int max31335_read_time(struct device *dev, struct rtc_time *tm)
{
struct max31335_data *max31335 = dev_get_drvdata(dev);
u8 date[7];
int ret;
+ unsigned int status;
- ret = regmap_bulk_read(max31335->regmap, MAX31335_SECONDS, date,
- sizeof(date));
+ ret = regmap_read(max31335->regmap, max31335->chip->int_status_reg, &status);
+ if (ret)
+ return ret;
+
+ if (status & max31335->chip->osf_bit)
+ return -EINVAL;
+
+ ret = regmap_bulk_read(max31335->regmap, max31335->chip->sec_reg, date, 7);
if (ret)
return ret;
tm->tm_sec = bcd2bin(date[0] & 0x7f);
tm->tm_min = bcd2bin(date[1] & 0x7f);
- tm->tm_hour = bcd2bin(date[2] & 0x3f);
+ tm->tm_hour = max31335_get_hour(date[2]);
tm->tm_wday = bcd2bin(date[3] & 0x7) - 1;
tm->tm_mday = bcd2bin(date[4] & 0x3f);
tm->tm_mon = bcd2bin(date[5] & 0x1f) - 1;
tm->tm_year = bcd2bin(date[6]) + 100;
- if (FIELD_GET(MAX31335_MONTH_CENTURY, date[5]))
+ if (FIELD_GET(MAX313XX_MONTH_CENTURY, date[5]))
tm->tm_year += 100;
return 0;
}
+static int max31335_pre_set_time(struct max31335_data *max31335)
+{
+ if (!max31335->clkin)
+ return 0;
+
+ /* Clkin needs to be disabled before setting time. */
+ switch (max31335->id) {
+ case ID_MAX31341:
+ case ID_MAX31342:
+ return regmap_clear_bits(max31335->regmap,
+ MAX31341_42_CONFIG1_REG,
+ MAX31341_42_CONFIG1_CLKIN_EN);
+ default:
+ return 0;
+ }
+}
+
+static int max31335_post_set_time(struct max31335_data *max31335)
+{
+ int ret;
+
+ switch (max31335->id) {
+ case ID_MAX31341:
+ case ID_MAX31342:
+ ret = regmap_set_bits(max31335->regmap, MAX3134X_CFG2_REG,
+ MAX3134X_CFG2_SET_RTC);
+ if (ret)
+ return ret;
+
+ fsleep(10000);
+
+ ret = regmap_clear_bits(max31335->regmap, MAX3134X_CFG2_REG,
+ MAX3134X_CFG2_SET_RTC);
+ if (ret)
+ return ret;
+
+ if (max31335->clkin)
+ ret = regmap_set_bits(max31335->regmap,
+ MAX31341_42_CONFIG1_REG,
+ MAX31341_42_CONFIG1_CLKIN_EN);
+
+ break;
+ default:
+ return 0;
+ }
+
+ return ret;
+}
+
static int max31335_set_time(struct device *dev, struct rtc_time *tm)
{
struct max31335_data *max31335 = dev_get_drvdata(dev);
u8 date[7];
+ int ret;
date[0] = bin2bcd(tm->tm_sec);
date[1] = bin2bcd(tm->tm_min);
@@ -260,20 +426,35 @@ static int max31335_set_time(struct device *dev, struct rtc_time *tm)
date[6] = bin2bcd(tm->tm_year % 100);
if (tm->tm_year >= 200)
- date[5] |= FIELD_PREP(MAX31335_MONTH_CENTURY, 1);
+ date[5] |= FIELD_PREP(MAX313XX_MONTH_CENTURY, 1);
- return regmap_bulk_write(max31335->regmap, MAX31335_SECONDS, date,
- sizeof(date));
+ ret = max31335_pre_set_time(max31335);
+ if (ret)
+ return ret;
+
+ if (max31335->chip->rst_bit) {
+ ret = regmap_clear_bits(max31335->regmap, max31335->chip->rst_reg,
+ max31335->chip->rst_bit);
+ if (ret)
+ return ret;
+ }
+
+ ret = regmap_bulk_write(max31335->regmap, max31335->chip->sec_reg, date, 7);
+ if (ret)
+ return ret;
+
+ return max31335_post_set_time(max31335);
}
static int max31335_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
{
struct max31335_data *max31335 = dev_get_drvdata(dev);
- int ret, ctrl, status;
+ unsigned int status, int_en;
struct rtc_time time;
u8 regs[6];
+ int ret;
- ret = regmap_bulk_read(max31335->regmap, MAX31335_ALM1_SEC, regs,
+ ret = regmap_bulk_read(max31335->regmap, max31335->chip->alarm1_sec_reg, regs,
sizeof(regs));
if (ret)
return ret;
@@ -292,16 +473,16 @@ static int max31335_read_alarm(struct device *dev, struct rtc_wkalrm *alrm)
if (time.tm_year >= 200)
alrm->time.tm_year += 100;
- ret = regmap_read(max31335->regmap, MAX31335_INT_EN1, &ctrl);
+ ret = regmap_read(max31335->regmap, max31335->chip->int_status_reg, &status);
if (ret)
return ret;
- ret = regmap_read(max31335->regmap, MAX31335_STATUS1, &status);
+ ret = regmap_read(max31335->regmap, max31335->chip->int_en_reg, &int_en);
if (ret)
return ret;
- alrm->enabled = FIELD_GET(MAX31335_INT_EN1_A1IE, ctrl);
- alrm->pending = FIELD_GET(MAX31335_STATUS1_A1F, status);
+ alrm->enabled = FIELD_GET(MAX313XX_INT_ALARM1, int_en);
+ alrm->pending = FIELD_GET(MAX313XX_INT_ALARM1, status);
return 0;
}
@@ -320,29 +501,28 @@ static int max31335_set_alarm(struct device *dev, struct rtc_wkalrm *alrm)
regs[4] = bin2bcd(alrm->time.tm_mon + 1);
regs[5] = bin2bcd(alrm->time.tm_year % 100);
- ret = regmap_bulk_write(max31335->regmap, MAX31335_ALM1_SEC,
- regs, sizeof(regs));
+ ret = regmap_bulk_write(max31335->regmap, max31335->chip->alarm1_sec_reg, regs,
+ sizeof(regs));
if (ret)
return ret;
- reg = FIELD_PREP(MAX31335_INT_EN1_A1IE, alrm->enabled);
- ret = regmap_update_bits(max31335->regmap, MAX31335_INT_EN1,
- MAX31335_INT_EN1_A1IE, reg);
+ reg = FIELD_PREP(MAX313XX_INT_ALARM1, alrm->enabled);
+ ret = regmap_update_bits(max31335->regmap, max31335->chip->int_en_reg,
+ MAX313XX_INT_ALARM1, reg);
if (ret)
return ret;
- ret = regmap_update_bits(max31335->regmap, MAX31335_STATUS1,
- MAX31335_STATUS1_A1F, 0);
-
- return 0;
+ /* Clear status register */
+ return regmap_read(max31335->regmap, max31335->chip->int_status_reg, ®);
}
static int max31335_alarm_irq_enable(struct device *dev, unsigned int enabled)
{
struct max31335_data *max31335 = dev_get_drvdata(dev);
- return regmap_update_bits(max31335->regmap, MAX31335_INT_EN1,
- MAX31335_INT_EN1_A1IE, enabled);
+ return regmap_update_bits(max31335->regmap, max31335->chip->int_en_reg,
+ MAX313XX_INT_ALARM1,
+ FIELD_PREP(MAX313XX_INT_ALARM1, enabled));
}
static irqreturn_t max31335_handle_irq(int irq, void *dev_id)
@@ -351,8 +531,8 @@ static irqreturn_t max31335_handle_irq(int irq, void *dev_id)
bool status;
int ret;
- ret = regmap_update_bits_check(max31335->regmap, MAX31335_STATUS1,
- MAX31335_STATUS1_A1F, 0, &status);
+ ret = regmap_update_bits_check(max31335->regmap, max31335->chip->int_status_reg,
+ MAX313XX_INT_ALARM1, 0, &status);
if (ret)
return IRQ_HANDLED;
@@ -370,11 +550,13 @@ static const struct rtc_class_ops max31335_rtc_ops = {
.alarm_irq_enable = max31335_alarm_irq_enable,
};
-static int max31335_trickle_charger_setup(struct device *dev,
- struct max31335_data *max31335)
+static int max31335_trickle_charger_setup(struct device *dev)
{
+ struct max31335_data *max31335 = dev_get_drvdata(dev);
+ bool trickle_diode_en;
+ int index, reg;
u32 ohms, chargeable;
- int i, trickle_cfg;
+ int ret;
const char *diode;
if (device_property_read_u32(dev, "aux-voltage-chargeable",
@@ -388,98 +570,151 @@ static int max31335_trickle_charger_setup(struct device *dev,
return 0;
if (!strcmp(diode, "schottky"))
- trickle_cfg = MAX31335_TRICKLE_SCHOTTKY_DIODE;
+ trickle_diode_en = 0;
else if (!strcmp(diode, "standard+schottky"))
- trickle_cfg = MAX31335_TRICKLE_STANDARD_DIODE;
+ trickle_diode_en = 1;
else
return dev_err_probe(dev, -EINVAL,
"Invalid tc-diode value: %s\n", diode);
- for (i = 0; i < ARRAY_SIZE(max31335_trickle_resistors); i++)
- if (ohms == max31335_trickle_resistors[i])
- break;
-
- if (i >= ARRAY_SIZE(max31335_trickle_resistors))
+ if (!max31335->chip->trickle_reg)
return 0;
- i = i + trickle_cfg;
+ index = find_closest(ohms, max313xx_trickle_ohms,
+ ARRAY_SIZE(max313xx_trickle_ohms)) + 1;
- return regmap_write(max31335->regmap, MAX31335_TRICKLE_REG,
- FIELD_PREP(MAX31335_TRICKLE_REG_TRICKLE, i) |
- FIELD_PREP(MAX31335_TRICKLE_REG_EN_TRICKLE,
- chargeable));
+ switch (max31335->id) {
+ case ID_MAX31329:
+ reg = FIELD_PREP(MAX31329_TRICKLE_ENABLE_BIT, chargeable) |
+ FIELD_PREP(MAX31329_43_TRICKLE_RES_MASK, index) |
+ FIELD_PREP(MAX31329_43_TRICKLE_DIODE_EN, trickle_diode_en);
+ break;
+ case ID_MAX31331:
+ case ID_MAX31334:
+ reg = FIELD_PREP(MAX3133X_TRICKLE_ENABLE_BIT, chargeable) |
+ FIELD_PREP(MAX3133X_TRICKLE_RES_MASK, index) |
+ FIELD_PREP(MAX3133X_TRICKLE_DIODE_EN, trickle_diode_en);
+ break;
+ case ID_MAX31341:
+ if (index == 1)
+ index = 0;
+
+ reg = FIELD_PREP(MAX31341_TRICKLE_ENABLE_BIT, chargeable) |
+ FIELD_PREP(MAX31341_TRICKLE_RES_MASK, index) |
+ FIELD_PREP(MAX31341_TRICKLE_DIODE_EN, trickle_diode_en);
+
+ ret = regmap_set_bits(max31335->regmap, MAX31341_POWER_MGMT_REG,
+ MAX31341_POWER_MGMT_TRICKLE_BIT);
+ if (ret)
+ return ret;
+
+ break;
+ case ID_MAX31343:
+ reg = FIELD_PREP(MAX31329_43_TRICKLE_RES_MASK, index) |
+ FIELD_PREP(MAX31329_43_TRICKLE_DIODE_EN, trickle_diode_en) |
+ FIELD_PREP(MAX31343_TRICKLE_ENABLE_MASK,
+ MAX31343_TRICKLE_ENABLE_CODE);
+ break;
+ default:
+ return -EOPNOTSUPP;
+ }
+
+ return regmap_write(max31335->regmap, max31335->chip->trickle_reg, reg);
}
static unsigned long max31335_clkout_recalc_rate(struct clk_hw *hw,
unsigned long parent_rate)
{
struct max31335_data *max31335 = clk_hw_to_max31335(hw);
+ const struct clkout_cfg *clkout = max31335->chip->clkout;
unsigned int freq_mask;
unsigned int reg;
int ret;
- ret = regmap_read(max31335->regmap, MAX31335_RTC_CONFIG2, ®);
+ ret = regmap_read(max31335->regmap, clkout->reg, ®);
if (ret)
return 0;
- freq_mask = __roundup_pow_of_two(ARRAY_SIZE(max31335_clkout_freq)) - 1;
+ freq_mask = __roundup_pow_of_two(clkout->freq_size) - 1;
- return max31335_clkout_freq[reg & freq_mask];
+ return clkout->freq_avail[(reg >> clkout->freq_pos) & freq_mask];
}
static long max31335_clkout_round_rate(struct clk_hw *hw, unsigned long rate,
unsigned long *prate)
{
+ struct max31335_data *max31335 = clk_hw_to_max31335(hw);
+ struct clkout_cfg *clkout = max31335->chip->clkout;
int index;
- index = find_closest(rate, max31335_clkout_freq,
- ARRAY_SIZE(max31335_clkout_freq));
-
- return max31335_clkout_freq[index];
+ index = find_closest(rate, clkout->freq_avail, clkout->freq_size);
+ return clkout->freq_avail[index];
}
static int max31335_clkout_set_rate(struct clk_hw *hw, unsigned long rate,
unsigned long parent_rate)
{
struct max31335_data *max31335 = clk_hw_to_max31335(hw);
+ struct clkout_cfg *clkout = max31335->chip->clkout;
unsigned int freq_mask;
int index;
- index = find_closest(rate, max31335_clkout_freq,
- ARRAY_SIZE(max31335_clkout_freq));
- freq_mask = __roundup_pow_of_two(ARRAY_SIZE(max31335_clkout_freq)) - 1;
+ index = find_closest(rate, clkout->freq_avail, clkout->freq_size);
+ freq_mask = __roundup_pow_of_two(clkout->freq_size) - 1;
- return regmap_update_bits(max31335->regmap, MAX31335_RTC_CONFIG2,
- freq_mask, index);
+ return regmap_update_bits(max31335->regmap, clkout->reg,
+ freq_mask << clkout->freq_pos,
+ index << clkout->freq_pos);
}
static int max31335_clkout_enable(struct clk_hw *hw)
{
struct max31335_data *max31335 = clk_hw_to_max31335(hw);
+ struct clkout_cfg *clkout = max31335->chip->clkout;
- return regmap_set_bits(max31335->regmap, MAX31335_RTC_CONFIG2,
- MAX31335_RTC_CONFIG2_ENCLKO);
+ if (clkout->en_invert)
+ return regmap_clear_bits(max31335->regmap, clkout->reg,
+ clkout->en_bit);
+
+ return regmap_set_bits(max31335->regmap, clkout->reg, clkout->en_bit);
}
static void max31335_clkout_disable(struct clk_hw *hw)
{
struct max31335_data *max31335 = clk_hw_to_max31335(hw);
+ struct clkout_cfg *clkout = max31335->chip->clkout;
- regmap_clear_bits(max31335->regmap, MAX31335_RTC_CONFIG2,
- MAX31335_RTC_CONFIG2_ENCLKO);
+ switch (max31335->id) {
+ case ID_MAX31331:
+ case ID_MAX31334:
+ if (max31335->irq > 0) {
+ dev_err(max31335->rtc->dev.parent,
+ "clkout cannot be disabled when IRQ is requested");
+ return;
+ }
+ break;
+ default:
+ break;
+ }
+
+ if (clkout->en_invert)
+ regmap_set_bits(max31335->regmap, clkout->reg, clkout->en_bit);
+ else
+ regmap_clear_bits(max31335->regmap, clkout->reg, clkout->en_bit);
}
static int max31335_clkout_is_enabled(struct clk_hw *hw)
{
struct max31335_data *max31335 = clk_hw_to_max31335(hw);
+ struct clkout_cfg *clkout = max31335->chip->clkout;
unsigned int reg;
int ret;
- ret = regmap_read(max31335->regmap, MAX31335_RTC_CONFIG2, ®);
+ ret = regmap_read(max31335->regmap, clkout->reg, ®);
if (ret)
return ret;
- return !!(reg & MAX31335_RTC_CONFIG2_ENCLKO);
+ return !!(reg & clkout->en_bit) ^ clkout->en_invert;
}
static const struct clk_ops max31335_clkout_ops = {
@@ -500,7 +735,7 @@ static int max31335_nvmem_reg_read(void *priv, unsigned int offset,
void *val, size_t bytes)
{
struct max31335_data *max31335 = priv;
- unsigned int reg = MAX31335_TS0_SEC_1_128 + offset;
+ unsigned int reg = max31335->chip->ram_reg + offset;
return regmap_bulk_read(max31335->regmap, reg, val, bytes);
}
@@ -509,7 +744,7 @@ static int max31335_nvmem_reg_write(void *priv, unsigned int offset,
void *val, size_t bytes)
{
struct max31335_data *max31335 = priv;
- unsigned int reg = MAX31335_TS0_SEC_1_128 + offset;
+ unsigned int reg = max31335->chip->ram_reg + offset;
return regmap_bulk_write(max31335->regmap, reg, val, bytes);
}
@@ -518,7 +753,6 @@ static struct nvmem_config max31335_nvmem_cfg = {
.reg_read = max31335_nvmem_reg_read,
.reg_write = max31335_nvmem_reg_write,
.word_size = 8,
- .size = MAX31335_RAM_SIZE,
};
#if IS_REACHABLE(HWMON)
@@ -533,8 +767,7 @@ static int max31335_read_temp(struct device *dev, enum hwmon_sensor_types type,
if (type != hwmon_temp || attr != hwmon_temp_input)
return -EOPNOTSUPP;
- ret = regmap_bulk_read(max31335->regmap, MAX31335_TEMP_DATA_MSB,
- reg, 2);
+ ret = regmap_bulk_read(max31335->regmap, max31335->chip->temp_reg, reg, 2);
if (ret)
return ret;
@@ -571,117 +804,312 @@ static const struct hwmon_chip_info max31335_chip_info = {
};
#endif
+static int max31335_init(struct max31335_data *max31335)
+{
+ int ret;
+
+ switch (max31335->id) {
+ case ID_MAX31341:
+ case ID_MAX31342:
+ ret = regmap_clear_bits(max31335->regmap, MAX31341_42_CONFIG1_REG,
+ MAX31341_42_CONFIG1_OSC_DISABLE);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(max31335->regmap, MAX31341_42_CONFIG1_REG,
+ MAX31341_42_CONFIG1_SWRST);
+ default:
+ return 0;
+ }
+}
+
static int max31335_clkout_register(struct device *dev)
{
struct max31335_data *max31335 = dev_get_drvdata(dev);
int ret;
if (!device_property_present(dev, "#clock-cells"))
- return regmap_clear_bits(max31335->regmap, MAX31335_RTC_CONFIG2,
- MAX31335_RTC_CONFIG2_ENCLKO);
+ return 0;
+ max31335_clk_init.name = max31335->chip->clkout_name;
+ device_property_read_string(dev, "clock-output-names",
+ &max31335_clk_init.name);
max31335->clkout.init = &max31335_clk_init;
ret = devm_clk_hw_register(dev, &max31335->clkout);
if (ret)
return dev_err_probe(dev, ret, "cannot register clock\n");
- ret = devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
- &max31335->clkout);
- if (ret)
- return dev_err_probe(dev, ret, "cannot add hw provider\n");
+ return devm_of_clk_add_hw_provider(dev, of_clk_hw_simple_get,
+ &max31335->clkout);
+}
- max31335->clkout.clk = devm_clk_get_enabled(dev, NULL);
- if (IS_ERR(max31335->clkout.clk))
- return dev_err_probe(dev, PTR_ERR(max31335->clkout.clk),
- "cannot enable clkout\n");
+static int max31335_find_clkin_freq_index(struct clk *clk)
+{
+ unsigned long rate = clk_get_rate(clk);
+ int freq;
+ int i;
+
+ i = find_closest(rate, max313xx_clkin_freq,
+ ARRAY_SIZE(max313xx_clkin_freq));
+ if (max313xx_clkin_freq[i] == rate)
+ return i;
+
+ for (i = ARRAY_SIZE(max313xx_clkin_freq) - 1; i >= 0; i--) {
+ freq = max313xx_clkin_freq[i];
+ if (freq == clk_round_rate(clk, freq))
+ return i;
+ }
+
+ /* supplied clock cannot produce one of desired frequency rate */
+ return -ENODEV;
+}
+
+static int max31335_clkin_init(struct device *dev)
+{
+ struct max31335_data *max31335 = dev_get_drvdata(dev);
+ int rate;
+ int ret;
+
+ max31335->clkin = devm_clk_get_enabled(dev, NULL);
+ if (IS_ERR(max31335->clkin)) {
+ if (PTR_ERR(max31335->clkin) == -ENOENT)
+ max31335->clkin = NULL;
+ else
+ return dev_err_probe(dev, PTR_ERR(max31335->clkin),
+ "error while clkin setup\n");
+ }
+
+ if (!max31335->clkin) {
+ switch (max31335->id) {
+ case ID_MAX31329:
+ return regmap_clear_bits(max31335->regmap,
+ MAX31329_CONFIG2_REG,
+ MAX31329_CONFIG2_CLKIN_EN);
+ case ID_MAX31341:
+ case ID_MAX31342:
+ return regmap_clear_bits(max31335->regmap,
+ MAX31341_42_CONFIG1_REG,
+ MAX31341_42_CONFIG1_CLKIN_EN);
+ default:
+ return 0;
+ }
+ }
+
+ rate = max31335_find_clkin_freq_index(max31335->clkin);
+ if (rate < 0)
+ return dev_err_probe(dev, rate,
+ "clkin cannot produce required frequency\n");
+
+ ret = clk_set_rate(max31335->clkin, max313xx_clkin_freq[rate]);
+ if (ret)
+ return ret;
+
+ switch (max31335->id) {
+ case ID_MAX31329:
+ ret = regmap_update_bits(max31335->regmap, MAX31329_CONFIG2_REG,
+ MAX31329_CONFIG2_CLKIN_FREQ, rate);
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(max31335->regmap, MAX31329_CONFIG2_REG,
+ MAX31329_CONFIG2_CLKIN_EN);
+ case ID_MAX31341:
+ case ID_MAX31342:
+ ret = regmap_update_bits(max31335->regmap, MAX31341_42_CONFIG1_REG,
+ MAX31341_42_CONFIG1_CLKIN_FREQ,
+ FIELD_PREP(MAX31341_42_CONFIG1_CLKIN_FREQ, rate));
+ if (ret)
+ return ret;
+
+ return regmap_set_bits(max31335->regmap, MAX31341_42_CONFIG1_REG,
+ MAX31341_42_CONFIG1_CLKIN_EN);
+ default:
+ max31335->clkin = NULL;
+ dev_warn(dev, "device does not have clock input\n");
+ return 0;
+ }
+}
+
+static int max31335_irq_init(struct device *dev, const char *devname)
+{
+ struct max31335_data *max31335 = dev_get_drvdata(dev);
+ int ret;
+
+ switch (max31335->id) {
+ case ID_MAX31328:
+ break;
+ case ID_MAX31331:
+ case ID_MAX31334:
+ if (max31335->clkout.clk) {
+ /*
+ * interrupt muxing depends on clkout so enable clkout
+ * if configured before requesting interrupt
+ */
+ ret = clk_prepare_enable(max31335->clkout.clk);
+ if (ret)
+ return dev_err_probe(dev, ret,
+ "cannot enable clkout\n");
+ }
+ break;
+ default:
+ if (max31335->clkin) {
+ if (max31335->clkout.clk && max31335->irq > 0)
+ return dev_err_probe(dev, -EOPNOTSUPP,
+ "irq not possible when both clkin and clkout are configured\n");
+
+ if (max31335->irq <= 0)
+ break;
+
+ /* clkout needs to be disabled for interrupt */
+ if (max31335->chip->clkout->en_invert)
+ ret = regmap_set_bits(max31335->regmap,
+ max31335->chip->clkout->reg,
+ max31335->chip->clkout->en_bit);
+ else
+ ret = regmap_clear_bits(max31335->regmap,
+ max31335->chip->clkout->reg,
+ max31335->chip->clkout->en_bit);
+
+ if (ret)
+ return ret;
+ }
+ break;
+ }
+
+ if (max31335->irq > 0) {
+ return devm_request_threaded_irq(dev, max31335->irq, NULL,
+ &max31335_handle_irq, IRQF_ONESHOT,
+ devname, max31335);
+ } else if (device_property_read_bool(dev, "wakeup-source")) {
+ clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, max31335->rtc->features);
+ return device_init_wakeup(dev, true);
+ }
+
+ clear_bit(RTC_FEATURE_ALARM, max31335->rtc->features);
return 0;
}
static int max31335_probe(struct i2c_client *client)
{
+ const struct i2c_device_id *id = i2c_client_get_device_id(client);
+ struct device *dev = &client->dev;
struct max31335_data *max31335;
#if IS_REACHABLE(HWMON)
struct device *hwmon;
#endif
+ const void *match;
int ret;
max31335 = devm_kzalloc(&client->dev, sizeof(*max31335), GFP_KERNEL);
if (!max31335)
return -ENOMEM;
+ dev_set_drvdata(&client->dev, max31335);
+
max31335->regmap = devm_regmap_init_i2c(client, ®map_config);
- if (IS_ERR(max31335->regmap))
- return PTR_ERR(max31335->regmap);
+ if (IS_ERR(max31335->regmap)) {
+ return dev_err_probe(dev, PTR_ERR(max31335->regmap),
+ "regmap init failed\n");
+ }
i2c_set_clientdata(client, max31335);
- max31335->rtc = devm_rtc_allocate_device(&client->dev);
+ match = device_get_match_data(dev);
+ if (match)
+ max31335->chip = match;
+ else if (id)
+ max31335->chip = (struct chip_desc *)id->driver_data;
+ else
+ return -ENODEV;
+
+ max31335->id = max31335->chip - chip;
+
+ ret = max31335_init(max31335);
+ if (ret)
+ return ret;
+
+ max31335->rtc = devm_rtc_allocate_device(dev);
if (IS_ERR(max31335->rtc))
return PTR_ERR(max31335->rtc);
max31335->rtc->ops = &max31335_rtc_ops;
max31335->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000;
max31335->rtc->range_max = RTC_TIMESTAMP_END_2199;
- max31335->rtc->alarm_offset_max = 24 * 60 * 60;
+ if (max31335->id == ID_MAX31335)
+ max31335->rtc->alarm_offset_max = 24 * 60 * 60;
- ret = max31335_clkout_register(&client->dev);
+ max31335->irq = client->irq;
+
+ ret = max31335_clkout_register(dev);
if (ret)
return ret;
- if (client->irq > 0) {
- ret = devm_request_threaded_irq(&client->dev, client->irq,
- NULL, max31335_handle_irq,
- IRQF_ONESHOT,
- "max31335", max31335);
- if (ret) {
- dev_warn(&client->dev,
- "unable to request IRQ, alarm max31335 disabled\n");
- client->irq = 0;
- }
+ ret = max31335_clkin_init(dev);
+ if (ret)
+ return ret;
+
+ /* IRQ wiring depends on the clock configuration so parse them first */
+ ret = max31335_irq_init(dev, client->name);
+ if (ret)
+ return ret;
+
+ if (max31335->chip->ram_size) {
+ max31335_nvmem_cfg.size = max31335->chip->ram_size;
+ max31335_nvmem_cfg.priv = max31335;
+
+ ret = devm_rtc_nvmem_register(max31335->rtc, &max31335_nvmem_cfg);
+ if (ret)
+ dev_warn(dev, "cannot register rtc nvmem\n");
}
- if (!client->irq)
- clear_bit(RTC_FEATURE_ALARM, max31335->rtc->features);
-
- max31335_nvmem_cfg.priv = max31335;
- ret = devm_rtc_nvmem_register(max31335->rtc, &max31335_nvmem_cfg);
- if (ret)
- return dev_err_probe(&client->dev, ret,
- "cannot register rtc nvmem\n");
-
#if IS_REACHABLE(HWMON)
- hwmon = devm_hwmon_device_register_with_info(&client->dev, client->name,
- max31335,
- &max31335_chip_info,
- NULL);
- if (IS_ERR(hwmon))
- return dev_err_probe(&client->dev, PTR_ERR(hwmon),
- "cannot register hwmon device\n");
+ if (max31335->chip->temp_reg) {
+ hwmon = devm_hwmon_device_register_with_info(dev, client->name,
+ max31335,
+ &max31335_chip_info,
+ NULL);
+ if (IS_ERR(hwmon))
+ dev_warn(dev, "cannot register hwmon device: %li\n",
+ PTR_ERR(hwmon));
+ }
#endif
- ret = max31335_trickle_charger_setup(&client->dev, max31335);
+ ret = max31335_trickle_charger_setup(dev);
if (ret)
return ret;
return devm_rtc_register_device(max31335->rtc);
}
-static const struct i2c_device_id max31335_id[] = {
- { "max31335", 0 },
- { }
-};
-
-MODULE_DEVICE_TABLE(i2c, max31335_id);
-
static const struct of_device_id max31335_of_match[] = {
- { .compatible = "adi,max31335" },
+ { .compatible = "adi,max31328", .data = &chip[ID_MAX31328] },
+ { .compatible = "adi,max31329", .data = &chip[ID_MAX31329] },
+ { .compatible = "adi,max31331", .data = &chip[ID_MAX31331] },
+ { .compatible = "adi,max31334", .data = &chip[ID_MAX31334] },
+ { .compatible = "adi,max31335", .data = &chip[ID_MAX31335] },
+ { .compatible = "adi,max31341", .data = &chip[ID_MAX31341] },
+ { .compatible = "adi,max31342", .data = &chip[ID_MAX31342] },
+ { .compatible = "adi,max31343", .data = &chip[ID_MAX31343] },
{ }
};
MODULE_DEVICE_TABLE(of, max31335_of_match);
+static const struct i2c_device_id max31335_id[] = {
+ { "max31328", (kernel_ulong_t)&chip[ID_MAX31328] },
+ { "max31329", (kernel_ulong_t)&chip[ID_MAX31329] },
+ { "max31331", (kernel_ulong_t)&chip[ID_MAX31331] },
+ { "max31334", (kernel_ulong_t)&chip[ID_MAX31334] },
+ { "max31335", (kernel_ulong_t)&chip[ID_MAX31335] },
+ { "max31341", (kernel_ulong_t)&chip[ID_MAX31341] },
+ { "max31342", (kernel_ulong_t)&chip[ID_MAX31342] },
+ { "max31343", (kernel_ulong_t)&chip[ID_MAX31343] },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max31335_id);
+
static struct i2c_driver max31335_driver = {
.driver = {
.name = "rtc-max31335",
@@ -693,5 +1121,8 @@ static struct i2c_driver max31335_driver = {
module_i2c_driver(max31335_driver);
MODULE_AUTHOR("Antoniu Miclaus <[email protected]>");
+MODULE_AUTHOR("Zeynep Arslanbenzer <[email protected]>");
+MODULE_AUTHOR("Ibrahim Tilki <[email protected]>");
MODULE_DESCRIPTION("MAX31335 RTC driver");
+MODULE_SOFTDEP("pre: regmap-i2c");
MODULE_LICENSE("GPL");
--
2.43.2
On 27/02/2024 14:03:09+1300, Chris Packham wrote:
> - max31335->clkout.clk = devm_clk_get_enabled(dev, NULL);
> - if (IS_ERR(max31335->clkout.clk))
> - return dev_err_probe(dev, PTR_ERR(max31335->clkout.clk),
> - "cannot enable clkout\n");
This is so ugly and should have never passed. I went weak in front of
Antoniu's insistance but I'm very close from ripping out the whole
driver from the kernel now.
Seriously, read all the comments that have been made around the IRQ/clk
stuff and come back with a proper solution.
> +static int max31335_irq_init(struct device *dev, const char *devname)
> +{
> + struct max31335_data *max31335 = dev_get_drvdata(dev);
> + int ret;
> +
> + switch (max31335->id) {
> + case ID_MAX31328:
> + break;
> + case ID_MAX31331:
> + case ID_MAX31334:
> + if (max31335->clkout.clk) {
> + /*
> + * interrupt muxing depends on clkout so enable clkout
> + * if configured before requesting interrupt
> + */
> + ret = clk_prepare_enable(max31335->clkout.clk);
Do I get this right that this is about enabling its own output clock?
Why would you need to do that? Something else must be the consumer, not
the provider itself.
> + if (ret)
> + return dev_err_probe(dev, ret,
> + "cannot enable clkout\n");
> + }
> + break;
> + default:
> + if (max31335->clkin) {
> + if (max31335->clkout.clk && max31335->irq > 0)
> + return dev_err_probe(dev, -EOPNOTSUPP,
> + "irq not possible when both clkin and clkout are configured\n");
This is not true, the RTC is always a clock provider. What is not
possible is muxing the clock on the pin in a few configurations.
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
Hi Alexandre,
First off apologies if I've struck a nerve, that was never my intention.
I thought cleaning up an inflight patch series to get it landed would be
a straight forward thing.
On 27/02/24 15:29, Alexandre Belloni wrote:
> On 27/02/2024 14:03:09+1300, Chris Packham wrote:
>> - max31335->clkout.clk = devm_clk_get_enabled(dev, NULL);
>> - if (IS_ERR(max31335->clkout.clk))
>> - return dev_err_probe(dev, PTR_ERR(max31335->clkout.clk),
>> - "cannot enable clkout\n");
> This is so ugly and should have never passed. I went weak in front of
> Antoniu's insistance but I'm very close from ripping out the whole
> driver from the kernel now.
>
> Seriously, read all the comments that have been made around the IRQ/clk
> stuff and come back with a proper solution.
Yeah I'm only just getting to grips with this.
No idea about the MAX31335 itself but from the MAX31334 it looks like
the alarm1 interrupt will move from INTA to INTB. I think rather than
messing about with the clock stuff we'd be better off with a
pin-6-function = "alarm1" | "clkout" property.
>
>> +static int max31335_irq_init(struct device *dev, const char *devname)
>> +{
>> + struct max31335_data *max31335 = dev_get_drvdata(dev);
>> + int ret;
>> +
>> + switch (max31335->id) {
>> + case ID_MAX31328:
>> + break;
>> + case ID_MAX31331:
>> + case ID_MAX31334:
>> + if (max31335->clkout.clk) {
>> + /*
>> + * interrupt muxing depends on clkout so enable clkout
>> + * if configured before requesting interrupt
>> + */
>> + ret = clk_prepare_enable(max31335->clkout.clk);
> Do I get this right that this is about enabling its own output clock?
> Why would you need to do that? Something else must be the consumer, not
> the provider itself.
I don't think it does. It's just that based on the ENCLKO setting alarm1
will be output on a different pin. I don't think there's really a proper
way of disabling CLKOUT dynamically. Either it's enabled and all the
interrupts are on the INTA pin or it's disabled and alarm1 is moved to
the INTB. You can change the clkout frequency but not outright disable
it (without having a side-effect that impacts the interrupt assignment).
So I think my suggestion of making it part of the hardware description
is probably the sensible thing to do. It affects how the interrupts are
physically connected so I think it does belong in the device tree.
>
>> + if (ret)
>> + return dev_err_probe(dev, ret,
>> + "cannot enable clkout\n");
>> + }
>> + break;
>> + default:
>> + if (max31335->clkin) {
>> + if (max31335->clkout.clk && max31335->irq > 0)
>> + return dev_err_probe(dev, -EOPNOTSUPP,
>> + "irq not possible when both clkin and clkout are configured\n");
> This is not true, the RTC is always a clock provider. What is not
> possible is muxing the clock on the pin in a few configurations.
Yeah I don't understand that either. Not sure what clkin has to do with
anything here. The clkout stuff affects which irq pins are in play.
On Tue, Feb 27, 2024 at 02:03:10PM +1300, Chris Packham wrote:
> interrupts:
> + description:
> + Alarm1 interrupt line of the RTC. Some of the RTCs have two interrupt
> + lines and alarm1 interrupt muxing depends on the clockin/clockout
> + configuration.
> maxItems: 1
The maxItems: 1 looks odd here when you state "some of the RTCs have two
interrupt lines", which makes it sound as if there are actually two
interrupts that should be exposed here. If those two interrupts get
muxed to the same pin for output I'd suggest that you clarify that here.
Otherwise, this looks good to me - although I do wonder if the
authorship on the commit (attributed to the analog guys) is still
accurate.
Thanks,
Conor.
On 29/02/24 00:58, Conor Dooley wrote:
> On Tue, Feb 27, 2024 at 02:03:10PM +1300, Chris Packham wrote:
>
>> interrupts:
>> + description:
>> + Alarm1 interrupt line of the RTC. Some of the RTCs have two interrupt
>> + lines and alarm1 interrupt muxing depends on the clockin/clockout
>> + configuration.
>> maxItems: 1
> The maxItems: 1 looks odd here when you state "some of the RTCs have two
> interrupt lines", which makes it sound as if there are actually two
> interrupts that should be exposed here. If those two interrupts get
> muxed to the same pin for output I'd suggest that you clarify that here.
This may end up changing if I can come up with something that Alexandre
is happy with. Basically (some of) the chips have a configurable pin
that can either be dedicated to the ALARM1 output (annoyingly labelled
as INTB) or to a clock output. There is an INTA line that has other
interrupts and if the clock output option is used then it also has
ALARM1. The driver doesn't currently do anything with the other
interrupt sources so as written this needs to correspond to whichever
interrupt output is asserted for ALARM1.
> Otherwise, this looks good to me - although I do wonder if the
> authorship on the commit (attributed to the analog guys) is still
> accurate.
I think the binding is still pretty close to the last version sent out
by the analog folks. The driver at this point is probably unrecognizable
even though I've only really renamed stuff and moved functions around.
I was kind of hoping my prodding would be met with a "oh we've already
done this in our SDK, here's the latest version" but that hasn't
happened. I'm fairly close to dropping anything in this series that
isn't related to the MAX31334 as that is the only hardware I can
actually test.
>
> Thanks,
> Conor.
On Wed, Feb 28, 2024 at 08:21:35PM +0000, Chris Packham wrote:
>
> On 29/02/24 00:58, Conor Dooley wrote:
> > On Tue, Feb 27, 2024 at 02:03:10PM +1300, Chris Packham wrote:
> >
> >> interrupts:
> >> + description:
> >> + Alarm1 interrupt line of the RTC. Some of the RTCs have two interrupt
> >> + lines and alarm1 interrupt muxing depends on the clockin/clockout
> >> + configuration.
> >> maxItems: 1
> > The maxItems: 1 looks odd here when you state "some of the RTCs have two
> > interrupt lines", which makes it sound as if there are actually two
> > interrupts that should be exposed here. If those two interrupts get
> > muxed to the same pin for output I'd suggest that you clarify that here.
>
> This may end up changing if I can come up with something that Alexandre
> is happy with. Basically (some of) the chips have a configurable pin
> that can either be dedicated to the ALARM1 output (annoyingly labelled
> as INTB) or to a clock output. There is an INTA line that has other
> interrupts and if the clock output option is used then it also has
> ALARM1. The driver doesn't currently do anything with the other
> interrupt sources so as written this needs to correspond to whichever
> interrupt output is asserted for ALARM1.
So you're saying that depending on whether or not the clock output is
used, there could be two interrupts?
Without looking further, it sounds like you should be setting maxItems
to 1 if #clock-cells is present and to 2 if it is not. Then if there are
two interrupts provided, the driver is free to configure whatever way it
wants. If there aren't, send everything to INTA.
Am I missing something?
Cheers,
Conor.
On 29/02/2024 20:11:16+0000, Chris Packham wrote:
>
> On 1/03/24 07:07, Conor Dooley wrote:
> > On Wed, Feb 28, 2024 at 08:21:35PM +0000, Chris Packham wrote:
> >> On 29/02/24 00:58, Conor Dooley wrote:
> >>> On Tue, Feb 27, 2024 at 02:03:10PM +1300, Chris Packham wrote:
> >>>
> >>>> interrupts:
> >>>> + description:
> >>>> + Alarm1 interrupt line of the RTC. Some of the RTCs have two interrupt
> >>>> + lines and alarm1 interrupt muxing depends on the clockin/clockout
> >>>> + configuration.
> >>>> maxItems: 1
> >>> The maxItems: 1 looks odd here when you state "some of the RTCs have two
> >>> interrupt lines", which makes it sound as if there are actually two
> >>> interrupts that should be exposed here. If those two interrupts get
> >>> muxed to the same pin for output I'd suggest that you clarify that here.
> >> This may end up changing if I can come up with something that Alexandre
> >> is happy with. Basically (some of) the chips have a configurable pin
> >> that can either be dedicated to the ALARM1 output (annoyingly labelled
> >> as INTB) or to a clock output. There is an INTA line that has other
> >> interrupts and if the clock output option is used then it also has
> >> ALARM1. The driver doesn't currently do anything with the other
> >> interrupt sources so as written this needs to correspond to whichever
> >> interrupt output is asserted for ALARM1.
> > So you're saying that depending on whether or not the clock output is
> > used, there could be two interrupts?
> Correct.
> > Without looking further, it sounds like you should be setting maxItems
> > to 1 if #clock-cells is present and to 2 if it is not.
> My idea was an explicit property about the function of the INTB/CLKOUT
> pin. The current code does use #clock-cells as a proxy for this (and
> Alexandre has some concerns with how this is handled).
#clock-cells must not be used for pinmuxing, I can't see how anyone
would allow this.
> > Then if there are
> > two interrupts provided, the driver is free to configure whatever way it
> > wants. If there aren't, send everything to INTA.
> >
> > Am I missing something?
>
> Right now the only interrupt that the RTC cares about is for ALARM1
> (which moves between INTA and INTB depending on the clock config). There
> are other hardware events and an ALARM2 that can generate an interrupt
> but these are ignored. I don't think the rtc framework supports more
> than one alarm.
>
> Binding wise I think this should take 1 or 2 interrupts. For simplicity
> the first interrupt should always correspond to ALARM1 (which could be
> INTB or INTA depending on the hardware design). The 2nd interrupt (if
> supplied) would be for the other events (which we don't currently do
> anything with).
Not using an interrupt simply means the CPU doesn't care about it but
there are other components that may care for example a PMIC. If you
reason about what linux and the CPU it is running on can do, you will
cripple functionality and we will have to break the devicetree binding
later on to restore it.
We need to be able to express all the possible pin configurations with
the binding. I'm not sure why everyone is so resistant to use pinmuxing
to mux pins....
--
Alexandre Belloni, co-owner and COO, Bootlin
Embedded Linux and Kernel engineering
https://bootlin.com
On 1/03/24 07:07, Conor Dooley wrote:
> On Wed, Feb 28, 2024 at 08:21:35PM +0000, Chris Packham wrote:
>> On 29/02/24 00:58, Conor Dooley wrote:
>>> On Tue, Feb 27, 2024 at 02:03:10PM +1300, Chris Packham wrote:
>>>
>>>> interrupts:
>>>> + description:
>>>> + Alarm1 interrupt line of the RTC. Some of the RTCs have two interrupt
>>>> + lines and alarm1 interrupt muxing depends on the clockin/clockout
>>>> + configuration.
>>>> maxItems: 1
>>> The maxItems: 1 looks odd here when you state "some of the RTCs have two
>>> interrupt lines", which makes it sound as if there are actually two
>>> interrupts that should be exposed here. If those two interrupts get
>>> muxed to the same pin for output I'd suggest that you clarify that here.
>> This may end up changing if I can come up with something that Alexandre
>> is happy with. Basically (some of) the chips have a configurable pin
>> that can either be dedicated to the ALARM1 output (annoyingly labelled
>> as INTB) or to a clock output. There is an INTA line that has other
>> interrupts and if the clock output option is used then it also has
>> ALARM1. The driver doesn't currently do anything with the other
>> interrupt sources so as written this needs to correspond to whichever
>> interrupt output is asserted for ALARM1.
> So you're saying that depending on whether or not the clock output is
> used, there could be two interrupts?
Correct.
> Without looking further, it sounds like you should be setting maxItems
> to 1 if #clock-cells is present and to 2 if it is not.
My idea was an explicit property about the function of the INTB/CLKOUT
pin. The current code does use #clock-cells as a proxy for this (and
Alexandre has some concerns with how this is handled).
> Then if there are
> two interrupts provided, the driver is free to configure whatever way it
> wants. If there aren't, send everything to INTA.
>
> Am I missing something?
Right now the only interrupt that the RTC cares about is for ALARM1
(which moves between INTA and INTB depending on the clock config). There
are other hardware events and an ALARM2 that can generate an interrupt
but these are ignored. I don't think the rtc framework supports more
than one alarm.
Binding wise I think this should take 1 or 2 interrupts. For simplicity
the first interrupt should always correspond to ALARM1 (which could be
INTB or INTA depending on the hardware design). The 2nd interrupt (if
supplied) would be for the other events (which we don't currently do
anything with).