20250827在荣品RD-RK3588-MID开发板的Android13下确认RTL芯片hym8563

console:/ $

console:/ $ time

console:/ $ date

console:/ $ hwclock -r

1|console:/ $

1|console:/ $ su

console:/ # hw

hw/ hwclock hwservicemanager

console:/ # hwclock -r

2025-08-20 09:25:07+0000

console:/ #

130|console:/ # date -s "2025-08-27 19:11:00"

Wed Aug 27 19:11:00 CST 2025

console:/ #

console:/ # hwclock -r

2025-08-20 09:26:21+0000

console:/ # hwclock -w

console:/ # hwclock -r

2025-08-27 11:11:25+0000

console:/ #

20250827在荣品RD-RK3588-MID开发板的Android13下确认RTL芯片hym8563

2025/8/27 14:52

1、相关驱动文件:

Z:\RD-RK3588_Android13\kernel-5.10\drivers\rtc\rtc-hym8563.c

Z:\rk-android13-20250818RD-RK3588-MID\kernel-5.10\drivers\rtc\rtc-hym8563.c

Z:\RD-RK3588_Android13\kernel-5.10\drivers\rtc\Kconfig

config RTC_DRV_HYM8563
tristate "Haoyu Microelectronics HYM8563"
depends on OF
help
Say Y to enable support for the HYM8563 I2C RTC chip. Apart
from the usual rtc functions it provides a clock output of
up to 32kHz.

This driver can also be built as a module. If so, the module
will be called rtc-hym8563.

Z:\RD-RK3588_Android13\kernel-5.10\drivers\rtc\Makefile

obj-$(CONFIG_RTC_DRV_GOLDFISH) += rtc-goldfish.o

obj-(CONFIG_RTC_DRV_HID_SENSOR_TIME) += rtc-hid-sensor-time.o obj-(CONFIG_RTC_DRV_HYM8563) += rtc-hym8563.o

obj-$(CONFIG_RTC_DRV_IMXDI) += rtc-imxdi.o

Z:\RD-RK3588_Android13\kernel-5.10\arch\arm64\configs\rockchip_defconfig

CONFIG_RTC_CLASS=y
CONFIG_RTC_DRV_HYM8563=y

CONFIG_RTC_DRV_RK808=y

https://blog.csdn.net/qq_44647100/article/details/137227570?spm=1001.2101.3001.6650.15\&utm_medium=distribute.wap_relevant.none-task-blog-2\~default\~BlogCommendFromBaidu\~Rate-15-137227570-blog-104753379.237^v3^wap_relevant_t0_download\&depth_1-utm_source=distribute.wap_relevant.none-task-blog-2\~default\~BlogCommendFromBaidu\~Rate-15-137227570-blog-104753379.237^v3^wap_relevant_t0_download\&share_token=77b06e92-470d-4887-8014-0a7d202b3a2e

RK3568 RTC驱动实验

https://blog.csdn.net/qq_44647100/article/details/137227570

https://blog.csdn.net/weixin_42550185/article/details/145658675?spm=1001.2101.3001.6650.17\&utm_medium=distribute.wap_relevant.none-task-blog-2\~default\~BlogCommendFromBaidu\~Rate-17-145658675-blog-122539755.237^v3^wap_relevant_t0_download\&depth_1-utm_source=distribute.wap_relevant.none-task-blog-2\~default\~BlogCommendFromBaidu\~Rate-17-145658675-blog-122539755.237^v3^wap_relevant_t0_download\&share_token=eadd6e60-c1a3-4826-9a82-b0775a26e854

Linux第107步_Linux之PCF8563实验

https://blog.csdn.net/weixin_42550185/article/details/145658675

console:/ console:/ dmesg | grep 8563

15.749157\] \[ T1\] rtc-hym8563 4-0051: rtc information is invalid \[ 15.755871\] \[ T1\] rtc-hym8563 4-0051: registered as rtc0 \[ 15.756884\] \[ T1\] rtc-hym8563 4-0051: setting system clock to 2021-01-01T12:00:00 UTC (1609502400) console:/ $ ![](https://i-blog.csdnimg.cn/direct/4e653e93584e47d589284bdf414df6d2.png) console:/ $ console:/ $ time 0m00.09s user 0m00.12s system console:/ $ console:/ $ date Wed Aug 20 16:43:06 CST 2025 console:/ $ console:/ $ date -s "2025-08-27 16:41:00" date: cannot set date: Operation not permitted Wed Aug 27 16:41:00 CST 2025 1\|console:/ $ 1\|console:/ $ date Wed Aug 20 16:43:38 CST 2025 console:/ $ console:/ $ hwclock \[ 777.834080\]\[ T251\] type=1400 audit(1755679442.703:411): avc: denied { execute } for comm="sh" name="toybox_vendor" dev="dm-3" ino=268 scontext=u:r:shell:s0 tcontext=u:object_r:vendor_toolbox_exec:s0 tclass=file permissive=1 \^C 130\|console:/ $ 130\|console:/ $ hwclock -w hwclock: /dev/rtc0: Permission denied 1\|console:/ $ 1\|console:/ $ su console:/ # console:/ # hwclock -w console:/ # console:/ # hwclock -r 2025-08-20 08:44:45+0000 console:/ # console:/ $ console:/ $ date Wed Aug 20 17:23:46 CST 2025 console:/ $ console:/ $ time 0m00.02s user 0m00.01s system console:/ $ console:/ $ console:/ $ time 0m00.02s user 0m00.01s system console:/ $ console:/ $ date Wed Aug 20 17:24:10 CST 2025 console:/ $ console:/ $ hwclock -r hwclock: /dev/rtc0: Permission denied 1\|console:/ $ 1\|console:/ $ su console:/ # hw hw/ hwclock hwservicemanager console:/ # hwclock -r 2025-08-20 09:25:07+0000 console:/ # 130\|console:/ # date -s "2025-08-27 19:11:00" Wed Aug 27 19:11:00 CST 2025 console:/ # console:/ # hwclock -r 2025-08-20 09:26:21+0000 console:/ # hwclock -w console:/ # console:/ # hwclock -w console:/ # hwclock -r 2025-08-27 11:11:25+0000 console:/ # console:/ # hwclock -r 2025-08-27 11:11:28+0000 console:/ # ![](https://i-blog.csdnimg.cn/direct/dc385add6d8845798b6361e13d552fa1.png) ![](https://i-blog.csdnimg.cn/direct/0189afd7fed444f2b35fa6aa404a5e5d.png) console:/ # console:/ # console:/ # i2cdetect -y 4 0 1 2 3 4 5 6 7 8 9 a b c d e f 00: -- -- -- -- -- -- -- -- -- -- -- -- -- 10: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 20: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 30: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 40: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 50: -- UU -- -- -- -- -- -- -- -- -- -- -- -- -- -- 60: -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- -- 70: -- -- -- -- -- -- -- -- console:/ # console:/ # i2cdump -y 4 0x51 i2cdump: ioctl 703: Device or resource busy 1\|console:/ # 1\|console:/ # ![](https://i-blog.csdnimg.cn/direct/ad55363b94174db79071b1d49a3e39ac.png) 参考资料: 百度:rk3588 hym8563 wakealarm 【有中断唤醒部分】 https://huaweicloud.csdn.net/64e5c3c5a3cccc782cc56331.html rk3568 android11 RTC hym8563调试 https://blog.csdn.net/weixin_74253011/article/details/146016652 RK3588V2--HYM8563TS RTC 实时时钟适配移植 https://www.cnblogs.com/yikoulinux/p/16736139.html Linux驱动\|rtc-hym8563移植笔记 【有Android层的改动】 一、 移植基于平台 soc : rk3568 board: EVB1-DDR4-V10 软 件:Android 11 Linux:4.19.232 https://blog.csdn.net/zg9uagfv/article/details/147782826?spm=1001.2101.3001.4242.1\&utm_medium=distribute.wap_relevant.none-task-blog-2\~default\~baidujs_baidulandingword\~default-0-147782826-blog-132163859.237%5Ev3%5Ewap_relevant_t0_download\&share_token=bc6626b4-65e0-4b5b-8c76-ee86fe99ba05 rk3568 android11上安装rtc-hym8563 https://blog.csdn.net/zg9uagfv/article/details/147782826 设置开机时间,如设置 120 秒后开机: #120秒后定时开机 echo +120 \> /sys/class/rtc/rtc0/wakealarm # 查看开机时间 cat /sys/class/rtc/rtc0/wakealarm #关机 reboot -p https://blog.csdn.net/qq_45063188/article/details/148586092?share_token=aa687d3c-1401-4182-8fa1-93a692b69d6f RK3568调试-外挂RTC芯片ISL1208 https://mp.weixin.qq.com/s/JbV9gKL8FRaknPZcI4fUOg RK3568 HDMI显示驱动调试记录 console:/sys/kernel/debug/dri/0 # cat summary Video Port0: ACTIVE Connector: HDMI-A-1 bus_format\[100a\]: RGB888_1X24 overlay_mode\[0\] output_mode\[f\] color_space\[0\], eotf:0 Display mode: 1920x1080p60 clk\[148500\] real_clk\[148500\] type\[40\] flag\[5

H: 1920 2008 2052 2200

V: 1080 1084 1089 1125

Esmart0-win0: ACTIVE

win_id: 8

format: AB24 little-endian (0x34324241) SDR[0] color_space[0] glb_alpha[0xff]

rotate: xmirror: 0 ymirror: 0 rotate_90: 0 rotate_270: 0

csc: y2r[0] r2y[0] csc mode[0]

zpos: 1

src: pos[0, 0] rect[1920 x 1080]

dst: pos[0, 0] rect[1920 x 1080]

buf[0]: addr: 0x00000000f0f76000 pitch: 7680 offset: 0

Cluster0-win0: ACTIVE

win_id: 0

format: AB24 little-endian (0x34324241)[AFBC] SDR[0] color_space[0] glb_alpha[0xff]

rotate: xmirror: 0 ymirror: 0 rotate_90: 1 rotate_270: 0

csc: y2r[0] r2y[0] csc mode[0]

zpos: 0

src: pos[276, 58] rect[728 x 1164]

dst: pos[0, 0] rect[1920 x 1080]

buf[0]: addr: 0x00000000f3ba4000 pitch: 5120 offset: 0

Video Port1: DISABLED

Video Port2: DISABLED
Video Port3: ACTIVE
Connector: DSI-1
bus_format[100a]: RGB888_1X24
overlay_mode[0] output_mode[0] color_space[0], eotf:0
Display mode: 1200x1920p60
clk[149000] real_clk[149000] type[48] flag[a]
H: 1200 1216 1226 1243
V: 1920 1954 1966 1998
Esmart3-win0: ACTIVE
win_id: 11
format: AB24 little-endian (0x34324241) SDR[0] color_space[0] glb_alpha[0xff]
rotate: xmirror: 0 ymirror: 0 rotate_90: 0 rotate_270: 0
csc: y2r[0] r2y[0] csc mode[0]
zpos: 2
src: pos[0, 0] rect[1200 x 1920]
dst: pos[0, 0] rect[1200 x 1920]
buf[0]: addr: 0x00000000f41fd000 pitch: 4800 offset: 0
Cluster3-win0: ACTIVE

win_id: 6

format: AB24 little-endian (0x34324241)[AFBC] SDR[0] color_space[0] glb_alpha[0xff]

rotate: xmirror: 0 ymirror: 0 rotate_90: 0 rotate_270: 0

csc: y2r[0] r2y[0] csc mode[0]

zpos: 0

src: pos[276, 58] rect[728 x 1164]

dst: pos[0, 0] rect[1200 x 1920]

buf[0]: addr: 0x00000000f3ba4000 pitch: 5120 offset: 0

Cluster3-win1: ACTIVE

win_id: 7

format: AB24 little-endian (0x34324241)[AFBC] SDR[0] color_space[0] glb_alpha[0xff]

rotate: xmirror: 0 ymirror: 0 rotate_90: 0 rotate_270: 0

csc: y2r[0] r2y[0] csc mode[0]

zpos: 1

src: pos[0, 0] rect[1200 x 1920]

dst: pos[0, 0] rect[1200 x 1920]

buf[0]: addr: 0x00000000f3292000 pitch: 4800 offset: 0

console:/sys/kernel/debug/dri/0 #

Z:\rk-android13-20250818RD-RK3588-MID\kernel-5.10\drivers\rtc\rtc-hym8563.c

// SPDX-License-Identifier: GPL-2.0-only

/*

* Haoyu HYM8563 RTC driver

*

* Copyright (C) 2013 MundoReader S.L.

* Author: Heiko Stuebner <heiko@sntech.de>

*

* based on rtc-HYM8563

* Copyright (C) 2010 ROCKCHIP, Inc.

*/

#include <linux/module.h>

#include <linux/clk-provider.h>

#include <linux/i2c.h>

#include <linux/bcd.h>

#include <linux/rtc.h>

#define HYM8563_CTL1 0x00

#define HYM8563_CTL1_TEST BIT(7)

#define HYM8563_CTL1_STOP BIT(5)

#define HYM8563_CTL1_TESTC BIT(3)

#define HYM8563_CTL2 0x01

#define HYM8563_CTL2_TI_TP BIT(4)

#define HYM8563_CTL2_AF BIT(3)

#define HYM8563_CTL2_TF BIT(2)

#define HYM8563_CTL2_AIE BIT(1)

#define HYM8563_CTL2_TIE BIT(0)

#define HYM8563_SEC 0x02

#define HYM8563_SEC_VL BIT(7)

#define HYM8563_SEC_MASK 0x7f

#define HYM8563_MIN 0x03

#define HYM8563_MIN_MASK 0x7f

#define HYM8563_HOUR 0x04

#define HYM8563_HOUR_MASK 0x3f

#define HYM8563_DAY 0x05

#define HYM8563_DAY_MASK 0x3f

#define HYM8563_WEEKDAY 0x06

#define HYM8563_WEEKDAY_MASK 0x07

#define HYM8563_MONTH 0x07

#define HYM8563_MONTH_CENTURY BIT(7)

#define HYM8563_MONTH_MASK 0x1f

#define HYM8563_YEAR 0x08

#define HYM8563_ALM_MIN 0x09

#define HYM8563_ALM_HOUR 0x0a

#define HYM8563_ALM_DAY 0x0b

#define HYM8563_ALM_WEEK 0x0c

/* Each alarm check can be disabled by setting this bit in the register */

#define HYM8563_ALM_BIT_DISABLE BIT(7)

#define HYM8563_CLKOUT 0x0d

#define HYM8563_CLKOUT_ENABLE BIT(7)

#define HYM8563_CLKOUT_32768 0

#define HYM8563_CLKOUT_1024 1

#define HYM8563_CLKOUT_32 2

#define HYM8563_CLKOUT_1 3

#define HYM8563_CLKOUT_MASK 3

#define HYM8563_TMR_CTL 0x0e

#define HYM8563_TMR_CTL_ENABLE BIT(7)

#define HYM8563_TMR_CTL_4096 0

#define HYM8563_TMR_CTL_64 1

#define HYM8563_TMR_CTL_1 2

#define HYM8563_TMR_CTL_1_60 3

#define HYM8563_TMR_CTL_MASK 3

#define HYM8563_TMR_CNT 0x0f

struct hym8563 {

struct i2c_client *client;

struct rtc_device *rtc;

#ifdef CONFIG_COMMON_CLK

struct clk_hw clkout_hw;

#endif

};

/*

* RTC handling

*/

static int hym8563_rtc_read_time(struct device *dev, struct rtc_time *tm)

{

struct i2c_client *client = to_i2c_client(dev);

u8 buf[7];

int ret;

ret = i2c_smbus_read_i2c_block_data(client, HYM8563_SEC, 7, buf);

if (ret < 0)

return ret;

tm->tm_sec = bcd2bin(buf[0] & HYM8563_SEC_MASK);

tm->tm_min = bcd2bin(buf[1] & HYM8563_MIN_MASK);

tm->tm_hour = bcd2bin(buf[2] & HYM8563_HOUR_MASK);

tm->tm_mday = bcd2bin(buf[3] & HYM8563_DAY_MASK);

tm->tm_wday = bcd2bin(buf[4] & HYM8563_WEEKDAY_MASK); /* 0 = Sun */

tm->tm_mon = bcd2bin(buf[5] & HYM8563_MONTH_MASK) - 1; /* 0 = Jan */

tm->tm_year = bcd2bin(buf[6]) + 100;

return 0;

}

static int hym8563_rtc_set_time(struct device *dev, struct rtc_time *tm)

{

struct i2c_client *client = to_i2c_client(dev);

u8 buf[7];

int ret;

/* Years >= 2100 are to far in the future, 19XX is to early */

if (tm->tm_year < 100 || tm->tm_year >= 200)

return -EINVAL;

buf[0] = bin2bcd(tm->tm_sec);

buf[1] = bin2bcd(tm->tm_min);

buf[2] = bin2bcd(tm->tm_hour);

buf[3] = bin2bcd(tm->tm_mday);

buf[4] = bin2bcd(tm->tm_wday);

buf[5] = bin2bcd(tm->tm_mon + 1);

/*

* While the HYM8563 has a century flag in the month register,

* it does not seem to carry it over a subsequent write/read.

* So we'll limit ourself to 100 years, starting at 2000 for now.

*/

buf[6] = bin2bcd(tm->tm_year - 100);

/*

* CTL1 only contains TEST-mode bits apart from stop,

* so no need to read the value first

*/

ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1,

HYM8563_CTL1_STOP);

if (ret < 0)

return ret;

ret = i2c_smbus_write_i2c_block_data(client, HYM8563_SEC, 7, buf);

if (ret < 0)

return ret;

ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0);

if (ret < 0)

return ret;

return 0;

}

static int hym8563_rtc_alarm_irq_enable(struct device *dev,

unsigned int enabled)

{

struct i2c_client *client = to_i2c_client(dev);

int data;

data = i2c_smbus_read_byte_data(client, HYM8563_CTL2);

if (data < 0)

return data;

if (enabled)

data |= HYM8563_CTL2_AIE;

else

data &= ~HYM8563_CTL2_AIE;

return i2c_smbus_write_byte_data(client, HYM8563_CTL2, data);

};

static int hym8563_rtc_read_alarm(struct device *dev, struct rtc_wkalrm *alm)

{

struct i2c_client *client = to_i2c_client(dev);

struct rtc_time *alm_tm = &alm->time;

u8 buf[4];

int ret;

ret = i2c_smbus_read_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf);

if (ret < 0)

return ret;

/* The alarm only has a minute accuracy */

alm_tm->tm_sec = 0;

alm_tm->tm_min = (buf[0] & HYM8563_ALM_BIT_DISABLE) ?

-1 :

bcd2bin(buf[0] & HYM8563_MIN_MASK);

alm_tm->tm_hour = (buf[1] & HYM8563_ALM_BIT_DISABLE) ?

-1 :

bcd2bin(buf[1] & HYM8563_HOUR_MASK);

alm_tm->tm_mday = (buf[2] & HYM8563_ALM_BIT_DISABLE) ?

-1 :

bcd2bin(buf[2] & HYM8563_DAY_MASK);

alm_tm->tm_wday = (buf[3] & HYM8563_ALM_BIT_DISABLE) ?

-1 :

bcd2bin(buf[3] & HYM8563_WEEKDAY_MASK);

ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2);

if (ret < 0)

return ret;

if (ret & HYM8563_CTL2_AIE)

alm->enabled = 1;

return 0;

}

static int hym8563_rtc_set_alarm(struct device *dev, struct rtc_wkalrm *alm)

{

struct i2c_client *client = to_i2c_client(dev);

struct rtc_time *alm_tm = &alm->time;

u8 buf[4];

int ret;

/*

* The alarm has no seconds so deal with it

*/

if (alm_tm->tm_sec) {

alm_tm->tm_sec = 0;

alm_tm->tm_min++;

if (alm_tm->tm_min >= 60) {

alm_tm->tm_min = 0;

alm_tm->tm_hour++;

if (alm_tm->tm_hour >= 24) {

alm_tm->tm_hour = 0;

alm_tm->tm_mday++;

alm_tm->tm_wday++;

if (alm_tm->tm_wday > 6)

alm_tm->tm_wday = 0;

switch (alm_tm->tm_mon + 1) {

case 1:

case 3:

case 5:

case 7:

case 8:

case 10:

case 12:

if (alm_tm->tm_mday > 31)

alm_tm->tm_mday = 1;

break;

case 4:

case 6:

case 9:

case 11:

if (alm_tm->tm_mday > 30)

alm_tm->tm_mday = 1;

break;

case 2:

if (alm_tm->tm_year / 4 == 0) {

if (alm_tm->tm_mday > 29)

alm_tm->tm_mday = 1;

} else if (alm_tm->tm_mday > 28) {

alm_tm->tm_mday = 1;

}

break;

}

}

}

}

ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2);

if (ret < 0)

return ret;

ret &= ~HYM8563_CTL2_AIE;

ret = i2c_smbus_write_byte_data(client, HYM8563_CTL2, ret);

if (ret < 0)

return ret;

buf[0] = (alm_tm->tm_min < 60 && alm_tm->tm_min >= 0) ?

bin2bcd(alm_tm->tm_min) : HYM8563_ALM_BIT_DISABLE;

buf[1] = (alm_tm->tm_hour < 24 && alm_tm->tm_hour >= 0) ?

bin2bcd(alm_tm->tm_hour) : HYM8563_ALM_BIT_DISABLE;

buf[2] = (alm_tm->tm_mday <= 31 && alm_tm->tm_mday >= 1) ?

bin2bcd(alm_tm->tm_mday) : HYM8563_ALM_BIT_DISABLE;

buf[3] = (alm_tm->tm_wday < 7 && alm_tm->tm_wday >= 0) ?

bin2bcd(alm_tm->tm_wday) : HYM8563_ALM_BIT_DISABLE;

ret = i2c_smbus_write_i2c_block_data(client, HYM8563_ALM_MIN, 4, buf);

if (ret < 0)

return ret;

return hym8563_rtc_alarm_irq_enable(dev, alm->enabled);

}

static const struct rtc_class_ops hym8563_rtc_ops = {

.read_time = hym8563_rtc_read_time,

.set_time = hym8563_rtc_set_time,

.alarm_irq_enable = hym8563_rtc_alarm_irq_enable,

.read_alarm = hym8563_rtc_read_alarm,

.set_alarm = hym8563_rtc_set_alarm,

};

/*

* Handling of the clkout

*/

#ifdef CONFIG_COMMON_CLK

#define clkout_hw_to_hym8563(_hw) container_of(_hw, struct hym8563, clkout_hw)

static int clkout_rates[] = {

32768,

1024,

32,

1,

};

static unsigned long hym8563_clkout_recalc_rate(struct clk_hw *hw,

unsigned long parent_rate)

{

struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw);

struct i2c_client *client = hym8563->client;

int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT);

if (ret < 0)

return 0;

ret &= HYM8563_CLKOUT_MASK;

return clkout_rates[ret];

}

static long hym8563_clkout_round_rate(struct clk_hw *hw, unsigned long rate,

unsigned long *prate)

{

int i;

for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)

if (clkout_rates[i] <= rate)

return clkout_rates[i];

return 0;

}

static int hym8563_clkout_set_rate(struct clk_hw *hw, unsigned long rate,

unsigned long parent_rate)

{

struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw);

struct i2c_client *client = hym8563->client;

int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT);

int i;

if (ret < 0)

return ret;

for (i = 0; i < ARRAY_SIZE(clkout_rates); i++)

if (clkout_rates[i] == rate) {

ret &= ~HYM8563_CLKOUT_MASK;

ret |= i;

return i2c_smbus_write_byte_data(client,

HYM8563_CLKOUT, ret);

}

return -EINVAL;

}

static int hym8563_clkout_control(struct clk_hw *hw, bool enable)

{

struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw);

struct i2c_client *client = hym8563->client;

int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT);

if (ret < 0)

return ret;

if (enable)

ret |= HYM8563_CLKOUT_ENABLE;

else

ret &= ~HYM8563_CLKOUT_ENABLE;

return i2c_smbus_write_byte_data(client, HYM8563_CLKOUT, ret);

}

static int hym8563_clkout_prepare(struct clk_hw *hw)

{

return hym8563_clkout_control(hw, 1);

}

static void hym8563_clkout_unprepare(struct clk_hw *hw)

{

hym8563_clkout_control(hw, 0);

}

static int hym8563_clkout_is_prepared(struct clk_hw *hw)

{

struct hym8563 *hym8563 = clkout_hw_to_hym8563(hw);

struct i2c_client *client = hym8563->client;

int ret = i2c_smbus_read_byte_data(client, HYM8563_CLKOUT);

if (ret < 0)

return ret;

return !!(ret & HYM8563_CLKOUT_ENABLE);

}

static const struct clk_ops hym8563_clkout_ops = {

.prepare = hym8563_clkout_prepare,

.unprepare = hym8563_clkout_unprepare,

.is_prepared = hym8563_clkout_is_prepared,

.recalc_rate = hym8563_clkout_recalc_rate,

.round_rate = hym8563_clkout_round_rate,

.set_rate = hym8563_clkout_set_rate,

};

static struct clk *hym8563_clkout_register_clk(struct hym8563 *hym8563)

{

struct i2c_client *client = hym8563->client;

struct device_node *node = client->dev.of_node;

struct clk *clk;

struct clk_init_data init;

init.name = "hym8563-clkout";

init.ops = &hym8563_clkout_ops;

init.flags = CLK_IS_CRITICAL;

init.parent_names = NULL;

init.num_parents = 0;

hym8563->clkout_hw.init = &init;

/* optional override of the clockname */

of_property_read_string(node, "clock-output-names", &init.name);

/* register the clock */

clk = clk_register(&client->dev, &hym8563->clkout_hw);

if (!IS_ERR(clk))

of_clk_add_provider(node, of_clk_src_simple_get, clk);

return clk;

}

#endif

/*

* The alarm interrupt is implemented as a level-low interrupt in the

* hym8563, while the timer interrupt uses a falling edge.

* We don't use the timer at all, so the interrupt is requested to

* use the level-low trigger.

*/

static irqreturn_t hym8563_irq(int irq, void *dev_id)

{

struct hym8563 *hym8563 = (struct hym8563 *)dev_id;

struct i2c_client *client = hym8563->client;

struct mutex *lock = &hym8563->rtc->ops_lock;

int data, ret;

mutex_lock(lock);

/* Clear the alarm flag */

data = i2c_smbus_read_byte_data(client, HYM8563_CTL2);

if (data < 0) {

dev_err(&client->dev, "%s: error reading i2c data %d\n",

func, data);

goto out;

}

data &= ~HYM8563_CTL2_AF;

ret = i2c_smbus_write_byte_data(client, HYM8563_CTL2, data);

if (ret < 0) {

dev_err(&client->dev, "%s: error writing i2c data %d\n",

func, ret);

}

out:

mutex_unlock(lock);

return IRQ_HANDLED;

}

static int hym8563_init_device(struct i2c_client *client)

{

int ret;

/* Clear stop flag if present */

ret = i2c_smbus_write_byte_data(client, HYM8563_CTL1, 0);

if (ret < 0)

return ret;

ret = i2c_smbus_read_byte_data(client, HYM8563_CTL2);

if (ret < 0)

return ret;

/* Disable alarm and timer interrupts */

ret &= ~HYM8563_CTL2_AIE;

ret &= ~HYM8563_CTL2_TIE;

/* Clear any pending alarm and timer flags */

if (ret & HYM8563_CTL2_AF)

ret &= ~HYM8563_CTL2_AF;

if (ret & HYM8563_CTL2_TF)

ret &= ~HYM8563_CTL2_TF;

ret &= ~HYM8563_CTL2_TI_TP;

return i2c_smbus_write_byte_data(client, HYM8563_CTL2, ret);

}

#ifdef CONFIG_PM_SLEEP

static int hym8563_suspend(struct device *dev)

{

struct i2c_client *client = to_i2c_client(dev);

int ret;

if (device_may_wakeup(dev)) {

ret = enable_irq_wake(client->irq);

if (ret) {

dev_err(dev, "enable_irq_wake failed, %d\n", ret);

return ret;

}

}

return 0;

}

static int hym8563_resume(struct device *dev)

{

struct i2c_client *client = to_i2c_client(dev);

if (device_may_wakeup(dev))

disable_irq_wake(client->irq);

return 0;

}

#endif

static SIMPLE_DEV_PM_OPS(hym8563_pm_ops, hym8563_suspend, hym8563_resume);

static int hym8563_probe(struct i2c_client *client,

const struct i2c_device_id *id)

{

struct hym8563 *hym8563;

int ret;

/*

* hym8563 initial time(2021_1_1_12:00:00),

* avoid hym8563 read time error

*/

struct rtc_time tm_read, tm = {

.tm_wday = 0,

.tm_year = 121,

.tm_mon = 0,

.tm_mday = 1,

.tm_hour = 12,

.tm_min = 0,

.tm_sec = 0,

};

hym8563 = devm_kzalloc(&client->dev, sizeof(*hym8563), GFP_KERNEL);

if (!hym8563)

return -ENOMEM;

hym8563->client = client;

i2c_set_clientdata(client, hym8563);

ret = hym8563_init_device(client);

if (ret) {

dev_err(&client->dev, "could not init device, %d\n", ret);

return ret;

}

if (client->irq > 0) {

ret = devm_request_threaded_irq(&client->dev, client->irq,

NULL, hym8563_irq,

IRQF_TRIGGER_LOW | IRQF_ONESHOT,

client->name, hym8563);

if (ret < 0) {

dev_err(&client->dev, "irq %d request failed, %d\n",

client->irq, ret);

return ret;

}

}

if (client->irq > 0 ||

device_property_read_bool(&client->dev, "wakeup-source")) {

device_init_wakeup(&client->dev, true);

}

/* check state of calendar information */

ret = i2c_smbus_read_byte_data(client, HYM8563_SEC);

if (ret < 0)

return ret;

dev_info(&client->dev, "rtc information is %s\n",

(ret & HYM8563_SEC_VL) ? "invalid" : "valid");

hym8563_rtc_read_time(&client->dev, &tm_read);

if ((ret & HYM8563_SEC_VL) || (tm_read.tm_year < 70) || (tm_read.tm_year > 200) ||

(tm_read.tm_mon == -1) || (rtc_valid_tm(&tm_read) != 0))

hym8563_rtc_set_time(&client->dev, &tm);

hym8563->rtc = devm_rtc_device_register(&client->dev, client->name,

&hym8563_rtc_ops, THIS_MODULE);

if (IS_ERR(hym8563->rtc))

return PTR_ERR(hym8563->rtc);

/* the hym8563 alarm only supports a minute accuracy */

hym8563->rtc->uie_unsupported = 1;

#ifdef CONFIG_COMMON_CLK

hym8563_clkout_register_clk(hym8563);

#endif

// rpdzkj: first use maybe disable irq

hym8563_rtc_alarm_irq_enable(&client->dev, 0);

return 0;

}

static const struct i2c_device_id hym8563_id[] = {

{ "hym8563", 0 },

{},

};

MODULE_DEVICE_TABLE(i2c, hym8563_id);

static const struct of_device_id hym8563_dt_idtable[] = {

{ .compatible = "haoyu,hym8563" },

{},

};

MODULE_DEVICE_TABLE(of, hym8563_dt_idtable);

static struct i2c_driver hym8563_driver = {

.driver = {

.name = "rtc-hym8563",

.pm = &hym8563_pm_ops,

.of_match_table = hym8563_dt_idtable,

},

.probe = hym8563_probe,

.id_table = hym8563_id,

};

module_i2c_driver(hym8563_driver);

MODULE_AUTHOR("Heiko Stuebner <heiko@sntech.de>");

MODULE_DESCRIPTION("HYM8563 RTC driver");

MODULE_LICENSE("GPL");