rtc: add DesignWare RTC driver

Adding DW real time clock support. This driver is used by the Quark SE
and Quark D2000 SoCs.

Change-Id: Iba8ddee1b1b5fee298db95b63418e152774662a4
Signed-off-by: Anas Nashif <anas.nashif@intel.com>
This commit is contained in:
Anas Nashif 2015-11-21 20:58:15 -05:00
commit 9d6deb4e8c
8 changed files with 392 additions and 0 deletions

View file

@ -53,4 +53,6 @@ source "drivers/pinmux/Kconfig"
source "drivers/adc/Kconfig"
source "drivers/rtc/Kconfig"
endmenu

View file

@ -18,3 +18,4 @@ obj-$(CONFIG_I2C) += i2c/
obj-$(CONFIG_PWM) += pwm/
obj-$(CONFIG_ADC) += adc/
obj-$(CONFIG_ETHERNET) += ethernet/
obj-$(CONFIG_RTC) += rtc/

20
drivers/rtc/Kconfig Normal file
View file

@ -0,0 +1,20 @@
menuconfig RTC
bool "Real-Time Clock"
default n
help
Enable options for Real-Time Clock drivers.
if RTC
config RTC_DW
bool "Build Designware RTC Driver"
default n
help
Designware RTC driver.
config RTC_IRQ_PRI
int "RTC Interrupt Priority"
default 2
help
RTC Interrupt Priority.
endif # RTC

2
drivers/rtc/Makefile Normal file
View file

@ -0,0 +1,2 @@
obj-$(CONFIG_RTC) += rtc_static_irq_stubs.o
obj-$(CONFIG_RTC_DW) += rtc_dw.o

156
drivers/rtc/rtc_dw.c Normal file
View file

@ -0,0 +1,156 @@
/*
* Copyright (c) 2015 Intel Corporation.
*
* Licensed 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.
*/
#include <nanokernel.h>
#include <sys_io.h>
#include <stdio.h>
#include <rtc.h>
#include <init.h>
#include "board.h"
#include "rtc_dw.h"
#define CLK_RTC_DIV_DEF_MASK (0xFFFFFF83)
#define CCU_RTC_CLK_DIV_EN (2)
static void (*rtc_dw_cb_fn)(void);
static void rtc_dw_set_div(const enum clk_rtc_div div)
{
/* set default division mask */
uint32_t reg =
sys_read32(CLOCK_SYSTEM_CLOCK_CONTROL) & CLK_RTC_DIV_DEF_MASK;
reg |= (div << CCU_RTC_CLK_DIV_OFFSET);
sys_write32(reg, CLOCK_SYSTEM_CLOCK_CONTROL);
/* CLK Div en bit must be written from 0 -> 1 to apply new value */
sys_set_bit(CLOCK_SYSTEM_CLOCK_CONTROL, CCU_RTC_CLK_DIV_EN);
}
/**
* @brief Function to enable clock gating for the RTC
* @return N/A
*/
static void rtc_dw_enable(void)
{
sys_set_bit(CLOCK_PERIPHERAL_BASE_ADDR, 11);
sys_set_bit(CLOCK_PERIPHERAL_BASE_ADDR, 1);
}
/**
* @brief Function to disable clock gating for the RTC
* @return N/A
*/
static void rtc_dw_disable(void)
{
sys_clear_bit(CLOCK_PERIPHERAL_BASE_ADDR, 11);
}
/**
* @brief RTC alarm ISR
*
* calls a user defined callback
*
* @return N/A
*/
void rtc_dw_isr(void)
{
/* Disable RTC interrupt */
sys_clear_bit(RTC_BASE_ADDR + RTC_CCR, 0);
if (rtc_dw_cb_fn) {
(*rtc_dw_cb_fn)();
}
/* clear interrupt */
sys_read32(RTC_BASE_ADDR + RTC_EOI);
}
IRQ_CONNECT_STATIC(rtc, INT_RTC_IRQ, CONFIG_RTC_IRQ_PRI, rtc_dw_isr, 0);
/**
* @brief Sets an RTC alarm
* @param alarm_val Alarm value
* @return 0 on success
*/
static int rtc_dw_set_alarm(const uint32_t alarm_val)
{
sys_set_bit(RTC_BASE_ADDR + RTC_CCR, 0);
sys_write32(alarm_val, RTC_BASE_ADDR + RTC_CMR);
return DEV_OK;
}
/**
* @brief Function to configure the RTC
* @param config pointer to a RTC configuration structure
* @return 0 on success
*/
static int rtc_dw_set_config(struct rtc_config *config)
{
/* Set RTC divider - 32768 / 32.768 khz = 1 second. */
rtc_dw_set_div(RTC_DIVIDER);
/* set initial RTC value */
sys_write32(config->init_val, RTC_BASE_ADDR + RTC_CLR);
/* clear any pending interrupts */
sys_read32(RTC_BASE_ADDR + RTC_EOI);
rtc_dw_cb_fn = config->cb_fn;
if (config->alarm_enable) {
rtc_dw_set_alarm(config->alarm_val);
} else {
sys_clear_bit(RTC_BASE_ADDR + RTC_CCR, 0);
}
return DEV_OK;
}
/**
* @brief Read current RTC value
* @return current rtc value
*/
static uint32_t rtc_dw_read(void)
{
return sys_read32(RTC_BASE_ADDR + RTC_CCVR);
}
static struct rtc_driver_api funcs = {
.set_config = rtc_dw_set_config,
.read = rtc_dw_read,
.enable = rtc_dw_enable,
.disable = rtc_dw_disable,
.set_alarm = rtc_dw_set_alarm,
};
int rtc_dw_init(struct device *dev)
{
IRQ_CONFIG(rtc, INT_RTC_IRQ, 0);
irq_enable(INT_RTC_IRQ);
SCSS_INTERRUPT->int_rtc_mask = INT_UNMASK_IA;
dev->driver_api = &funcs;
return DEV_OK;
}
struct rtc_dw_dev_config rtc_dev = {
.base_address = RTC_BASE_ADDR,
};
#ifdef CONFIG_RTC_DW
DECLARE_DEVICE_INIT_CONFIG(rtc, RTC_DRV_NAME, &rtc_dw_init, &rtc_dev);
nano_early_init(rtc, NULL);
#endif

53
drivers/rtc/rtc_dw.h Normal file
View file

@ -0,0 +1,53 @@
/*
* Copyright (c) 2015 Intel Corporation.
*
* Licensed 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.
*/
#ifndef _RTC_DW_H_
#define _RTC_DW_H_
#include <board.h>
#include <device.h>
#include <rtc.h>
#define RTC_DRV_NAME "rtc"
#define RTC_CCVR (0x0)
#define RTC_CMR (0x4)
#define RTC_CLR (0x8)
#define RTC_CCR (0xC)
#define RTC_STAT (0x10)
#define RTC_RSTAT (0x14)
#define RTC_EOI (0x18)
#define RTC_COMP_VERSION (0x1C)
#define RTC_INTERRUPT_ENABLE (1 << 0)
#define RTC_INTERRUPT_MASK (1 << 1)
#define RTC_ENABLE (1 << 2)
#define RTC_WRAP_ENABLE (1 << 3)
#define RTC_CLK_DIV_EN (1 << 2)
#define RTC_CLK_DIV_MASK (0xF << 3)
#define RTC_CLK_DIV_1_HZ (0xF << 3)
#define RTC_CLK_DIV_32768_HZ (0x0 << 3)
#define RTC_CLK_DIV_8192_HZ (0x2 << 3)
#define RTC_CLK_DIV_4096_HZ (0x3 << 3)
struct rtc_dw_dev_config {
uint32_t base_address;
};
int rtc_dw_init(struct device *dev);
#endif /* _RTC_DW_H_ */

View file

@ -0,0 +1,32 @@
/*
* Copyright (c) 2015, Intel Corportation.
*
* Licensed 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.
*/
#define _ASMLANGUAGE
#include <arch/x86/asm.h>
#include <drivers/ioapic.h>
#if defined(CONFIG_RTC_DW)
#if defined(CONFIG_IOAPIC)
ioapic_mkstub rtc rtc_dw_isr 0
#endif
#endif
/* externs (internal APIs) */
GTEXT(_IntEnt)
GTEXT(_IntExit)

126
include/rtc.h Normal file
View file

@ -0,0 +1,126 @@
/*
* Copyright (c) 2015 Intel Corporation.
*
* Licensed 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.
*/
#ifndef _RTC_H_
#define _RTC_H_
#include <stdint.h>
#include <device.h>
#include <misc/util.h>
enum clk_rtc_div {
CLK_RTC_DIV_1,
CLK_RTC_DIV_2,
CLK_RTC_DIV_4,
CLK_RTC_DIV_8,
CLK_RTC_DIV_16,
CLK_RTC_DIV_32,
CLK_RTC_DIV_64,
CLK_RTC_DIV_128,
CLK_RTC_DIV_256,
CLK_RTC_DIV_512,
CLK_RTC_DIV_1024,
CLK_RTC_DIV_2048,
CLK_RTC_DIV_4096,
CLK_RTC_DIV_8192,
CLK_RTC_DIV_16384,
CLK_RTC_DIV_32768
};
#ifndef BIT
#define BIT(n) (1UL << (n))
#endif
#define RTC_DIVIDER CLK_RTC_DIV_1
/** Number of RTC ticks in a second */
#define RTC_ALARM_SECOND (32768 / BIT(RTC_DIVIDER))
/** Number of RTC ticks in a minute */
#define RTC_ALARM_MINUTE (RTC_ALARM_SECOND * 60)
/** Number of RTC ticks in an hour */
#define RTC_ALARM_HOUR (RTC_ALARM_MINUTE * 60)
/** Number of RTC ticks in a day */
#define RTC_ALARM_DAY (RTC_ALARM_HOUR * 24)
struct rtc_config {
uint32_t init_val;
/*!< enable/disable alarm */
uint8_t alarm_enable;
/*!< initial configuration value for the 32bit RTC alarm value */
uint32_t alarm_val;
/*!< Pointer to function to call when alarm value matches current RTC value */
void (*cb_fn)(void);
};
typedef void (*rtc_api_enable)(void);
typedef void (*rtc_api_disable)(void);
typedef int (*rtc_api_set_config)(struct rtc_config *config);
typedef int (*rtc_api_set_alarm)(const uint32_t alarm_val);
typedef uint32_t (*rtc_api_read)(void);
struct rtc_driver_api {
rtc_api_enable enable;
rtc_api_disable disable;
rtc_api_read read;
rtc_api_set_config set_config;
rtc_api_set_alarm set_alarm;
};
static inline uint32_t rtc_read(struct device *dev)
{
struct rtc_driver_api *api;
api = (struct rtc_driver_api *)dev->driver_api;
return api->read();
}
static inline void rtc_enable(struct device *dev)
{
struct rtc_driver_api *api;
api = (struct rtc_driver_api *)dev->driver_api;
api->enable();
}
static inline void rtc_disable(struct device *dev)
{
struct rtc_driver_api *api;
api = (struct rtc_driver_api *)dev->driver_api;
api->disable();
}
static inline int rtc_set_config(struct device *dev, struct rtc_config *cfg)
{
struct rtc_driver_api *api;
api = (struct rtc_driver_api *)dev->driver_api;
return api->set_config(cfg);
}
static inline int rtc_set_alarm(struct device *dev, const uint32_t alarm_val)
{
struct rtc_driver_api *api;
api = (struct rtc_driver_api *)dev->driver_api;
return api->set_alarm(alarm_val);
}
#endif