Files
banban/ql-bootloader/common/src/quec_spi_drv.c
T

614 lines
17 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
/*========================================================================================================================
Copyright (c) 2022 Quectel Wireless Solution, Co., Ltd. All Rights Reserved.
Quectel Wireless Solution Proprietary and Confidential.
========================================================================================================================*/
/*=================================================================
EDIT HISTORY FOR MODULE
This section contains comments describing changes made to the module.
Notice that changes are listed in reverse chronological order.
WHEN WHO WHAT, WHERE, WHY
------------ ------- -------------------------------------------------------------------------------
2022/04/26 Fias.fan 此文件用于LCD的SPI驱动,不建议客户改动,且要注意不同LCD的驱动时序有差别
==========================================================================================================================*/
#include "common.h"
#include "bsp.h"
#include "mfpr_api.h"
#include "quec_ssp_lcd.h"
#include "quec_spi_drv.h"
#include "quec_startup_gpio.h"
//此延时函数,用于LCD-spi相关的延时
static __inline void uudelay(int us)
{
volatile uint32_t i;
for(i=0; i<us*100;i++)
i = i+1;
}
static __inline void mdelay(int ms){
volatile uint32_t i;
for(i=0; i<ms;i++)
uudelay(10);
}
struct s_spi_ctx g_spi_ctx;
void spi_uninit(struct s_spi_ctx *spi_ctx)
{
//ToDo...
}
struct s_spi_ctx* spi_init(uint32_t sclk, struct spi_info *info, int32_t work_mode)
{
int dividor=0;
struct s_spi_ctx *spi_ctx = &g_spi_ctx;
int reg = 0;
LCDLOGI("spi_init +++\r\n");
if(info == NULL || info->timing == NULL){
LCDLOGE("ERROR: spi_init, Invalid param!\r\n");
return NULL;
}
if(LCD_WORK_MODE_INTERRUPT == work_mode)
{
LCDLOGW("WARNING: spi_init, no interrupt mode supported! change to polling mode!\r\n");
work_mode = LCD_WORK_MODE_POLLING;
}
if((info->format != SPI_FORMAT_RGB565) &&
(info->format != SPI_FORMAT_RGB666) && (info->data_lane_num == 1))
{
LCDLOGE("ERROR: spi_init, Format error!\r\n");
return NULL;
}
spi_ctx->cur_path = 0;
spi_ctx->cur_cs = 0;
spi_ctx->sclk = sclk;
spi_ctx->info.line_num = info->line_num;
spi_ctx->info.interface_id = info->interface_id;
spi_ctx->info.data_lane_num = info->data_lane_num;
spi_ctx->info.format = info->format;
spi_ctx->info.device_id = info->device_id;
spi_ctx->info.sample_edge = info->sample_edge;
spi_ctx->info.force_cs = info->force_cs;
spi_ctx->info.endian = info->endian;
spi_ctx->info.timing = info->timing;
spi_ctx->work_mode = work_mode;
spi_ctx->status = SPI_STATUS_INIT;
spi_lcd_init();
LCDLOGI("spi_init ----\r\n");
return spi_ctx;
}
int spi_update(struct s_spi_ctx *spi_ctx, int32_t work_mode)
{
LCDLOGI("spi_update +++\r\n");
if(LCD_WORK_MODE_INTERRUPT == work_mode){
LCDLOGW("WARNING: spi_init, no interrupt mode supported! change to polling mode!\r\n");
work_mode = LCD_WORK_MODE_POLLING;
}
spi_ctx->work_mode = work_mode;
LCDLOGI("spi_update ----\r\n");
return 0;
}
int spi_write_cmd(struct s_spi_ctx *spi_ctx, uint32_t cmd, uint32_t bits)
{
if(NULL == spi_ctx || bits > 32 || 0 == bits){
LCDLOGE("ERROR: spi_write_cmd, Invalid param\r\n");
return -1;
}
if(SPI_STATUS_INIT != spi_ctx->status){
LCDLOGE("ERROR: spi_write_cmd, Invalid mcu status\r\n");
return -1;
}
if((spi_ctx->info.force_cs == 1) && (spi_ctx->cur_cs == 0)){
LCDLOGE("ERROR: spi_write_cmd, Invalid CS status\r\n");
return -1;
}
if (4 == spi_ctx->info.line_num)
{
quec_gpio_set_value(LCD_SPI_RS, 0);
}
quec_gpio_set_value(LCD_SPI_CS, 0);
ssp_write_cmd_data_to_LCD(0, cmd, bits, spi_ctx->info.line_num);
mdelay(5);//20231026 Increase the delay ,slow down cs pull up speed to ensure successful LCD register initialization
quec_gpio_set_value(LCD_SPI_CS, 1);
return 0;
}
int spi_write_data(struct s_spi_ctx *spi_ctx, uint32_t data, uint32_t bits)
{
if(NULL == spi_ctx || bits > 32 || 0 == bits){
LCDLOGE("ERROR: spi_write_data, Invalid param\r\n");
return -1;
}
if(SPI_STATUS_INIT != spi_ctx->status){
LCDLOGE("ERROR: spi_write_data, Invalid mcu status\r\n");
return -1;
}
if((spi_ctx->info.force_cs == 1) && (spi_ctx->cur_cs == 0)){
LCDLOGE("ERROR: spi_write_data, Invalid CS status\r\n");
return -1;
}
if (4 == spi_ctx->info.line_num)
{
quec_gpio_set_value(LCD_SPI_RS, 1);
}
quec_gpio_set_value(LCD_SPI_CS, 0);
ssp_write_cmd_data_to_LCD(1, data, bits, spi_ctx->info.line_num);
mdelay(5);//20231026 Increase the delay ,slow down cs pull up speed to ensure successful LCD register initialization
quec_gpio_set_value(LCD_SPI_CS, 1);
return 0;
}
int spi_read_data(struct s_spi_ctx *spi_ctx, uint32_t cmd, uint32_t cmd_bits,
uint32_t *data, uint32_t data_bits)
{
uint32_t value_and = 0;
value_and = (1 << data_bits) - 1;
if(NULL == spi_ctx || cmd_bits > 32 || data_bits > 32){
LCDLOGE("ERROR: spi_read_data, Invalid param\r\n");
return -1;
}
if(SPI_STATUS_INIT != spi_ctx->status){
LCDLOGE("ERROR: spi_read_data, Invalid mcu status\r\n");
return -1;
}
if((spi_ctx->info.force_cs == 1) && (spi_ctx->cur_cs == 0)){
LCDLOGE("ERROR: spi_read_data, Invalid CS status\r\n");
return -1;
}
if (4 == spi_ctx->info.line_num)
{
quec_gpio_set_value(LCD_SPI_RS, 0);
}
quec_gpio_set_value(LCD_SPI_CS, 0);
ssp_write_cmd_data_to_LCD(0, cmd, cmd_bits, spi_ctx->info.line_num);
mdelay(10);//延时的时间的长短的依据是什么?
ssp_read_data_from_LCD(data, data_bits);
quec_gpio_set_value(LCD_SPI_CS, 1);
return 0;
}
/* 2021-01-27 added for CraneM & CraneL*/
#define TX_FIFO_INT_EN (0x1<<3) //SSP_INT_EN[3]
#define RX_FIFO_INT_EN (0x1<<2) //SSP_INT_EN[2]
#define TIMEOUT_EN (0x1<<1) //SSP_INT_EN[1]
#define PERI_TRAIL_EN (0x1<<0) //SSP_INT_EN[0]
#define SSPSFRM_M (0x0<<4) //SSP_TOP_CTRL[4]
#define SSPSCLK_M (0x0<<3) //SSP_TOP_CTRL[3]
#define FRF_SPI (0x0<<1) //SSP_TOP_CTRL[2:1]
#define SSP_FORMAT FRF_SPI
#define RX_AUTO_FULL_CTRL_EN (0x1<<17) //SSP_FIFO_CTRL[17]
#define FIFO_UNPACKING (0x0<<16) //SSP_FIFO_CTRL[16]
#define RX_THRES_7 (0x7<<5) //SSP_FIFO_CTRL[9:5]
#define TX_THRES_7 (0x7<<0) //SSP_FIFO_CTRL[4:0]
#define SSE_EN (0x1<<0) //SSP_TOP_CTRL[0]
#define SSP0_BASE_ADDRESS 0xD401B000
#define APB_SSP0_CLK_RST 0xd401501c
#define SSP2_BASE_ADDRESS 0xD401C000
#define APB_SSP2_CLK_RST 0xd401504c
#define DW_9BITS (0x8 << 5)
#define SSP_DATAR 0x10
#define SSP_STATUS 0x14
static __inline void reg_write(uint32_t reg, uint32_t val)
{
(*(volatile unsigned long *)(reg)) = val;
}
static __inline void ssp0_reg_write(uint32_t reg, uint32_t val)
{
reg_write(SSP0_BASE_ADDRESS + reg, val);
}
static __inline uint32_t ssp0_reg_read(uint32_t reg)
{
return (*(volatile uint32_t *)(SSP0_BASE_ADDRESS + reg));
}
static __inline void ssp2_reg_write(uint32_t reg, uint32_t val)
{
reg_write(SSP2_BASE_ADDRESS + reg, val);
}
static __inline uint32_t ssp2_reg_read(uint32_t reg)
{
return (*(volatile uint32_t *)(SSP2_BASE_ADDRESS + reg));
}
void spi_dump(void)
{
UINT32 i = 0;
#ifdef _QUEC_LCD_SSP0_ //打开宏则选用SSP0通道
for ( i = 0; i < 12; i++) {
LCDLOGE("---SSP0 start dump---\r\n");
LCDLOGE("SSPI0 offset: 0x%x, value:0x%lx\r\n", i * 4, ssp0_reg_read(i * 4));
}
LCDLOGE("SPS0 offset: 0x54, value:0x%lx\r\n", ssp0_reg_read(0x54));
LCDLOGE("---SSP0 dump end---\r\n");
#elif defined _QUEC_LCD_SSP2_ //默认选用SSP2通道
for ( i = 0; i < 12; i++) {
LCDLOGE("---SSP2 start dump---\r\n");
LCDLOGE("SSPI2 offset: 0x%x, value:0x%lx\r\n", i * 4, ssp2_reg_read(i * 4));
}
LCDLOGE("SPS2 offset: 0x54, value:0x%lx\r\n", ssp2_reg_read(0x54));
LCDLOGE("---SSP2 dump end---\r\n");
#endif
}
void spi_set_clk_rate(unsigned int clk_dev)
{
if (clk_dev >= SPI_CLK_LIMIT) {
LCDLOGE("[Error]: not supported spi clk\r\n");
return;
}
#ifdef _QUEC_LCD_SSP0_ //打开宏则选用SSP0通道
reg_write(APB_SSP0_CLK_RST, 0x3 | (clk_dev << 4));
#elif defined _QUEC_LCD_SSP2_ //默认选用SSP2通道
reg_write(APB_SSP2_CLK_RST, 0x3 | (clk_dev << 4));
#endif
}
int quec_lcd_spi_cs_init(GPIO_PIN_NUMBER_E gpio_pin)
{
if(CHIP_IS_CRANEM || CHIP_IS_CRANEL||CHIP_IS_CRANELRH)
{
ql_gpio_init(gpio_pin,PIN_DIRECTION_OUT,PIN_PULL_DISABLE,PIN_LEVEL_HIGH); //CS--默认状态为高
LCDLOGI("quec_lcd_spi_cs_init GPIO(%d)\r\n",gpio_pin);
return 0;
}
//使用LCD-SPi控制器控制CS引脚
//ToDo
}
void spi_lcd_init(void)
{
uint32_t ssp_top_cfg=0, ssp_fifo_cfg=0;
uint32_t ssp_int_en_cfg=0, ssp_to_cfg=0;
//ToDo----SPI pin的初始化 ?
if(CHIP_IS_CRANEL||CHIP_IS_CRANELRH) //Fias 2022-04-25 EC600M GPIO配置
{
quec_lcd_spi_cs_init(LCD_SPI_CS); //CS
//600MCN_XX
#if defined (__QUECTEL_PROJECT_EC600MCN_XX__) \
|| defined (__QUECTEL_PROJECT_EC600MCN_LA__) || defined (__QUECTEL_PROJECT_EC600MCN_LE__) \
|| defined (__QUECTEL_PROJECT_EC600MCN_LC__)|| defined (__QUECTEL_PROJECT_EC600MCN_LF__)\
|| defined (__QUECTEL_PROJECT_EC600MCN_MC__)
quec_set_gpio_mfprReg(LCD_SPI_CLK,(MFP_PULL_HIGH|MFP_DRIVE_MEDIUM|MFP_AF1)); //CLK
quec_set_gpio_mfprReg(LCD_SPI_DOUT,(MFP_PULL_HIGH|MFP_DRIVE_MEDIUM|MFP_AF1)); //TX
//800MCN_XX
#elif defined (__QUECTEL_PROJECT_EC800MCN_XX__) \
|| defined(__QUECTEL_PROJECT_EC800MCN_GA__) || defined (__QUECTEL_PROJECT_EC800MCN_LA__)\
|| defined (__QUECTEL_PROJECT_EG810MCN_GA__)|| defined (__QUECTEL_PROJECT_EC800MCN_CC__) \
|| defined (__QUECTEL_PROJECT_EC800MCN_NC__)|| defined (__QUECTEL_PROJECT_EC800MCN_MC__)\
|| defined (__QUECTEL_PROJECT_EC800MCN_LF__)|| defined (__QUECTEL_PROJECT_EG810MCN_LA__)\
|| defined (__QUECTEL_PROJECT_EG800PCN_LA__)
quec_set_gpio_mfprReg(LCD_SPI_CLK,(MFP_PULL_HIGH|MFP_DRIVE_MEDIUM|MFP_AF2)); //CLK
quec_set_gpio_mfprReg(LCD_SPI_DOUT,(MFP_PULL_HIGH|MFP_DRIVE_MEDIUM|MFP_AF2)); //TX
//800AK
#elif defined (__QUECTEL_PROJECT_EG800AKCN_XX__)
quec_set_gpio_mfprReg(LCD_SPI_CLK,(MFP_PULL_HIGH|MFP_DRIVE_MEDIUM|MFP_AF1)); //CLK
quec_set_gpio_mfprReg(LCD_SPI_DOUT,(MFP_PULL_HIGH|MFP_DRIVE_MEDIUM|MFP_AF1)); //TX
#else
LCDLOGI("Please configure the project\n");
#endif
}
#ifdef _QUEC_LCD_SSP0_
reg_write(APB_SSP0_CLK_RST, 0x7);
#elif defined _QUEC_LCD_SSP2_ //默认选用SSP2通道
reg_write(APB_SSP2_CLK_RST, 0x7);
#endif
spi_set_clk_rate(LCD_SCLK_READ_FREQ);
ssp_top_cfg = 0x4d00;
ssp_to_cfg = 0x200;
ssp_fifo_cfg = 0x200e7;
ssp_int_en_cfg = 0xf;
#ifdef _QUEC_LCD_SSP0_ //打开宏则选用SSP0通道
ssp0_reg_write(0, ssp_top_cfg);
ssp0_reg_write(0x4, ssp_fifo_cfg);
ssp0_reg_write(0x8, ssp_int_en_cfg);
ssp0_reg_write(0xc, ssp_to_cfg);
ssp0_reg_write(0, ssp_top_cfg | 0x1);
#elif defined _QUEC_LCD_SSP2_ //默认选用SSP2通道
ssp2_reg_write(0, ssp_top_cfg);
ssp2_reg_write(0x4, ssp_fifo_cfg);
ssp2_reg_write(0x8, ssp_int_en_cfg);
ssp2_reg_write(0xc, ssp_to_cfg);
ssp2_reg_write(0, ssp_top_cfg | 0x1);
#endif
}
void ssp_write_cmd_data_to_LCD(uint32_t cmd, uint32_t data,uint32_t data_bits,uint16_t line_num)
{
int reg=0;
if ((data_bits < 1) || (data_bits > 31)) {
LCDLOGE("[Error]: not supported spi data bits\r\n");
return;
}
#ifdef _QUEC_LCD_SSP0_ //打开宏则选用SSP0通道
ssp0_reg_write(0x54, 0x5);
reg = ssp0_reg_read(0) & ~(0x1f << 5);
if (line_num == 3) {
ssp0_reg_write(0, reg | (data_bits << 5));//9 bits
if (!cmd)
ssp0_reg_write(SSP_DATAR, data);
else
ssp0_reg_write(SSP_DATAR, (1 << data_bits) | data);
} else if (line_num == 4) {
ssp0_reg_write(0, reg | ((data_bits - 1) << 5));
ssp0_reg_write(SSP_DATAR, data);
} else
{
LCDLOGE("[ERROR]: not supported spi line number_ssp0_reg!\r\n");
}
mdelay(1);
#elif defined _QUEC_LCD_SSP2_ //默认选用SSP2通道
ssp2_reg_write(0x54, 0x5);
reg = ssp2_reg_read(0) & ~(0x1f << 5);
if (line_num == 3)
{
ssp2_reg_write(0, reg | (data_bits << 5));//9 bits
if (!cmd)
ssp2_reg_write(SSP_DATAR, data);
else
ssp2_reg_write(SSP_DATAR, (1 << data_bits) | data);
}
else if (line_num == 4)
{
ssp2_reg_write(0, reg | ((data_bits - 1) << 5));
ssp2_reg_write(SSP_DATAR, data);
}
else
LCDLOGE("[ERROR]: not supported spi line number!\r\n");
mdelay(1);
#endif
}
void ssp_write_data_to_LCD_Gram(uint16_t* data, uint32_t sum_pixel,uint32_t data_bits, uint16_t line_num)
{
int i=0, cnt=0, reg=0;
if ((data_bits < 1) || (data_bits > 31)) {
LCDLOGE("[Error]: not supported spi data bits\r\n");
return;
}
#ifdef _QUEC_LCD_SSP0_ //打开宏则选用SSP0通道
ssp0_reg_write(0x54, 0x5);
reg = ssp0_reg_read(0) & ~(0x1f << 5);
if (line_num == 3) {
ssp0_reg_write(0, reg | (8 << 5));
} else if (line_num == 4) {
ssp0_reg_write(0, reg | ((data_bits -1) << 5));
}
for (i = 0; i < sum_pixel; i++)
{
cnt = 0;
/* wait for TX not full *///只有当Tx fifo不为满状态才可以继续向Fifo里写数据
while (!(ssp0_reg_read(SSP_STATUS) & (0x1 << 6))) {
if (cnt ++ > 1000000) {
LCDLOGE("ERROR: spi write byte error_ssp0_reg!\r\n");
return;
}
}
if (line_num == 3)
{
if ((data_bits == 16) || (data_bits == 8))
{
ssp0_reg_write(SSP_DATAR, (1 << 8) | (*(data + i) >> 8));
cnt = 0;
while (!(ssp0_reg_read(SSP_STATUS) & (0x1 << 6))) {
if (cnt ++ > 1000000) {
LCDLOGE("ERROR: spi write byte error_ssp0_reg!\r\n");
return;
}
}
ssp0_reg_write(SSP_DATAR, (0x1 << 8) | (*(data + i) & 0xff));
} else
LCDLOGE("spi write byte not support length_ssp0_reg!\r\n");
}
else if (line_num == 4)
{
ssp0_reg_write(SSP_DATAR, *(data + i));
}
else
{
LCDLOGE("ERROR: spi line number error_ssp0_reg!\r\n");
return;
}
}
#elif defined _QUEC_LCD_SSP2_ //默认选用SSP2通道
ssp2_reg_write(0x54, 0x5);
reg = ssp2_reg_read(0) & ~(0x1f << 5);
if (line_num == 3) {
ssp2_reg_write(0, reg | (8 << 5));
} else if (line_num == 4) {
ssp2_reg_write(0, reg | ((data_bits -1) << 5));
}
for (i = 0; i < sum_pixel; i++) {
cnt = 0;
/* wait for TX not full */
while (!(ssp2_reg_read(SSP_STATUS) & (0x1 << 6))) {
if (cnt ++ > 1000000) {
LCDLOGE("ERROR: spi write byte error!\r\n");
return;
}
}
if (line_num == 3) {
if ((data_bits == 16) || (data_bits == 8)) {
ssp2_reg_write(SSP_DATAR, (1 << 8) | (*(data + i) >> 8));
cnt = 0;
while (!(ssp2_reg_read(SSP_STATUS) & (0x1 << 6))) {
if (cnt ++ > 1000000) {
LCDLOGE("ERROR: spi write byte error!\r\n");
return;
}
}
ssp2_reg_write(SSP_DATAR, (0x1 << 8) | (*(data + i) & 0xff));
} else
LCDLOGE("spi write byte not support length!\r\n");
} else if (line_num == 4) {
ssp2_reg_write(SSP_DATAR, *(data + i));
} else {
LCDLOGE("ERROR: spi line number error!\r\n");
return;
}
}
#endif
}
void ssp_read_data_from_LCD(uint32_t *data, uint32_t data_bits)
{
int reg = 0;
int status = 0;
#ifdef _QUEC_LCD_SSP0_ //打开宏则选用SSP0通道
reg = ssp0_reg_read(0) & ~(0x1f << 5);
if ((data_bits < 1) || (data_bits > 31)) {
LCDLOGE("[Error]: not supported spi data bits_ssp0_reg\r\n");
return;
}
if (data_bits > 8)
ssp0_reg_write(0, reg | (data_bits << 5));
else
ssp0_reg_write(0, reg | ((data_bits -1) << 5)); //8 bit
ssp0_reg_write(0x54, 0x7);
ssp0_reg_write(SSP_DATAR, 0x0);
while (((status = ssp0_reg_read(SSP_STATUS)) & (0x1 << 14)) == 0);
*data = ssp0_reg_read(SSP_DATAR);
#elif defined _QUEC_LCD_SSP2_ //默认选用SSP2通道
reg = ssp2_reg_read(0) & ~(0x1f << 5);
if ((data_bits < 1) || (data_bits > 31)) {
LCDLOGE("[Error]: not supported spi data bits\r\n");
return;
}
if (data_bits > 8)
ssp2_reg_write(0, reg | (data_bits << 5));
else
ssp2_reg_write(0, reg | ((data_bits -1) << 5)); //8 bit
ssp2_reg_write(0x54, 0x7);
ssp2_reg_write(SSP_DATAR, 0x0);
while (((status = ssp2_reg_read(SSP_STATUS)) & (0x1 << 14)) == 0);
*data = ssp2_reg_read(SSP_DATAR);
#endif
}
void wait_ssp_write_data_to_LCD_complete(void)
{
int cnt = 0;
#ifdef _QUEC_LCD_SSP0_ //打开宏则选用SSP0通道
/* wait for TX empty */
while ((ssp0_reg_read(SSP_STATUS) & 0xfc0) != 0x40) {
if (cnt++ > 1000000) {
uudelay(1);//20231026Improve the timeout detection time
LCDLOGE("ERROR: spi write byte error_ssp0_reg!\r\n");
return;
}
}
#elif defined _QUEC_LCD_SSP2_ //默认选用SSP2通道
/* wait for TX empty */
while ((ssp2_reg_read(SSP_STATUS) & 0xfc0) != 0x40) {
if (cnt++ > 1000000) {
uudelay(1);//20231026Improve the timeout detection time
LCDLOGE("ERROR: spi write byte error!\r\n");
return;
}
}
#endif
}