xref: /kernel/linux/linux-5.10/arch/m68k/atari/time.c (revision 8c2ecf20)
18c2ecf20Sopenharmony_ci/*
28c2ecf20Sopenharmony_ci * linux/arch/m68k/atari/time.c
38c2ecf20Sopenharmony_ci *
48c2ecf20Sopenharmony_ci * Atari time and real time clock stuff
58c2ecf20Sopenharmony_ci *
68c2ecf20Sopenharmony_ci * Assembled of parts of former atari/config.c 97-12-18 by Roman Hodek
78c2ecf20Sopenharmony_ci *
88c2ecf20Sopenharmony_ci * This file is subject to the terms and conditions of the GNU General Public
98c2ecf20Sopenharmony_ci * License.  See the file COPYING in the main directory of this archive
108c2ecf20Sopenharmony_ci * for more details.
118c2ecf20Sopenharmony_ci */
128c2ecf20Sopenharmony_ci
138c2ecf20Sopenharmony_ci#include <linux/types.h>
148c2ecf20Sopenharmony_ci#include <linux/mc146818rtc.h>
158c2ecf20Sopenharmony_ci#include <linux/interrupt.h>
168c2ecf20Sopenharmony_ci#include <linux/init.h>
178c2ecf20Sopenharmony_ci#include <linux/rtc.h>
188c2ecf20Sopenharmony_ci#include <linux/bcd.h>
198c2ecf20Sopenharmony_ci#include <linux/clocksource.h>
208c2ecf20Sopenharmony_ci#include <linux/delay.h>
218c2ecf20Sopenharmony_ci#include <linux/export.h>
228c2ecf20Sopenharmony_ci
238c2ecf20Sopenharmony_ci#include <asm/atariints.h>
248c2ecf20Sopenharmony_ci
258c2ecf20Sopenharmony_ciDEFINE_SPINLOCK(rtc_lock);
268c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(rtc_lock);
278c2ecf20Sopenharmony_ci
288c2ecf20Sopenharmony_cistatic u64 atari_read_clk(struct clocksource *cs);
298c2ecf20Sopenharmony_ci
308c2ecf20Sopenharmony_cistatic struct clocksource atari_clk = {
318c2ecf20Sopenharmony_ci	.name   = "mfp",
328c2ecf20Sopenharmony_ci	.rating = 100,
338c2ecf20Sopenharmony_ci	.read   = atari_read_clk,
348c2ecf20Sopenharmony_ci	.mask   = CLOCKSOURCE_MASK(32),
358c2ecf20Sopenharmony_ci	.flags  = CLOCK_SOURCE_IS_CONTINUOUS,
368c2ecf20Sopenharmony_ci};
378c2ecf20Sopenharmony_ci
388c2ecf20Sopenharmony_cistatic u32 clk_total;
398c2ecf20Sopenharmony_cistatic u8 last_timer_count;
408c2ecf20Sopenharmony_ci
418c2ecf20Sopenharmony_cistatic irqreturn_t mfp_timer_c_handler(int irq, void *dev_id)
428c2ecf20Sopenharmony_ci{
438c2ecf20Sopenharmony_ci	irq_handler_t timer_routine = dev_id;
448c2ecf20Sopenharmony_ci	unsigned long flags;
458c2ecf20Sopenharmony_ci
468c2ecf20Sopenharmony_ci	local_irq_save(flags);
478c2ecf20Sopenharmony_ci	do {
488c2ecf20Sopenharmony_ci		last_timer_count = st_mfp.tim_dt_c;
498c2ecf20Sopenharmony_ci	} while (last_timer_count == 1);
508c2ecf20Sopenharmony_ci	clk_total += INT_TICKS;
518c2ecf20Sopenharmony_ci	timer_routine(0, NULL);
528c2ecf20Sopenharmony_ci	local_irq_restore(flags);
538c2ecf20Sopenharmony_ci
548c2ecf20Sopenharmony_ci	return IRQ_HANDLED;
558c2ecf20Sopenharmony_ci}
568c2ecf20Sopenharmony_ci
578c2ecf20Sopenharmony_civoid __init
588c2ecf20Sopenharmony_ciatari_sched_init(irq_handler_t timer_routine)
598c2ecf20Sopenharmony_ci{
608c2ecf20Sopenharmony_ci    /* set Timer C data Register */
618c2ecf20Sopenharmony_ci    st_mfp.tim_dt_c = INT_TICKS;
628c2ecf20Sopenharmony_ci    /* start timer C, div = 1:100 */
638c2ecf20Sopenharmony_ci    st_mfp.tim_ct_cd = (st_mfp.tim_ct_cd & 15) | 0x60;
648c2ecf20Sopenharmony_ci    /* install interrupt service routine for MFP Timer C */
658c2ecf20Sopenharmony_ci    if (request_irq(IRQ_MFP_TIMC, mfp_timer_c_handler, IRQF_TIMER, "timer",
668c2ecf20Sopenharmony_ci                    timer_routine))
678c2ecf20Sopenharmony_ci	pr_err("Couldn't register timer interrupt\n");
688c2ecf20Sopenharmony_ci
698c2ecf20Sopenharmony_ci    clocksource_register_hz(&atari_clk, INT_CLK);
708c2ecf20Sopenharmony_ci}
718c2ecf20Sopenharmony_ci
728c2ecf20Sopenharmony_ci/* ++andreas: gettimeoffset fixed to check for pending interrupt */
738c2ecf20Sopenharmony_ci
748c2ecf20Sopenharmony_cistatic u64 atari_read_clk(struct clocksource *cs)
758c2ecf20Sopenharmony_ci{
768c2ecf20Sopenharmony_ci	unsigned long flags;
778c2ecf20Sopenharmony_ci	u8 count;
788c2ecf20Sopenharmony_ci	u32 ticks;
798c2ecf20Sopenharmony_ci
808c2ecf20Sopenharmony_ci	local_irq_save(flags);
818c2ecf20Sopenharmony_ci	/* Ensure that the count is monotonically decreasing, even though
828c2ecf20Sopenharmony_ci	 * the result may briefly stop changing after counter wrap-around.
838c2ecf20Sopenharmony_ci	 */
848c2ecf20Sopenharmony_ci	count = min(st_mfp.tim_dt_c, last_timer_count);
858c2ecf20Sopenharmony_ci	last_timer_count = count;
868c2ecf20Sopenharmony_ci
878c2ecf20Sopenharmony_ci	ticks = INT_TICKS - count;
888c2ecf20Sopenharmony_ci	ticks += clk_total;
898c2ecf20Sopenharmony_ci	local_irq_restore(flags);
908c2ecf20Sopenharmony_ci
918c2ecf20Sopenharmony_ci	return ticks;
928c2ecf20Sopenharmony_ci}
938c2ecf20Sopenharmony_ci
948c2ecf20Sopenharmony_ci
958c2ecf20Sopenharmony_cistatic void mste_read(struct MSTE_RTC *val)
968c2ecf20Sopenharmony_ci{
978c2ecf20Sopenharmony_ci#define COPY(v) val->v=(mste_rtc.v & 0xf)
988c2ecf20Sopenharmony_ci	do {
998c2ecf20Sopenharmony_ci		COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
1008c2ecf20Sopenharmony_ci		COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
1018c2ecf20Sopenharmony_ci		COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
1028c2ecf20Sopenharmony_ci		COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
1038c2ecf20Sopenharmony_ci		COPY(year_tens) ;
1048c2ecf20Sopenharmony_ci	/* prevent from reading the clock while it changed */
1058c2ecf20Sopenharmony_ci	} while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
1068c2ecf20Sopenharmony_ci#undef COPY
1078c2ecf20Sopenharmony_ci}
1088c2ecf20Sopenharmony_ci
1098c2ecf20Sopenharmony_cistatic void mste_write(struct MSTE_RTC *val)
1108c2ecf20Sopenharmony_ci{
1118c2ecf20Sopenharmony_ci#define COPY(v) mste_rtc.v=val->v
1128c2ecf20Sopenharmony_ci	do {
1138c2ecf20Sopenharmony_ci		COPY(sec_ones) ; COPY(sec_tens) ; COPY(min_ones) ;
1148c2ecf20Sopenharmony_ci		COPY(min_tens) ; COPY(hr_ones) ; COPY(hr_tens) ;
1158c2ecf20Sopenharmony_ci		COPY(weekday) ; COPY(day_ones) ; COPY(day_tens) ;
1168c2ecf20Sopenharmony_ci		COPY(mon_ones) ; COPY(mon_tens) ; COPY(year_ones) ;
1178c2ecf20Sopenharmony_ci		COPY(year_tens) ;
1188c2ecf20Sopenharmony_ci	/* prevent from writing the clock while it changed */
1198c2ecf20Sopenharmony_ci	} while (val->sec_ones != (mste_rtc.sec_ones & 0xf));
1208c2ecf20Sopenharmony_ci#undef COPY
1218c2ecf20Sopenharmony_ci}
1228c2ecf20Sopenharmony_ci
1238c2ecf20Sopenharmony_ci#define	RTC_READ(reg)				\
1248c2ecf20Sopenharmony_ci    ({	unsigned char	__val;			\
1258c2ecf20Sopenharmony_ci		(void) atari_writeb(reg,&tt_rtc.regsel);	\
1268c2ecf20Sopenharmony_ci		__val = tt_rtc.data;		\
1278c2ecf20Sopenharmony_ci		__val;				\
1288c2ecf20Sopenharmony_ci	})
1298c2ecf20Sopenharmony_ci
1308c2ecf20Sopenharmony_ci#define	RTC_WRITE(reg,val)			\
1318c2ecf20Sopenharmony_ci    do {					\
1328c2ecf20Sopenharmony_ci		atari_writeb(reg,&tt_rtc.regsel);	\
1338c2ecf20Sopenharmony_ci		tt_rtc.data = (val);		\
1348c2ecf20Sopenharmony_ci	} while(0)
1358c2ecf20Sopenharmony_ci
1368c2ecf20Sopenharmony_ci
1378c2ecf20Sopenharmony_ci#define HWCLK_POLL_INTERVAL	5
1388c2ecf20Sopenharmony_ci
1398c2ecf20Sopenharmony_ciint atari_mste_hwclk( int op, struct rtc_time *t )
1408c2ecf20Sopenharmony_ci{
1418c2ecf20Sopenharmony_ci    int hour, year;
1428c2ecf20Sopenharmony_ci    int hr24=0;
1438c2ecf20Sopenharmony_ci    struct MSTE_RTC val;
1448c2ecf20Sopenharmony_ci
1458c2ecf20Sopenharmony_ci    mste_rtc.mode=(mste_rtc.mode | 1);
1468c2ecf20Sopenharmony_ci    hr24=mste_rtc.mon_tens & 1;
1478c2ecf20Sopenharmony_ci    mste_rtc.mode=(mste_rtc.mode & ~1);
1488c2ecf20Sopenharmony_ci
1498c2ecf20Sopenharmony_ci    if (op) {
1508c2ecf20Sopenharmony_ci        /* write: prepare values */
1518c2ecf20Sopenharmony_ci
1528c2ecf20Sopenharmony_ci        val.sec_ones = t->tm_sec % 10;
1538c2ecf20Sopenharmony_ci        val.sec_tens = t->tm_sec / 10;
1548c2ecf20Sopenharmony_ci        val.min_ones = t->tm_min % 10;
1558c2ecf20Sopenharmony_ci        val.min_tens = t->tm_min / 10;
1568c2ecf20Sopenharmony_ci        hour = t->tm_hour;
1578c2ecf20Sopenharmony_ci        if (!hr24) {
1588c2ecf20Sopenharmony_ci	    if (hour > 11)
1598c2ecf20Sopenharmony_ci		hour += 20 - 12;
1608c2ecf20Sopenharmony_ci	    if (hour == 0 || hour == 20)
1618c2ecf20Sopenharmony_ci		hour += 12;
1628c2ecf20Sopenharmony_ci        }
1638c2ecf20Sopenharmony_ci        val.hr_ones = hour % 10;
1648c2ecf20Sopenharmony_ci        val.hr_tens = hour / 10;
1658c2ecf20Sopenharmony_ci        val.day_ones = t->tm_mday % 10;
1668c2ecf20Sopenharmony_ci        val.day_tens = t->tm_mday / 10;
1678c2ecf20Sopenharmony_ci        val.mon_ones = (t->tm_mon+1) % 10;
1688c2ecf20Sopenharmony_ci        val.mon_tens = (t->tm_mon+1) / 10;
1698c2ecf20Sopenharmony_ci        year = t->tm_year - 80;
1708c2ecf20Sopenharmony_ci        val.year_ones = year % 10;
1718c2ecf20Sopenharmony_ci        val.year_tens = year / 10;
1728c2ecf20Sopenharmony_ci        val.weekday = t->tm_wday;
1738c2ecf20Sopenharmony_ci        mste_write(&val);
1748c2ecf20Sopenharmony_ci        mste_rtc.mode=(mste_rtc.mode | 1);
1758c2ecf20Sopenharmony_ci        val.year_ones = (year % 4);	/* leap year register */
1768c2ecf20Sopenharmony_ci        mste_rtc.mode=(mste_rtc.mode & ~1);
1778c2ecf20Sopenharmony_ci    }
1788c2ecf20Sopenharmony_ci    else {
1798c2ecf20Sopenharmony_ci        mste_read(&val);
1808c2ecf20Sopenharmony_ci        t->tm_sec = val.sec_ones + val.sec_tens * 10;
1818c2ecf20Sopenharmony_ci        t->tm_min = val.min_ones + val.min_tens * 10;
1828c2ecf20Sopenharmony_ci        hour = val.hr_ones + val.hr_tens * 10;
1838c2ecf20Sopenharmony_ci	if (!hr24) {
1848c2ecf20Sopenharmony_ci	    if (hour == 12 || hour == 12 + 20)
1858c2ecf20Sopenharmony_ci		hour -= 12;
1868c2ecf20Sopenharmony_ci	    if (hour >= 20)
1878c2ecf20Sopenharmony_ci                hour += 12 - 20;
1888c2ecf20Sopenharmony_ci        }
1898c2ecf20Sopenharmony_ci	t->tm_hour = hour;
1908c2ecf20Sopenharmony_ci	t->tm_mday = val.day_ones + val.day_tens * 10;
1918c2ecf20Sopenharmony_ci        t->tm_mon  = val.mon_ones + val.mon_tens * 10 - 1;
1928c2ecf20Sopenharmony_ci        t->tm_year = val.year_ones + val.year_tens * 10 + 80;
1938c2ecf20Sopenharmony_ci        t->tm_wday = val.weekday;
1948c2ecf20Sopenharmony_ci    }
1958c2ecf20Sopenharmony_ci    return 0;
1968c2ecf20Sopenharmony_ci}
1978c2ecf20Sopenharmony_ci
1988c2ecf20Sopenharmony_ciint atari_tt_hwclk( int op, struct rtc_time *t )
1998c2ecf20Sopenharmony_ci{
2008c2ecf20Sopenharmony_ci    int sec=0, min=0, hour=0, day=0, mon=0, year=0, wday=0;
2018c2ecf20Sopenharmony_ci    unsigned long	flags;
2028c2ecf20Sopenharmony_ci    unsigned char	ctrl;
2038c2ecf20Sopenharmony_ci    int pm = 0;
2048c2ecf20Sopenharmony_ci
2058c2ecf20Sopenharmony_ci    ctrl = RTC_READ(RTC_CONTROL); /* control registers are
2068c2ecf20Sopenharmony_ci                                   * independent from the UIP */
2078c2ecf20Sopenharmony_ci
2088c2ecf20Sopenharmony_ci    if (op) {
2098c2ecf20Sopenharmony_ci        /* write: prepare values */
2108c2ecf20Sopenharmony_ci
2118c2ecf20Sopenharmony_ci        sec  = t->tm_sec;
2128c2ecf20Sopenharmony_ci        min  = t->tm_min;
2138c2ecf20Sopenharmony_ci        hour = t->tm_hour;
2148c2ecf20Sopenharmony_ci        day  = t->tm_mday;
2158c2ecf20Sopenharmony_ci        mon  = t->tm_mon + 1;
2168c2ecf20Sopenharmony_ci        year = t->tm_year - atari_rtc_year_offset;
2178c2ecf20Sopenharmony_ci        wday = t->tm_wday + (t->tm_wday >= 0);
2188c2ecf20Sopenharmony_ci
2198c2ecf20Sopenharmony_ci        if (!(ctrl & RTC_24H)) {
2208c2ecf20Sopenharmony_ci	    if (hour > 11) {
2218c2ecf20Sopenharmony_ci		pm = 0x80;
2228c2ecf20Sopenharmony_ci		if (hour != 12)
2238c2ecf20Sopenharmony_ci		    hour -= 12;
2248c2ecf20Sopenharmony_ci	    }
2258c2ecf20Sopenharmony_ci	    else if (hour == 0)
2268c2ecf20Sopenharmony_ci		hour = 12;
2278c2ecf20Sopenharmony_ci        }
2288c2ecf20Sopenharmony_ci
2298c2ecf20Sopenharmony_ci        if (!(ctrl & RTC_DM_BINARY)) {
2308c2ecf20Sopenharmony_ci	    sec = bin2bcd(sec);
2318c2ecf20Sopenharmony_ci	    min = bin2bcd(min);
2328c2ecf20Sopenharmony_ci	    hour = bin2bcd(hour);
2338c2ecf20Sopenharmony_ci	    day = bin2bcd(day);
2348c2ecf20Sopenharmony_ci	    mon = bin2bcd(mon);
2358c2ecf20Sopenharmony_ci	    year = bin2bcd(year);
2368c2ecf20Sopenharmony_ci	    if (wday >= 0)
2378c2ecf20Sopenharmony_ci		wday = bin2bcd(wday);
2388c2ecf20Sopenharmony_ci        }
2398c2ecf20Sopenharmony_ci    }
2408c2ecf20Sopenharmony_ci
2418c2ecf20Sopenharmony_ci    /* Reading/writing the clock registers is a bit critical due to
2428c2ecf20Sopenharmony_ci     * the regular update cycle of the RTC. While an update is in
2438c2ecf20Sopenharmony_ci     * progress, registers 0..9 shouldn't be touched.
2448c2ecf20Sopenharmony_ci     * The problem is solved like that: If an update is currently in
2458c2ecf20Sopenharmony_ci     * progress (the UIP bit is set), the process sleeps for a while
2468c2ecf20Sopenharmony_ci     * (50ms). This really should be enough, since the update cycle
2478c2ecf20Sopenharmony_ci     * normally needs 2 ms.
2488c2ecf20Sopenharmony_ci     * If the UIP bit reads as 0, we have at least 244 usecs until the
2498c2ecf20Sopenharmony_ci     * update starts. This should be enough... But to be sure,
2508c2ecf20Sopenharmony_ci     * additionally the RTC_SET bit is set to prevent an update cycle.
2518c2ecf20Sopenharmony_ci     */
2528c2ecf20Sopenharmony_ci
2538c2ecf20Sopenharmony_ci    while( RTC_READ(RTC_FREQ_SELECT) & RTC_UIP ) {
2548c2ecf20Sopenharmony_ci	if (in_atomic() || irqs_disabled())
2558c2ecf20Sopenharmony_ci	    mdelay(1);
2568c2ecf20Sopenharmony_ci	else
2578c2ecf20Sopenharmony_ci	    schedule_timeout_interruptible(HWCLK_POLL_INTERVAL);
2588c2ecf20Sopenharmony_ci    }
2598c2ecf20Sopenharmony_ci
2608c2ecf20Sopenharmony_ci    local_irq_save(flags);
2618c2ecf20Sopenharmony_ci    RTC_WRITE( RTC_CONTROL, ctrl | RTC_SET );
2628c2ecf20Sopenharmony_ci    if (!op) {
2638c2ecf20Sopenharmony_ci        sec  = RTC_READ( RTC_SECONDS );
2648c2ecf20Sopenharmony_ci        min  = RTC_READ( RTC_MINUTES );
2658c2ecf20Sopenharmony_ci        hour = RTC_READ( RTC_HOURS );
2668c2ecf20Sopenharmony_ci        day  = RTC_READ( RTC_DAY_OF_MONTH );
2678c2ecf20Sopenharmony_ci        mon  = RTC_READ( RTC_MONTH );
2688c2ecf20Sopenharmony_ci        year = RTC_READ( RTC_YEAR );
2698c2ecf20Sopenharmony_ci        wday = RTC_READ( RTC_DAY_OF_WEEK );
2708c2ecf20Sopenharmony_ci    }
2718c2ecf20Sopenharmony_ci    else {
2728c2ecf20Sopenharmony_ci        RTC_WRITE( RTC_SECONDS, sec );
2738c2ecf20Sopenharmony_ci        RTC_WRITE( RTC_MINUTES, min );
2748c2ecf20Sopenharmony_ci        RTC_WRITE( RTC_HOURS, hour + pm);
2758c2ecf20Sopenharmony_ci        RTC_WRITE( RTC_DAY_OF_MONTH, day );
2768c2ecf20Sopenharmony_ci        RTC_WRITE( RTC_MONTH, mon );
2778c2ecf20Sopenharmony_ci        RTC_WRITE( RTC_YEAR, year );
2788c2ecf20Sopenharmony_ci        if (wday >= 0) RTC_WRITE( RTC_DAY_OF_WEEK, wday );
2798c2ecf20Sopenharmony_ci    }
2808c2ecf20Sopenharmony_ci    RTC_WRITE( RTC_CONTROL, ctrl & ~RTC_SET );
2818c2ecf20Sopenharmony_ci    local_irq_restore(flags);
2828c2ecf20Sopenharmony_ci
2838c2ecf20Sopenharmony_ci    if (!op) {
2848c2ecf20Sopenharmony_ci        /* read: adjust values */
2858c2ecf20Sopenharmony_ci
2868c2ecf20Sopenharmony_ci        if (hour & 0x80) {
2878c2ecf20Sopenharmony_ci	    hour &= ~0x80;
2888c2ecf20Sopenharmony_ci	    pm = 1;
2898c2ecf20Sopenharmony_ci	}
2908c2ecf20Sopenharmony_ci
2918c2ecf20Sopenharmony_ci	if (!(ctrl & RTC_DM_BINARY)) {
2928c2ecf20Sopenharmony_ci	    sec = bcd2bin(sec);
2938c2ecf20Sopenharmony_ci	    min = bcd2bin(min);
2948c2ecf20Sopenharmony_ci	    hour = bcd2bin(hour);
2958c2ecf20Sopenharmony_ci	    day = bcd2bin(day);
2968c2ecf20Sopenharmony_ci	    mon = bcd2bin(mon);
2978c2ecf20Sopenharmony_ci	    year = bcd2bin(year);
2988c2ecf20Sopenharmony_ci	    wday = bcd2bin(wday);
2998c2ecf20Sopenharmony_ci        }
3008c2ecf20Sopenharmony_ci
3018c2ecf20Sopenharmony_ci        if (!(ctrl & RTC_24H)) {
3028c2ecf20Sopenharmony_ci	    if (!pm && hour == 12)
3038c2ecf20Sopenharmony_ci		hour = 0;
3048c2ecf20Sopenharmony_ci	    else if (pm && hour != 12)
3058c2ecf20Sopenharmony_ci		hour += 12;
3068c2ecf20Sopenharmony_ci        }
3078c2ecf20Sopenharmony_ci
3088c2ecf20Sopenharmony_ci        t->tm_sec  = sec;
3098c2ecf20Sopenharmony_ci        t->tm_min  = min;
3108c2ecf20Sopenharmony_ci        t->tm_hour = hour;
3118c2ecf20Sopenharmony_ci        t->tm_mday = day;
3128c2ecf20Sopenharmony_ci        t->tm_mon  = mon - 1;
3138c2ecf20Sopenharmony_ci        t->tm_year = year + atari_rtc_year_offset;
3148c2ecf20Sopenharmony_ci        t->tm_wday = wday - 1;
3158c2ecf20Sopenharmony_ci    }
3168c2ecf20Sopenharmony_ci
3178c2ecf20Sopenharmony_ci    return( 0 );
3188c2ecf20Sopenharmony_ci}
3198c2ecf20Sopenharmony_ci
3208c2ecf20Sopenharmony_ci/*
3218c2ecf20Sopenharmony_ci * Local variables:
3228c2ecf20Sopenharmony_ci *  c-indent-level: 4
3238c2ecf20Sopenharmony_ci *  tab-width: 8
3248c2ecf20Sopenharmony_ci * End:
3258c2ecf20Sopenharmony_ci */
326