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