xref: /optee_os/core/drivers/atmel_rtc.c (revision 0074d94120ab58cc03247c82efae561aac869f79)
135abff2fSClément Léger // SPDX-License-Identifier: BSD-2-Clause
235abff2fSClément Léger /*
335abff2fSClément Léger  * Copyright 2022 Microchip
435abff2fSClément Léger  *
535abff2fSClément Léger  * Driver for AT91 RTC
635abff2fSClément Léger  */
735abff2fSClément Léger 
835abff2fSClément Léger #include <assert.h>
9b2e4b77eSClément Léger #include <drivers/atmel_rtc.h>
1035abff2fSClément Léger #include <drivers/rtc.h>
1135abff2fSClément Léger #include <io.h>
1235abff2fSClément Léger #include <kernel/dt.h>
139e3c57c8SEtienne Carriere #include <kernel/dt_driver.h>
1435abff2fSClément Léger #include <matrix.h>
1535abff2fSClément Léger #include <mm/core_memprot.h>
166370f75dSTony Han #include <platform_config.h>
1735abff2fSClément Léger 
1835abff2fSClément Léger #define RTC_VAL(reg, val)	(((val) >> RTC_## reg ## _SHIFT) & \
1935abff2fSClément Léger 				 RTC_## reg ##_MASK)
2035abff2fSClément Léger 
2135abff2fSClément Léger #define RTC_SET_VAL(reg, val)	SHIFT_U32((val) & RTC_## reg ##_MASK, \
2235abff2fSClément Léger 					  RTC_## reg ## _SHIFT)
2335abff2fSClément Léger 
2435abff2fSClément Léger #define RTC_CR			0x0
2535abff2fSClément Léger #define RTC_CR_UPDCAL		BIT(1)
2635abff2fSClément Léger #define RTC_CR_UPDTIM		BIT(0)
2735abff2fSClément Léger 
2835abff2fSClément Léger #define RTC_MR			0x4
2935abff2fSClément Léger #define RTC_MR_HR_MODE		BIT(0)
3035abff2fSClément Léger #define RTC_MR_PERSIAN		BIT(1)
3135abff2fSClément Léger #define RTC_MR_UTC		BIT(2)
3235abff2fSClément Léger #define RTC_MR_NEGPPM		BIT(4)
3335abff2fSClément Léger #define RTC_MR_CORR_SHIFT	8
3435abff2fSClément Léger #define RTC_MR_CORR_MASK	GENMASK_32(6, 0)
3535abff2fSClément Léger #define RTC_MR_CORR(val)	RTC_VAL(val, MR_CORR)
3635abff2fSClément Léger #define RTC_MR_HIGHPPM		BIT(15)
3735abff2fSClément Léger 
3835abff2fSClément Léger #define RTC_TIMR		0x8
3935abff2fSClément Léger #define RTC_CALR		0xC
4035abff2fSClément Léger 
4135abff2fSClément Léger #define RTC_SR			0x18
4235abff2fSClément Léger #define RTC_SR_ACKUPD		BIT(0)
4335abff2fSClément Léger #define RTC_SR_SEC		BIT(2)
4435abff2fSClément Léger 
4535abff2fSClément Léger #define RTC_SCCR		0x1C
4635abff2fSClément Léger #define RTC_SCCR_ACKCLR		BIT(0)
4735abff2fSClément Léger #define RTC_SCCR_SECCLR		BIT(2)
4835abff2fSClément Léger 
4935abff2fSClément Léger #define RTC_VER			0x2C
5035abff2fSClément Léger #define RTC_VER_NVTIM		BIT(0)
5135abff2fSClément Léger #define RTC_VER_NVCAL		BIT(1)
5235abff2fSClément Léger 
5335abff2fSClément Léger #define RTC_TSTR0		0xB0
5435abff2fSClément Léger #define RTC_TSDR0		0xB4
5535abff2fSClément Léger 
5635abff2fSClément Léger #define RTC_TSSR0		0xB8
5735abff2fSClément Léger #define RTC_TSSR_DET_OFFSET	16
5835abff2fSClément Léger #define RTC_TSSR_DET_COUNT	8
5935abff2fSClément Léger #define RTC_TSSR_TST_PIN	BIT(2)
6035abff2fSClément Léger #define RTC_TSSR_JTAG		BIT(3)
6135abff2fSClément Léger 
6235abff2fSClément Léger /* Layout of Time registers */
6335abff2fSClément Léger #define RTC_TIME_BACKUP	BIT(31)
6435abff2fSClément Léger #define RTC_TIME_HOUR_SHIFT	16
6535abff2fSClément Léger #define RTC_TIME_HOUR_MASK	GENMASK_32(5, 0)
6635abff2fSClément Léger #define RTC_TIME_MIN_SHIFT	8
6735abff2fSClément Léger #define RTC_TIME_MIN_MASK	GENMASK_32(6, 0)
6835abff2fSClément Léger #define RTC_TIME_SEC_SHIFT	0
6935abff2fSClément Léger #define RTC_TIME_SEC_MASK	GENMASK_32(6, 0)
7035abff2fSClément Léger 
7135abff2fSClément Léger /* Layout of Calendar registers */
7235abff2fSClément Léger #define RTC_CAL_DATE_SHIFT	24
7335abff2fSClément Léger #define RTC_CAL_DATE_MASK	GENMASK_32(5, 0)
7435abff2fSClément Léger #define RTC_CAL_DAY_SHIFT	21
7535abff2fSClément Léger #define RTC_CAL_DAY_MASK	GENMASK_32(2, 0)
7635abff2fSClément Léger #define RTC_CAL_MONTH_SHIFT	16
7735abff2fSClément Léger #define RTC_CAL_MONTH_MASK	GENMASK_32(4, 0)
7835abff2fSClément Léger #define RTC_CAL_YEAR_SHIFT	8
7935abff2fSClément Léger #define RTC_CAL_YEAR_MASK	GENMASK_32(7, 0)
8035abff2fSClément Léger #define RTC_CAL_CENT_SHIFT	0
8135abff2fSClément Léger #define RTC_CAL_CENT_MASK	GENMASK_32(6, 0)
8235abff2fSClément Léger 
8335abff2fSClément Léger #define ATMEL_RTC_CORR_DIVIDEND		3906000
8435abff2fSClément Léger #define ATMEL_RTC_CORR_LOW_RATIO	20
8535abff2fSClément Léger 
8635abff2fSClément Léger static vaddr_t rtc_base;
8735abff2fSClément Léger 
bcd_decode(uint8_t dcb_val)8835abff2fSClément Léger static uint8_t bcd_decode(uint8_t dcb_val)
8935abff2fSClément Léger {
9035abff2fSClément Léger 	return (dcb_val & 0xF) + (dcb_val >> 4) * 10;
9135abff2fSClément Léger }
9235abff2fSClément Léger 
bcd_encode(uint32_t value)9335abff2fSClément Léger static uint8_t bcd_encode(uint32_t value)
9435abff2fSClément Léger {
9535abff2fSClément Léger 	return ((value / 10) << 4) + value % 10;
9635abff2fSClément Léger }
9735abff2fSClément Léger 
atmel_rtc_read(unsigned int offset)9835abff2fSClément Léger static uint32_t atmel_rtc_read(unsigned int offset)
9935abff2fSClément Léger {
10035abff2fSClément Léger 	return io_read32(rtc_base + offset);
10135abff2fSClément Léger }
10235abff2fSClément Léger 
atmel_rtc_write(unsigned int offset,uint32_t val)10335abff2fSClément Léger static void atmel_rtc_write(unsigned int offset, uint32_t val)
10435abff2fSClément Léger {
10535abff2fSClément Léger 	return io_write32(rtc_base + offset, val);
10635abff2fSClément Léger }
10735abff2fSClément Léger 
atmel_decode_date(unsigned int time_reg,unsigned int cal_reg,struct optee_rtc_time * tm)10835abff2fSClément Léger static void atmel_decode_date(unsigned int time_reg, unsigned int cal_reg,
10935abff2fSClément Léger 			      struct optee_rtc_time *tm)
11035abff2fSClément Léger {
11135abff2fSClément Léger 	uint32_t time = 0;
11235abff2fSClément Léger 	uint32_t date = 0;
11335abff2fSClément Léger 
11435abff2fSClément Léger 	/* Must read twice in case it changes */
11535abff2fSClément Léger 	do {
11635abff2fSClément Léger 		time = atmel_rtc_read(time_reg);
11735abff2fSClément Léger 		date = atmel_rtc_read(cal_reg);
11835abff2fSClément Léger 	} while ((time != atmel_rtc_read(time_reg)) ||
11935abff2fSClément Léger 		 (date != atmel_rtc_read(cal_reg)));
12035abff2fSClément Léger 
12135abff2fSClément Léger 	tm->tm_wday = bcd_decode(RTC_VAL(CAL_DAY, date)) - 1;
12235abff2fSClément Léger 	tm->tm_mday = bcd_decode(RTC_VAL(CAL_DATE, date));
12335abff2fSClément Léger 	tm->tm_mon = bcd_decode(RTC_VAL(CAL_MONTH, date)) - 1;
12435abff2fSClément Léger 	tm->tm_year = bcd_decode(RTC_VAL(CAL_CENT, date)) * 100;
12535abff2fSClément Léger 	tm->tm_year += bcd_decode(RTC_VAL(CAL_YEAR, date));
12635abff2fSClément Léger 
12735abff2fSClément Léger 	tm->tm_hour = bcd_decode(RTC_VAL(TIME_HOUR, time));
12835abff2fSClément Léger 	tm->tm_min = bcd_decode(RTC_VAL(TIME_MIN, time));
12935abff2fSClément Léger 	tm->tm_sec = bcd_decode(RTC_VAL(TIME_SEC, time));
13035abff2fSClément Léger }
13135abff2fSClément Léger 
atmel_rtc_get_time(struct rtc * rtc __unused,struct optee_rtc_time * tm)13235abff2fSClément Léger static TEE_Result atmel_rtc_get_time(struct rtc *rtc __unused,
13335abff2fSClément Léger 				     struct optee_rtc_time *tm)
13435abff2fSClément Léger {
13535abff2fSClément Léger 	atmel_decode_date(RTC_TIMR, RTC_CALR, tm);
13635abff2fSClément Léger 
13735abff2fSClément Léger 	return TEE_SUCCESS;
13835abff2fSClément Léger }
13935abff2fSClément Léger 
atmel_rtc_get_tamper_timestamp(struct optee_rtc_time * tm)140b2e4b77eSClément Léger TEE_Result atmel_rtc_get_tamper_timestamp(struct optee_rtc_time *tm)
141b2e4b77eSClément Léger {
142b2e4b77eSClément Léger 	if (!rtc_base)
143b2e4b77eSClément Léger 		return TEE_ERROR_NOT_SUPPORTED;
144b2e4b77eSClément Léger 
145b2e4b77eSClément Léger 	atmel_decode_date(RTC_TSTR0, RTC_TSDR0, tm);
146b2e4b77eSClément Léger 
147b2e4b77eSClément Léger 	return TEE_SUCCESS;
148b2e4b77eSClément Léger }
149b2e4b77eSClément Léger 
atmel_rtc_set_time(struct rtc * rtc __unused,struct optee_rtc_time * tm)15035abff2fSClément Léger static TEE_Result atmel_rtc_set_time(struct rtc *rtc __unused,
15135abff2fSClément Léger 				     struct optee_rtc_time *tm)
15235abff2fSClément Léger {
15335abff2fSClément Léger 	uint32_t cr = 0;
15435abff2fSClément Léger 	uint32_t sr = 0;
15535abff2fSClément Léger 	uint32_t err = 0;
15635abff2fSClément Léger 
15735abff2fSClément Léger 	/* First, wait for UPDCAL/UPDTIM to be 0 */
15835abff2fSClément Léger 	do {
15935abff2fSClément Léger 		cr = atmel_rtc_read(RTC_CR);
16035abff2fSClément Léger 	} while (cr & (RTC_CR_UPDCAL | RTC_CR_UPDTIM));
16135abff2fSClément Léger 
16235abff2fSClément Léger 	/* Stop Time/Calendar for update */
16335abff2fSClément Léger 	atmel_rtc_write(RTC_CR, cr | RTC_CR_UPDCAL | RTC_CR_UPDTIM);
16435abff2fSClément Léger 
16535abff2fSClément Léger 	do {
16635abff2fSClément Léger 		sr = atmel_rtc_read(RTC_SR);
16735abff2fSClément Léger 	} while (!(sr & RTC_SR_ACKUPD));
16835abff2fSClément Léger 
16935abff2fSClément Léger 	atmel_rtc_write(RTC_SCCR, RTC_SCCR_ACKCLR);
17035abff2fSClément Léger 
17135abff2fSClément Léger 	atmel_rtc_write(RTC_TIMR,
17235abff2fSClément Léger 			RTC_SET_VAL(TIME_SEC, bcd_encode(tm->tm_sec)) |
17335abff2fSClément Léger 			RTC_SET_VAL(TIME_MIN, bcd_encode(tm->tm_min)) |
17435abff2fSClément Léger 			RTC_SET_VAL(TIME_HOUR, bcd_encode(tm->tm_hour)));
17535abff2fSClément Léger 
17635abff2fSClément Léger 	atmel_rtc_write(RTC_CALR,
17735abff2fSClément Léger 			RTC_SET_VAL(CAL_CENT,
17835abff2fSClément Léger 				    bcd_encode(tm->tm_year / 100)) |
17935abff2fSClément Léger 			RTC_SET_VAL(CAL_YEAR, bcd_encode(tm->tm_year % 100)) |
18035abff2fSClément Léger 			RTC_SET_VAL(CAL_MONTH, bcd_encode(tm->tm_mon + 1)) |
18135abff2fSClément Léger 			RTC_SET_VAL(CAL_DAY, bcd_encode(tm->tm_wday + 1)) |
18235abff2fSClément Léger 			RTC_SET_VAL(CAL_DATE, bcd_encode(tm->tm_mday)));
18335abff2fSClément Léger 
18435abff2fSClément Léger 	err = atmel_rtc_read(RTC_VER);
18535abff2fSClément Léger 	if (err) {
18635abff2fSClément Léger 		if (err & RTC_VER_NVTIM)
18735abff2fSClément Léger 			DMSG("Invalid time programmed");
18835abff2fSClément Léger 		if (err & RTC_VER_NVCAL)
18935abff2fSClément Léger 			DMSG("Invalid date programmed");
19035abff2fSClément Léger 
19135abff2fSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
19235abff2fSClément Léger 	}
19335abff2fSClément Léger 
19435abff2fSClément Léger 	/* Restart Time/Calendar */
19535abff2fSClément Léger 	atmel_rtc_write(RTC_CR, cr);
19635abff2fSClément Léger 
19735abff2fSClément Léger 	return TEE_SUCCESS;
19835abff2fSClément Léger }
19935abff2fSClément Léger 
atmel_rtc_get_offset(struct rtc * rtc __unused,long * offset)20035abff2fSClément Léger static TEE_Result atmel_rtc_get_offset(struct rtc *rtc __unused, long *offset)
20135abff2fSClément Léger {
20235abff2fSClément Léger 	uint32_t mr = atmel_rtc_read(RTC_MR);
20335abff2fSClément Léger 	long val = RTC_VAL(MR_CORR, mr);
20435abff2fSClément Léger 
20535abff2fSClément Léger 	if (!val) {
20635abff2fSClément Léger 		*offset = 0;
20735abff2fSClément Léger 		return TEE_SUCCESS;
20835abff2fSClément Léger 	}
20935abff2fSClément Léger 
21035abff2fSClément Léger 	val++;
21135abff2fSClément Léger 
21235abff2fSClément Léger 	if (!(mr & RTC_MR_HIGHPPM))
21335abff2fSClément Léger 		val *= ATMEL_RTC_CORR_LOW_RATIO;
21435abff2fSClément Léger 
21535abff2fSClément Léger 	val = UDIV_ROUND_NEAREST(ATMEL_RTC_CORR_DIVIDEND, val);
21635abff2fSClément Léger 
21735abff2fSClément Léger 	if (!(mr & RTC_MR_NEGPPM))
21835abff2fSClément Léger 		val = -val;
21935abff2fSClément Léger 
22035abff2fSClément Léger 	*offset = val;
22135abff2fSClément Léger 
22235abff2fSClément Léger 	return TEE_SUCCESS;
22335abff2fSClément Léger }
22435abff2fSClément Léger 
atmel_rtc_set_offset(struct rtc * rtc __unused,long offset)22535abff2fSClément Léger static TEE_Result atmel_rtc_set_offset(struct rtc *rtc  __unused, long offset)
22635abff2fSClément Léger {
22735abff2fSClément Léger 	long corr = 0;
22835abff2fSClément Léger 	uint32_t mr = 0;
22935abff2fSClément Léger 
23035abff2fSClément Léger 	if (offset > ATMEL_RTC_CORR_DIVIDEND / 2)
23135abff2fSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
23235abff2fSClément Léger 	if (offset < -ATMEL_RTC_CORR_DIVIDEND / 2)
23335abff2fSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
23435abff2fSClément Léger 
23535abff2fSClément Léger 	mr = atmel_rtc_read(RTC_MR);
23635abff2fSClément Léger 	mr &= ~(RTC_MR_NEGPPM | RTC_MR_CORR_MASK | RTC_MR_HIGHPPM);
23735abff2fSClément Léger 
23835abff2fSClément Léger 	if (offset > 0)
23935abff2fSClément Léger 		mr |= RTC_MR_NEGPPM;
24035abff2fSClément Léger 	else
24135abff2fSClément Léger 		offset = -offset;
24235abff2fSClément Léger 
24335abff2fSClément Léger 	/* offset less than 764 ppb, disable correction */
24435abff2fSClément Léger 	if (offset < 764) {
24535abff2fSClément Léger 		atmel_rtc_write(RTC_MR, mr & ~RTC_MR_NEGPPM);
24635abff2fSClément Léger 
24735abff2fSClément Léger 		return TEE_SUCCESS;
24835abff2fSClément Léger 	}
24935abff2fSClément Léger 
25035abff2fSClément Léger 	/*
25135abff2fSClément Léger 	 * 29208 ppb is the perfect cutoff between low range and high range
25235abff2fSClément Léger 	 * low range values are never better than high range value after that.
25335abff2fSClément Léger 	 */
25435abff2fSClément Léger 	if (offset < 29208) {
25535abff2fSClément Léger 		corr = UDIV_ROUND_NEAREST(ATMEL_RTC_CORR_DIVIDEND,
25635abff2fSClément Léger 					  offset * ATMEL_RTC_CORR_LOW_RATIO);
25735abff2fSClément Léger 	} else {
25835abff2fSClément Léger 		corr = UDIV_ROUND_NEAREST(ATMEL_RTC_CORR_DIVIDEND, offset);
25935abff2fSClément Léger 		mr |= RTC_MR_HIGHPPM;
26035abff2fSClément Léger 	}
26135abff2fSClément Léger 
26235abff2fSClément Léger 	corr = MIN(corr, 128);
26335abff2fSClément Léger 
26435abff2fSClément Léger 	mr |= ((corr - 1) & RTC_MR_CORR_MASK) << RTC_MR_CORR_SHIFT;
26535abff2fSClément Léger 
26635abff2fSClément Léger 	atmel_rtc_write(RTC_MR, mr);
26735abff2fSClément Léger 
26835abff2fSClément Léger 	return TEE_SUCCESS;
26935abff2fSClément Léger }
27035abff2fSClément Léger 
27135abff2fSClément Léger static const struct rtc_ops atmel_rtc_ops = {
27235abff2fSClément Léger 	.get_time = atmel_rtc_get_time,
27335abff2fSClément Léger 	.set_time = atmel_rtc_set_time,
27435abff2fSClément Léger 	.get_offset = atmel_rtc_get_offset,
27535abff2fSClément Léger 	.set_offset = atmel_rtc_set_offset,
27635abff2fSClément Léger };
27735abff2fSClément Léger 
27835abff2fSClément Léger static struct rtc atmel_rtc = {
27935abff2fSClément Léger 	.ops = &atmel_rtc_ops,
280*0074d941SGatien Chevallier 	.range_min = RTC_TIME(1900, 0, 1, 1, 0, 0, 0, 0),
281*0074d941SGatien Chevallier 	.range_max = RTC_TIME(2099, 11, 31, 4, 23, 59, 59, 999),
28235abff2fSClément Léger };
28335abff2fSClément Léger 
284fd286f75STony Han /* Non-null reference for compat data */
285fd286f75STony Han static const uint8_t rtc_always_secure;
286fd286f75STony Han 
atmel_rtc_probe(const void * fdt,int node,const void * compat_data)28735abff2fSClément Léger static TEE_Result atmel_rtc_probe(const void *fdt, int node,
288fd286f75STony Han 				  const void *compat_data)
28935abff2fSClément Léger {
29035abff2fSClément Léger 	size_t size = 0;
29135abff2fSClément Léger 
29235abff2fSClément Léger 	if (rtc_base)
29335abff2fSClément Léger 		return TEE_ERROR_GENERIC;
29435abff2fSClément Léger 
295f354a5d8SGatien Chevallier 	if (fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
29635abff2fSClément Léger 		return TEE_ERROR_BAD_PARAMETERS;
29735abff2fSClément Léger 
298fd286f75STony Han 	if (compat_data != &rtc_always_secure)
29935abff2fSClément Léger 		matrix_configure_periph_secure(AT91C_ID_SYS);
30035abff2fSClément Léger 
301a5d5bbc8SVesa Jääskeläinen 	if (dt_map_dev(fdt, node, &rtc_base, &size, DT_MAP_AUTO) < 0)
30235abff2fSClément Léger 		return TEE_ERROR_GENERIC;
30335abff2fSClément Léger 
30435abff2fSClément Léger 	atmel_rtc_write(RTC_CR, 0);
30535abff2fSClément Léger 	/* Enable 24 hours Gregorian mode (this is a clear bits operation !) */
30635abff2fSClément Léger 	io_clrbits32(rtc_base + RTC_MR, RTC_MR_PERSIAN | RTC_MR_UTC |
30735abff2fSClément Léger 		     RTC_MR_HR_MODE);
30835abff2fSClément Léger 
30935abff2fSClément Léger 	rtc_register(&atmel_rtc);
31035abff2fSClément Léger 
31135abff2fSClément Léger 	return TEE_SUCCESS;
31235abff2fSClément Léger }
31335abff2fSClément Léger 
31435abff2fSClément Léger static const struct dt_device_match atmel_rtc_match_table[] = {
31535abff2fSClément Léger 	{ .compatible = "atmel,sama5d2-rtc" },
316fd286f75STony Han 	{
317fd286f75STony Han 		.compatible = "microchip,sama7g5-rtc",
318fd286f75STony Han 		.compat_data = &rtc_always_secure,
319fd286f75STony Han 	},
32035abff2fSClément Léger 	{ }
32135abff2fSClément Léger };
32235abff2fSClément Léger 
32335abff2fSClément Léger DEFINE_DT_DRIVER(atmel_rtc_dt_driver) = {
32435abff2fSClément Léger 	.name = "atmel_rtc",
32535abff2fSClément Léger 	.type = DT_DRIVER_NOTYPE,
32635abff2fSClément Léger 	.match_table = atmel_rtc_match_table,
32735abff2fSClément Léger 	.probe = atmel_rtc_probe,
32835abff2fSClément Léger };
32935abff2fSClément Léger 
330