/**************************************************************************** * drivers/timers/pl031.c * * Licensed to the Apache Software Foundation (ASF) under one or more * contributor license agreements. See the NOTICE file distributed with * this work for additional information regarding copyright ownership. The * ASF licenses this file to you under the Apache License, Version 2.0 (the * "License"); you may not use this file except in compliance with the * License. You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, WITHOUT * WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the * License for the specific language governing permissions and limitations * under the License. * ****************************************************************************/ /**************************************************************************** * Included Files ****************************************************************************/ #include #include #include #include /**************************************************************************** * Pre-processor Definitions ****************************************************************************/ #define pl031_getreg(b,o) (*(FAR volatile uint32_t *)((b) + (o))) #define pl031_putreg(v,b,o) (*((FAR volatile uint32_t *)((b) + (o))) = (v)) #define PL031_RTCDR 0x00 /* RO Data read register */ #define PL031_RTCMR 0x04 /* RW Match register */ #define PL031_RTCLR 0x08 /* RW Data load register */ #define PL031_RTCCR 0x0c /* RW Control register */ #define PL031_RTCIMSC 0x10 /* RW Interrupt mask and set register */ #define PL031_RTCRIS 0x14 /* RO Raw interrupt status register */ #define PL031_RTCMIS 0x18 /* RO Masked interrupt status register */ #define PL031_RTCICR 0x1c /* WO Interrupt clear register */ /* ST variants have additional timer functionality */ #define PL031_RTCTDR 0x20 /* Timer data read register */ #define PL031_RTCTLR 0x24 /* Timer data load register */ #define PL031_RTCTCR 0x28 /* Timer control register */ #define PL031_RTCYDR 0x30 /* Year data read register */ #define PL031_RTCYMR 0x34 /* Year match register */ #define PL031_RTCYLR 0x38 /* Year data load register */ #define PL031_RTCBIT_AI (1 << 0) /* Alarm interrupt bit */ /**************************************************************************** * Private Functions ****************************************************************************/ static int pl031_rdtime(FAR struct rtc_lowerhalf_s *lower, FAR struct rtc_time *rtctime); static int pl031_settime(FAR struct rtc_lowerhalf_s *lower, FAR const struct rtc_time *rtctime); static bool pl031_havesettime(FAR struct rtc_lowerhalf_s *lower); #if defined(CONFIG_RTC_ALARM) static int pl031_alarm_irq(int irq, FAR void *context, FAR void *arg); static int pl031_setalarm(FAR struct rtc_lowerhalf_s *lower, FAR const struct lower_setalarm_s *alarminfo); static int pl031_rdalarm(FAR struct rtc_lowerhalf_s *lower, FAR struct lower_rdalarm_s *alarminfo); static int pl031_cancelalarm(FAR struct rtc_lowerhalf_s *lower, int alarmid); static int pl031_setrelative(FAR struct rtc_lowerhalf_s *lower, FAR const struct lower_setrelative_s *alarminfo); #endif /**************************************************************************** * Private Data ****************************************************************************/ static const struct rtc_ops_s g_rtc_ops = { .rdtime = pl031_rdtime, .settime = pl031_settime, .havesettime = pl031_havesettime, #ifdef CONFIG_RTC_ALARM .setalarm = pl031_setalarm, .setrelative = pl031_setrelative, .cancelalarm = pl031_cancelalarm, .rdalarm = pl031_rdalarm, #endif }; /**************************************************************************** * Private Types ****************************************************************************/ struct pl031_lowerhalf_s { FAR const struct rtc_ops_s *ops; uintptr_t base; struct lower_setalarm_s alarm; }; /**************************************************************************** * Name: pl031_rdtime ****************************************************************************/ static int pl031_rdtime(FAR struct rtc_lowerhalf_s *lower, FAR struct rtc_time *rtctime) { FAR struct pl031_lowerhalf_s *priv = (FAR struct pl031_lowerhalf_s *)lower; time_t time; DEBUGASSERT(priv != NULL && rtctime != NULL); time = pl031_getreg(priv->base, PL031_RTCDR); gmtime_r(&time, (FAR struct tm *)rtctime); return 0; } /**************************************************************************** * Name: pl031_settime ****************************************************************************/ static int pl031_settime(FAR struct rtc_lowerhalf_s *lower, FAR const struct rtc_time *rtctime) { FAR struct pl031_lowerhalf_s *priv = (FAR struct pl031_lowerhalf_s *)lower; time_t time; DEBUGASSERT(priv != NULL && rtctime != NULL); time = timegm((FAR struct tm *)rtctime); pl031_putreg(time, priv->base, PL031_RTCLR); return 0; } /**************************************************************************** * Name: pl031_havesettime ****************************************************************************/ static bool pl031_havesettime(FAR struct rtc_lowerhalf_s *lower) { return true; } #ifdef CONFIG_RTC_ALARM /**************************************************************************** * Name: pl031_alarm_callback ****************************************************************************/ static int pl031_alarm_irq(int irq, FAR void *context, FAR void *arg) { FAR struct pl031_lowerhalf_s *lower = (FAR struct pl031_lowerhalf_s *)arg; rtc_alarm_callback_t cb; FAR void *priv; cb = lower->alarm.cb; priv = lower->alarm.priv; if (cb != NULL) { cb(priv, 0); } pl031_putreg(1, lower->base, PL031_RTCICR); return OK; } /**************************************************************************** * Name: pl031_setalarm ****************************************************************************/ static int pl031_setalarm(FAR struct rtc_lowerhalf_s *lower, FAR const struct lower_setalarm_s *alarminfo) { FAR struct pl031_lowerhalf_s *priv = (FAR struct pl031_lowerhalf_s *)lower; time_t time; DEBUGASSERT(priv != NULL && alarminfo != NULL); priv->alarm.time = alarminfo->time; priv->alarm.cb = alarminfo->cb; priv->alarm.priv = alarminfo->priv; time = timegm((FAR struct tm *)&alarminfo->time); pl031_putreg(time, priv->base, PL031_RTCMR); pl031_putreg(1, priv->base, PL031_RTCIMSC); return OK; } /**************************************************************************** * Name: pl031_rdalarm ****************************************************************************/ static int pl031_rdalarm(FAR struct rtc_lowerhalf_s *lower, FAR struct lower_rdalarm_s *alarminfo) { FAR struct pl031_lowerhalf_s *priv = (FAR struct pl031_lowerhalf_s *)lower; DEBUGASSERT(priv != NULL && alarminfo != NULL); *alarminfo->time = priv->alarm.time; return 0; } /**************************************************************************** * Name: pl031_cancelalarm ****************************************************************************/ int pl031_cancelalarm(FAR struct rtc_lowerhalf_s *lower, int alarmid) { FAR struct pl031_lowerhalf_s *priv = (FAR struct pl031_lowerhalf_s *)lower; DEBUGASSERT(priv != NULL); pl031_putreg(0, priv->base, PL031_RTCIMSC); return OK; } /**************************************************************************** * Name: pl031_setrelative ****************************************************************************/ static int pl031_setrelative(FAR struct rtc_lowerhalf_s *lower, FAR const struct lower_setrelative_s *alarminfo) { FAR struct pl031_lowerhalf_s *priv = (FAR struct pl031_lowerhalf_s *)lower; struct lower_setalarm_s setalarm; time_t time; DEBUGASSERT(priv != NULL && alarminfo != NULL); setalarm.id = alarminfo->id; setalarm.cb = alarminfo->cb; setalarm.priv = alarminfo->priv; time = pl031_getreg(priv->base, PL031_RTCDR); time += alarminfo->reltime; gmtime_r(&time, (FAR struct tm *)&setalarm.time); return pl031_setalarm(lower, &setalarm); } #endif /**************************************************************************** * Name: up_rtc_initialize * * Description: * Initialize the qemu RTC per the selected configuration. This function * is called once during the OS initialization sequence * * Input Parameters: * None * * Returned Value: * Zero (OK) on success; a negated errno on failure * ****************************************************************************/ FAR struct rtc_lowerhalf_s *pl031_initialize(uintptr_t base, int irq) { FAR struct pl031_lowerhalf_s *rtc_lowerhalf = kmm_zalloc(sizeof(*rtc_lowerhalf)); rtc_lowerhalf->ops = &g_rtc_ops; rtc_lowerhalf->base = base; #ifdef CONFIG_RTC_ALARM irq_attach(irq, pl031_alarm_irq, rtc_lowerhalf); up_enable_irq(irq); #endif return (FAR struct rtc_lowerhalf_s *)rtc_lowerhalf; }