kendryte-freertos-sdk/lib/bsp/device/sccb.c

186 lines
5.6 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 <driver.h>
#include <dvp.h>
#include <fpioa.h>
#include <hal.h>
#include <io.h>
#include <semphr.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sysctl.h>
#include "fpioa_cfg.h"
/* SCCB Controller */
#define COMMON_ENTRY \
sccb_data* data = (sccb_data*)userdata; \
volatile struct dvp_t* sccb = (volatile struct dvp_t*)data->base_addr; \
(void)sccb;
typedef struct
{
enum sysctl_clock_e clock;
uintptr_t base_addr;
SemaphoreHandle_t free_mutex;
} sccb_data;
static void sccb_install(void* userdata)
{
COMMON_ENTRY;
sysctl_clock_enable(data->clock);
set_bit_mask(&sccb->sccb_cfg, DVP_SCCB_SCL_LCNT_MASK | DVP_SCCB_SCL_HCNT_MASK, DVP_SCCB_SCL_LCNT(500) | DVP_SCCB_SCL_HCNT(500));
data->free_mutex = xSemaphoreCreateMutex();
}
static int sccb_open(void* userdata)
{
return 1;
}
static void sccb_close(void* userdata)
{
}
/* SCCB Device */
#define COMMON_DEV_ENTRY \
sccb_dev_data* dev_data = (sccb_dev_data*)userdata; \
sccb_data* data = (sccb_data*)dev_data->sccb_data; \
volatile struct dvp_t* sccb = (volatile struct dvp_t*)data->base_addr;
typedef struct
{
sccb_data* sccb_data;
size_t slaveAddress;
size_t address_width;
} sccb_dev_data;
static void sccb_dev_install(void* userdata);
static int sccb_dev_open(void* userdata);
static void sccb_dev_close(void* userdata);
static uint8_t sccb_dev_read_byte(uint16_t reg_address, void* userdata);
static void sccb_dev_write_byte(uint16_t reg_address, uint8_t value, void* userdata);
static sccb_device_driver_t* sccb_get_device(size_t slaveAddress, size_t address_width, void* userdata)
{
configASSERT(address_width == 8 || address_width == 16);
sccb_device_driver_t* driver = (sccb_device_driver_t*)malloc(sizeof(sccb_device_driver_t));
memset(driver, 0, sizeof(sccb_device_driver_t));
sccb_dev_data* dev_data = (sccb_dev_data*)malloc(sizeof(sccb_dev_data));
dev_data->slaveAddress = slaveAddress;
dev_data->address_width = address_width;
dev_data->sccb_data = userdata;
driver->base.userdata = dev_data;
driver->base.install = sccb_dev_install;
driver->base.open = sccb_dev_open;
driver->base.close = sccb_dev_close;
driver->read_byte = sccb_dev_read_byte;
driver->write_byte = sccb_dev_write_byte;
return driver;
}
static void entry_exclusive(sccb_dev_data* dev_data)
{
sccb_data* data = (sccb_data*)dev_data->sccb_data;
configASSERT(xSemaphoreTake(data->free_mutex, portMAX_DELAY) == pdTRUE);
}
static void exit_exclusive(sccb_dev_data* dev_data)
{
sccb_data* data = (sccb_data*)dev_data->sccb_data;
xSemaphoreGive(data->free_mutex);
}
static void sccb_dev_install(void* userdata)
{
}
static int sccb_dev_open(void* userdata)
{
return 1;
}
static void sccb_dev_close(void* userdata)
{
}
static void dvp_sccb_start_transfer(volatile struct dvp_t* dvp)
{
while (dvp->sts & DVP_STS_SCCB_EN)
;
dvp->sts = DVP_STS_SCCB_EN | DVP_STS_SCCB_EN_WE;
while (dvp->sts & DVP_STS_SCCB_EN)
;
}
static uint8_t sccb_dev_read_byte(uint16_t reg_address, void* userdata)
{
COMMON_DEV_ENTRY;
entry_exclusive(dev_data);
if (dev_data->address_width == 8)
{
set_bit_mask(&sccb->sccb_cfg, DVP_SCCB_BYTE_NUM_MASK, DVP_SCCB_BYTE_NUM_2);
sccb->sccb_ctl = DVP_SCCB_WRITE_ENABLE | DVP_SCCB_DEVICE_ADDRESS(dev_data->slaveAddress) | DVP_SCCB_REG_ADDRESS(reg_address);
}
else
{
set_bit_mask(&sccb->sccb_cfg, DVP_SCCB_BYTE_NUM_MASK, DVP_SCCB_BYTE_NUM_3);
sccb->sccb_ctl = DVP_SCCB_WRITE_ENABLE | DVP_SCCB_DEVICE_ADDRESS(dev_data->slaveAddress) | DVP_SCCB_REG_ADDRESS(reg_address >> 8) | DVP_SCCB_WDATA_BYTE0(reg_address & 0xFF);
}
dvp_sccb_start_transfer(sccb);
sccb->sccb_ctl = DVP_SCCB_DEVICE_ADDRESS(dev_data->slaveAddress);
dvp_sccb_start_transfer(sccb);
uint8_t ret = DVP_SCCB_RDATA_BYTE(sccb->sccb_cfg);
exit_exclusive(dev_data);
return ret;
}
static void sccb_dev_write_byte(uint16_t reg_address, uint8_t value, void* userdata)
{
COMMON_DEV_ENTRY;
entry_exclusive(dev_data);
if (dev_data->address_width == 8)
{
set_bit_mask(&sccb->sccb_cfg, DVP_SCCB_BYTE_NUM_MASK, DVP_SCCB_BYTE_NUM_3);
sccb->sccb_ctl = DVP_SCCB_WRITE_ENABLE | DVP_SCCB_DEVICE_ADDRESS(dev_data->slaveAddress) | DVP_SCCB_REG_ADDRESS(reg_address) | DVP_SCCB_WDATA_BYTE0(value);
}
else
{
set_bit_mask(&sccb->sccb_cfg, DVP_SCCB_BYTE_NUM_MASK, DVP_SCCB_BYTE_NUM_4);
sccb->sccb_ctl = DVP_SCCB_WRITE_ENABLE | DVP_SCCB_DEVICE_ADDRESS(dev_data->slaveAddress) | DVP_SCCB_REG_ADDRESS(reg_address >> 8) | DVP_SCCB_WDATA_BYTE0(reg_address & 0xFF) | DVP_SCCB_WDATA_BYTE1(value);
}
dvp_sccb_start_transfer(sccb);
exit_exclusive(dev_data);
}
static sccb_data dev0_data = {SYSCTL_CLOCK_DVP, DVP_BASE_ADDR, NULL};
const sccb_driver_t g_sccb_driver_sccb0 = {{&dev0_data, sccb_install, sccb_open, sccb_close}, sccb_get_device};