1 | // SPDX-License-Identifier: GPL-2.0-only |
2 | /* |
3 | * Haoyu HYM8563 RTC driver |
4 | * |
5 | * Copyright (C) 2013 MundoReader S.L. |
6 | * Author: Heiko Stuebner <heiko@sntech.de> |
7 | * |
8 | * based on rtc-HYM8563 |
9 | * Copyright (C) 2010 ROCKCHIP, Inc. |
10 | */ |
11 | |
12 | #include <linux/module.h> |
13 | #include <linux/clk-provider.h> |
14 | #include <linux/i2c.h> |
15 | #include <linux/bcd.h> |
16 | #include <linux/rtc.h> |
17 | |
18 | #define HYM8563_CTL1 0x00 |
19 | #define HYM8563_CTL1_TEST BIT(7) |
20 | #define HYM8563_CTL1_STOP BIT(5) |
21 | #define HYM8563_CTL1_TESTC BIT(3) |
22 | |
23 | #define HYM8563_CTL2 0x01 |
24 | #define HYM8563_CTL2_TI_TP BIT(4) |
25 | #define HYM8563_CTL2_AF BIT(3) |
26 | #define HYM8563_CTL2_TF BIT(2) |
27 | #define HYM8563_CTL2_AIE BIT(1) |
28 | #define HYM8563_CTL2_TIE BIT(0) |
29 | |
30 | #define HYM8563_SEC 0x02 |
31 | #define HYM8563_SEC_VL BIT(7) |
32 | #define HYM8563_SEC_MASK 0x7f |
33 | |
34 | #define HYM8563_MIN 0x03 |
35 | #define HYM8563_MIN_MASK 0x7f |
36 | |
37 | #define HYM8563_HOUR 0x04 |
38 | #define HYM8563_HOUR_MASK 0x3f |
39 | |
40 | #define HYM8563_DAY 0x05 |
41 | #define HYM8563_DAY_MASK 0x3f |
42 | |
43 | #define HYM8563_WEEKDAY 0x06 |
44 | #define HYM8563_WEEKDAY_MASK 0x07 |
45 | |
46 | #define HYM8563_MONTH 0x07 |
47 | #define HYM8563_MONTH_CENTURY BIT(7) |
48 | #define HYM8563_MONTH_MASK 0x1f |
49 | |
50 | #define HYM8563_YEAR 0x08 |
51 | |
52 | #define HYM8563_ALM_MIN 0x09 |
53 | #define HYM8563_ALM_HOUR 0x0a |
54 | #define HYM8563_ALM_DAY 0x0b |
55 | #define HYM8563_ALM_WEEK 0x0c |
56 | |
57 | /* Each alarm check can be disabled by setting this bit in the register */ |
58 | #define HYM8563_ALM_BIT_DISABLE BIT(7) |
59 | |
60 | #define HYM8563_CLKOUT 0x0d |
61 | #define HYM8563_CLKOUT_ENABLE BIT(7) |
62 | #define HYM8563_CLKOUT_32768 0 |
63 | #define HYM8563_CLKOUT_1024 1 |
64 | #define HYM8563_CLKOUT_32 2 |
65 | #define HYM8563_CLKOUT_1 3 |
66 | #define HYM8563_CLKOUT_MASK 3 |
67 | |
68 | #define HYM8563_TMR_CTL 0x0e |
69 | #define HYM8563_TMR_CTL_ENABLE BIT(7) |
70 | #define HYM8563_TMR_CTL_4096 0 |
71 | #define HYM8563_TMR_CTL_64 1 |
72 | #define HYM8563_TMR_CTL_1 2 |
73 | #define HYM8563_TMR_CTL_1_60 3 |
74 | #define HYM8563_TMR_CTL_MASK 3 |
75 | |
76 | #define HYM8563_TMR_CNT 0x0f |
77 | |
78 | struct hym8563 { |
79 | struct i2c_client *client; |
80 | struct rtc_device *rtc; |
81 | #ifdef CONFIG_COMMON_CLK |
82 | struct clk_hw clkout_hw; |
83 | #endif |
84 | }; |
85 | |
86 | /* |
87 | * RTC handling |
88 | */ |
89 | |
90 | static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm) |
91 | { |
92 | struct i2c_client *client = to_i2c_client(dev); |
93 | u8 buf[7]; |
94 | int ret; |
95 | |
96 | ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, length: 7, values: buf); |
97 | if (ret < 0) |
98 | return ret; |
99 | |
100 | if (buf[0] & HYM8563_SEC_VL) { |
101 | dev_warn(&client->dev, |
102 | "no valid clock/calendar values available\n" ); |
103 | return -EINVAL; |
104 | } |
105 | |
106 | tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK); |
107 | tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK); |
108 | tm->tm_hour = bcd2bin(buf[2] & HYM8563_HOUR_MASK); |
109 | tm->tm_mday = bcd2bin(buf[3] & HYM8563_DAY_MASK); |
110 | tm->tm_wday = bcd2bin(buf[4] & HYM8563_WEEKDAY_MASK); /* 0 = Sun */ |
111 | tm->tm_mon = bcd2bin(buf[5] & HYM8563_MONTH_MASK) - 1; /* 0 = Jan */ |
112 | tm->tm_year = bcd2bin(buf[6]) + 100; |
113 | |
114 | return 0; |
115 | } |
116 | |
117 | static int hym8563_rtc_set_time(struct device *dev, struct rtc_time *tm) |
118 | { |
119 | struct i2c_client *client = to_i2c_client(dev); |
120 | u8 buf[7]; |
121 | int ret; |
122 | |
123 | /* Years >= 2100 are to far in the future, 19XX is to early */ |
124 | if (tm->tm_year < 100 || tm->tm_year >= 200) |
125 | return -EINVAL; |
126 | |
127 | buf[0] = bin2bcd(tm->tm_sec); |
128 | buf[1] = bin2bcd(tm->tm_min); |
129 | buf[2] = bin2bcd(tm->tm_hour); |
130 | buf[3] = bin2bcd(tm->tm_mday); |
131 | buf[4] = bin2bcd(tm->tm_wday); |
132 | buf[5] = bin2bcd(tm->tm_mon + 1); |
133 | |
134 | /* |
135 | * While the HYM8563 has a century flag in the month register, |
136 | * it does not seem to carry it over a subsequent write/read. |
137 | * So we'll limit ourself to 100 years, starting at 2000 for now. |
138 | */ |
139 | buf[6] = bin2bcd(tm->tm_year - 100); |
140 | |
141 | /* |
142 | * CTL1 only contains TEST-mode bits apart from stop, |
143 | * so no need to read the value first |
144 | */ |
145 | ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, |
146 | HYM8563_CTL1_STOP); |
147 | if (ret < 0) |
148 | return ret; |
149 | |
150 | ret = i2c_smbus_write_i2c_block_data(client, HYM8563_SEC, length: 7, values: buf); |
151 | if (ret < 0) |
152 | return ret; |
153 | |
154 | ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, value: 0); |
155 | if (ret < 0) |
156 | return ret; |
157 | |
158 | return 0; |
159 | } |
160 | |
161 | static int hym8563_rtc_alarm_irq_enable(struct device *dev, |
162 | unsigned int enabled) |
163 | { |
164 | struct i2c_client *client = to_i2c_client(dev); |
165 | int data; |
166 | |
167 | data = i2c_smbus_read_byte_data(client, HYM8563_CTL2); |
168 | if (data < 0) |
169 | return data; |
170 | |
171 | if (enabled) |
172 | data |= HYM8563_CTL2_AIE; |
173 | else |
174 | data &= ~HYM8563_CTL2_AIE; |
175 | |
176 | return i2c_smbus_write_byte_data(client, HYM8563_CTL2, value: data); |
177 | }; |
178 | |
179 | static int hym8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm) |
180 | { |
181 | struct i2c_client *client = to_i2c_client(dev); |
182 | struct rtc_time *alm_tm = &alm->time; |
183 | u8 buf[4]; |
184 | int ret; |
185 | |
186 | ret = i2c_smbus_read_i2c_block_data(client, HYM8563_ALM_MIN, length: 4, values: buf); |
187 | if (ret < 0) |
188 | return ret; |
189 | |
190 | /* The alarm only has a minute accuracy */ |
191 | alm_tm->tm_sec = 0; |
192 | |
193 | alm_tm->tm_min = (buf[0] & HYM8563_ALM_BIT_DISABLE) ? |
194 | -1 : |
195 | bcd2bin(buf[0] & HYM8563_MIN_MASK); |
196 | alm_tm->tm_hour = (buf[1] & HYM8563_ALM_BIT_DISABLE) ? |
197 | -1 : |
198 | bcd2bin(buf[1] & HYM8563_HOUR_MASK); |
199 | alm_tm->tm_mday = (buf[2] & HYM8563_ALM_BIT_DISABLE) ? |
200 | -1 : |
201 | bcd2bin(buf[2] & HYM8563_DAY_MASK); |
202 | alm_tm->tm_wday = (buf[3] & HYM8563_ALM_BIT_DISABLE) ? |
203 | -1 : |
204 | bcd2bin(buf[3] & HYM8563_WEEKDAY_MASK); |
205 | |
206 | ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2); |
207 | if (ret < 0) |
208 | return ret; |
209 | |
210 | if (ret & HYM8563_CTL2_AIE) |
211 | alm->enabled = 1; |
212 | |
213 | return 0; |
214 | } |
215 | |
216 | static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm) |
217 | { |
218 | struct i2c_client *client = to_i2c_client(dev); |
219 | struct rtc_time *alm_tm = &alm->time; |
220 | u8 buf[4]; |
221 | int ret; |
222 | |
223 | ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2); |
224 | if (ret < 0) |
225 | return ret; |
226 | |
227 | ret &= ~HYM8563_CTL2_AIE; |
228 | |
229 | ret = i2c_smbus_write_byte_data(client, HYM8563_CTL2, value: ret); |
230 | if (ret < 0) |
231 | return ret; |
232 | |
233 | buf[0] = (alm_tm->tm_min < 60 && alm_tm->tm_min >= 0) ? |
234 | bin2bcd(alm_tm->tm_min) : HYM8563_ALM_BIT_DISABLE; |
235 | |
236 | buf[1] = (alm_tm->tm_hour < 24 && alm_tm->tm_hour >= 0) ? |
237 | bin2bcd(alm_tm->tm_hour) : HYM8563_ALM_BIT_DISABLE; |
238 | |
239 | buf[2] = (alm_tm->tm_mday <= 31 && alm_tm->tm_mday >= 1) ? |
240 | bin2bcd(alm_tm->tm_mday) : HYM8563_ALM_BIT_DISABLE; |
241 | |
242 | buf[3] = (alm_tm->tm_wday < 7 && alm_tm->tm_wday >= 0) ? |
243 | bin2bcd(alm_tm->tm_wday) : HYM8563_ALM_BIT_DISABLE; |
244 | |
245 | ret = i2c_smbus_write_i2c_block_data(client, HYM8563_ALM_MIN, length: 4, values: buf); |
246 | if (ret < 0) |
247 | return ret; |
248 | |
249 | return hym8563_rtc_alarm_irq_enable(dev, enabled: alm->enabled); |
250 | } |
251 | |
252 | static const struct rtc_class_ops hym8563_rtc_ops = { |
253 | .read_time = hym8563_rtc_read_time, |
254 | .set_time = hym8563_rtc_set_time, |
255 | .alarm_irq_enable = hym8563_rtc_alarm_irq_enable, |
256 | .read_alarm = hym8563_rtc_read_alarm, |
257 | .set_alarm = hym8563_rtc_set_alarm, |
258 | }; |
259 | |
260 | /* |
261 | * Handling of the clkout |
262 | */ |
263 | |
264 | #ifdef CONFIG_COMMON_CLK |
265 | #define clkout_hw_to_hym8563(_hw) container_of(_hw, struct hym8563, clkout_hw) |
266 | |
267 | static int clkout_rates[] = { |
268 | 32768, |
269 | 1024, |
270 | 32, |
271 | 1, |
272 | }; |
273 | |
274 | static unsigned long hym8563_clkout_recalc_rate(struct clk_hw *hw, |
275 | unsigned long parent_rate) |
276 | { |
277 | struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw); |
278 | struct i2c_client *client = hym8563->client; |
279 | int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT); |
280 | |
281 | if (ret < 0) |
282 | return 0; |
283 | |
284 | ret &= HYM8563_CLKOUT_MASK; |
285 | return clkout_rates[ret]; |
286 | } |
287 | |
288 | static long hym8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate, |
289 | unsigned long *prate) |
290 | { |
291 | int i; |
292 | |
293 | for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) |
294 | if (clkout_rates[i] <= rate) |
295 | return clkout_rates[i]; |
296 | |
297 | return 0; |
298 | } |
299 | |
300 | static int hym8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate, |
301 | unsigned long parent_rate) |
302 | { |
303 | struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw); |
304 | struct i2c_client *client = hym8563->client; |
305 | int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT); |
306 | int i; |
307 | |
308 | if (ret < 0) |
309 | return ret; |
310 | |
311 | for (i = 0; i < ARRAY_SIZE(clkout_rates); i++) |
312 | if (clkout_rates[i] == rate) { |
313 | ret &= ~HYM8563_CLKOUT_MASK; |
314 | ret |= i; |
315 | return i2c_smbus_write_byte_data(client, |
316 | HYM8563_CLKOUT, value: ret); |
317 | } |
318 | |
319 | return -EINVAL; |
320 | } |
321 | |
322 | static int hym8563_clkout_control(struct clk_hw *hw, bool enable) |
323 | { |
324 | struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw); |
325 | struct i2c_client *client = hym8563->client; |
326 | int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT); |
327 | |
328 | if (ret < 0) |
329 | return ret; |
330 | |
331 | if (enable) |
332 | ret |= HYM8563_CLKOUT_ENABLE; |
333 | else |
334 | ret &= ~HYM8563_CLKOUT_ENABLE; |
335 | |
336 | return i2c_smbus_write_byte_data(client, HYM8563_CLKOUT, value: ret); |
337 | } |
338 | |
339 | static int hym8563_clkout_prepare(struct clk_hw *hw) |
340 | { |
341 | return hym8563_clkout_control(hw, enable: 1); |
342 | } |
343 | |
344 | static void hym8563_clkout_unprepare(struct clk_hw *hw) |
345 | { |
346 | hym8563_clkout_control(hw, enable: 0); |
347 | } |
348 | |
349 | static int hym8563_clkout_is_prepared(struct clk_hw *hw) |
350 | { |
351 | struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw); |
352 | struct i2c_client *client = hym8563->client; |
353 | int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT); |
354 | |
355 | if (ret < 0) |
356 | return ret; |
357 | |
358 | return !!(ret & HYM8563_CLKOUT_ENABLE); |
359 | } |
360 | |
361 | static const struct clk_ops hym8563_clkout_ops = { |
362 | .prepare = hym8563_clkout_prepare, |
363 | .unprepare = hym8563_clkout_unprepare, |
364 | .is_prepared = hym8563_clkout_is_prepared, |
365 | .recalc_rate = hym8563_clkout_recalc_rate, |
366 | .round_rate = hym8563_clkout_round_rate, |
367 | .set_rate = hym8563_clkout_set_rate, |
368 | }; |
369 | |
370 | static struct clk *hym8563_clkout_register_clk(struct hym8563 *hym8563) |
371 | { |
372 | struct i2c_client *client = hym8563->client; |
373 | struct device_node *node = client->dev.of_node; |
374 | struct clk *clk; |
375 | struct clk_init_data init; |
376 | int ret; |
377 | |
378 | ret = i2c_smbus_write_byte_data(client, HYM8563_CLKOUT, |
379 | value: 0); |
380 | if (ret < 0) |
381 | return ERR_PTR(error: ret); |
382 | |
383 | init.name = "hym8563-clkout" ; |
384 | init.ops = &hym8563_clkout_ops; |
385 | init.flags = 0; |
386 | init.parent_names = NULL; |
387 | init.num_parents = 0; |
388 | hym8563->clkout_hw.init = &init; |
389 | |
390 | /* optional override of the clockname */ |
391 | of_property_read_string(np: node, propname: "clock-output-names" , out_string: &init.name); |
392 | |
393 | /* register the clock */ |
394 | clk = clk_register(dev: &client->dev, hw: &hym8563->clkout_hw); |
395 | |
396 | if (!IS_ERR(ptr: clk)) |
397 | of_clk_add_provider(np: node, clk_src_get: of_clk_src_simple_get, data: clk); |
398 | |
399 | return clk; |
400 | } |
401 | #endif |
402 | |
403 | /* |
404 | * The alarm interrupt is implemented as a level-low interrupt in the |
405 | * hym8563, while the timer interrupt uses a falling edge. |
406 | * We don't use the timer at all, so the interrupt is requested to |
407 | * use the level-low trigger. |
408 | */ |
409 | static irqreturn_t hym8563_irq(int irq, void *dev_id) |
410 | { |
411 | struct hym8563 *hym8563 = (struct hym8563 *)dev_id; |
412 | struct i2c_client *client = hym8563->client; |
413 | int data, ret; |
414 | |
415 | rtc_lock(hym8563->rtc); |
416 | |
417 | /* Clear the alarm flag */ |
418 | |
419 | data = i2c_smbus_read_byte_data(client, HYM8563_CTL2); |
420 | if (data < 0) { |
421 | dev_err(&client->dev, "%s: error reading i2c data %d\n" , |
422 | __func__, data); |
423 | goto out; |
424 | } |
425 | |
426 | data &= ~HYM8563_CTL2_AF; |
427 | |
428 | ret = i2c_smbus_write_byte_data(client, HYM8563_CTL2, value: data); |
429 | if (ret < 0) { |
430 | dev_err(&client->dev, "%s: error writing i2c data %d\n" , |
431 | __func__, ret); |
432 | } |
433 | |
434 | out: |
435 | rtc_unlock(hym8563->rtc); |
436 | return IRQ_HANDLED; |
437 | } |
438 | |
439 | static int hym8563_init_device(struct i2c_client *client) |
440 | { |
441 | int ret; |
442 | |
443 | /* Clear stop flag if present */ |
444 | ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, value: 0); |
445 | if (ret < 0) |
446 | return ret; |
447 | |
448 | ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2); |
449 | if (ret < 0) |
450 | return ret; |
451 | |
452 | /* Disable alarm and timer interrupts */ |
453 | ret &= ~HYM8563_CTL2_AIE; |
454 | ret &= ~HYM8563_CTL2_TIE; |
455 | |
456 | /* Clear any pending alarm and timer flags */ |
457 | if (ret & HYM8563_CTL2_AF) |
458 | ret &= ~HYM8563_CTL2_AF; |
459 | |
460 | if (ret & HYM8563_CTL2_TF) |
461 | ret &= ~HYM8563_CTL2_TF; |
462 | |
463 | ret &= ~HYM8563_CTL2_TI_TP; |
464 | |
465 | return i2c_smbus_write_byte_data(client, HYM8563_CTL2, value: ret); |
466 | } |
467 | |
468 | #ifdef CONFIG_PM_SLEEP |
469 | static int hym8563_suspend(struct device *dev) |
470 | { |
471 | struct i2c_client *client = to_i2c_client(dev); |
472 | int ret; |
473 | |
474 | if (device_may_wakeup(dev)) { |
475 | ret = enable_irq_wake(irq: client->irq); |
476 | if (ret) { |
477 | dev_err(dev, "enable_irq_wake failed, %d\n" , ret); |
478 | return ret; |
479 | } |
480 | } |
481 | |
482 | return 0; |
483 | } |
484 | |
485 | static int hym8563_resume(struct device *dev) |
486 | { |
487 | struct i2c_client *client = to_i2c_client(dev); |
488 | |
489 | if (device_may_wakeup(dev)) |
490 | disable_irq_wake(irq: client->irq); |
491 | |
492 | return 0; |
493 | } |
494 | #endif |
495 | |
496 | static SIMPLE_DEV_PM_OPS(hym8563_pm_ops, hym8563_suspend, hym8563_resume); |
497 | |
498 | static int hym8563_probe(struct i2c_client *client) |
499 | { |
500 | struct hym8563 *hym8563; |
501 | int ret; |
502 | |
503 | hym8563 = devm_kzalloc(dev: &client->dev, size: sizeof(*hym8563), GFP_KERNEL); |
504 | if (!hym8563) |
505 | return -ENOMEM; |
506 | |
507 | hym8563->rtc = devm_rtc_allocate_device(dev: &client->dev); |
508 | if (IS_ERR(ptr: hym8563->rtc)) |
509 | return PTR_ERR(ptr: hym8563->rtc); |
510 | |
511 | hym8563->client = client; |
512 | i2c_set_clientdata(client, data: hym8563); |
513 | |
514 | ret = hym8563_init_device(client); |
515 | if (ret) { |
516 | dev_err(&client->dev, "could not init device, %d\n" , ret); |
517 | return ret; |
518 | } |
519 | |
520 | if (client->irq > 0) { |
521 | unsigned long irqflags = IRQF_TRIGGER_LOW; |
522 | |
523 | if (dev_fwnode(&client->dev)) |
524 | irqflags = 0; |
525 | |
526 | ret = devm_request_threaded_irq(dev: &client->dev, irq: client->irq, |
527 | NULL, thread_fn: hym8563_irq, |
528 | irqflags: irqflags | IRQF_ONESHOT, |
529 | devname: client->name, dev_id: hym8563); |
530 | if (ret < 0) { |
531 | dev_err(&client->dev, "irq %d request failed, %d\n" , |
532 | client->irq, ret); |
533 | return ret; |
534 | } |
535 | } |
536 | |
537 | if (client->irq > 0 || |
538 | device_property_read_bool(dev: &client->dev, propname: "wakeup-source" )) { |
539 | device_init_wakeup(dev: &client->dev, enable: true); |
540 | } |
541 | |
542 | /* check state of calendar information */ |
543 | ret = i2c_smbus_read_byte_data(client, HYM8563_SEC); |
544 | if (ret < 0) |
545 | return ret; |
546 | |
547 | dev_dbg(&client->dev, "rtc information is %s\n" , |
548 | (ret & HYM8563_SEC_VL) ? "invalid" : "valid" ); |
549 | |
550 | hym8563->rtc->ops = &hym8563_rtc_ops; |
551 | set_bit(RTC_FEATURE_ALARM_RES_MINUTE, addr: hym8563->rtc->features); |
552 | clear_bit(RTC_FEATURE_UPDATE_INTERRUPT, addr: hym8563->rtc->features); |
553 | |
554 | #ifdef CONFIG_COMMON_CLK |
555 | hym8563_clkout_register_clk(hym8563); |
556 | #endif |
557 | |
558 | return devm_rtc_register_device(hym8563->rtc); |
559 | } |
560 | |
561 | static const struct i2c_device_id hym8563_id[] = { |
562 | { "hym8563" , 0 }, |
563 | {}, |
564 | }; |
565 | MODULE_DEVICE_TABLE(i2c, hym8563_id); |
566 | |
567 | static const struct of_device_id hym8563_dt_idtable[] = { |
568 | { .compatible = "haoyu,hym8563" }, |
569 | {}, |
570 | }; |
571 | MODULE_DEVICE_TABLE(of, hym8563_dt_idtable); |
572 | |
573 | static struct i2c_driver hym8563_driver = { |
574 | .driver = { |
575 | .name = "rtc-hym8563" , |
576 | .pm = &hym8563_pm_ops, |
577 | .of_match_table = hym8563_dt_idtable, |
578 | }, |
579 | .probe = hym8563_probe, |
580 | .id_table = hym8563_id, |
581 | }; |
582 | |
583 | module_i2c_driver(hym8563_driver); |
584 | |
585 | MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>" ); |
586 | MODULE_DESCRIPTION("HYM8563 RTC driver" ); |
587 | MODULE_LICENSE("GPL" ); |
588 | |