/* 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 #include "io.h" #include "plic.h" #include "stddef.h" #include "sysctl.h" #include "timer.h" #include "utils.h" /** * @brief Private definitions for the timer instance */ typedef struct timer_instance { timer_device_number_t device; timer_channel_number_t channel; timer_callback_t callback; void *ctx; bool single_shot; } timer_instance_t; volatile timer_instance_t timer_instance[TIMER_DEVICE_MAX][TIMER_CHANNEL_MAX]; volatile kendryte_timer_t *const timer[3] = { (volatile kendryte_timer_t *)TIMER0_BASE_ADDR, (volatile kendryte_timer_t *)TIMER1_BASE_ADDR, (volatile kendryte_timer_t *)TIMER2_BASE_ADDR}; void timer_init(timer_device_number_t timer_number) { for(size_t i = 0; i < TIMER_CHANNEL_MAX; i++) timer_instance[timer_number][i] = (const timer_instance_t){ .device = 0, .channel = 0, .callback = NULL, .ctx = NULL, .single_shot = 0, }; sysctl_clock_enable(SYSCTL_CLOCK_TIMER0 + timer_number); } void timer_set_clock_div(timer_device_number_t timer_number, uint32_t div) { sysctl_clock_set_threshold(timer_number == 0 ? SYSCTL_THRESHOLD_TIMER0 : timer_number == 1 ? SYSCTL_THRESHOLD_TIMER1 : SYSCTL_THRESHOLD_TIMER2, div); } void timer_enable(timer_device_number_t timer_number, timer_channel_number_t channel) { timer[timer_number]->channel[channel].control |= TIMER_CR_ENABLE; } void timer_disable(timer_device_number_t timer_number, timer_channel_number_t channel) { timer[timer_number]->channel[channel].control &= (~TIMER_CR_ENABLE); } void timer_enable_pwm(timer_device_number_t timer_number, timer_channel_number_t channel) { timer[timer_number]->channel[channel].control |= TIMER_CR_PWM_ENABLE; } void timer_disable_pwm(timer_device_number_t timer_number, timer_channel_number_t channel) { timer[timer_number]->channel[channel].control &= (~TIMER_CR_PWM_ENABLE); } void timer_enable_interrupt(timer_device_number_t timer_number, timer_channel_number_t channel) { timer[timer_number]->channel[channel].control &= (~TIMER_CR_INTERRUPT_MASK); } void timer_disable_interrupt(timer_device_number_t timer_number, timer_channel_number_t channel) { timer[timer_number]->channel[channel].control |= TIMER_CR_INTERRUPT_MASK; } void timer_set_mode(timer_device_number_t timer_number, timer_channel_number_t channel, uint32_t mode) { timer[timer_number]->channel[channel].control &= (~TIMER_CR_MODE_MASK); timer[timer_number]->channel[channel].control |= mode; } void timer_set_reload(timer_device_number_t timer_number, timer_channel_number_t channel, uint32_t count) { timer[timer_number]->channel[channel].load_count = count; } void timer_set_reload2(timer_device_number_t timer_number, timer_channel_number_t channel, uint32_t count) { timer[timer_number]->load_count2[channel] = count; } uint32_t timer_get_count(timer_device_number_t timer_number, timer_channel_number_t channel) { return timer[timer_number]->channel[channel].current_value; } uint32_t timer_get_reload(timer_device_number_t timer_number, timer_channel_number_t channel) { return timer[timer_number]->channel[channel].load_count; } uint32_t timer_get_reload2(timer_device_number_t timer_number, timer_channel_number_t channel) { return timer[timer_number]->load_count2[channel]; } uint32_t timer_get_interrupt_status(timer_device_number_t timer_number) { return timer[timer_number]->intr_stat; } uint32_t timer_get_raw_interrupt_status(timer_device_number_t timer_number) { return timer[timer_number]->raw_intr_stat; } uint32_t timer_channel_get_interrupt_status(timer_device_number_t timer_number, timer_channel_number_t channel) { return timer[timer_number]->channel[channel].intr_stat; } void timer_clear_interrupt(timer_device_number_t timer_number) { timer[timer_number]->eoi = timer[timer_number]->eoi; } void timer_channel_clear_interrupt(timer_device_number_t timer_number, timer_channel_number_t channel) { timer[timer_number]->channel[channel].eoi = timer[timer_number]->channel[channel].eoi; } void timer_set_enable(timer_device_number_t timer_number, timer_channel_number_t channel, uint32_t enable) { if(enable) timer[timer_number]->channel[channel].control = TIMER_CR_USER_MODE | TIMER_CR_ENABLE; else timer[timer_number]->channel[channel].control = TIMER_CR_INTERRUPT_MASK; } size_t timer_set_interval(timer_device_number_t timer_number, timer_channel_number_t channel, size_t nanoseconds) { uint32_t clk_freq = sysctl_clock_get_freq(SYSCTL_CLOCK_TIMER0 + timer_number); double min_step = 1e9 / clk_freq; size_t value = (size_t)(nanoseconds / min_step); configASSERT(value > 0 && value < UINT32_MAX); timer[timer_number]->channel[channel].load_count = (uint32_t)value; return (size_t)(min_step * value); } typedef void (*timer_ontick)(); timer_ontick time_irq[3][4] = {NULL}; static int timer_isr(void *parm) { uint32_t timer_number; for(timer_number = 0; timer_number < 3; timer_number++) { if(parm == timer[timer_number]) break; } uint32_t channel = timer[timer_number]->intr_stat; size_t i = 0; for(i = 0; i < 4; i++) { if(channel & 1) { if(time_irq[timer_number][i]) (time_irq[timer_number][i])(); break; } channel >>= 1; } readl(&timer[timer_number]->eoi); return 0; } void timer_set_irq(timer_device_number_t timer_number, timer_channel_number_t channel, void (*func)(), uint32_t priority) { time_irq[timer_number][channel] = func; if(channel < 2) { plic_set_priority(IRQN_TIMER0A_INTERRUPT + timer_number * 2, priority); plic_irq_register(IRQN_TIMER0A_INTERRUPT + timer_number * 2, timer_isr, (void *)timer[timer_number]); plic_irq_enable(IRQN_TIMER0A_INTERRUPT + timer_number * 2); } else { plic_set_priority(IRQN_TIMER0B_INTERRUPT + timer_number * 2, priority); plic_irq_register(IRQN_TIMER0B_INTERRUPT + timer_number * 2, timer_isr, (void *)timer[timer_number]); plic_irq_enable(IRQN_TIMER0B_INTERRUPT + timer_number * 2); } } /** * @brief Get the timer irqn by device and channel object * * @note Internal function, not public * @param device The device * @param channel The channel * @return plic_irq_t IRQ number */ static plic_irq_t get_timer_irqn_by_device_and_channel(timer_device_number_t device, timer_channel_number_t channel) { if(device < TIMER_DEVICE_MAX && channel < TIMER_CHANNEL_MAX) { /* * Select timer interrupt part * Hierarchy of Timer interrupt to PLIC * +---------+ +-----------+ * | 0+----+ | | * | | +--+0A | * | 1+----+ | | * | TIMER0 | | | * | 2+----+ | | * | | +--+0B | * | 3+----+ | | * +---------+ | | * | | * +---------+ | | * | 0+----+ | | * | | +--+1A | * | 1+----+ | | * | TIMER1 | | PLIC | * | 2+----+ | | * | | +--+1B | * | 3+----+ | | * +---------+ | | * | | * +---------+ | | * | 0+----+ | | * | | +--+2A | * | 1+----+ | | * | TIMER2 | | | * | 2+----+ | | * | | +--+2B | * | 3+----+ | | * +---------+ +-----------+ * */ if(channel < 2) { /* It is part A interrupt, offset + 0 */ return IRQN_TIMER0A_INTERRUPT + device * 2; } else { /* It is part B interrupt, offset + 1 */ return IRQN_TIMER0B_INTERRUPT + device * 2; } } return IRQN_NO_INTERRUPT; } /** * @brief Process user callback function * * @note Internal function, not public * @param ctx The context * @return int The callback result */ static int timer_interrupt_handler(void *ctx) { timer_instance_t *instance = (timer_instance_t *)ctx; timer_device_number_t device = instance->device; timer_channel_number_t channel = instance->channel; uint32_t channel_int_stat = timer[device]->intr_stat; uint32_t irq_channel = (channel >> 1) << 1; channel_int_stat >>= irq_channel; for(size_t i = irq_channel; i < irq_channel + 2; i++) { /* Check every bit for interrupt status */ if(channel_int_stat & 1) { if(timer_instance[device][i].callback) { /* Process user callback function */ timer_instance[device][i].callback(timer_instance[device][i].ctx); /* Check if this timer is a single shot timer */ if(timer_instance[device][i].single_shot) { /* Single shot timer, disable it */ timer_set_enable(device, i, 0); } } /* Clear timer interrupt flag for specific channel */ readl(&timer[device]->channel[i].eoi); } channel_int_stat >>= 1; } /* * NOTE: * Don't read timer[device]->eoi here, or you will lost some interrupt * readl(&timer[device]->eoi); */ return 0; } int timer_irq_register(timer_device_number_t device, timer_channel_number_t channel, int is_single_shot, uint32_t priority, timer_callback_t callback, void *ctx) { if(device < TIMER_DEVICE_MAX && channel < TIMER_CHANNEL_MAX) { plic_irq_t irq_number = get_timer_irqn_by_device_and_channel(device, channel); timer_instance[device][channel] = (const timer_instance_t){ .device = device, .channel = channel, .callback = callback, .ctx = ctx, .single_shot = is_single_shot, }; plic_set_priority(irq_number, priority); plic_irq_register(irq_number, timer_interrupt_handler, (void *)&timer_instance[device][channel]); plic_irq_enable(irq_number); return 0; } return -1; } int timer_irq_unregister(timer_device_number_t device, timer_channel_number_t channel) { if(device < TIMER_DEVICE_MAX && channel < TIMER_CHANNEL_MAX) { timer_instance[device][channel] = (const timer_instance_t){ .device = 0, .channel = 0, .callback = NULL, .ctx = NULL, .single_shot = 0, }; /* Combine 0 and 1 to A interrupt, 2 and 3 to B interrupt */ if((!(timer_instance[device][TIMER_CHANNEL_0].callback || timer_instance[device][TIMER_CHANNEL_1].callback)) || (!(timer_instance[device][TIMER_CHANNEL_2].callback || timer_instance[device][TIMER_CHANNEL_3].callback))) { plic_irq_t irq_number = get_timer_irqn_by_device_and_channel(device, channel); plic_irq_unregister(irq_number); } return 0; } return -1; }