diff options
author | Dianlong Li <long17.cool@163.com> | 2018-12-13 05:13:50 -0500 |
---|---|---|
committer | Alexandre Belloni <alexandre.belloni@bootlin.com> | 2019-01-10 15:51:22 -0500 |
commit | 1d67a232105b4dec2b10b2689f9cb89cc366d62f (patch) | |
tree | 1406e04e62ecb4e086295fd8bb74c860855300d4 /drivers/rtc | |
parent | a1c1eae4690aacd3a7ce9df97b47194ac2bf44e2 (diff) |
rtc: sd3078: new driver.
The sd3078 is a combination RTC and SRAM device with I2C interface.
Signed-off-by: Dianlong Li <long17.cool@163.com>
Signed-off-by: Alexandre Belloni <alexandre.belloni@bootlin.com>
Diffstat (limited to 'drivers/rtc')
-rw-r--r-- | drivers/rtc/Kconfig | 9 | ||||
-rw-r--r-- | drivers/rtc/Makefile | 1 | ||||
-rw-r--r-- | drivers/rtc/rtc-sd3078.c | 232 |
3 files changed, 242 insertions, 0 deletions
diff --git a/drivers/rtc/Kconfig b/drivers/rtc/Kconfig index 225b0b8516f3..a24fe8b7b98a 100644 --- a/drivers/rtc/Kconfig +++ b/drivers/rtc/Kconfig | |||
@@ -646,6 +646,15 @@ config RTC_DRV_S5M | |||
646 | This driver can also be built as a module. If so, the module | 646 | This driver can also be built as a module. If so, the module |
647 | will be called rtc-s5m. | 647 | will be called rtc-s5m. |
648 | 648 | ||
649 | config RTC_DRV_SD3078 | ||
650 | tristate "ZXW Crystal SD3078" | ||
651 | help | ||
652 | If you say yes here you get support for the ZXW Crystal | ||
653 | SD3078 RTC chips. | ||
654 | |||
655 | This driver can also be built as a module. If so, the module | ||
656 | will be called rtc-sd3078 | ||
657 | |||
649 | endif # I2C | 658 | endif # I2C |
650 | 659 | ||
651 | comment "SPI RTC drivers" | 660 | comment "SPI RTC drivers" |
diff --git a/drivers/rtc/Makefile b/drivers/rtc/Makefile index df022d820bee..39eb6de4ad74 100644 --- a/drivers/rtc/Makefile +++ b/drivers/rtc/Makefile | |||
@@ -149,6 +149,7 @@ obj-$(CONFIG_RTC_DRV_S3C) += rtc-s3c.o | |||
149 | obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o | 149 | obj-$(CONFIG_RTC_DRV_S5M) += rtc-s5m.o |
150 | obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o | 150 | obj-$(CONFIG_RTC_DRV_SA1100) += rtc-sa1100.o |
151 | obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o | 151 | obj-$(CONFIG_RTC_DRV_SC27XX) += rtc-sc27xx.o |
152 | obj-$(CONFIG_RTC_DRV_SD3078) += rtc-sd3078.o | ||
152 | obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o | 153 | obj-$(CONFIG_RTC_DRV_SH) += rtc-sh.o |
153 | obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o | 154 | obj-$(CONFIG_RTC_DRV_SIRFSOC) += rtc-sirfsoc.o |
154 | obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o | 155 | obj-$(CONFIG_RTC_DRV_SNVS) += rtc-snvs.o |
diff --git a/drivers/rtc/rtc-sd3078.c b/drivers/rtc/rtc-sd3078.c new file mode 100644 index 000000000000..a70d7311d039 --- /dev/null +++ b/drivers/rtc/rtc-sd3078.c | |||
@@ -0,0 +1,232 @@ | |||
1 | // SPDX-License-Identifier: GPL-2.0 | ||
2 | /* | ||
3 | * Real Time Clock (RTC) Driver for sd3078 | ||
4 | * Copyright (C) 2018 Zoro Li | ||
5 | */ | ||
6 | |||
7 | #include <linux/bcd.h> | ||
8 | #include <linux/i2c.h> | ||
9 | #include <linux/module.h> | ||
10 | #include <linux/regmap.h> | ||
11 | #include <linux/rtc.h> | ||
12 | #include <linux/slab.h> | ||
13 | |||
14 | #define SD3078_REG_SC 0x00 | ||
15 | #define SD3078_REG_MN 0x01 | ||
16 | #define SD3078_REG_HR 0x02 | ||
17 | #define SD3078_REG_DW 0x03 | ||
18 | #define SD3078_REG_DM 0x04 | ||
19 | #define SD3078_REG_MO 0x05 | ||
20 | #define SD3078_REG_YR 0x06 | ||
21 | |||
22 | #define SD3078_REG_CTRL1 0x0f | ||
23 | #define SD3078_REG_CTRL2 0x10 | ||
24 | #define SD3078_REG_CTRL3 0x11 | ||
25 | |||
26 | #define KEY_WRITE1 0x80 | ||
27 | #define KEY_WRITE2 0x04 | ||
28 | #define KEY_WRITE3 0x80 | ||
29 | |||
30 | #define NUM_TIME_REGS (SD3078_REG_YR - SD3078_REG_SC + 1) | ||
31 | |||
32 | /* | ||
33 | * The sd3078 has write protection | ||
34 | * and we can choose whether or not to use it. | ||
35 | * Write protection is turned off by default. | ||
36 | */ | ||
37 | #define WRITE_PROTECT_EN 0 | ||
38 | |||
39 | struct sd3078 { | ||
40 | struct rtc_device *rtc; | ||
41 | struct regmap *regmap; | ||
42 | }; | ||
43 | |||
44 | /* | ||
45 | * In order to prevent arbitrary modification of the time register, | ||
46 | * when modification of the register, | ||
47 | * the "write" bit needs to be written in a certain order. | ||
48 | * 1. set WRITE1 bit | ||
49 | * 2. set WRITE2 bit | ||
50 | * 3. set WRITE3 bit | ||
51 | */ | ||
52 | static void sd3078_enable_reg_write(struct sd3078 *sd3078) | ||
53 | { | ||
54 | regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2, | ||
55 | KEY_WRITE1, KEY_WRITE1); | ||
56 | regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, | ||
57 | KEY_WRITE2, KEY_WRITE2); | ||
58 | regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, | ||
59 | KEY_WRITE3, KEY_WRITE3); | ||
60 | } | ||
61 | |||
62 | #if WRITE_PROTECT_EN | ||
63 | /* | ||
64 | * In order to prevent arbitrary modification of the time register, | ||
65 | * we should disable the write function. | ||
66 | * when disable write, | ||
67 | * the "write" bit needs to be clear in a certain order. | ||
68 | * 1. clear WRITE2 bit | ||
69 | * 2. clear WRITE3 bit | ||
70 | * 3. clear WRITE1 bit | ||
71 | */ | ||
72 | static void sd3078_disable_reg_write(struct sd3078 *sd3078) | ||
73 | { | ||
74 | regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, | ||
75 | KEY_WRITE2, 0); | ||
76 | regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL1, | ||
77 | KEY_WRITE3, 0); | ||
78 | regmap_update_bits(sd3078->regmap, SD3078_REG_CTRL2, | ||
79 | KEY_WRITE1, 0); | ||
80 | } | ||
81 | #endif | ||
82 | |||
83 | static int sd3078_rtc_read_time(struct device *dev, struct rtc_time *tm) | ||
84 | { | ||
85 | unsigned char hour; | ||
86 | unsigned char rtc_data[NUM_TIME_REGS] = {0}; | ||
87 | struct i2c_client *client = to_i2c_client(dev); | ||
88 | struct sd3078 *sd3078 = i2c_get_clientdata(client); | ||
89 | int ret; | ||
90 | |||
91 | ret = regmap_bulk_read(sd3078->regmap, SD3078_REG_SC, rtc_data, | ||
92 | NUM_TIME_REGS); | ||
93 | if (ret < 0) { | ||
94 | dev_err(dev, "reading from RTC failed with err:%d\n", ret); | ||
95 | return ret; | ||
96 | } | ||
97 | |||
98 | tm->tm_sec = bcd2bin(rtc_data[SD3078_REG_SC] & 0x7F); | ||
99 | tm->tm_min = bcd2bin(rtc_data[SD3078_REG_MN] & 0x7F); | ||
100 | |||
101 | /* | ||
102 | * The sd3078 supports 12/24 hour mode. | ||
103 | * When getting time, | ||
104 | * we need to convert the 12 hour mode to the 24 hour mode. | ||
105 | */ | ||
106 | hour = rtc_data[SD3078_REG_HR]; | ||
107 | if (hour & 0x80) /* 24H MODE */ | ||
108 | tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x3F); | ||
109 | else if (hour & 0x20) /* 12H MODE PM */ | ||
110 | tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F) + 12; | ||
111 | else /* 12H MODE AM */ | ||
112 | tm->tm_hour = bcd2bin(rtc_data[SD3078_REG_HR] & 0x1F); | ||
113 | |||
114 | tm->tm_mday = bcd2bin(rtc_data[SD3078_REG_DM] & 0x3F); | ||
115 | tm->tm_wday = rtc_data[SD3078_REG_DW] & 0x07; | ||
116 | tm->tm_mon = bcd2bin(rtc_data[SD3078_REG_MO] & 0x1F) - 1; | ||
117 | tm->tm_year = bcd2bin(rtc_data[SD3078_REG_YR]) + 100; | ||
118 | |||
119 | return 0; | ||
120 | } | ||
121 | |||
122 | static int sd3078_rtc_set_time(struct device *dev, struct rtc_time *tm) | ||
123 | { | ||
124 | unsigned char rtc_data[NUM_TIME_REGS]; | ||
125 | struct i2c_client *client = to_i2c_client(dev); | ||
126 | struct sd3078 *sd3078 = i2c_get_clientdata(client); | ||
127 | int ret; | ||
128 | |||
129 | rtc_data[SD3078_REG_SC] = bin2bcd(tm->tm_sec); | ||
130 | rtc_data[SD3078_REG_MN] = bin2bcd(tm->tm_min); | ||
131 | rtc_data[SD3078_REG_HR] = bin2bcd(tm->tm_hour) | 0x80; | ||
132 | rtc_data[SD3078_REG_DM] = bin2bcd(tm->tm_mday); | ||
133 | rtc_data[SD3078_REG_DW] = tm->tm_wday & 0x07; | ||
134 | rtc_data[SD3078_REG_MO] = bin2bcd(tm->tm_mon) + 1; | ||
135 | rtc_data[SD3078_REG_YR] = bin2bcd(tm->tm_year - 100); | ||
136 | |||
137 | #if WRITE_PROTECT_EN | ||
138 | sd3078_enable_reg_write(sd3078); | ||
139 | #endif | ||
140 | |||
141 | ret = regmap_bulk_write(sd3078->regmap, SD3078_REG_SC, rtc_data, | ||
142 | NUM_TIME_REGS); | ||
143 | if (ret < 0) { | ||
144 | dev_err(dev, "writing to RTC failed with err:%d\n", ret); | ||
145 | return ret; | ||
146 | } | ||
147 | |||
148 | #if WRITE_PROTECT_EN | ||
149 | sd3078_disable_reg_write(sd3078); | ||
150 | #endif | ||
151 | |||
152 | return 0; | ||
153 | } | ||
154 | |||
155 | static const struct rtc_class_ops sd3078_rtc_ops = { | ||
156 | .read_time = sd3078_rtc_read_time, | ||
157 | .set_time = sd3078_rtc_set_time, | ||
158 | }; | ||
159 | |||
160 | static const struct regmap_config regmap_config = { | ||
161 | .reg_bits = 8, | ||
162 | .val_bits = 8, | ||
163 | .max_register = 0x11, | ||
164 | }; | ||
165 | |||
166 | static int sd3078_probe(struct i2c_client *client, | ||
167 | const struct i2c_device_id *id) | ||
168 | { | ||
169 | int ret; | ||
170 | struct sd3078 *sd3078; | ||
171 | |||
172 | if (!i2c_check_functionality(client->adapter, I2C_FUNC_I2C)) | ||
173 | return -ENODEV; | ||
174 | |||
175 | sd3078 = devm_kzalloc(&client->dev, sizeof(*sd3078), GFP_KERNEL); | ||
176 | if (!sd3078) | ||
177 | return -ENOMEM; | ||
178 | |||
179 | sd3078->regmap = devm_regmap_init_i2c(client, ®map_config); | ||
180 | if (IS_ERR(sd3078->regmap)) { | ||
181 | dev_err(&client->dev, "regmap allocation failed\n"); | ||
182 | return PTR_ERR(sd3078->regmap); | ||
183 | } | ||
184 | |||
185 | i2c_set_clientdata(client, sd3078); | ||
186 | |||
187 | sd3078->rtc = devm_rtc_allocate_device(&client->dev); | ||
188 | if (IS_ERR(sd3078->rtc)) | ||
189 | return PTR_ERR(sd3078->rtc); | ||
190 | |||
191 | sd3078->rtc->ops = &sd3078_rtc_ops; | ||
192 | sd3078->rtc->range_min = RTC_TIMESTAMP_BEGIN_2000; | ||
193 | sd3078->rtc->range_max = RTC_TIMESTAMP_END_2099; | ||
194 | |||
195 | ret = rtc_register_device(sd3078->rtc); | ||
196 | if (ret) { | ||
197 | dev_err(&client->dev, "failed to register rtc device\n"); | ||
198 | return ret; | ||
199 | } | ||
200 | |||
201 | sd3078_enable_reg_write(sd3078); | ||
202 | |||
203 | return 0; | ||
204 | } | ||
205 | |||
206 | static const struct i2c_device_id sd3078_id[] = { | ||
207 | {"sd3078", 0}, | ||
208 | { } | ||
209 | }; | ||
210 | MODULE_DEVICE_TABLE(i2c, sd3078_id); | ||
211 | |||
212 | static const struct of_device_id rtc_dt_match[] = { | ||
213 | { .compatible = "whwave,sd3078" }, | ||
214 | {}, | ||
215 | }; | ||
216 | MODULE_DEVICE_TABLE(of, rtc_dt_match); | ||
217 | |||
218 | struct i2c_driver sd3078_driver = { | ||
219 | .driver = { | ||
220 | .name = "sd3078", | ||
221 | .owner = THIS_MODULE, | ||
222 | .of_match_table = of_match_ptr(rtc_dt_match), | ||
223 | }, | ||
224 | .probe = sd3078_probe, | ||
225 | .id_table = sd3078_id, | ||
226 | }; | ||
227 | |||
228 | module_i2c_driver(sd3078_driver); | ||
229 | |||
230 | MODULE_AUTHOR("Dianlong Li <long17.cool@163.com>"); | ||
231 | MODULE_DESCRIPTION("SD3078 RTC driver"); | ||
232 | MODULE_LICENSE("GPL v2"); | ||