incubator-nuttx/drivers/timers/pl031.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;
}