summaryrefslogtreecommitdiff
path: root/meta-openbmc-mods/meta-common/recipes-kernel/linux/linux-aspeed/0008-vegman-kernel-add-RTC-driver-for-PCHC620.patch
blob: 1622a01e5fcf5afcc6419c94e9943ffcc5982b8a (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
From b80e1dea7595519edbabf3e12b6d31f0e128f901 Mon Sep 17 00:00:00 2001
From: Ivan Mikhaylov <i.mikhaylov@yadro.com>
Date: Mon, 12 Jul 2021 10:46:22 +0300
Subject: [PATCH] vegman: kernel: add RTC driver for PCHC620

Signed-off-by: Ivan Mikhaylov <i.mikhaylov@yadro.com>
---
 drivers/rtc/Kconfig       |  10 +++
 drivers/rtc/Makefile      |   1 +
 drivers/rtc/rtc-pchc620.c | 150 ++++++++++++++++++++++++++++++++++++++
 3 files changed, 161 insertions(+)
 create mode 100644 drivers/rtc/rtc-pchc620.c

diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig
index 1adf9f815652..79aba84dd19f 100644
--- a/drivers/rtc/Kconfig
+++ b/drivers/rtc/Kconfig
@@ -564,6 +564,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 4ac8f19fb631..87ab76563a59 100644
--- a/drivers/rtc/Makefile
+++ b/drivers/rtc/Makefile
@@ -118,6 +118,7 @@ obj-$(CONFIG_RTC_DRV_MXC_V2)	+= rtc-mxc_v2.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");
-- 
2.31.1