Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

RTC: add basic RTC support in OP-TEE #5179

Merged
merged 5 commits into from
Mar 2, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions core/arch/arm/dts/sama5d2.dtsi
Original file line number Diff line number Diff line change
Expand Up @@ -712,6 +712,8 @@
reg = <0xf80480b0 0x30>;
interrupts = <74 IRQ_TYPE_LEVEL_HIGH 7>;
clocks = <&clk32k>;
status = "disabled";
secure-status = "okay";
};

i2s0: i2s@f8050000 {
Expand Down
4 changes: 4 additions & 0 deletions core/arch/arm/plat-sam/conf.mk
Original file line number Diff line number Diff line change
Expand Up @@ -80,3 +80,7 @@ ifeq ($(CFG_WDT_SM_HANDLER),y)
CFG_WDT_SM_HANDLER_ID := 0x2000500
endif
CFG_ATMEL_WDT ?= y

CFG_DRIVERS_RTC ?= y
CFG_RTC_PTA ?= y
CFG_ATMEL_RTC ?= y
309 changes: 309 additions & 0 deletions core/drivers/atmel_rtc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,309 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright 2022 Microchip
*
* Driver for AT91 RTC
*/

#include <assert.h>
#include <drivers/rtc.h>
#include <io.h>
#include <kernel/dt.h>
#include <matrix.h>
#include <mm/core_memprot.h>
#include <sama5d2.h>

#define RTC_VAL(reg, val) (((val) >> RTC_## reg ## _SHIFT) & \
RTC_## reg ##_MASK)

#define RTC_SET_VAL(reg, val) SHIFT_U32((val) & RTC_## reg ##_MASK, \
RTC_## reg ## _SHIFT)

#define RTC_CR 0x0
#define RTC_CR_UPDCAL BIT(1)
#define RTC_CR_UPDTIM BIT(0)

#define RTC_MR 0x4
#define RTC_MR_HR_MODE BIT(0)
#define RTC_MR_PERSIAN BIT(1)
#define RTC_MR_UTC BIT(2)
#define RTC_MR_NEGPPM BIT(4)
#define RTC_MR_CORR_SHIFT 8
#define RTC_MR_CORR_MASK GENMASK_32(6, 0)
#define RTC_MR_CORR(val) RTC_VAL(val, MR_CORR)
#define RTC_MR_HIGHPPM BIT(15)

#define RTC_TIMR 0x8
#define RTC_CALR 0xC

#define RTC_SR 0x18
#define RTC_SR_ACKUPD BIT(0)
#define RTC_SR_SEC BIT(2)

#define RTC_SCCR 0x1C
#define RTC_SCCR_ACKCLR BIT(0)
#define RTC_SCCR_SECCLR BIT(2)

#define RTC_VER 0x2C
#define RTC_VER_NVTIM BIT(0)
#define RTC_VER_NVCAL BIT(1)

#define RTC_TSTR0 0xB0
#define RTC_TSDR0 0xB4

#define RTC_TSSR0 0xB8
#define RTC_TSSR_DET_OFFSET 16
#define RTC_TSSR_DET_COUNT 8
#define RTC_TSSR_TST_PIN BIT(2)
#define RTC_TSSR_JTAG BIT(3)

/* Layout of Time registers */
#define RTC_TIME_BACKUP BIT(31)
#define RTC_TIME_HOUR_SHIFT 16
#define RTC_TIME_HOUR_MASK GENMASK_32(5, 0)
#define RTC_TIME_MIN_SHIFT 8
#define RTC_TIME_MIN_MASK GENMASK_32(6, 0)
#define RTC_TIME_SEC_SHIFT 0
#define RTC_TIME_SEC_MASK GENMASK_32(6, 0)

/* Layout of Calendar registers */
#define RTC_CAL_DATE_SHIFT 24
#define RTC_CAL_DATE_MASK GENMASK_32(5, 0)
#define RTC_CAL_DAY_SHIFT 21
#define RTC_CAL_DAY_MASK GENMASK_32(2, 0)
#define RTC_CAL_MONTH_SHIFT 16
#define RTC_CAL_MONTH_MASK GENMASK_32(4, 0)
#define RTC_CAL_YEAR_SHIFT 8
#define RTC_CAL_YEAR_MASK GENMASK_32(7, 0)
#define RTC_CAL_CENT_SHIFT 0
#define RTC_CAL_CENT_MASK GENMASK_32(6, 0)

#define ATMEL_RTC_CORR_DIVIDEND 3906000
#define ATMEL_RTC_CORR_LOW_RATIO 20

static vaddr_t rtc_base;

static uint8_t bcd_decode(uint8_t dcb_val)
{
return (dcb_val & 0xF) + (dcb_val >> 4) * 10;
}

static uint8_t bcd_encode(uint32_t value)
{
return ((value / 10) << 4) + value % 10;
}

static uint32_t atmel_rtc_read(unsigned int offset)
{
return io_read32(rtc_base + offset);
}

static void atmel_rtc_write(unsigned int offset, uint32_t val)
{
return io_write32(rtc_base + offset, val);
}

static void atmel_decode_date(unsigned int time_reg, unsigned int cal_reg,
struct optee_rtc_time *tm)
{
uint32_t time = 0;
uint32_t date = 0;

/* Must read twice in case it changes */
do {
time = atmel_rtc_read(time_reg);
date = atmel_rtc_read(cal_reg);
} while ((time != atmel_rtc_read(time_reg)) ||
(date != atmel_rtc_read(cal_reg)));

tm->tm_wday = bcd_decode(RTC_VAL(CAL_DAY, date)) - 1;
tm->tm_mday = bcd_decode(RTC_VAL(CAL_DATE, date));
tm->tm_mon = bcd_decode(RTC_VAL(CAL_MONTH, date)) - 1;
tm->tm_year = bcd_decode(RTC_VAL(CAL_CENT, date)) * 100;
tm->tm_year += bcd_decode(RTC_VAL(CAL_YEAR, date));

tm->tm_hour = bcd_decode(RTC_VAL(TIME_HOUR, time));
tm->tm_min = bcd_decode(RTC_VAL(TIME_MIN, time));
tm->tm_sec = bcd_decode(RTC_VAL(TIME_SEC, time));
}

static TEE_Result atmel_rtc_get_time(struct rtc *rtc __unused,
struct optee_rtc_time *tm)
{
atmel_decode_date(RTC_TIMR, RTC_CALR, tm);

return TEE_SUCCESS;
}

static TEE_Result atmel_rtc_set_time(struct rtc *rtc __unused,
struct optee_rtc_time *tm)
{
uint32_t cr = 0;
uint32_t sr = 0;
uint32_t err = 0;

/* First, wait for UPDCAL/UPDTIM to be 0 */
do {
cr = atmel_rtc_read(RTC_CR);
} while (cr & (RTC_CR_UPDCAL | RTC_CR_UPDTIM));

/* Stop Time/Calendar for update */
atmel_rtc_write(RTC_CR, cr | RTC_CR_UPDCAL | RTC_CR_UPDTIM);

do {
sr = atmel_rtc_read(RTC_SR);
} while (!(sr & RTC_SR_ACKUPD));

atmel_rtc_write(RTC_SCCR, RTC_SCCR_ACKCLR);

atmel_rtc_write(RTC_TIMR,
RTC_SET_VAL(TIME_SEC, bcd_encode(tm->tm_sec)) |
RTC_SET_VAL(TIME_MIN, bcd_encode(tm->tm_min)) |
RTC_SET_VAL(TIME_HOUR, bcd_encode(tm->tm_hour)));

atmel_rtc_write(RTC_CALR,
RTC_SET_VAL(CAL_CENT,
bcd_encode(tm->tm_year / 100)) |
RTC_SET_VAL(CAL_YEAR, bcd_encode(tm->tm_year % 100)) |
RTC_SET_VAL(CAL_MONTH, bcd_encode(tm->tm_mon + 1)) |
RTC_SET_VAL(CAL_DAY, bcd_encode(tm->tm_wday + 1)) |
RTC_SET_VAL(CAL_DATE, bcd_encode(tm->tm_mday)));

err = atmel_rtc_read(RTC_VER);
if (err) {
if (err & RTC_VER_NVTIM)
DMSG("Invalid time programmed");
if (err & RTC_VER_NVCAL)
DMSG("Invalid date programmed");

return TEE_ERROR_BAD_PARAMETERS;
}

/* Restart Time/Calendar */
atmel_rtc_write(RTC_CR, cr);

return TEE_SUCCESS;
}

static TEE_Result atmel_rtc_get_offset(struct rtc *rtc __unused, long *offset)
{
uint32_t mr = atmel_rtc_read(RTC_MR);
long val = RTC_VAL(MR_CORR, mr);

if (!val) {
*offset = 0;
return TEE_SUCCESS;
}

val++;

if (!(mr & RTC_MR_HIGHPPM))
val *= ATMEL_RTC_CORR_LOW_RATIO;

val = UDIV_ROUND_NEAREST(ATMEL_RTC_CORR_DIVIDEND, val);

if (!(mr & RTC_MR_NEGPPM))
val = -val;

*offset = val;

return TEE_SUCCESS;
}

static TEE_Result atmel_rtc_set_offset(struct rtc *rtc __unused, long offset)
{
long corr = 0;
uint32_t mr = 0;

if (offset > ATMEL_RTC_CORR_DIVIDEND / 2)
return TEE_ERROR_BAD_PARAMETERS;
if (offset < -ATMEL_RTC_CORR_DIVIDEND / 2)
return TEE_ERROR_BAD_PARAMETERS;

mr = atmel_rtc_read(RTC_MR);
mr &= ~(RTC_MR_NEGPPM | RTC_MR_CORR_MASK | RTC_MR_HIGHPPM);

if (offset > 0)
mr |= RTC_MR_NEGPPM;
else
offset = -offset;

/* offset less than 764 ppb, disable correction */
if (offset < 764) {
atmel_rtc_write(RTC_MR, mr & ~RTC_MR_NEGPPM);

return TEE_SUCCESS;
}

/*
* 29208 ppb is the perfect cutoff between low range and high range
* low range values are never better than high range value after that.
*/
if (offset < 29208) {
corr = UDIV_ROUND_NEAREST(ATMEL_RTC_CORR_DIVIDEND,
offset * ATMEL_RTC_CORR_LOW_RATIO);
} else {
corr = UDIV_ROUND_NEAREST(ATMEL_RTC_CORR_DIVIDEND, offset);
mr |= RTC_MR_HIGHPPM;
}

corr = MIN(corr, 128);

mr |= ((corr - 1) & RTC_MR_CORR_MASK) << RTC_MR_CORR_SHIFT;

atmel_rtc_write(RTC_MR, mr);

return TEE_SUCCESS;
}

static const struct rtc_ops atmel_rtc_ops = {
.get_time = atmel_rtc_get_time,
.set_time = atmel_rtc_set_time,
.get_offset = atmel_rtc_get_offset,
.set_offset = atmel_rtc_set_offset,
};

static struct rtc atmel_rtc = {
.ops = &atmel_rtc_ops,
.range_min = { 1900, 1, 1, 0, 0, 0, 0 },
.range_max = { 2099, 12, 31, 23, 59, 59, 0 },
};

static TEE_Result atmel_rtc_probe(const void *fdt, int node,
const void *compat_data __unused)
{
size_t size = 0;

if (rtc_base)
return TEE_ERROR_GENERIC;

if (_fdt_get_status(fdt, node) != DT_STATUS_OK_SEC)
return TEE_ERROR_BAD_PARAMETERS;

matrix_configure_periph_secure(AT91C_ID_SYS);

if (dt_map_dev(fdt, node, &rtc_base, &size) < 0)
return TEE_ERROR_GENERIC;

atmel_rtc_write(RTC_CR, 0);
/* Enable 24 hours Gregorian mode (this is a clear bits operation !) */
io_clrbits32(rtc_base + RTC_MR, RTC_MR_PERSIAN | RTC_MR_UTC |
RTC_MR_HR_MODE);

rtc_register(&atmel_rtc);

return TEE_SUCCESS;
}

static const struct dt_device_match atmel_rtc_match_table[] = {
{ .compatible = "atmel,sama5d2-rtc" },
{ }
};

DEFINE_DT_DRIVER(atmel_rtc_dt_driver) = {
.name = "atmel_rtc",
.type = DT_DRIVER_NOTYPE,
.match_table = atmel_rtc_match_table,
.probe = atmel_rtc_probe,
};

21 changes: 21 additions & 0 deletions core/drivers/rtc/rtc.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
// SPDX-License-Identifier: BSD-2-Clause
/*
* Copyright 2022 Microchip
*/

#include <assert.h>
#include <drivers/rtc.h>
#include <tee_api_types.h>

struct rtc *rtc_device;

void rtc_register(struct rtc *rtc)
{
clementleger marked this conversation as resolved.
Show resolved Hide resolved
/* One RTC is supported only */
assert(!rtc_device);

/* RTC should *at least* allow to get the time */
assert(rtc && rtc->ops && rtc->ops->get_time);

rtc_device = rtc;
}
1 change: 1 addition & 0 deletions core/drivers/rtc/sub.mk
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
srcs-$(CFG_DRIVERS_RTC) += rtc.c
2 changes: 2 additions & 0 deletions core/drivers/sub.mk
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ srcs-$(CFG_ATMEL_RSTC) += atmel_rstc.c
srcs-$(CFG_ATMEL_SHDWC) += atmel_shdwc.c atmel_shdwc_a32.S
srcs-$(CFG_ATMEL_SAIC) += atmel_saic.c
srcs-$(CFG_ATMEL_WDT) += atmel_wdt.c
srcs-$(CFG_ATMEL_RTC) += atmel_rtc.c
srcs-$(CFG_AMLOGIC_UART) += amlogic_uart.c
srcs-$(CFG_MVEBU_UART) += mvebu_uart.c
srcs-$(CFG_STM32_BSEC) += stm32_bsec.c
Expand Down Expand Up @@ -57,3 +58,4 @@ subdirs-$(CFG_SCMI_MSG_DRIVERS) += scmi-msg
subdirs-y += imx
subdirs-y += pm
subdirs-y += wdt
subdirs-y += rtc
Loading