summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorIvan Mikhaylov <i.mikhaylov@yadro.com>2021-07-12 10:46:22 +0300
committerJae Hyun Yoo <jae.hyun.yoo@linux.intel.com>2021-10-28 00:37:05 +0300
commita8b6e881e9ea363bd05590e6e58d31085e40c802 (patch)
tree71cd3a457846e8123ddea3a43440f3fd54fee01a
parent551692a3d6e4acad61e20848dfbbd4544a8eb5ec (diff)
downloadlinux-a8b6e881e9ea363bd05590e6e58d31085e40c802.tar.xz
vegman: kernel: add RTC driver for PCHC620
Signed-off-by: Ivan Mikhaylov <i.mikhaylov@yadro.com>
-rw-r--r--drivers/rtc/Kconfig10
-rw-r--r--drivers/rtc/Makefile1
-rw-r--r--drivers/rtc/rtc-pchc620.c150
3 files changed, 161 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index f7bf87097a9f..ff17f64d518e 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -555,6 +555,16 @@ config RTC_DRV_PALMAS
This driver can also be built as a module. If so, the module
will be called rtc-palma.
+config RTC_DRV_PCHC620
+ tristate "PCH C620 RTC driver"
+ help
+ If you say yes here you get support for the Intel C620 Series PCH
+ built-in read-only RTC. This driver is not for in-system use on x86,
+ but rather is for external access over I2C from a BMC.
+
+ This driver can also be built as a module. If so, the module
+ will be called rtc-pchc620.
+
config RTC_DRV_TPS6586X
tristate "TI TPS6586X RTC driver"
depends on MFD_TPS6586X
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile
index 2dd0dd956b0e..4280feb0f8b7 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -112,6 +112,7 @@ obj-$(CONFIG_RTC_DRV_NTXEC) += rtc-ntxec.o
obj-$(CONFIG_RTC_DRV_OMAP) += rtc-omap.o
obj-$(CONFIG_RTC_DRV_OPAL) += rtc-opal.o
obj-$(CONFIG_RTC_DRV_PALMAS) += rtc-palmas.o
+obj-$(CONFIG_RTC_DRV_PCHC620) += rtc-pchc620.o
obj-$(CONFIG_RTC_DRV_PCAP) += rtc-pcap.o
obj-$(CONFIG_RTC_DRV_PCF2123) += rtc-pcf2123.o
obj-$(CONFIG_RTC_DRV_PCF2127) += rtc-pcf2127.o
diff --git a/drivers/rtc/rtc-pchc620.c b/drivers/rtc/rtc-pchc620.c
new file mode 100644
index 000000000000..a944b327ca67
--- /dev/null
+++ b/drivers/rtc/rtc-pchc620.c
@@ -0,0 +1,150 @@
+// SPDX-License-Identifier: GPL-2.0+
+/*
+ * RTC driver for PCHC620
+ * Copyright (C) 2021 YADRO
+ */
+
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/rtc.h>
+#include <linux/bcd.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+
+#define PCH_REG_FORCE_OFF 0x00
+#define PCH_REG_SC 0x09
+#define PCH_REG_MN 0x0a
+#define PCH_REG_HR 0x0b
+#define PCH_REG_DW 0x0c
+#define PCH_REG_DM 0x0d
+#define PCH_REG_MO 0x0e
+#define PCH_REG_YR 0x0f
+
+#define NUM_TIME_REGS (PCH_REG_YR - PCH_REG_SC + 1)
+
+struct pch {
+ struct rtc_device *rtc;
+ struct regmap *regmap;
+};
+
+static int pchc620_rtc_read_time(struct device *dev, struct rtc_time *tm)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pch *pch = i2c_get_clientdata(client);
+ unsigned char rtc_data[NUM_TIME_REGS] = {0};
+ int rc;
+
+ rc = regmap_bulk_read(pch->regmap, PCH_REG_SC, rtc_data, NUM_TIME_REGS);
+ if (rc < 0) {
+ dev_err(dev, "Fail to read time reg(%d)\n", rc);
+ return rc;
+ }
+
+ tm->tm_sec = bcd2bin(rtc_data[0]);
+ tm->tm_min = bcd2bin(rtc_data[1]);
+ tm->tm_hour = bcd2bin(rtc_data[2]);
+ tm->tm_wday = rtc_data[3];
+ tm->tm_mday = bcd2bin(rtc_data[4]);
+ tm->tm_mon = bcd2bin(rtc_data[5]) - 1;
+ tm->tm_year = bcd2bin(rtc_data[6]) + 100;
+
+ return 0;
+}
+
+static ssize_t pch_force_off(struct device *dev,
+ struct device_attribute *attr,
+ const char *buf, size_t count)
+{
+ struct i2c_client *client = to_i2c_client(dev);
+ struct pch *pch = i2c_get_clientdata(client);
+ unsigned long val;
+ int rc;
+
+ if (kstrtoul(buf, 10, &val))
+ return -EINVAL;
+
+ if (val) {
+ /* 0x02 host force off */
+ rc = regmap_write(pch->regmap, PCH_REG_FORCE_OFF, 0x2);
+ if (rc < 0) {
+ dev_err(dev, "Fail to read time reg(%d)\n", rc);
+ return rc;
+ }
+ }
+
+ return 0;
+}
+static DEVICE_ATTR(force_off, S_IWUSR | S_IWGRP, NULL, pch_force_off);
+
+static const struct rtc_class_ops pchc620_rtc_ops = {
+ .read_time = pchc620_rtc_read_time,
+};
+
+static const struct regmap_config pchc620_rtc_regmap_config = {
+ .reg_bits = 8,
+ .val_bits = 8,
+ .use_single_read = true,
+};
+
+static int pchc620_rtc_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ struct pch *pch;
+ int rc;
+
+ pch = devm_kzalloc(&client->dev, sizeof(*pch), GFP_KERNEL);
+ if (!pch)
+ return -ENOMEM;
+
+ pch->regmap = devm_regmap_init_i2c(client, &pchc620_rtc_regmap_config);
+ if (IS_ERR(pch->regmap)) {
+ dev_err(&client->dev, "regmap_init failed\n");
+ return PTR_ERR(pch->regmap);
+ }
+
+ i2c_set_clientdata(client, pch);
+
+ pch->rtc = devm_rtc_device_register(&client->dev, "pch-rtc",
+ &pchc620_rtc_ops, THIS_MODULE);
+ if (IS_ERR(pch->rtc))
+ return PTR_ERR(pch->rtc);
+
+ rc = sysfs_create_file(&client->dev.kobj, &dev_attr_force_off.attr);
+ if (rc)
+ return rc;
+
+ return 0;
+}
+
+static int pchc620_rtc_remove(struct i2c_client *client)
+{
+ sysfs_remove_file(&client->dev.kobj, &dev_attr_force_off.attr);
+ return 0;
+}
+
+static const struct i2c_device_id pchc620_rtc_id[] = {
+ { "pchc620-rtc", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, pchc620_rtc_id);
+
+static const struct of_device_id pchc620_rtc_of_match[] = {
+ { .compatible = "rtc,pchc620", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, pchc620_rtc_of_match);
+
+static struct i2c_driver pchc620_rtc_driver = {
+ .driver = {
+ .name = "pchc620-rtc",
+ .of_match_table = pchc620_rtc_of_match,
+ },
+ .probe = pchc620_rtc_probe,
+ .remove = pchc620_rtc_remove,
+ .id_table = pchc620_rtc_id,
+};
+module_i2c_driver(pchc620_rtc_driver);
+
+MODULE_DESCRIPTION("RTC PCHC620 driver");
+MODULE_AUTHOR("Ivan Mikhaylov <i.mikhaylov@yadro.com>");
+MODULE_LICENSE("GPL");