288 lines
9.5 KiB
C
288 lines
9.5 KiB
C
/****************************************************************************
|
|
* 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 <nuttx/config.h>
|
|
|
|
#include <nuttx/timers/rtc.h>
|
|
#include <nuttx/timers/pl031.h>
|
|
#include <stdio.h>
|
|
|
|
/****************************************************************************
|
|
* 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;
|
|
}
|