lists.openwall.net | lists / announce owl-users owl-dev john-users john-dev passwdqc-users yescrypt popa3d-users / oss-security kernel-hardening musl sabotage tlsify passwords / crypt-dev xvendor / Bugtraq Full-Disclosure linux-kernel linux-netdev linux-ext4 linux-hardening linux-cve-announce PHC | |
Open Source and information security mailing list archives
| ||
|
Message-ID: <20211016192118.255624-2-alexey_firago@mentor.com> Date: Sat, 16 Oct 2021 22:21:17 +0300 From: Alexey Firago <alexey_firago@...tor.com> To: <a.zummo@...ertech.it>, <alexandre.belloni@...tlin.com>, <robh+dt@...nel.org> CC: <linux-rtc@...r.kernel.org>, <devicetree@...r.kernel.org>, <linux-kernel@...r.kernel.org>, Alexey Firago <alexey_firago@...tor.com> Subject: [PATCH 1/2] rtc: max31343: Add a driver for Maxim MAX31343 New driver for the Maxim Integrated MAX31343 RTC. RTC supports: - Date/time - 2 alarm timers - Clock outputs - Temperature sensor - 64-Byte RAM Driver currently supports: - Date/time - RAM - Temperature sensor Signed-off-by: Alexey Firago <alexey_firago@...tor.com> --- drivers/rtc/Kconfig | 10 + drivers/rtc/Makefile | 1 + drivers/rtc/rtc-max31343.c | 494 +++++++++++++++++++++++++++++++++++++ 3 files changed, 505 insertions(+) create mode 100644 drivers/rtc/rtc-max31343.c diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index e1bc5214494e..6653f369ea2b 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig @@ -323,6 +323,16 @@ config RTC_DRV_LP8788 help Say Y to enable support for the LP8788 RTC/ALARM driver. +config RTC_DRV_MAX31343 + tristate "Maxim MAX31343" + select REGMAP_I2C + help + If you say yes here you will get support for the + Maxim MAX31343 I2C RTC chip. + + This driver can also be built as a module. If so, the module + will be called rtc-max31343. + config RTC_DRV_MAX6900 tristate "Maxim MAX6900" help diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index 5ceeafe4d5b2..7b8d2386104b 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile @@ -89,6 +89,7 @@ obj-$(CONFIG_RTC_DRV_M41T94) += rtc-m41t94.o obj-$(CONFIG_RTC_DRV_M48T35) += rtc-m48t35.o obj-$(CONFIG_RTC_DRV_M48T59) += rtc-m48t59.o obj-$(CONFIG_RTC_DRV_M48T86) += rtc-m48t86.o +obj-$(CONFIG_RTC_DRV_MAX31343) += rtc-max31343.o obj-$(CONFIG_RTC_DRV_MAX6900) += rtc-max6900.o obj-$(CONFIG_RTC_DRV_MAX6902) += rtc-max6902.o obj-$(CONFIG_RTC_DRV_MAX6916) += rtc-max6916.o diff --git a/drivers/rtc/rtc-max31343.c b/drivers/rtc/rtc-max31343.c new file mode 100644 index 000000000000..f32bd085851b --- /dev/null +++ b/drivers/rtc/rtc-max31343.c @@ -0,0 +1,494 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * rtc class driver for the Maxim MAX31343 chip + * + * Copyright (C) 2021 Alexey Firago <alexey_firago@...tor.com> + * + * Datasheet - https://datasheets.maximintegrated.com/en/ds/MAX31343.pdf + * + */ + +#include <linux/bcd.h> +#include <linux/bitfield.h> +#include <linux/bitops.h> +#include <linux/hwmon.h> +#include <linux/i2c.h> +#include <linux/kernel.h> +#include <linux/math.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/regmap.h> +#include <linux/rtc.h> + +/* REGISTERS */ +/* Control section */ +#define MAX31343_REG_STATUS (0x00) +#define STATUS_A1F BIT(0) /* Alarm1 interrupt flag */ +#define STATUS_A2F BIT(1) /* Alarm2 interrupt flag */ +#define STATUS_TIF BIT(2) /* Timer interrupt flag */ +#define STATUS_TSF BIT(3) /* Temp sense data flag */ +#define STATUS_PFAIL BIT(5) /* Power-fail flag */ +#define STATUS_OSF BIT(6) /* Oscillator stop flag */ +#define STATUS_PSDECT BIT(7) /* Power source indicator */ + +#define MAX31343_REG_INT_EN (0x01) +#define INT_EN_A1IE BIT(0) /* Alarm1 interrupt enable */ +#define INT_EN_A2IE BIT(1) /* Alarm2 interrupt enable */ +#define INT_EN_TIE BIT(2) /* Timer interrupt enable */ +#define INT_EN_TSIE BIT(3) /* Temp sense ready enable */ +#define INT_EN_PFAILE BIT(5) /* Power-fail interrupt enable */ +#define INT_EN_DOSF BIT(6) /* Disable oscillator flag */ + +#define MAX31343_REG_RTC_RESET (0x02) +#define RTC_RESET_SWRST BIT(0) /* Software reset bit */ + +#define MAX31343_REG_RTC_CFG1 (0x03) +#define RTC_CFG1_ENOSC BIT(1) /* Enable for oscillator */ +#define RTC_CFG1_I2C_TIMEOUT BIT(3) /* I2C timeout enable */ +#define RTC_CFG1_DATA_RET BIT(4) /* Data retention bit */ + +#define MAX31343_REG_RTC_CFG2 (0x04) +#define RTC_CFG2_SQW_HZ GENMASK(2, 0) /* SQW clock frequency */ +#define RTC_CFG2_CLKO_HZ GENMASK(6, 3) /* Clock frequency on CLKO */ +#define RTC_CFG2_ENCLKO BIT(7) /* CLKO enable */ + +#define MAX31343_REG_TIMER_CFG (0x05) +#define TIMER_CFG_TFS GENMASK(1, 0) /* Timer frequency */ +#define TIMER_CFG_TRPT BIT(2) /* Timer repeat mode */ +#define TIMER_CFG_TPAUSE BIT(3) /* Timer Pause */ +#define TIMER_CFG_TE BIT(4) /* Timer enable */ + +/* RTC section */ +#define MAX31343_REG_SEC (0x06) +#define SEC10_MASK GENMASK(6, 4) /* RTC seconds in multiples of 10 */ +#define SEC_MASK GENMASK(3, 0) /* RTC seconds value */ + +#define MAX31343_REG_MIN (0x07) +#define MIN10_MASK GENMASK(6, 4) /* RTC minutes in multiples of 10 */ +#define MIN_MASK GENMASK(3, 0) /* RTC minutes value */ + +#define MAX31343_REG_HRS (0x08) +#define HRS10_MASK GENMASK(5, 4) /* RTC hours in multiples of 10 */ +#define HRS_MASK GENMASK(3, 0) /* RTC hours value */ + +#define MAX31343_REG_DAY (0x09) +#define WDAY_MASK GENMASK(2, 0) /* RTC days */ + +#define MAX31343_REG_DATE (0x0a) +#define DATE10_MASK GENMASK(5, 4) /* RTC date in multiples of 10 */ +#define DATE_MASK GENMASK(3, 0) /* RTC date */ + +#define MAX31343_REG_MONTH (0x0b) +#define CENTURY BIT(7) /* Century bit */ +#define MON10_MASK BIT(4) /* RTC months in multiples of 10 */ +#define MON_MASK GENMASK(3, 0) /* RTC months */ + +#define MAX31343_REG_YEAR (0x0c) +#define YEAR_10 GENMASK(7, 4) /* RTC years in multiples of 10 */ +#define YEAR GENMASK(3, 0) /* RTC years */ + +/* Alarm 1 section */ +#define MAX31343_REG_ALM1_SEC (0x0d) +#define A1M1 BIT(7) /* Alarm1 mask bit for seconds */ +#define ALM1SEC_10 GENMASK(6, 4) /* Alarm1 seconds in multiples of 10 */ +#define ALM1SEC GENMASK(3, 0) /* Alarm1 seconds */ + +#define MAX31343_REG_ALM1_MIN (0x0e) +#define A1M2 BIT(7) /* Alarm1 mask bit for minutes */ +#define ALM1MIN_10 GENMASK(6, 4) /* Alarm1 minutes in multiples of 10 */ +#define ALM1MIN GENMASK(3, 0) /* Alarm1 minutes */ + +#define MAX31343_REG_ALM1_HRS (0x0f) +#define A1M3 BIT(7) /* Alarm1 mask bit for hours */ +#define ALM1HRS_10 BIT(4) /* Alarm1 hours in multiples of 10 */ +#define ALM1HRS GENMASK(3, 0) /* Alarm1 hours */ + +#define MAX31343_REG_ALM1DAY_DATE (0x10) +#define A1M4 BIT(7) /* Alarm1 mask bit for day/date */ +#define ALM1DY_DT BIT(6) /* Alarm1 date/day match switch */ +#define ALM1DATE_10 GENMASK(5, 4) /* Alarm1 date in multiples of 10 */ +#define ALM1DAYDATE GENMASK(3, 0) /* Alarm1 day/date */ + +#define MAX31343_REG_ALM1_MON (0x11) +#define A1M5 BIT(7) /* Alarm1 mask bit for month */ +#define A1M6 BIT(6) /* Alarm1 mask bit for year */ +#define ALM1MON_10 BIT(4) /* Alarm1 months in multiples of 10 */ +#define ALM1MON GENMASK(3, 0) /* Alarm1 months */ + +#define MAX31343_REG_ALM1_YEAR (0x12) +#define ALM1YEAR_10 GENMASK(7, 4) /* Alarm1 year in multiples of 10 */ +#define ALM1YEAR GENMASK(3, 0) /* Alarm1 years */ + +/* Alarm 2 section */ +#define MAX31343_REG_ALM2_MIN (0x13) +#define A2M2 BIT(7) /* Alarm2 mask bit for minutes */ +#define ALM2MIN_10 GENMASK(6, 4) /* Alarm2 minutes in multiples of 10 */ +#define ALM2MIN GENMASK(3, 0) /* Alarm2 minutes */ + +#define MAX31343_REG_ALM2_HRS (0x14) +#define A2M3 BIT(7) /* Alarm2 mask bit for hours */ +#define ALM2HRS_10 BIT(4) /* Alarm2 hours in multiples of 10 */ +#define ALM2HRS GENMASK(3, 0) /* Alarm2 hours */ + +#define MAX31343_REG_ALM2_DAY_DATE (0x15) +#define A2M4 BIT(7) /* Alarm2 mask bit for day/date */ +#define ALM2DY_DT BIT(6) /* Alarm2 date/day match switch */ +#define ALM2DATE_10 GENMASK(5, 4) /* Alarm2 date in multiples of 10 */ +#define ALM2DAYDATE GENMASK(3, 0) /* Alarm2 day/date */ + +/* Countdown Timer section */ +#define MAX31343_REG_TIMER_COUNT (0x16) +#define MAX31343_REG_TIMER_INIT (0x17) + +/* Power Management section */ +#define MAX31343_REG_PWR_MGMT (0x18) +#define PFVT GENMASK(5, 4) /* Power fail threshold voltage */ +#define D_VBACK_SEL BIT(3) /* Backup battery select */ +#define D_MAN_SEL BIT(2) /* Power supply selection method */ + +#define MAX31343_REG_TRICKLE_REG (0x19) +#define TCHE GENMASK(7, 4) /* Trickle charger enable */ +#define D_TRICKLE GENMASK(3, 0) /* Charging path for trickle charger */ + +/* Temperature Sensor section */ +#define MAX31343_REG_TEMP_MSB (0x1a) +#define MAX31343_REG_TEMP_LSB (0x1b) + +#define MAX31343_REG_TS_CONFIG (0x1c) +#define AUTOMODE BIT(7) /* Automatic temp. measurement */ +#define ONESHOTMODE BIT(6) /* One-shot temp. measurement */ +#define TTSINT GENMASK(5, 3) /* Temp. measurement interval */ + +/* RAM section */ +#define MAX31343_REG_RAM (0x22) +#define MAX31343_RAM_SIZE 64 + +struct max31343_rtc_data { + struct rtc_device *rtc; + struct regmap *regmap; +}; + +static int max31343_rtc_read_time(struct device *dev, struct rtc_time *tm) +{ + struct max31343_rtc_data *max31343 = dev_get_drvdata(dev); + u8 date[7]; + int ret, status; + + ret = regmap_read(max31343->regmap, MAX31343_REG_STATUS, &status); + if (ret < 0) + return ret; + + if (status & STATUS_PFAIL) + return -EINVAL; + + ret = regmap_bulk_read(max31343->regmap, MAX31343_REG_SEC, date, sizeof(date)); + if (ret) + return ret; + + tm->tm_sec = bcd2bin(date[0] & (SEC10_MASK | SEC_MASK)); + tm->tm_min = bcd2bin(date[1] & (MIN10_MASK | MIN_MASK)); + tm->tm_hour = bcd2bin(date[2] & (HRS10_MASK | HRS_MASK)); + tm->tm_wday = date[3] & WDAY_MASK; + tm->tm_mday = bcd2bin(date[4] & (DATE10_MASK | DATE_MASK)); + tm->tm_mon = bcd2bin(date[5] & (MON10_MASK | MON_MASK)) - 1; + tm->tm_year = bcd2bin(date[6]) + + ((date[5] & CENTURY) ? 200 : 100); + return 0; +} + +static int max31343_rtc_set_time(struct device *dev, struct rtc_time *tm) +{ + struct max31343_rtc_data *max31343 = dev_get_drvdata(dev); + u8 date[7]; + int ret; + + dev_dbg(dev, "RTC set time %04d-%02d-%02d %02d/%02d/%02d\n", + tm->tm_year + 1900, tm->tm_mon, tm->tm_mday, + tm->tm_hour, tm->tm_min, tm->tm_sec); + + date[0] = bin2bcd(tm->tm_sec); + date[1] = bin2bcd(tm->tm_min); + date[2] = bin2bcd(tm->tm_hour); + date[3] = tm->tm_wday; + date[4] = bin2bcd(tm->tm_mday); + date[5] = bin2bcd(tm->tm_mon + 1); + + if (tm->tm_year >= 200) + date[5] |= CENTURY; + date[6] = bin2bcd(tm->tm_year % 100); + + ret = regmap_bulk_write(max31343->regmap, MAX31343_REG_SEC, date, + sizeof(date)); + return ret; +} + +static int max31343_read_temp(struct device *dev, long *mC) +{ + struct max31343_rtc_data *max31343 = dev_get_drvdata(dev); + u8 buf[2]; + int temp; + int ret; + + /* Check for AUTOMODE/ONESHOTMODE first */ + ret = regmap_test_bits(max31343->regmap, MAX31343_REG_TS_CONFIG, AUTOMODE); + if (ret < 0) + return ret; + + /* If not in AUTOMODE, set ONESHOTMODE and wait till measurement is ready */ + if (ret == 0) { + ret = regmap_update_bits(max31343->regmap, MAX31343_REG_TS_CONFIG, + ONESHOTMODE, ONESHOTMODE); + if (ret < 0) + return ret; + do { + ret = regmap_test_bits(max31343->regmap, MAX31343_REG_TS_CONFIG, + ONESHOTMODE); + if (ret < 0) + return ret; + } while (ret != 0); + } + + ret = regmap_bulk_read(max31343->regmap, MAX31343_REG_TEMP_MSB, buf, + sizeof(buf)); + if (ret) + return ret; + temp = sign_extend32(buf[0], 7) << 2; + temp |= FIELD_GET(GENMASK(7, 6), buf[1]); + *mC = (temp * 1000) / 4; + return 0; +} + +static umode_t max31343_hwmon_is_visible(const void *data, enum hwmon_sensor_types type, + u32 attr, int channel) +{ + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return 0644; + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return 0444; + } + break; + default: + break; + } + return 0; +} + +static int max31343_read_temp_interval(struct device *dev, long *val) +{ + struct max31343_rtc_data *max31343 = dev_get_drvdata(dev); + unsigned int reg; + int ret; + + ret = regmap_read(max31343->regmap, MAX31343_REG_TS_CONFIG, ®); + if (ret) + return ret; + + /* + * Automatic measurement mode disabled. + * Measurements performed upon request, just return 0 interval. + */ + if (!(reg & AUTOMODE)) { + *val = 0; + return 0; + } + + /* + * Bits [5:3] are storing interval code (TTSINT). + * Supported codes are [0x0:0x7], which maps to [1sec:128sec]. + * Value in seconds is 2^TTSINT + */ + *val = int_pow(2, ((reg & TTSINT) >> 3)); + return 0; +} + +static int max31343_write_temp_interval(struct device *dev, long val) +{ + struct max31343_rtc_data *max31343 = dev_get_drvdata(dev); + unsigned int reg; + + /* 0 interval means one-shot measurment mode, just clear AUTOMODE */ + if (val == 0) + return regmap_update_bits(max31343->regmap, MAX31343_REG_TS_CONFIG, + AUTOMODE, 0); + if (val > 128) + val = 128; + if (val < 1) + val = 1; + reg = ilog2(val) << 3; + reg |= AUTOMODE; + return regmap_write(max31343->regmap, MAX31343_REG_TS_CONFIG, reg); +} + +static int max31343_hwmon_write(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long val) +{ + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return max31343_write_temp_interval(dev, val); + } + break; + default: + break; + } + + return -EOPNOTSUPP; +} + +static int max31343_hwmon_read(struct device *dev, enum hwmon_sensor_types type, + u32 attr, int channel, long *val) +{ + switch (type) { + case hwmon_chip: + switch (attr) { + case hwmon_chip_update_interval: + return max31343_read_temp_interval(dev, val); + } + break; + case hwmon_temp: + switch (attr) { + case hwmon_temp_input: + return max31343_read_temp(dev, val); + } + break; + default: + break; + } + return -EOPNOTSUPP; +} + +static const struct hwmon_channel_info *max31343_hwmon_info[] = { + HWMON_CHANNEL_INFO(chip, HWMON_C_REGISTER_TZ | HWMON_C_UPDATE_INTERVAL), + HWMON_CHANNEL_INFO(temp, HWMON_T_INPUT), + NULL +}; + +static const struct hwmon_ops max31343_hwmon_hwmon_ops = { + .is_visible = max31343_hwmon_is_visible, + .read = max31343_hwmon_read, + .write = max31343_hwmon_write, +}; + +static const struct hwmon_chip_info max31343_hwmon_chip_info = { + .ops = &max31343_hwmon_hwmon_ops, + .info = max31343_hwmon_info, +}; + +static void max31343_hwmon_register(struct device *dev) +{ + struct max31343_data *max31343 = dev_get_drvdata(dev); + struct device *hwmon_dev; + + if (!IS_REACHABLE(CONFIG_HWMON)) + return; + + hwmon_dev = devm_hwmon_device_register_with_info(dev, "max31343", max31343, + &max31343_hwmon_chip_info, NULL); + if (IS_ERR(hwmon_dev)) { + dev_warn(dev, "unable to register hwmon device %ld\n", + PTR_ERR(hwmon_dev)); + } +} + +static int max31343_nvram_write(void *priv, unsigned int offset, void *val, size_t bytes) +{ + return regmap_bulk_write(priv, MAX31343_REG_RAM + offset, val, bytes); +} + +static int max31343_nvram_read(void *priv, unsigned int offset, void *val, size_t bytes) +{ + return regmap_bulk_read(priv, MAX31343_REG_RAM + offset, val, bytes); +} + +static const struct rtc_class_ops max31343_rtc_ops = { + .read_time = max31343_rtc_read_time, + .set_time = max31343_rtc_set_time, +}; + +static const struct regmap_config max31343_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = MAX31343_REG_RAM + MAX31343_RAM_SIZE, +}; + +static int +max31343_probe(struct i2c_client *client, const struct i2c_device_id *id) +{ + struct max31343_rtc_data *max31343 = NULL; + int ret, status; + struct nvmem_config nvmem_cfg = { + .name = "max31343_nvram", + .word_size = 1, + .stride = 1, + .size = MAX31343_RAM_SIZE, + .type = NVMEM_TYPE_BATTERY_BACKED, + .reg_read = max31343_nvram_read, + .reg_write = max31343_nvram_write, + }; + + max31343 = devm_kzalloc(&client->dev, sizeof(struct max31343_rtc_data), + GFP_KERNEL); + if (!max31343) + return -ENOMEM; + + max31343->regmap = devm_regmap_init_i2c(client, &max31343_regmap_config); + if (IS_ERR(max31343->regmap)) + return PTR_ERR(max31343->regmap); + + i2c_set_clientdata(client, max31343); + + ret = regmap_read(max31343->regmap, MAX31343_REG_STATUS, &status); + if (ret < 0) + return ret; + + max31343->rtc = devm_rtc_allocate_device(&client->dev); + if (IS_ERR(max31343->rtc)) + return PTR_ERR(max31343->rtc); + + max31343->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; + max31343->rtc->range_max = RTC_TIMESTAMP_END_2199; + max31343->rtc->ops = &max31343_rtc_ops; + ret = devm_rtc_register_device(max31343->rtc); + if (ret) + return ret; + + nvmem_cfg.priv = max31343->regmap; + devm_rtc_nvmem_register(max31343->rtc, &nvmem_cfg); + max31343_hwmon_register(&client->dev); + return 0; +} + +static const struct i2c_device_id max31343_id[] = { + { "max31343", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, max31343_id); + +static const __maybe_unused struct of_device_id max31343_of_match[] = { + { .compatible = "maxim,max31343" }, + { } +}; +MODULE_DEVICE_TABLE(of, max31343_of_match); + +static struct i2c_driver max31343_driver = { + .driver = { + .name = "rtc-max31343", + .of_match_table = of_match_ptr(max31343_of_match), + }, + .probe = max31343_probe, + .id_table = max31343_id, +}; + +module_i2c_driver(max31343_driver); + +MODULE_DESCRIPTION("Maxim MAX31343 RTC driver"); +MODULE_AUTHOR("Alexey Firago <alexey_firago@...tor.com>"); +MODULE_LICENSE("GPL"); -- 2.25.1
Powered by blists - more mailing lists