167 lines
5.2 KiB
C
167 lines
5.2 KiB
C
/* Copyright 2018 Canaan Inc.
|
|
*
|
|
* 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.
|
|
*/
|
|
/* Copyright 2018 Canaan Inc.
|
|
*
|
|
* 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 <FreeRTOS.h>
|
|
#include <driver.h>
|
|
#include <fpioa.h>
|
|
#include <hal.h>
|
|
#include <io.h>
|
|
#include <limits.h>
|
|
#include <plic.h>
|
|
#include <semphr.h>
|
|
#include <stdio.h>
|
|
#include <sysctl.h>
|
|
#include <timer.h>
|
|
#include "fpioa_cfg.h"
|
|
|
|
#define COMMON_ENTRY \
|
|
timer_data* data = (timer_data*)userdata; \
|
|
volatile struct timer_t* timer = (volatile struct timer_t*)data->base_addr; \
|
|
(void)timer;
|
|
|
|
typedef struct
|
|
{
|
|
uintptr_t base_addr;
|
|
enum sysctl_clock_e clock;
|
|
enum plic_irq_t irq;
|
|
size_t channel;
|
|
timer_ontick ontick;
|
|
void* ontick_data;
|
|
} timer_data;
|
|
|
|
static void timer_isr(void* userdata);
|
|
|
|
static void timer_install(void* userdata)
|
|
{
|
|
COMMON_ENTRY;
|
|
|
|
if (data->channel == 0)
|
|
{
|
|
sysctl_clock_enable(data->clock);
|
|
|
|
readl(&timer->eoi);
|
|
size_t i;
|
|
for (i = 0; i < 4; i++)
|
|
timer->channel[i].control = TIMER_CR_INTERRUPT_MASK;
|
|
|
|
pic_set_irq_handler(data->irq, timer_isr, userdata);
|
|
pic_set_irq_handler(data->irq + 1, timer_isr, userdata);
|
|
pic_set_irq_priority(data->irq, 1);
|
|
pic_set_irq_priority(data->irq + 1, 1);
|
|
pic_set_irq_enable(data->irq, 1);
|
|
pic_set_irq_enable(data->irq + 1, 1);
|
|
}
|
|
}
|
|
|
|
static int timer_open(void* userdata)
|
|
{
|
|
COMMON_ENTRY;
|
|
return 1;
|
|
}
|
|
|
|
static void timer_close(void* userdata)
|
|
{
|
|
}
|
|
|
|
static size_t timer_set_interval(size_t nanoseconds, void* userdata)
|
|
{
|
|
COMMON_ENTRY;
|
|
uint32_t clk_freq = sysctl_clock_get_freq(data->clock);
|
|
double min_step = 1e9 / clk_freq;
|
|
size_t value = (size_t)(nanoseconds / min_step);
|
|
configASSERT(value > 0 && value < UINT32_MAX);
|
|
timer->channel[data->channel].load_count = (uint32_t)value;
|
|
return (size_t)(min_step * value);
|
|
}
|
|
|
|
static void timer_set_ontick(timer_ontick ontick, void* ontick_data, void* userdata)
|
|
{
|
|
COMMON_ENTRY;
|
|
data->ontick_data = ontick_data;
|
|
data->ontick = ontick;
|
|
}
|
|
|
|
static void timer_set_enable(int enable, void* userdata)
|
|
{
|
|
COMMON_ENTRY;
|
|
if (enable)
|
|
timer->channel[data->channel].control = TIMER_CR_USER_MODE | TIMER_CR_ENABLE;
|
|
else
|
|
timer->channel[data->channel].control = TIMER_CR_INTERRUPT_MASK;
|
|
}
|
|
|
|
static void timer_isr(void* userdata)
|
|
{
|
|
COMMON_ENTRY;
|
|
uint32_t channel = timer->intr_stat;
|
|
size_t i = 0;
|
|
for (i = 0; i < 4; i++)
|
|
{
|
|
if (channel & 1)
|
|
{
|
|
timer_data* td = data + i;
|
|
if (td->ontick)
|
|
td->ontick(td->ontick_data);
|
|
}
|
|
|
|
channel >>= 1;
|
|
}
|
|
|
|
readl(&timer->eoi);
|
|
}
|
|
|
|
/* clang-format off */
|
|
#define DEFINE_TIMER_DATA(i) \
|
|
{ TIMER##i##_BASE_ADDR, SYSCTL_CLOCK_TIMER##i, IRQN_TIMER##i##A_INTERRUPT, 0, NULL, NULL }, \
|
|
{ TIMER##i##_BASE_ADDR, SYSCTL_CLOCK_TIMER##i, IRQN_TIMER##i##A_INTERRUPT, 1, NULL, NULL }, \
|
|
{ TIMER##i##_BASE_ADDR, SYSCTL_CLOCK_TIMER##i, IRQN_TIMER##i##A_INTERRUPT, 2, NULL, NULL }, \
|
|
{ TIMER##i##_BASE_ADDR, SYSCTL_CLOCK_TIMER##i, IRQN_TIMER##i##A_INTERRUPT, 3, NULL, NULL }
|
|
/* clang format on */
|
|
|
|
#define INIT_TIMER_DRIVER(i) { { &dev_data[i], timer_install, timer_open, timer_close }, timer_set_interval, timer_set_ontick, timer_set_enable }
|
|
|
|
static timer_data dev_data[12] =
|
|
{
|
|
DEFINE_TIMER_DATA(0),
|
|
DEFINE_TIMER_DATA(1),
|
|
DEFINE_TIMER_DATA(2)
|
|
};
|
|
|
|
const timer_driver_t g_timer_driver_timer0 = INIT_TIMER_DRIVER(0);
|
|
const timer_driver_t g_timer_driver_timer1 = INIT_TIMER_DRIVER(1);
|
|
const timer_driver_t g_timer_driver_timer2 = INIT_TIMER_DRIVER(2);
|
|
const timer_driver_t g_timer_driver_timer3 = INIT_TIMER_DRIVER(3);
|
|
const timer_driver_t g_timer_driver_timer4 = INIT_TIMER_DRIVER(4);
|
|
const timer_driver_t g_timer_driver_timer5 = INIT_TIMER_DRIVER(5);
|
|
const timer_driver_t g_timer_driver_timer6 = INIT_TIMER_DRIVER(6);
|
|
const timer_driver_t g_timer_driver_timer7 = INIT_TIMER_DRIVER(7);
|
|
const timer_driver_t g_timer_driver_timer8 = INIT_TIMER_DRIVER(8);
|
|
const timer_driver_t g_timer_driver_timer9 = INIT_TIMER_DRIVER(9);
|
|
const timer_driver_t g_timer_driver_timer10 = INIT_TIMER_DRIVER(10);
|
|
const timer_driver_t g_timer_driver_timer11 = INIT_TIMER_DRIVER(11);
|