730 lines
21 KiB
C
730 lines
21 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.
|
|
*/
|
|
#include <FreeRTOS.h>
|
|
#include <atomic.h>
|
|
#include <semphr.h>
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <string.h>
|
|
#include <sysctl.h>
|
|
#include <uarths.h>
|
|
#include "devices.h"
|
|
#include "driver.h"
|
|
#include "hal.h"
|
|
|
|
#define MAX_HANDLES 256
|
|
#define HANDLE_OFFSET 256
|
|
#define MAX_CUSTOM_DRIVERS 32
|
|
|
|
#define DEFINE_INSTALL_DRIVER(type) \
|
|
static void install_##type##_drivers() \
|
|
{ \
|
|
driver_registry_t* head = g_##type##_drivers; \
|
|
while (head->name) \
|
|
{ \
|
|
const driver_base_t* driver = head->driver; \
|
|
driver->install(driver->userdata); \
|
|
head++; \
|
|
} \
|
|
}
|
|
|
|
typedef struct
|
|
{
|
|
driver_registry_t* driver_reg;
|
|
} _file;
|
|
|
|
static _file* handles_[MAX_HANDLES] = {0};
|
|
static driver_registry_t g_custom_drivers[MAX_CUSTOM_DRIVERS] = {0};
|
|
|
|
uintptr_t fft_file_;
|
|
uintptr_t aes_file_;
|
|
uintptr_t sha256_file_;
|
|
|
|
DEFINE_INSTALL_DRIVER(hal);
|
|
DEFINE_INSTALL_DRIVER(dma);
|
|
DEFINE_INSTALL_DRIVER(system);
|
|
|
|
driver_registry_t* find_free_driver(driver_registry_t* registry, const char* name)
|
|
{
|
|
driver_registry_t* head = registry;
|
|
while (head->name)
|
|
{
|
|
if (strcmp(name, head->name) == 0)
|
|
{
|
|
driver_base_t* driver = (driver_base_t*)head->driver;
|
|
if (driver->open(driver->userdata))
|
|
return head;
|
|
else
|
|
return NULL;
|
|
}
|
|
|
|
head++;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static driver_registry_t* install_custom_driver_core(const char* name, driver_type type, const void* driver)
|
|
{
|
|
size_t i = 0;
|
|
driver_registry_t* head = g_custom_drivers;
|
|
for (i = 0; i < MAX_CUSTOM_DRIVERS; i++, head++)
|
|
{
|
|
if (!head->name)
|
|
{
|
|
head->name = strdup(name);
|
|
head->type = type;
|
|
head->driver = driver;
|
|
return head;
|
|
}
|
|
}
|
|
|
|
configASSERT(!"Max custom drivers exceeded.");
|
|
return NULL;
|
|
}
|
|
|
|
void install_drivers()
|
|
{
|
|
install_system_drivers();
|
|
|
|
fft_file_ = io_open("/dev/fft0");
|
|
aes_file_ = io_open("/dev/aes0");
|
|
sha256_file_ = io_open("/dev/sha256");
|
|
}
|
|
|
|
static _file* io_alloc_file(driver_registry_t* driver_reg)
|
|
{
|
|
if (driver_reg)
|
|
{
|
|
_file* file = (_file*)malloc(sizeof(_file));
|
|
if (!file)
|
|
return NULL;
|
|
file->driver_reg = driver_reg;
|
|
return file;
|
|
}
|
|
|
|
return NULL;
|
|
}
|
|
|
|
static _file* io_open_reg(driver_registry_t* registry, const char* name, _file** file)
|
|
{
|
|
driver_registry_t* driver_reg = find_free_driver(registry, name);
|
|
_file* ret = io_alloc_file(driver_reg);
|
|
*file = ret;
|
|
return ret;
|
|
}
|
|
|
|
/* Generic IO Implementation Helper Macros */
|
|
|
|
#define DEFINE_READ_PROXY(tl, tu) \
|
|
if (rfile->driver_reg->type == DRIVER_##tu) \
|
|
{ \
|
|
const tl##_driver_t* tl = (const tl##_driver_t*)rfile->driver_reg->driver; \
|
|
return tl->read(buffer, len, tl->base.userdata); \
|
|
}
|
|
|
|
#define DEFINE_WRITE_PROXY(tl, tu) \
|
|
if (rfile->driver_reg->type == DRIVER_##tu) \
|
|
{ \
|
|
const tl##_driver_t* tl = (const tl##_driver_t*)rfile->driver_reg->driver; \
|
|
return tl->write(buffer, len, tl->base.userdata); \
|
|
}
|
|
|
|
#define DEFINE_CONTROL_PROXY(tl, tu) \
|
|
if (rfile->driver_reg->type == DRIVER_##tu) \
|
|
{ \
|
|
const tl##_driver_t* tl = (const tl##_driver_t*)rfile->driver_reg->driver; \
|
|
return tl->io_control(control_code, write_buffer, write_len, read_buffer, read_len, tl->base.userdata); \
|
|
}
|
|
|
|
static void dma_add_free();
|
|
|
|
int io_read(uintptr_t file, char* buffer, size_t len)
|
|
{
|
|
_file* rfile = (_file*)handles_[file - HANDLE_OFFSET];
|
|
/* clang-format off */
|
|
DEFINE_READ_PROXY(uart, UART)
|
|
else DEFINE_READ_PROXY(i2c_device, I2C_DEVICE)
|
|
else DEFINE_READ_PROXY(spi_device, SPI_DEVICE)
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
/* clang-format on */
|
|
}
|
|
|
|
static void io_free(_file* file)
|
|
{
|
|
if (file)
|
|
{
|
|
if (file->driver_reg->type == DRIVER_DMA)
|
|
dma_add_free();
|
|
|
|
driver_base_t* driver = (driver_base_t*)file->driver_reg->driver;
|
|
driver->close(driver->userdata);
|
|
|
|
free(file);
|
|
}
|
|
}
|
|
|
|
static uintptr_t io_alloc_handle(_file* file)
|
|
{
|
|
if (file)
|
|
{
|
|
size_t i, j;
|
|
for (i = 0; i < 2; i++)
|
|
{
|
|
for (j = 0; j < MAX_HANDLES; j++)
|
|
{
|
|
if (atomic_cas(handles_ + j, 0, file) == 0)
|
|
return j + HANDLE_OFFSET;
|
|
}
|
|
}
|
|
|
|
io_free(file);
|
|
return 0;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
uintptr_t io_open(const char* name)
|
|
{
|
|
_file* file = 0;
|
|
if (io_open_reg(g_system_drivers, name, &file))
|
|
{
|
|
}
|
|
else if (io_open_reg(g_hal_drivers, name, &file))
|
|
{
|
|
}
|
|
|
|
if (file)
|
|
return io_alloc_handle(file);
|
|
configASSERT(file);
|
|
return 0;
|
|
}
|
|
|
|
int io_close(uintptr_t file)
|
|
{
|
|
if (file)
|
|
{
|
|
_file* rfile = (_file*)handles_[file - HANDLE_OFFSET];
|
|
io_free(rfile);
|
|
atomic_set(handles_ + file - HANDLE_OFFSET, 0);
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int io_write(uintptr_t file, const char* buffer, size_t len)
|
|
{
|
|
_file* rfile = (_file*)handles_[file - HANDLE_OFFSET];
|
|
/* clang-format off */
|
|
DEFINE_WRITE_PROXY(uart, UART)
|
|
else DEFINE_WRITE_PROXY(i2c_device, I2C_DEVICE)
|
|
else DEFINE_WRITE_PROXY(spi_device, SPI_DEVICE)
|
|
else
|
|
{
|
|
return -1;
|
|
}
|
|
/* clang-format on */
|
|
}
|
|
|
|
int io_control(uintptr_t file, size_t control_code, const char* write_buffer, size_t write_len, char* read_buffer, size_t read_len)
|
|
{
|
|
_file* rfile = (_file*)handles_[file - HANDLE_OFFSET];
|
|
DEFINE_CONTROL_PROXY(custom, CUSTOM)
|
|
|
|
return -1;
|
|
}
|
|
|
|
/* Device IO Implementation Helper Macros */
|
|
|
|
#define COMMON_ENTRY(tl, tu) \
|
|
_file* rfile = (_file*)handles_[file - HANDLE_OFFSET]; \
|
|
configASSERT(rfile->driver_reg->type == DRIVER_##tu); \
|
|
const tl##_driver_t* tl = (const tl##_driver_t*)rfile->driver_reg->driver;
|
|
|
|
#define COMMON_ENTRY_FILE(file, tl, tu) \
|
|
_file* rfile = (_file*)handles_[file - HANDLE_OFFSET]; \
|
|
configASSERT(rfile->driver_reg->type == DRIVER_##tu); \
|
|
const tl##_driver_t* tl = (const tl##_driver_t*)rfile->driver_reg->driver;
|
|
|
|
/* UART */
|
|
|
|
void uart_config(uintptr_t file, size_t baud_rate, size_t data_width, uart_stopbit stopbit, uart_parity parity)
|
|
{
|
|
COMMON_ENTRY(uart, UART);
|
|
uart->config(baud_rate, data_width, stopbit, parity, uart->base.userdata);
|
|
}
|
|
|
|
/* GPIO */
|
|
|
|
size_t gpio_get_pin_count(uintptr_t file)
|
|
{
|
|
COMMON_ENTRY(gpio, GPIO);
|
|
return gpio->pin_count;
|
|
}
|
|
|
|
void gpio_set_drive_mode(uintptr_t file, size_t pin, gpio_drive_mode mode)
|
|
{
|
|
COMMON_ENTRY(gpio, GPIO);
|
|
gpio->set_drive_mode(gpio->base.userdata, pin, mode);
|
|
}
|
|
|
|
void gpio_set_pin_edge(uintptr_t file, size_t pin, gpio_pin_edge edge)
|
|
{
|
|
COMMON_ENTRY(gpio, GPIO);
|
|
gpio->set_pin_edge(gpio->base.userdata, pin, edge);
|
|
}
|
|
|
|
void gpio_set_onchanged(uintptr_t file, size_t pin, gpio_onchanged callback, void* userdata)
|
|
{
|
|
COMMON_ENTRY(gpio, GPIO);
|
|
gpio->set_onchanged(gpio->base.userdata, pin, callback, userdata);
|
|
}
|
|
|
|
gpio_pin_value gpio_get_pin_value(uintptr_t file, size_t pin)
|
|
{
|
|
COMMON_ENTRY(gpio, GPIO);
|
|
return gpio->get_pin_value(gpio->base.userdata, pin);
|
|
}
|
|
|
|
void gpio_set_pin_value(uintptr_t file, size_t pin, gpio_pin_value value)
|
|
{
|
|
COMMON_ENTRY(gpio, GPIO);
|
|
gpio->set_pin_value(gpio->base.userdata, pin, value);
|
|
}
|
|
|
|
/* I2C */
|
|
|
|
uintptr_t i2c_get_device(uintptr_t file, const char* name, size_t slave_address, size_t address_width, i2c_bus_speed_mode bus_speed_mode)
|
|
{
|
|
COMMON_ENTRY(i2c, I2C);
|
|
i2c_device_driver_t* driver = i2c->get_device(slave_address, address_width, bus_speed_mode, i2c->base.userdata);
|
|
driver_registry_t* reg = install_custom_driver_core(name, DRIVER_I2C_DEVICE, driver);
|
|
return io_alloc_handle(io_alloc_file(reg));
|
|
}
|
|
|
|
int i2c_dev_transfer_sequential(uintptr_t file, const char* write_buffer, size_t write_len, char* read_buffer, size_t read_len)
|
|
{
|
|
COMMON_ENTRY(i2c_device, I2C_DEVICE);
|
|
return i2c_device->transfer_sequential(write_buffer, write_len, read_buffer, read_len, i2c_device->base.userdata);
|
|
}
|
|
|
|
void i2c_config_as_slave(uintptr_t file, size_t slave_address, size_t address_width, i2c_bus_speed_mode bus_speed_mode, i2c_slave_handler* handler)
|
|
{
|
|
COMMON_ENTRY(i2c, I2C);
|
|
i2c->config_as_slave(slave_address, address_width, bus_speed_mode, handler, i2c->base.userdata);
|
|
}
|
|
|
|
/* I2S */
|
|
|
|
void i2s_config_as_render(uintptr_t file, const audio_format_t* format, size_t delay_ms, i2s_align_mode align_mode, size_t channels_mask)
|
|
{
|
|
COMMON_ENTRY(i2s, I2S);
|
|
i2s->config_as_render(format, delay_ms, align_mode, channels_mask, i2s->base.userdata);
|
|
}
|
|
|
|
void i2s_config_as_capture(uintptr_t file, const audio_format_t* format, size_t delay_ms, i2s_align_mode align_mode, size_t channels_mask)
|
|
{
|
|
COMMON_ENTRY(i2s, I2S);
|
|
i2s->config_as_capture(format, delay_ms, align_mode, channels_mask, i2s->base.userdata);
|
|
}
|
|
|
|
void i2s_get_buffer(uintptr_t file, char** buffer, size_t* frames)
|
|
{
|
|
COMMON_ENTRY(i2s, I2S);
|
|
i2s->get_buffer(buffer, frames, i2s->base.userdata);
|
|
}
|
|
|
|
void i2s_release_buffer(uintptr_t file, size_t frames)
|
|
{
|
|
COMMON_ENTRY(i2s, I2S);
|
|
i2s->release_buffer(frames, i2s->base.userdata);
|
|
}
|
|
|
|
void i2s_start(uintptr_t file)
|
|
{
|
|
COMMON_ENTRY(i2s, I2S);
|
|
i2s->start(i2s->base.userdata);
|
|
}
|
|
|
|
void i2s_stop(uintptr_t file)
|
|
{
|
|
COMMON_ENTRY(i2s, I2S);
|
|
i2s->stop(i2s->base.userdata);
|
|
}
|
|
|
|
/* SPI */
|
|
|
|
uintptr_t spi_get_device(uintptr_t file, const char* name, spi_mode mode, spi_frame_format frame_format, size_t chip_select_line, size_t data_bit_length)
|
|
{
|
|
COMMON_ENTRY(spi, SPI);
|
|
spi_device_driver_t* driver = spi->get_device(mode, frame_format, chip_select_line, data_bit_length, spi->base.userdata);
|
|
driver_registry_t* reg = install_custom_driver_core(name, DRIVER_SPI_DEVICE, driver);
|
|
return io_alloc_handle(io_alloc_file(reg));
|
|
}
|
|
|
|
void spi_dev_config(uintptr_t file, size_t instruction_length, size_t address_length, size_t wait_cycles, spi_addr_inst_trans_mode trans_mode)
|
|
{
|
|
COMMON_ENTRY(spi_device, SPI_DEVICE);
|
|
spi_device->config(instruction_length, address_length, wait_cycles, trans_mode, spi_device->base.userdata);
|
|
}
|
|
|
|
double spi_dev_set_speed(uintptr_t file, double speed)
|
|
{
|
|
COMMON_ENTRY(spi_device, SPI_DEVICE);
|
|
return spi_device->set_speed(speed, spi_device->base.userdata);
|
|
}
|
|
|
|
int spi_dev_transfer_full_duplex(uintptr_t file, const char* write_buffer, size_t write_len, char* read_buffer, size_t read_len)
|
|
{
|
|
COMMON_ENTRY(spi_device, SPI_DEVICE);
|
|
return spi_device->transfer_full_duplex(write_buffer, write_len, read_buffer, read_len, spi_device->base.userdata);
|
|
}
|
|
|
|
int spi_dev_transfer_sequential(uintptr_t file, const char* write_buffer, size_t write_len, char* read_buffer, size_t read_len)
|
|
{
|
|
COMMON_ENTRY(spi_device, SPI_DEVICE);
|
|
return spi_device->transfer_sequential(write_buffer, write_len, read_buffer, read_len, spi_device->base.userdata);
|
|
}
|
|
|
|
void spi_dev_fill(uintptr_t file, size_t instruction, size_t address, uint32_t value, size_t count)
|
|
{
|
|
COMMON_ENTRY(spi_device, SPI_DEVICE);
|
|
return spi_device->fill(instruction, address, value, count, spi_device->base.userdata);
|
|
}
|
|
|
|
/* DVP */
|
|
|
|
void dvp_config(uintptr_t file, size_t width, size_t height, int auto_enable)
|
|
{
|
|
COMMON_ENTRY(dvp, DVP);
|
|
dvp->config(width, height, auto_enable, dvp->base.userdata);
|
|
}
|
|
|
|
void dvp_enable_frame(uintptr_t file)
|
|
{
|
|
COMMON_ENTRY(dvp, DVP);
|
|
dvp->enable_frame(dvp->base.userdata);
|
|
}
|
|
|
|
size_t dvp_get_output_num(uintptr_t file)
|
|
{
|
|
COMMON_ENTRY(dvp, DVP);
|
|
return dvp->output_num;
|
|
}
|
|
|
|
void dvp_set_signal(uintptr_t file, dvp_signal_type type, int value)
|
|
{
|
|
COMMON_ENTRY(dvp, DVP);
|
|
dvp->set_signal(type, value, dvp->base.userdata);
|
|
}
|
|
|
|
void dvp_set_output_enable(uintptr_t file, size_t index, int enable)
|
|
{
|
|
COMMON_ENTRY(dvp, DVP);
|
|
dvp->set_output_enable(index, enable, dvp->base.userdata);
|
|
}
|
|
|
|
void dvp_set_output_attributes(uintptr_t file, size_t index, video_format format, void* output_buffer)
|
|
{
|
|
COMMON_ENTRY(dvp, DVP);
|
|
dvp->set_output_attributes(index, format, output_buffer, dvp->base.userdata);
|
|
}
|
|
|
|
void dvp_set_frame_event_enable(uintptr_t file, video_frame_event event, int enable)
|
|
{
|
|
COMMON_ENTRY(dvp, DVP);
|
|
dvp->set_frame_event_enable(event, enable, dvp->base.userdata);
|
|
}
|
|
|
|
void dvp_set_on_frame_event(uintptr_t file, dvp_on_frame_event callback, void* callback_data)
|
|
{
|
|
COMMON_ENTRY(dvp, DVP);
|
|
dvp->set_on_frame_event(callback, callback_data, dvp->base.userdata);
|
|
}
|
|
|
|
/* SSCB */
|
|
|
|
uintptr_t sccb_get_device(uintptr_t file, const char* name, size_t slave_address, size_t address_width)
|
|
{
|
|
COMMON_ENTRY(sccb, SCCB);
|
|
sccb_device_driver_t* driver = sccb->get_device(slave_address, address_width, sccb->base.userdata);
|
|
driver_registry_t* reg = install_custom_driver_core(name, DRIVER_SCCB_DEVICE, driver);
|
|
return io_alloc_handle(io_alloc_file(reg));
|
|
}
|
|
|
|
uint8_t sccb_dev_read_byte(uintptr_t file, uint16_t reg_address)
|
|
{
|
|
COMMON_ENTRY(sccb_device, SCCB_DEVICE);
|
|
return sccb_device->read_byte(reg_address, sccb_device->base.userdata);
|
|
}
|
|
|
|
void sccb_dev_write_byte(uintptr_t file, uint16_t reg_address, uint8_t value)
|
|
{
|
|
COMMON_ENTRY(sccb_device, SCCB_DEVICE);
|
|
sccb_device->write_byte(reg_address, value, sccb_device->base.userdata);
|
|
}
|
|
|
|
/* FFT */
|
|
|
|
void fft_complex_uint16(fft_point point, fft_direction direction, uint32_t shifts_mask, const uint16_t* input, uint16_t* output)
|
|
{
|
|
COMMON_ENTRY_FILE(fft_file_, fft, FFT);
|
|
fft->complex_uint16(point, direction, shifts_mask, input, output, fft->base.userdata);
|
|
}
|
|
|
|
/* AES */
|
|
|
|
void aes_decrypt(aes_parameter* aes_param)
|
|
{
|
|
COMMON_ENTRY_FILE(aes_file_, aes, AES);
|
|
aes->decrypt(aes_param, aes->base.userdata);
|
|
}
|
|
void aes_encrypt(aes_parameter* aes_param)
|
|
{
|
|
COMMON_ENTRY_FILE(aes_file_, aes, AES);
|
|
aes->encrypt(aes_param, aes->base.userdata);
|
|
}
|
|
|
|
/* SHA */
|
|
|
|
void sha256_str(const char* str, size_t length, uint8_t* hash)
|
|
{
|
|
COMMON_ENTRY_FILE(sha256_file_, sha256, SHA256);
|
|
sha256->sha_str(str, length, hash, sha256->base.userdata);
|
|
}
|
|
|
|
/* TIMER */
|
|
|
|
size_t timer_set_interval(uintptr_t file, size_t nanoseconds)
|
|
{
|
|
COMMON_ENTRY(timer, TIMER);
|
|
return timer->set_interval(nanoseconds, timer->base.userdata);
|
|
}
|
|
|
|
void timer_set_ontick(uintptr_t file, timer_ontick ontick, void* ontick_data)
|
|
{
|
|
COMMON_ENTRY(timer, TIMER);
|
|
timer->set_ontick(ontick, ontick_data, timer->base.userdata);
|
|
}
|
|
|
|
void timer_set_enable(uintptr_t file, int enable)
|
|
{
|
|
COMMON_ENTRY(timer, TIMER);
|
|
timer->set_enable(enable, timer->base.userdata);
|
|
}
|
|
|
|
/* PWM */
|
|
|
|
size_t pwm_get_pin_count(uintptr_t file)
|
|
{
|
|
COMMON_ENTRY(pwm, PWM);
|
|
return pwm->pin_count;
|
|
}
|
|
|
|
double pwm_set_frequency(uintptr_t file, double frequency)
|
|
{
|
|
COMMON_ENTRY(pwm, PWM);
|
|
return pwm->set_frequency(frequency, pwm->base.userdata);
|
|
}
|
|
|
|
double pwm_set_active_duty_cycle_percentage(uintptr_t file, size_t pin, double duty_cycle_percentage)
|
|
{
|
|
COMMON_ENTRY(pwm, PWM);
|
|
return pwm->set_active_duty_cycle_percentage(pin, duty_cycle_percentage, pwm->base.userdata);
|
|
}
|
|
|
|
void pwm_set_enable(uintptr_t file, size_t pin, int enable)
|
|
{
|
|
COMMON_ENTRY(pwm, PWM);
|
|
pwm->set_enable(pin, enable, pwm->base.userdata);
|
|
}
|
|
|
|
/* RTC */
|
|
|
|
void rtc_get_datetime(uintptr_t file, datetime_t* datetime)
|
|
{
|
|
COMMON_ENTRY(rtc, RTC);
|
|
rtc->get_datetime(datetime, rtc->base.userdata);
|
|
}
|
|
|
|
void rtc_set_datetime(uintptr_t file, const datetime_t* datetime)
|
|
{
|
|
COMMON_ENTRY(rtc, RTC);
|
|
rtc->set_datetime(datetime, rtc->base.userdata);
|
|
}
|
|
|
|
/* HAL */
|
|
|
|
static uintptr_t pic_file_;
|
|
|
|
typedef struct
|
|
{
|
|
pic_irq_handler pic_callbacks[MAX_IRQN];
|
|
void* callback_userdata[MAX_IRQN];
|
|
} pic_context_t;
|
|
|
|
static pic_context_t pic_context_;
|
|
static SemaphoreHandle_t dma_free_;
|
|
|
|
static void init_dma_system()
|
|
{
|
|
size_t count = 0;
|
|
driver_registry_t* head = g_dma_drivers;
|
|
while (head->name)
|
|
{
|
|
count++;
|
|
head++;
|
|
}
|
|
|
|
dma_free_ = xSemaphoreCreateCounting(count, count);
|
|
}
|
|
|
|
void install_hal()
|
|
{
|
|
install_hal_drivers();
|
|
pic_file_ = io_open("/dev/pic0");
|
|
configASSERT(pic_file_);
|
|
|
|
install_dma_drivers();
|
|
init_dma_system();
|
|
}
|
|
|
|
/* PIC */
|
|
|
|
void pic_set_irq_enable(size_t irq, int enable)
|
|
{
|
|
COMMON_ENTRY_FILE(pic_file_, pic, PIC);
|
|
pic->set_irq_enable(irq, enable, pic->base.userdata);
|
|
}
|
|
|
|
void pic_set_irq_priority(size_t irq, size_t priority)
|
|
{
|
|
COMMON_ENTRY_FILE(pic_file_, pic, PIC);
|
|
pic->set_irq_priority(irq, priority, pic->base.userdata);
|
|
}
|
|
|
|
void pic_set_irq_handler(size_t irq, pic_irq_handler handler, void* userdata)
|
|
{
|
|
atomic_set(pic_context_.callback_userdata + irq, userdata);
|
|
pic_context_.pic_callbacks[irq] = handler;
|
|
}
|
|
|
|
void kernel_iface_pic_on_irq(size_t irq)
|
|
{
|
|
pic_irq_handler handler = pic_context_.pic_callbacks[irq];
|
|
if (handler)
|
|
handler(pic_context_.callback_userdata[irq]);
|
|
}
|
|
|
|
/* DMA */
|
|
|
|
uintptr_t dma_open_free()
|
|
{
|
|
configASSERT(xSemaphoreTake(dma_free_, portMAX_DELAY) == pdTRUE);
|
|
|
|
driver_registry_t *head = g_dma_drivers, *driver_reg = NULL;
|
|
while (head->name)
|
|
{
|
|
driver_base_t* driver = (driver_base_t*)head->driver;
|
|
if (driver->open(driver->userdata))
|
|
{
|
|
driver_reg = head;
|
|
break;
|
|
}
|
|
|
|
head++;
|
|
}
|
|
|
|
configASSERT(driver_reg);
|
|
uintptr_t handle = io_alloc_handle(io_alloc_file(driver_reg));
|
|
return handle;
|
|
}
|
|
|
|
void dma_close(uintptr_t file)
|
|
{
|
|
io_close(file);
|
|
}
|
|
|
|
static void dma_add_free()
|
|
{
|
|
xSemaphoreGive(dma_free_);
|
|
}
|
|
|
|
void dma_set_select_request(uintptr_t file, uint32_t request)
|
|
{
|
|
COMMON_ENTRY(dma, DMA);
|
|
dma->set_select_request(request, dma->base.userdata);
|
|
}
|
|
|
|
void dma_transmit_async(uintptr_t file, const volatile void* src, volatile void* dest, int src_inc, int dest_inc, size_t element_size, size_t count, size_t burst_size, SemaphoreHandle_t completion_event)
|
|
{
|
|
COMMON_ENTRY(dma, DMA);
|
|
dma->transmit_async(src, dest, src_inc, dest_inc, element_size, count, burst_size, completion_event, dma->base.userdata);
|
|
}
|
|
|
|
void dma_transmit(uintptr_t file, const volatile void* src, volatile void* dest, int src_inc, int dest_inc, size_t element_size, size_t count, size_t burst_size)
|
|
{
|
|
SemaphoreHandle_t event = xSemaphoreCreateBinary();
|
|
dma_transmit_async(file, src, dest, src_inc, dest_inc, element_size, count, burst_size, event);
|
|
// printf("event: %p\n", event);
|
|
configASSERT(xSemaphoreTake(event, portMAX_DELAY) == pdTRUE);
|
|
vSemaphoreDelete(event);
|
|
}
|
|
|
|
void dma_loop_async(uintptr_t file, const volatile void** srcs, size_t src_num, volatile void** dests, size_t dest_num, int src_inc, int dest_inc, size_t element_size, size_t count, size_t burst_size, dma_stage_completion_handler stage_completion_handler, void* stage_completion_handler_data, SemaphoreHandle_t completion_event, int* stop_signal)
|
|
{
|
|
COMMON_ENTRY(dma, DMA);
|
|
dma->loop_async(srcs, src_num, dests, dest_num, src_inc, dest_inc, element_size, count, burst_size, stage_completion_handler, stage_completion_handler_data, completion_event, stop_signal, dma->base.userdata);
|
|
}
|
|
|
|
/* Custom Driver */
|
|
|
|
void install_custom_driver(const char* name, const custom_driver_t* driver)
|
|
{
|
|
install_custom_driver_core(name, DRIVER_CUSTOM, driver);
|
|
}
|
|
|
|
/* System */
|
|
|
|
uint32_t system_set_cpu_frequency(uint32_t frequency)
|
|
{
|
|
sysctl_clock_set_clock_select(SYSCTL_CLOCK_SELECT_ACLK, SYSCTL_SOURCE_IN0);
|
|
sysctl->pll0.pll_reset0 = 1;
|
|
|
|
uint32_t result = sysctl_pll_set_freq(SYSCTL_PLL0, SYSCTL_SOURCE_IN0, frequency * 2);
|
|
sysctl->pll0.pll_reset0 = 0;
|
|
while (1)
|
|
{
|
|
uint32_t lock = sysctl->pll_lock.pll_lock0 & 0x3;
|
|
if (lock == 0x3)
|
|
{
|
|
break;
|
|
}
|
|
else
|
|
{
|
|
sysctl->pll_lock.pll_slip_clear0 = 1;
|
|
}
|
|
}
|
|
|
|
sysctl->pll0.pll_out_en0 = 1;
|
|
sysctl_clock_set_clock_select(SYSCTL_CLOCK_SELECT_ACLK, SYSCTL_SOURCE_PLL0);
|
|
uart_init();
|
|
return result;
|
|
}
|