| // SPDX-License-Identifier: GPL-2.0-only | |
| /* | |
| * Analog Devices MAX313XX series I2C RTC driver | |
| * | |
| * Copyright 2022 Analog Devices Inc. | |
| */ | |
| #include <bcd.h> | |
| #include <dm.h> | |
| #include <i2c.h> | |
| #include <rtc.h> | |
| #include <dm/device_compat.h> | |
| #include <linux/bitfield.h> | |
| #include <linux/delay.h> | |
| #include <linux/kernel.h> | |
| /* common registers */ | |
| #define MAX313XX_INT_ALARM1 BIT(0) | |
| #define MAX313XX_INT_ALARM2 BIT(1) | |
| #define MAX313XX_HRS_F_12_24 BIT(6) | |
| #define MAX313XX_HRS_F_AM_PM BIT(5) | |
| #define MAX313XX_MONTH_CENTURY BIT(7) | |
| #define MAX313XX_TMR_CFG_ENABLE BIT(4) | |
| #define MAX313XX_TMR_CFG_FREQ_MASK GENMASK(1, 0) | |
| #define MAX313XX_TMR_CFG_FREQ_16HZ 0x03 | |
| #define MAX313XX_REG_MINUTE 0x01 | |
| #define MAX313XX_REG_HOUR 0x02 | |
| #define MAX313XX_TIME_SIZE 0x07 | |
| /* device specific registers */ | |
| #define MAX3134X_CFG2_REG 0x01 | |
| #define MAX3134X_CFG2_SET_RTC BIT(1) | |
| #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) | |
| #define MAX3133X_TRICKLE_RES_MASK GENMASK(2, 1) | |
| #define MAX3133X_TRICKLE_DIODE_EN BIT(3) | |
| #define MAX3133X_TRICKLE_ENABLE_BIT 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) | |
| #define MAX31329_CONFIG2_REG 0x04 | |
| #define MAX31329_CONFIG2_CLKIN_EN BIT(2) | |
| #define MAX31329_CONFIG2_CLKIN_FREQ 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) | |
| enum max313xx_ids { | |
| ID_MAX31328, | |
| ID_MAX31329, | |
| ID_MAX31331, | |
| ID_MAX31334, | |
| ID_MAX31341, | |
| ID_MAX31342, | |
| ID_MAX31343, | |
| MAX313XX_ID_NR | |
| }; | |
| /** | |
| * struct chip_desc - descriptor for MAX313xx variants | |
| * @sec_reg: Offset to seconds register. Used to denote the start of the | |
| * current time registers. | |
| * @alarm1_sec_reg: Offset to Alarm1 seconds register. Used to denote the | |
| * start of the alarm registers. | |
| * @int_en_reg: Offset to the interrupt enable register. | |
| * @int_status_reg: Offset to the interrupt status register. | |
| * @ram_reg: Offset to the timestamp RAM (which can be used as SRAM). | |
| * @ram_size: Size of the timestamp RAM. | |
| * @temp_reg: Offset to the temperature register (or 0 if temperature | |
| * sensor is not supported). | |
| * @trickle_reg: Offset to the trickle charger configuration register (or | |
| * 0 if trickle charger is not supported). | |
| * @rst_reg: Offset to the reset register. | |
| * @rst_bit: Bit within the reset register for the software reset. | |
| */ | |
| struct chip_desc { | |
| u8 sec_reg; | |
| u8 alarm1_sec_reg; | |
| u8 int_en_reg; | |
| u8 int_status_reg; | |
| u8 ram_reg; | |
| u8 ram_size; | |
| u8 temp_reg; | |
| u8 trickle_reg; | |
| u8 rst_reg; | |
| u8 rst_bit; | |
| }; | |
| struct max313xx_priv { | |
| enum max313xx_ids id; | |
| const struct chip_desc *chip; | |
| }; | |
| static const struct chip_desc chip[MAX313XX_ID_NR] = { | |
| [ID_MAX31328] = { | |
| .int_en_reg = 0x0E, | |
| .int_status_reg = 0x0F, | |
| .sec_reg = 0x00, | |
| .alarm1_sec_reg = 0x07, | |
| .temp_reg = 0x11, | |
| }, | |
| [ID_MAX31329] = { | |
| .int_en_reg = 0x01, | |
| .int_status_reg = 0x00, | |
| .sec_reg = 0x06, | |
| .alarm1_sec_reg = 0x0D, | |
| .ram_reg = 0x22, | |
| .ram_size = 64, | |
| .trickle_reg = 0x19, | |
| .rst_reg = 0x02, | |
| .rst_bit = BIT(0), | |
| }, | |
| [ID_MAX31331] = { | |
| .int_en_reg = 0x01, | |
| .int_status_reg = 0x00, | |
| .sec_reg = 0x08, | |
| .alarm1_sec_reg = 0x0F, | |
| .ram_reg = 0x20, | |
| .ram_size = 32, | |
| .trickle_reg = 0x1B, | |
| .rst_reg = 0x02, | |
| .rst_bit = BIT(0), | |
| }, | |
| [ID_MAX31334] = { | |
| .int_en_reg = 0x01, | |
| .int_status_reg = 0x00, | |
| .sec_reg = 0x09, | |
| .alarm1_sec_reg = 0x10, | |
| .ram_reg = 0x30, | |
| .ram_size = 32, | |
| .trickle_reg = 0x1E, | |
| .rst_reg = 0x02, | |
| .rst_bit = BIT(0), | |
| }, | |
| [ID_MAX31341] = { | |
| .int_en_reg = 0x04, | |
| .int_status_reg = 0x05, | |
| .sec_reg = 0x06, | |
| .alarm1_sec_reg = 0x0D, | |
| .ram_reg = 0x16, | |
| .ram_size = 64, | |
| .trickle_reg = 0x57, | |
| .rst_reg = 0x00, | |
| .rst_bit = BIT(0), | |
| }, | |
| [ID_MAX31342] = { | |
| .int_en_reg = 0x04, | |
| .int_status_reg = 0x05, | |
| .sec_reg = 0x06, | |
| .alarm1_sec_reg = 0x0D, | |
| .rst_reg = 0x00, | |
| .rst_bit = BIT(0), | |
| }, | |
| [ID_MAX31343] = { | |
| .int_en_reg = 0x01, | |
| .int_status_reg = 0x00, | |
| .sec_reg = 0x06, | |
| .alarm1_sec_reg = 0x0D, | |
| .ram_reg = 0x22, | |
| .ram_size = 64, | |
| .temp_reg = 0x1A, | |
| .trickle_reg = 0x19, | |
| .rst_reg = 0x02, | |
| .rst_bit = BIT(0), | |
| }, | |
| }; | |
| static const u32 max313xx_trickle_ohms[] = { 3000, 6000, 11000 }; | |
| static int max313xx_set_bits(struct udevice *dev, unsigned int reg, unsigned int bits) | |
| { | |
| int ret; | |
| ret = dm_i2c_reg_read(dev, reg); | |
| if (ret < 0) | |
| return ret; | |
| return dm_i2c_reg_write(dev, reg, ret | bits); | |
| } | |
| static int max313xx_clear_bits(struct udevice *dev, unsigned int reg, unsigned int bits) | |
| { | |
| int ret; | |
| ret = dm_i2c_reg_read(dev, reg); | |
| if (ret < 0) | |
| return ret; | |
| return dm_i2c_reg_write(dev, reg, ret & ~bits); | |
| } | |
| static int max313xx_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 max313xx_read_time(struct udevice *dev, struct rtc_time *t) | |
| { | |
| struct max313xx_priv *rtc = dev_get_priv(dev); | |
| u8 regs[7]; | |
| int ret; | |
| ret = dm_i2c_read(dev, rtc->chip->sec_reg, regs, 7); | |
| if (ret) | |
| return ret; | |
| t->tm_sec = bcd2bin(regs[0] & 0x7f); | |
| t->tm_min = bcd2bin(regs[1] & 0x7f); | |
| t->tm_hour = max313xx_get_hour(regs[2]); | |
| t->tm_wday = bcd2bin(regs[3] & 0x07) - 1; | |
| t->tm_mday = bcd2bin(regs[4] & 0x3f); | |
| t->tm_mon = bcd2bin(regs[5] & 0x1f); | |
| t->tm_year = bcd2bin(regs[6]) + 2000; | |
| if (FIELD_GET(MAX313XX_MONTH_CENTURY, regs[5])) | |
| t->tm_year += 100; | |
| dev_dbg(dev, "read %4d-%02d-%02d (wday=%d) %2d:%02d:%02d\n", | |
| t->tm_year, t->tm_mon, t->tm_mday, | |
| t->tm_wday, t->tm_hour, t->tm_min, t->tm_sec); | |
| return 0; | |
| } | |
| static int max313xx_set_time(struct udevice *dev, const struct rtc_time *t) | |
| { | |
| struct max313xx_priv *rtc = dev_get_priv(dev); | |
| u8 regs[7]; | |
| int ret; | |
| dev_dbg(dev, "set %4d-%02d-%02d (wday=%d) %2d:%02d:%02d\n", | |
| t->tm_year, t->tm_mon, t->tm_mday, | |
| t->tm_wday, t->tm_hour, t->tm_min, t->tm_sec); | |
| if (t->tm_year < 2000) { | |
| dev_err(dev, "year %d (before 2000) not supported\n", | |
| t->tm_year); | |
| return -EINVAL; | |
| } | |
| if (rtc->chip->rst_bit) { | |
| ret = max313xx_clear_bits(dev, rtc->chip->rst_reg, rtc->chip->rst_bit); | |
| if (ret) | |
| return ret; | |
| } | |
| regs[0] = bin2bcd(t->tm_sec); | |
| regs[1] = bin2bcd(t->tm_min); | |
| regs[2] = bin2bcd(t->tm_hour); | |
| regs[3] = bin2bcd(t->tm_wday + 1); | |
| regs[4] = bin2bcd(t->tm_mday); | |
| regs[5] = bin2bcd(t->tm_mon); | |
| regs[6] = bin2bcd((t->tm_year - 2000) % 100); | |
| if ((t->tm_year - 2000) >= 200) | |
| regs[5] |= FIELD_PREP(MAX313XX_MONTH_CENTURY, 1); | |
| ret = dm_i2c_write(dev, rtc->chip->sec_reg, regs, 7); | |
| if (ret) | |
| return ret; | |
| switch (rtc->id) { | |
| case ID_MAX31341: | |
| case ID_MAX31342: | |
| ret = max313xx_set_bits(dev, MAX3134X_CFG2_REG, | |
| MAX3134X_CFG2_SET_RTC); | |
| if (ret) | |
| return ret; | |
| udelay(10000); | |
| ret = max313xx_clear_bits(dev, MAX3134X_CFG2_REG, | |
| MAX3134X_CFG2_SET_RTC); | |
| if (ret) | |
| return ret; | |
| break; | |
| default: | |
| break; | |
| } | |
| return ret; | |
| } | |
| static int max313xx_reset(struct udevice *dev) | |
| { | |
| struct max313xx_priv *rtc = dev_get_priv(dev); | |
| int ret = -EINVAL; | |
| if (rtc->chip->rst_bit) | |
| ret = max313xx_set_bits(dev, rtc->chip->rst_reg, rtc->chip->rst_bit); | |
| return ret; | |
| } | |
| static int max313xx_read8(struct udevice *dev, unsigned int reg) | |
| { | |
| return dm_i2c_reg_read(dev, reg); | |
| } | |
| static int max313xx_write8(struct udevice *dev, unsigned int reg, int val) | |
| { | |
| return dm_i2c_reg_write(dev, reg, val); | |
| } | |
| static const struct rtc_ops max3133x_rtc_ops = { | |
| .get = max313xx_read_time, | |
| .set = max313xx_set_time, | |
| .reset = max313xx_reset, | |
| .read8 = max313xx_read8, | |
| .write8 = max313xx_write8, | |
| }; | |
| static int max313xx_init(struct udevice *dev) | |
| { | |
| struct max313xx_priv *rtc = dev_get_priv(dev); | |
| int ret; | |
| switch (rtc->id) { | |
| case ID_MAX31341: | |
| case ID_MAX31342: | |
| ret = max313xx_clear_bits(dev, MAX31341_42_CONFIG1_REG, | |
| MAX31341_42_CONFIG1_OSC_DISABLE); | |
| if (ret) | |
| return ret; | |
| return max313xx_set_bits(dev, MAX31341_42_CONFIG1_REG, | |
| MAX31341_42_CONFIG1_SWRST); | |
| default: | |
| return 0; | |
| } | |
| } | |
| static int max313xx_trickle_charger_setup(struct udevice *dev) | |
| { | |
| struct max313xx_priv *rtc = dev_get_priv(dev); | |
| bool diode; | |
| int index, reg; | |
| u32 ohms; | |
| u32 chargeable; | |
| int ret; | |
| if (dev_read_u32(dev, "trickle-resistor-ohms", &ohms) || | |
| dev_read_u32(dev, "aux-voltage-chargeable", &chargeable)) | |
| return 0; | |
| switch (chargeable) { | |
| case 0: | |
| diode = false; | |
| break; | |
| case 1: | |
| diode = true; | |
| break; | |
| default: | |
| dev_dbg(dev, "unsupported aux-voltage-chargeable value\n"); | |
| return -EINVAL; | |
| } | |
| if (!rtc->chip->trickle_reg) { | |
| dev_warn(dev, "device does not have trickle charger\n"); | |
| return -ENOTSUPP; | |
| } | |
| index = find_closest(ohms, max313xx_trickle_ohms, | |
| ARRAY_SIZE(max313xx_trickle_ohms)) + 1; | |
| switch (rtc->id) { | |
| case ID_MAX31329: | |
| reg = FIELD_PREP(MAX31329_TRICKLE_ENABLE_BIT, 1) | | |
| FIELD_PREP(MAX31329_43_TRICKLE_RES_MASK, index) | | |
| FIELD_PREP(MAX31329_43_TRICKLE_DIODE_EN, diode); | |
| break; | |
| case ID_MAX31331: | |
| case ID_MAX31334: | |
| reg = FIELD_PREP(MAX3133X_TRICKLE_ENABLE_BIT, 1) | | |
| FIELD_PREP(MAX3133X_TRICKLE_DIODE_EN, diode) | | |
| FIELD_PREP(MAX3133X_TRICKLE_RES_MASK, index); | |
| break; | |
| case ID_MAX31341: | |
| if (index == 1) | |
| index = 0; | |
| reg = FIELD_PREP(MAX31341_TRICKLE_ENABLE_BIT, 1) | | |
| FIELD_PREP(MAX31341_TRICKLE_DIODE_EN, diode) | | |
| FIELD_PREP(MAX31341_TRICKLE_RES_MASK, index); | |
| ret = max313xx_set_bits(dev, 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, diode) | | |
| FIELD_PREP(MAX31343_TRICKLE_ENABLE_MASK, | |
| MAX31343_TRICKLE_ENABLE_CODE); | |
| break; | |
| default: | |
| return -EOPNOTSUPP; | |
| } | |
| return dm_i2c_reg_write(dev, rtc->chip->trickle_reg, reg); | |
| } | |
| static int max313xx_probe(struct udevice *dev) | |
| { | |
| struct max313xx_priv *max313xx = dev_get_priv(dev); | |
| int ret; | |
| max313xx->id = dev_get_driver_data(dev); | |
| max313xx->chip = &chip[max313xx->id]; | |
| ret = max313xx_init(dev); | |
| if (ret) | |
| return ret; | |
| return max313xx_trickle_charger_setup(dev); | |
| } | |
| static const struct udevice_id max313xx_of_id[] = { | |
| { .compatible = "adi,max31328", .data = ID_MAX31328 }, | |
| { .compatible = "adi,max31329", .data = ID_MAX31329 }, | |
| { .compatible = "adi,max31331", .data = ID_MAX31331 }, | |
| { .compatible = "adi,max31334", .data = ID_MAX31334 }, | |
| { .compatible = "adi,max31341", .data = ID_MAX31341 }, | |
| { .compatible = "adi,max31342", .data = ID_MAX31342 }, | |
| { .compatible = "adi,max31343", .data = ID_MAX31343 }, | |
| { } | |
| }; | |
| U_BOOT_DRIVER(rtc_max313xx) = { | |
| .name = "rtc-max313xx", | |
| .id = UCLASS_RTC, | |
| .probe = max313xx_probe, | |
| .of_match = max313xx_of_id, | |
| .priv_auto = sizeof(struct max313xx_priv), | |
| .ops = &max3133x_rtc_ops, | |
| }; |