summaryrefslogtreecommitdiff
path: root/drivers/rtc/rtc-pchc620.c
blob: a944b327ca672f685054bb81916ea73b3e57d65d (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
// 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");