// SPDX-License-Identifier: GPL-2.0-only /* * Analog Devices MAX313XX series I2C RTC driver * * Copyright 2022 Analog Devices Inc. */ #include #include #include #include #include #include #include #include /* 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, };