全志V3s 不论焊接还是使用很方便,唯一缺点就是IO不够,偶然发现联德盛 W806 竟然自带SDIO 设备接口,当然肯定还有ESP32模块 也是带的(,这里并不适用),选择SDIO 优点是速度快,方便扩展,调试这个W806 简直很要命,资料给的不全,很多时候就是猜测;
官方资料这里不放出来,到出都能找到,这里说一下 《SDIO Fn1地址映射关系》 这个表,
发送buffer 这里 使用的是地址 0x15000 ,注意:也就是 HOST 发送数据到W806 的地址,千万不要使用0x5000 ,0x16000-0x17fff 这里为 W806 发送数据到HOST 的地址,具体设备参见以下程序,
W806 SDIO配置程序 :
程序使用了一部分固定内存,在gcc-csky.ld 中定义 ,这里为SDIO 设备私有地址:
__min_heap_size = 0x18000;
PROVIDE (__ram_end = 0x20041D90);
PROVIDE (__heap_end = __ram_end);
#ifndef SDIO_SALVE_H_
#define SDIO_SALVE_H_
#include "stdint.h"
#define TLS_IO_AB_OFFSET (0x40011400 - 0x40011200)
/** io name */
enum tls_io_name {
WM_IO_PA_00 = 0, /**< gpio a0 */
WM_IO_PA_01, /**< gpio a1 */
WM_IO_PA_02, /**< gpio a2 */
WM_IO_PA_03, /**< gpio a3 */
WM_IO_PA_04, /**< gpio a4 */
WM_IO_PA_05, /**< gpio a5 */
WM_IO_PA_06, /**< gpio a6 */
WM_IO_PA_07, /**< gpio a7 */
WM_IO_PA_08, /**< gpio a8 */
WM_IO_PA_09, /**< gpio a9 */
WM_IO_PA_10, /**< gpio a10 */
WM_IO_PA_11, /**< gpio a11 */
WM_IO_PA_12, /**< gpio a12 */
WM_IO_PA_13, /**< gpio a13 */
WM_IO_PA_14, /**< gpio a14 */
WM_IO_PA_15, /**< gpio a15 */
WM_IO_PB_00, /**< gpio b0 */
WM_IO_PB_01, /**< gpio b1 */
WM_IO_PB_02, /**< gpio b2 */
WM_IO_PB_03, /**< gpio b3 */
WM_IO_PB_04, /**< gpio b4 */
WM_IO_PB_05, /**< gpio b5 */
WM_IO_PB_06, /**< gpio b6 */
WM_IO_PB_07, /**< gpio b7 */
WM_IO_PB_08, /**< gpio b8 */
WM_IO_PB_09, /**< gpio b9 */
WM_IO_PB_10, /**< gpio b10 */
WM_IO_PB_11, /**< gpio b11 */
WM_IO_PB_12, /**< gpio b12 */
WM_IO_PB_13, /**< gpio b13 */
WM_IO_PB_14, /**< gpio b14 */
WM_IO_PB_15, /**< gpio b15 */
WM_IO_PB_16, /**< gpio b16 */
WM_IO_PB_17, /**< gpio b17 */
WM_IO_PB_18, /**< gpio b18 */
WM_IO_PB_19, /**< gpio b19 */
WM_IO_PB_20, /**< gpio b20 */
WM_IO_PB_21, /**< gpio b21 */
WM_IO_PB_22, /**< gpio b22 */
WM_IO_PB_23, /**< gpio b23 */
WM_IO_PB_24, /**< gpio b24 */
WM_IO_PB_25, /**< gpio b25 */
WM_IO_PB_26, /**< gpio b26 */
WM_IO_PB_27, /**< gpio b27 */
WM_IO_PB_28, /**< gpio b28 */
WM_IO_PB_29, /**< gpio b29 */
WM_IO_PB_30, /**< gpio b30 */
WM_IO_PB_31 /**< gpio b31 */
};
#define tls_reg_write32(reg,val) WRITE_REG(*(uint32_t*)(reg),val)
#define tls_reg_read32(reg) READ_REG(*(uint32_t*)(reg))
/*****************************************************************************
* sdio/hspi sram partition
******************************************************************************/
/*see gcc_csky.ld in directory ld/w800*/
extern unsigned int __heap_end;
extern unsigned int __heap_start;
/* HSPI txbuf zone */
#define SLAVE_HSPI_SDIO_ADDR ((unsigned int)(&__heap_end))
/** SDIO interrupt bit definition */
#define SDIO_WP_INT_SRC_CMD_DOWN (1UL<<3)
#define SDIO_WP_INT_SRC_CMD_UP (1UL<<2)
#define SDIO_WP_INT_SRC_DATA_DOWN (1UL<<1)
#define SDIO_WP_INT_SRC_DATA_UP (1UL<<0)
/** Definition of send data descriptor structure */
struct tls_hspi_tx_desc {
volatile uint32_t valid_ctrl;
uint32_t buf_info;
uint32_t buf_addr[3];
uint32_t next_desc_addr;
};
/** Definition of receive data descriptor structure */
struct tls_hspi_rx_desc {
uint32_t valid_ctrl;
uint32_t buf_addr;
uint32_t next_desc_addr;
};
extern void sdio_salve_init(void);
struct buffer_bd{ // 4096 byte
uint32_t buffer0[400];// 400*4 =1600 byte
uint32_t buffer1[400];// 400*4 =1600 byte
uint32_t buffer2[224];// 224*4 =896 byte
};
typedef struct {
struct buffer_bd rx_buffer[3];// 4096*3;
struct tls_hspi_tx_desc wrapper_tx[3]; //6*4*3
struct buffer_bd tx_buffer[3];// 4096*3;
struct tls_hspi_rx_desc wrapper_rx[3];//3*3*3
uint32_t cis0_buffer[8];//4*8 =32 ,,,,,32*4 =128
uint32_t cis1_buffer[32+24];//56*4 =224,,,32*4 =128
uint32_t cmd_buffer[64];// 64*4 =256
}UsrMemDef_Type;// 0x626C
#define SDIO_USER_MEM ((UsrMemDef_Type *)SLAVE_HSPI_SDIO_ADDR)
extern uint16_t sdio_salve_send(uint8_t *data ,uint16_t len);
extern uint16_t sdio_salve_recv(uint8_t* data ,uint16_t rlen_max);
#endif
sdio_slave.c
#include "sdio_salve.h"
#include "wm_hal.h"
#include <stdio.h>
#include <string.h>
#include "FreeRTOS.h"
#include "task.h"
#include "string.h"
#include "wm_osal.h"
struct tls_hspi_rx_desc *curr_rx_desc;
struct tls_hspi_tx_desc *curr_tx_desc ;
#ifndef BIT
#define BIT(x) (1UL << (x))
#endif
static void io_cfg_option4(enum tls_io_name name)
{
uint8_t pin;
uint16_t offset;
if (name >= WM_IO_PB_00){
pin = name - WM_IO_PB_00;
offset = TLS_IO_AB_OFFSET;
}
else{
pin = name;
offset = 0;
}
WRITE_REG(*(uint32_t*)(HR_GPIO_AF_SEL + offset), READ_REG(*(uint32_t*)(HR_GPIO_AF_SEL + offset)) | BIT(pin)); /* gpio function */
WRITE_REG(*(uint32_t*)(HR_GPIO_AF_S1 + offset), READ_REG(*(uint32_t*)(HR_GPIO_AF_S1 + offset)) | BIT(pin));
WRITE_REG(*(uint32_t*)(HR_GPIO_AF_S0 + offset), READ_REG(*(uint32_t*)(HR_GPIO_AF_S0 + offset)) | BIT(pin));
}
//sdio_rxbd内存映射
void hspi_rx_init(UsrMemDef_Type *memp)
{
curr_rx_desc =memp->wrapper_rx;
for(int i=0;i<3;i++){
memp->wrapper_rx[i].valid_ctrl =BIT(31);
memp->wrapper_rx[i].buf_addr =(uint32_t)memp->tx_buffer[i].buffer0;
if(i!=2){
memp->wrapper_rx[i].next_desc_addr =(uint32_t)&memp->wrapper_rx[i+1];
}else{
memp->wrapper_rx[i].next_desc_addr =(uint32_t)&memp->wrapper_rx[0];
}
}
}
void hspi_tx_init(UsrMemDef_Type *memp)
{
curr_tx_desc =memp->wrapper_tx;
for(int i=0;i<3;i++){
memp->wrapper_tx[i].valid_ctrl =0;
memp->wrapper_tx[i].buf_info =0;
memp->wrapper_tx[i].buf_addr[0] =(uint32_t)memp->rx_buffer[i].buffer0;//虽然没有使用3块内存,但是我们预留了4096byte
memp->wrapper_tx[i].buf_addr[1] =0;//(uint32_t)memp->rx_buffer[i].buffer1;
memp->wrapper_tx[i].buf_addr[2] =0;//(uint32_t)memp->rx_buffer[i].buffer2;
if(i!=2){
memp->wrapper_tx[i].next_desc_addr =(uint32_t)&memp->wrapper_tx[i+1];
}else{
memp->wrapper_tx[i].next_desc_addr =(uint32_t)&memp->wrapper_tx[0];
}
}
}
#define TRANS_SIZE_EMAX 256
uint32_t g_form_len=0;
__attribute__((isr)) void SDIO_IRQHandler(void)
{
uint32_t int_src = tls_reg_read32(HR_SDIO_INT_SRC);
if(int_src&SDIO_WP_INT_SRC_DATA_DOWN){//接收到来自HOST 的数据
g_form_len=SDIO_SALVE->SDIO_T_COUNT;//获取SDIO 接收到数据长度
}
tls_reg_write32(HR_SDIO_INT_SRC, 0xf); //clear interrput
printf("int_src =%08x\r\n",int_src);
}
__attribute__((isr)) void HSPI_IRQHandler(void)
{
}
void cis_init(UsrMemDef_Type *memp )
{
uint32_t *CIS0 =(uint32_t*)memp->cis0_buffer;
*CIS0++ =0x000C0221;//为了识别 SDIO 卡,CISTPL_FUNCID 元组应存在于所有 CIS 区域中
*CIS0++ =0x00000422;//功能扩展 ,支持 0800 个块大小字节
*CIS0++ =0x04203208;//,25 Mb/Sec (0x32)
*CIS0++ =0x53470296;// 厂商设备代码 ,0x20 开头
*CIS0++ =0xffffffff;//end//
//5*4 =20 byte
uint32_t *CIS1 =(uint32_t*)memp->cis1_buffer;
*CIS1++ =0x000C0221;//为了识别 SDIO 卡,CISTPL_FUNCID 元组应存在于所有 CIS 区域中
*CIS1++ =0x01012a22;//功能扩展 ,bit[31-24] :0x01:表示设备支持低功耗支持CLK停止功能
*CIS1++ =0x00000020;// 20 表示支持2.0 的SDIO ,序列号32bit,
*CIS1++ =0x00000000;// 可用的CSA空间 32bit ,0x00000000 byte
*CIS1++ =0x08000300;//bit[15-8] 0x03:表示CSA区域只读,不可写,或格式化 800 个块大小
*CIS1++ =0x00FF8000;// ocr 32bit
*CIS1++ =0x010f0a08;//08:8mA ,0a:平均10mA,0f:最大16mA,最小电流:1mA 0x01
*CIS1++ =0x00000101;
*CIS1++ =0x00000000;
*CIS1++ =0x00000000;
*CIS1++ =0x00000000;
*CIS1++ =0x00000000;
/*
struct tuple_user{
uint8_t code;//0x91
uint8_t len;//64+26 =0x5A
u8 TPLSDIO_STD_ID;//0x01
uint8_t TPLSDIO_STD_TYPE; //0x00
uint8_t version[64];
uint32_t up_cmd_start;
uint32_t dn_cmd_start;
uint32_t b_cmd_size;
uint32_t waddr_start;
uint32_t raddr_start;
uint32_t b_addr_size;
};*/
*CIS1++ =0x00015A91;
uint32_t databuf =0;
char version[64] ;
memset(version,0,64);
sprintf(version,"This is HLK-W806 build:%s -%s",__DATE__,__TIME__);
uint8_t *str =(uint8_t*)version;
for(int i=0;i<16;i++,str+=4){
databuf =str[3];databuf<<=8;
databuf |=str[2];databuf<<=8;
databuf |=str[1];databuf<<=8;
databuf |=str[0];
*CIS1++ =databuf;
}
*CIS1++ =0x4000;//上行命令地址
*CIS1++ =0x4000+128;//下行命令地址
*CIS1++ =128;//命令大小
*CIS1++ =0x15000;//接收地址
*CIS1++ =0x16000;//发送地址
*CIS1++ =TRANS_SIZE_EMAX;//一次接收大小,太大也是浪费用不着 max 4096
*CIS1++ =0xffffffff;// stop -1byte
//(20+16)*4=144 byte
}
void sdio_salve_init(void)
{
//初始化引脚map :
io_cfg_option4(WM_IO_PB_06);/*CK*/
io_cfg_option4(WM_IO_PB_07);/*CMD*/
io_cfg_option4(WM_IO_PB_08);/*D0*/
io_cfg_option4(WM_IO_PB_09);/*D1*/
io_cfg_option4(WM_IO_PB_10);/*D2*/
io_cfg_option4(WM_IO_PB_11);/*D3*/
//初始化接收 发送链表
UsrMemDef_Type *memp =SDIO_USER_MEM;
memset((uint8_t*)memp,0,sizeof(UsrMemDef_Type));
hspi_rx_init(memp);
hspi_tx_init(memp);
NVIC_ClearPendingIRQ((IRQn_Type)SDIO_IRQn);
NVIC_EnableIRQ((IRQn_Type)SDIO_IRQn);
/* hspi data down(rx) */
SDIO_WRAPPER->SDIO_TX_BD_ADDR =(uint32_t)memp->wrapper_rx;//SDIO- TX 链接寄存器
SDIO_WRAPPER->SDIO_TX_BD_LINK_EN =0x1;//
SDIO_WRAPPER->SDIO_TX_EN =0x1;//
SDIO_WRAPPER->SDIO_RX_BD_ADDR =(uint32_t)memp->wrapper_tx;//SDIO -RX 链接寄存器
SDIO_WRAPPER->SDIO_RX_BD_LINK_EN =0x1;//
/* hspi cmd down */
tls_reg_write32(HR_SDIO_CMD_ADDR, (uint32_t)memp->cmd_buffer);
tls_reg_write32(HR_SDIO_CMD_SIZE, 256);
tls_reg_write32(HR_SDIO_DOWNCMDVALID, 0x1);
/* enable sdio module register */
tls_reg_write32(HR_SDIO_INT_MASK, 0x00);
cis_init(memp);//初始话 SDIO-CIS 寄存器
uint32_t SDIO_CIS0_ADDR =(uint32_t) memp->cis0_buffer;
uint32_t SDIO_CIS1_ADDR =(uint32_t) memp->cis1_buffer;
//设置CIS0 寄存器指针地址
SDIO_SALVE->CIS0_ADDR =SDIO_CIS0_ADDR-0x01000;
SDIO_SALVE->CIS1_ADDR =SDIO_CIS1_ADDR-0x02000;
printf("HR_SDIO_CIS0 =%08x ," ,SDIO_CIS0_ADDR);
printf("HR_SDIO_CIS1 =%08x \r\n" ,SDIO_CIS1_ADDR);
/* set sdio ready */
#ifdef SPEED_50Mhz
tls_reg_write32(HR_SDIO_PROG, 0x02FD);//high速设备
SDIO_SALVE->CIA_REG =0x06011232;//支持高速 ;
#else //25Mhz
tls_reg_write32(HR_SDIO_PROG, 0x03FD);//低速设备
SDIO_SALVE->CIA_REG =0x02011232;//不支持高速
#endif
SDIO_SALVE->AHB_T_COUNT=TRANS_SIZE_EMAX;//发送大小
SDIO_SALVE->FN1_ENA_REG =0x1;
}
uint16_t sdio_salve_send(uint8_t *data ,uint16_t len)
{
struct tls_hspi_tx_desc *tx_desc = curr_tx_desc;
while (tx_desc->valid_ctrl & BIT(31)){ //等待硬件发送完毕,自动清除该变量
vTaskDelay(500 / portTICK_PERIOD_MS);
}
if(len>TRANS_SIZE_EMAX){ //设置传输尺寸,不可变
len=TRANS_SIZE_EMAX;
}
memcpy((char*)tx_desc->buf_addr[0],data,len);
tx_desc->buf_info = len << 12; //设置传输大小
tx_desc->valid_ctrl = (1UL << 31);
tls_reg_write32(HR_SDIO_RXEN, 0x01);//启动传输命令
tx_desc = (struct tls_hspi_tx_desc *) tx_desc->next_desc_addr;//设置下次一次传输地址
curr_tx_desc = tx_desc;
return len;
}
uint16_t sdio_salve_recv(uint8_t* data ,uint16_t rlen_max)
{
uint16_t len=0;
struct tls_hspi_rx_desc *rx_desc = curr_rx_desc;
if(g_form_len>0){//获取SDIO 接收到数据长度
len =g_form_len;
g_form_len=0;
memcpy(data,(char*)rx_desc->buf_addr,TRANS_SIZE_EMAX);
rx_desc->valid_ctrl = BIT(31);
/* 设置hspi/sdio tx enable寄存器,让sdio硬件知道有可用的tx descriptor */
SDIO_WRAPPER->SDIO_TX_EN =0x1;
rx_desc = (struct tls_hspi_rx_desc *) rx_desc->next_desc_addr; //next ..
curr_rx_desc =rx_desc;
}
return len;
}
V3s SDIO 驱动部分 使用 Git 上俄罗斯网友的参考 ,GitHub - balmerdx/sdio_linux_fpga: Communication channel from FPGA (Alterra EP4CE10) and Linux (Lichee PI Allwinner V3S)
经过修改如下:
/*
* Author: Dmitriy 'Balmer' Poskryakov
* Created: 2020
* 2020 Balmer - use for own SDIO UART
*
* Based on Nicolas Pitre sdio_uart.c
*
* Based on drivers/serial/8250.c and drivers/serial/serial_core.c
* by Russell King.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or (at
* your option) any later version.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/sched.h>
#include <linux/mutex.h>
#include <linux/seq_file.h>
#include <linux/circ_buf.h>
#include <linux/kfifo.h>
#include <linux/slab.h>
#include <linux/cdev.h>
#include <linux/mmc/core.h>
#include <linux/mmc/card.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/uaccess.h>
#include "sdio_uart_user.h"
#define MY_REG_RX 0
#define MY_REG_TX 0
#define MY_REG_STATUS 1
int debug = 0;
module_param(debug, int, S_IRUGO);
//data ready
#define BIT_STATUS_DR 0x01
//transmit empty
#define BIT_STATUS_THRE 0x01
#define UART_NR 4 /* Number of UARTs this driver can handle */
#define FIFO_SIZE PAGE_SIZE
#define WAKEUP_CHARS 256
struct sdio_uart_port {
unsigned int index;
struct sdio_func *func;
struct mutex func_lock;
struct task_struct *in_sdio_uart_irq;
spinlock_t write_lock; //actually read/write lock
struct kfifo read_fifo;
struct device *file_dev;
struct cdev c_dev;
//
uint32_t up_cmd_start;
uint32_t dn_cmd_start;
uint32_t b_cmd_size;
uint32_t waddr_start;
uint32_t raddr_start;
uint32_t b_addr_size;
};
static dev_t balmer_dev_t = 0;
struct class *balmer_class = 0;
static struct sdio_uart_port *sdio_uart_table[UART_NR];
static DEFINE_SPINLOCK(sdio_uart_table_lock);
static void sdio_uart_port_remove(struct sdio_uart_port *port)
{
struct sdio_func *func;
spin_lock(&sdio_uart_table_lock);
sdio_uart_table[port->index] = NULL;
spin_unlock(&sdio_uart_table_lock);
/*
* We're killing a port that potentially still is in use by
* the tty layer. Be careful to prevent any further access
* to the SDIO function and arrange for the tty layer to
* give up on that port ASAP.
* Beware: the lock ordering is critical.
*/
mutex_lock(&port->func_lock);
func = port->func;
sdio_claim_host(func);
port->func = NULL;
mutex_unlock(&port->func_lock);
sdio_release_irq(func);
sdio_disable_func(func);
sdio_release_host(func);
}
static int sdio_uart_claim_func(struct sdio_uart_port *port)
{
mutex_lock(&port->func_lock);
if (unlikely(!port->func)) {
mutex_unlock(&port->func_lock);
return -ENODEV;
}
if (likely(port->in_sdio_uart_irq != current))
sdio_claim_host(port->func);
mutex_unlock(&port->func_lock);
return 0;
}
static inline void sdio_uart_release_func(struct sdio_uart_port *port)
{
if (likely(port->in_sdio_uart_irq != current))
sdio_release_host(port->func);
}
static inline u8 sdio_in(struct sdio_uart_port *port, int offset)
{
u8 c;
c = sdio_readb(port->func, offset, NULL);
return c;
}
static inline void sdio_out(struct sdio_uart_port *port, int offset, u8 value)
{
sdio_writeb(port->func, value, offset, NULL);
}
#define FUNC1_INT1 0x04 //bit0
#define FUNC1_RLEN_BIT_H7 0x1c //bit6:0
#define FUNC1_RLEN_BIT_L5 0x1d //bit7:3
/*
* This handles the interrupt from one port.
*/
static void sdio_uart_irq(struct sdio_func *func)
{
uint8_t *data;
uint16_t temp=0;
struct sdio_uart_port *port = sdio_get_drvdata(func);
/*
* In a few places sdio_uart_irq() is called directly instead of
* waiting for the actual interrupt to be raised and the SDIO IRQ
* thread scheduled in order to reduce latency. However, some
* interaction with the tty core may end up calling us back
* (serial echo, flow control, etc.) through those same places
* causing undesirable effects. Let's stop the recursion here.
*/
if (unlikely(port->in_sdio_uart_irq == current))
return;
port->in_sdio_uart_irq = current;
temp =sdio_readb(port->func, FUNC1_INT1, NULL);//see HLK-W801 kit ,SDIO DEV
if(temp&0x1){
// temp =sdio_readb(port->func, FUNC1_RLEN_BIT_H7, NULL);//read this auto clear interrput
// temp =(temp&0x7f)<<5;
// temp|=0x1f&(sdio_readb(port->func, FUNC1_RLEN_BIT_L5, NULL)>>3);
sdio_writeb(port->func, 0x1, FUNC1_INT1, NULL);
data =(char *)kzalloc(port->b_addr_size, GFP_KERNEL);
sdio_memcpy_fromio(port->func, data, port->raddr_start, port->b_addr_size);
kfifo_in_locked(&port->read_fifo, data, port->b_addr_size, &port->write_lock);
kfree(data);
}
port->in_sdio_uart_irq = NULL;
printk("int");
}
/*
static int sdio_receive_inline(struct sdio_func *func, char* buf, int size)
{
struct sdio_uart_port *port = sdio_get_drvdata(func);
int ret;
unsigned int addr = port->raddr_start;
if (unlikely(port->in_sdio_uart_irq == current))
return 0;
port->in_sdio_uart_irq = current;
// ret = sdio_readsb(port->func, buf, addr, size);
ret = sdio_memcpy_fromio(port->func, buf, addr, size);
port->in_sdio_uart_irq = NULL;
return (ret==0)?size:0;
}
*/
static int sdio_write_inline(struct sdio_func *func, const char* buf, int size)
{
struct sdio_uart_port *port = sdio_get_drvdata(func);
int ret;
unsigned int addr = port->waddr_start;
if (unlikely(port->in_sdio_uart_irq == current))
return 0;
port->in_sdio_uart_irq = current;
ret = sdio_memcpy_toio(port->func, addr, (char*)buf, size);//write addr add+++
port->in_sdio_uart_irq = NULL;
return (ret==0)?size:0;
}
static int sdio_uart_activate(struct sdio_uart_port *port)
{
int ret;
kfifo_reset(&port->read_fifo);
ret = sdio_uart_claim_func(port);
if (ret)
return ret;
ret = sdio_enable_func(port->func);
if (ret)
goto err1;
ret = sdio_claim_irq(port->func, sdio_uart_irq);// Register interrupt
if (ret)
goto err2;
// Kick the IRQ handler once while we're still holding the host lock
sdio_uart_irq(port->func);
sdio_uart_release_func(port);
if(debug)
printk("SDIO Driver: sdio_uart_activate complete\n");
return 0;
err2:
sdio_disable_func(port->func);
err1:
sdio_uart_release_func(port);
return ret;
}
static void sdio_uart_shutdown(struct sdio_uart_port *port)
{
int ret;
ret = sdio_uart_claim_func(port);
if (ret)
return;
sdio_release_irq(port->func);
sdio_disable_func(port->func);
sdio_uart_release_func(port);
}
static int sdio_uart_write(struct sdio_uart_port *port, const char *buf,
int count)
{
int ret;
if (!port->func){
return -ENODEV;
}
ret = sdio_uart_claim_func(port);
if (!ret) {
ret = sdio_write_inline(port->func, buf, count);
sdio_uart_release_func(port);
}
return ret;
}
/*
static int sdio_uart_read(struct sdio_uart_port *port, char *buf, int count)
{
int ret;
if (!port->func){
return -ENODEV;
}
ret = sdio_uart_claim_func(port);
if (!ret){
ret = sdio_receive_inline(port->func, buf, count);
sdio_uart_release_func(port);
}
return ret;
}
*/
/*
static int sdio_uart_write_room(struct sdio_uart_port *port)
{
return FIFO_SIZE - kfifo_len(&port->xmit_fifo);
}
*/
static int sdio_file_open(struct inode *inode, struct file *file)
{
struct sdio_uart_port *port;
if(debug)
printk("SDIO Driver: open()\n");
port = container_of(inode->i_cdev, struct sdio_uart_port, c_dev);
file->private_data = port;
return sdio_uart_activate(port);
}
static int sdio_file_close(struct inode *inode, struct file *file)
{
struct sdio_uart_port *port;
port = container_of(inode->i_cdev, struct sdio_uart_port, c_dev);
if(debug)
printk("SDIO Driver: close() portidx=%u\n", port->index);
sdio_uart_shutdown(port);
return 0;
}
static ssize_t sdio_file_read(struct file *file, char __user *buf, size_t len, loff_t *off)
{
char *data;
struct sdio_uart_port *port = file->private_data;
ssize_t read_size = 0;
if(kfifo_len(&port->read_fifo)==0){
return 0;
}
data =(char *)kzalloc(len, GFP_KERNEL);
if(data){
read_size = kfifo_out_locked(&port->read_fifo, data, len, &port->write_lock);
// read_size =sdio_uart_read(port,data,len);
if(read_size!=0){
if (copy_to_user(buf, data, read_size)!=0){
read_size =-EFAULT;
}
}
kfree(data);
}
return read_size;
}
static ssize_t sdio_file_write(
struct file *file, const char __user *buf, size_t len, loff_t *off)
{
struct sdio_uart_port *port = file->private_data;
ssize_t write_size = 0;
char *data =kzalloc(len, GFP_KERNEL);
if(copy_from_user(data, buf, len)==0){
write_size=sdio_uart_write(port, data, len);
}
kfree(data);
return write_size;
}
static long sdio_file_ioctl (
struct file *file, unsigned int cmd, unsigned long arg)
{
struct sdio_uart_port *port = file->private_data;
void __user *uarg = (void __user *) arg;
long rc = -EFAULT;
struct USER_SDIO_CMD52 user_cmd52;
int ret;
int func_number =0;
switch(cmd){
case USER_SDIO_CMD_b: //cmd52
ret=copy_from_user(&user_cmd52,uarg, 12);
if(ret){return -EFAULT;}
ret = sdio_uart_claim_func(port);
if (!ret) {
func_number =port->func->num;
port->func->num =user_cmd52.w_r_flag&BIT(31) ?1:0;
if((user_cmd52.w_r_flag&0x1)==1){
sdio_writeb(port->func, user_cmd52.value, user_cmd52.s_addr, &ret);
}else if((user_cmd52.w_r_flag&0x1)==0){
user_cmd52.value =sdio_readb(port->func,user_cmd52.s_addr,&ret);
}else{
rc = -EFAULT;
ret =1;
}
port->func->num =func_number;//back
if(ret!=0){
rc = -EFAULT;
}else{
ret =copy_to_user(uarg,&user_cmd52,12);
if(ret){rc = -EFAULT;}
else{
rc=0;
}
}
sdio_uart_release_func(port);
}
break;
case USER_SDIO_CMD_s:
ret=copy_from_user(&user_cmd52,uarg, 12);
if(ret){return -EFAULT;}
if(user_cmd52.s_addr==0x1){
port->waddr_start =user_cmd52.value;
rc=0;
}else if(user_cmd52.s_addr==0x2){
port->raddr_start =user_cmd52.value;
rc=0;
}else{
rc=-EFAULT;
}
break;
default:
rc = -ENOSYS;
break;
}
return rc;
}
static const struct file_operations sdio_uart_proc_fops = {
.owner = THIS_MODULE,
.open = sdio_file_open,
.release = sdio_file_close,
.read = sdio_file_read,
.write = sdio_file_write,
.unlocked_ioctl = sdio_file_ioctl,
};
static int sdio_init(struct sdio_uart_port *port)
{
struct sdio_func *func =port->func;
int err;
/* 3 1. init SDIO bus */
sdio_claim_host(func);
err = sdio_enable_func(func);
if (err) {
printk("%s: sdio_enable_func FAIL(%d)!\n", __func__, err);
goto release;
}
err = sdio_set_block_size(func, port->b_addr_size);//set transmit byte
if (err) {
printk("%s: sdio_set_block_size FAIL(%d)!\n", __func__, err);
goto release;
}
release:
sdio_release_host(func);
return err;
}
/*
struct tuple_user{
uint8_t code;//0x91
uint8_t len;//64+26 =0x5A
u8 TPLSDIO_STD_ID;//0x01
uint8_t TPLSDIO_STD_TYPE; //0x00
uint8_t version[64];
uint32_t up_cmd_start;
uint32_t dn_cmd_start;
uint32_t b_cmd_size;
uint32_t waddr_start;
uint32_t raddr_start;
uint32_t b_addr_size;
};*/
#define little_endian_32bit(x) \
(uint32_t)x[3]<<24|\
(uint32_t)x[2]<<16|\
(uint32_t)x[1]<<8|\
(uint32_t)x[0]
static int find_tuple(struct sdio_uart_port *port)
{
uint8_t *data=0;
char str[64];
struct sdio_func_tuple *tpl =port->func->tuples;
for (; tpl; tpl = tpl->next) {
//printk("tpl->code =%02x" ,tpl->code);
if (tpl->code != 0x91)//We have customized the SDIO type here
continue;
if (tpl->size < 88)
continue;
if (tpl->data[1] == 0) /* SUBTPL_SIOREG */
break;
}
if (!tpl) {
printk("%s: can't find tuple 0x91 subtuple 0 for UART class\n",
sdio_func_id(port->func));
return -EINVAL;
}
data =&tpl->data[2];
memcpy(str,data,64); str[63] =0;
if(debug)
printk("%s: version :%s",sdio_func_id(port->func),str);
data+=64;
port->up_cmd_start =little_endian_32bit(data);data+=4;
port->dn_cmd_start =little_endian_32bit(data);data+=4;
port->b_cmd_size =little_endian_32bit(data);data+=4;
port->waddr_start =little_endian_32bit(data);data+=4;
port->raddr_start =little_endian_32bit(data);data+=4;
port->b_addr_size =little_endian_32bit(data);data+=4;
if(debug){
printk("up_cmd_start =%08x ,dn_cmd_start =%08x ",port->up_cmd_start,port->dn_cmd_start);
printk("b_cmd_size =%08x ,waddr_start =%08x",port->b_cmd_size,port->waddr_start);
printk("raddr_start =%08x ,b_addr_size =%08x",port->raddr_start,port->b_addr_size);
printk("sdio:func->number =%d ",port->func->num);
}
return 0;
}
static int sdio_uart_probe(struct sdio_func *func,
const struct sdio_device_id *id)
{
struct sdio_uart_port *port;
int ret,index;
dev_t dev_idx;
if(debug)
printk("sdio_uart_probe start");
port = kzalloc(sizeof(struct sdio_uart_port), GFP_KERNEL);
if (!port)
return -ENOMEM;
if (func->class == SDIO_CLASS_UART) {
printk("%s: balmer own driver init UART over SDIO\n",
sdio_func_id(func));
} else {
ret = -EINVAL;
goto err1;
}
port->func = func;
if(0!=find_tuple(port)){
ret = -EINVAL;
goto err1;
}
sdio_set_drvdata(func, port);//join
mutex_init(&port->func_lock);
// mutex_init(&port->read_lock);
// mutex_lock(&port->read_lock);
// mutex_unlock(&port->read_lock);
spin_lock(&sdio_uart_table_lock);
ret=1;
for (index = 0; index < UART_NR; index++) {
if (!sdio_uart_table[index]) {
port->index = index;
sdio_uart_table[index] = port;
ret = 0;
break;
}
}
spin_unlock(&sdio_uart_table_lock);
if(ret){
printk("%s :sdio_uart_table run out ",__func__);
goto err1;
}
if(0!=sdio_init(port)){
printk("%s: initialize SDIO Failed!\n", __func__);
goto err1;
}
spin_lock_init(&port->write_lock);
if (kfifo_alloc(&port->read_fifo, port->b_addr_size*3, GFP_KERNEL)){
goto err1;
}
dev_idx = balmer_dev_t+port->index;
port->file_dev = device_create(balmer_class, NULL, dev_idx, NULL, "balmerSDIO%d", port->index);
if (IS_ERR(port->file_dev)){
sdio_uart_port_remove(port);
ret = PTR_ERR(port->file_dev);
printk("%s: device_create Failed! ret =%d\n", __func__,ret);
goto err1;
}
cdev_init(&port->c_dev, &sdio_uart_proc_fops);
ret = cdev_add(&port->c_dev, dev_idx, 1);
if (ret) {
cdev_del(&port->c_dev);
goto err2;
ret = -EINVAL;
}else{
ret=0;
}
if(debug)
printk("sdio_uart_probe all ok");
return ret;
err2:
device_destroy(balmer_class, dev_idx);
err1:
kfree(port);
sdio_set_drvdata(func, NULL);
return ret;
}
static void sdio_uart_remove(struct sdio_func *func)
{
struct sdio_uart_port *port = sdio_get_drvdata(func);
cdev_del(&port->c_dev);
kfifo_free(&port->read_fifo);
device_destroy(balmer_class, balmer_dev_t+port->index);
sdio_uart_port_remove(port);
kfree(port);
if(debug)
printk("sdio_uart_remove complete");
port =NULL;
}
static const struct sdio_device_id sdio_uart_ids[] = {
{ SDIO_DEVICE_CLASS(SDIO_CLASS_UART) },
{ /* end: all zeroes */ },
};
MODULE_DEVICE_TABLE(sdio, sdio_uart_ids);
static struct sdio_driver sdio_uart_driver = {
.probe = sdio_uart_probe,
.remove = sdio_uart_remove,
.name = "sdio_uart",
.id_table = sdio_uart_ids,
};
static int __init sdio_uart_init(void)
{
int ret;
if(debug)
printk("sdio_uart_init start");
if ((ret = alloc_chrdev_region(&balmer_dev_t, 0, 1, "balmer_dev_t")) < 0)
return ret;
balmer_class = class_create(THIS_MODULE, "balmer_sdio");
if (IS_ERR(balmer_class)){
ret = PTR_ERR(balmer_class);
err2:
unregister_chrdev_region(balmer_dev_t, UART_NR);
balmer_dev_t = 0;
}else{
ret = sdio_register_driver(&sdio_uart_driver);
if (ret){
class_destroy(balmer_class);
balmer_class = 0;
goto err2;
}
}
if(debug)
printk("sdio_uart_init =%d" ,ret);
return ret;
}
static void __exit sdio_uart_exit(void)
{
if(balmer_class){
class_destroy(balmer_class);
balmer_class = 0;
}
if(balmer_dev_t){
unregister_chrdev_region(balmer_dev_t, UART_NR);
balmer_dev_t = 0;
}
sdio_unregister_driver(&sdio_uart_driver);
if(debug)
printk("sdio_uart_exit ");
}
module_init(sdio_uart_init);
module_exit(sdio_uart_exit);
//MODULE_AUTHOR("Nicolas Pitre");
MODULE_AUTHOR("horli.2022-4-12");
MODULE_LICENSE("GPL");
实测效果:
测试代码:
#include <stdio.h>
#include <string.h>
#include <fcntl.h>
#include <termios.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ioctl.h>
#include "cmd.h"
#include "sdio_uart_user.h"
int do_help(struct tty *ptty, cmdt_list *pcmdt_list, int list_size)
{
int all;
char *cmdname = NULL;
cmdt **pcmdt;
int array_size;
int i, j;
if(ptty->argc >= 2)
{
cmdname = ptty->argv[1];
all = 0;
}
else
{
all = 1;
}
for(i = 0; i < list_size; i++)
{
array_size = pcmdt_list[i].cmdt_size;
pcmdt = pcmdt_list[i].pcmdt;
for(j = 0; j < array_size; j++)
{
if(all)
{
ptty->printf("%-20s - %s\r\n", pcmdt[j]->name, pcmdt[j]->help_t);
}
else
{
if(strcmp(cmdname, pcmdt[j]->name) == 0)
{
ptty->printf("\r\n%s\r\n", pcmdt[j]->help_f);
return 0;
}
}
}
}
ptty->printf("\r\n");
return 0;
}
int do_main_help(struct tty *ptty)
{
cmdt_list *pcmdt_list;
int list_size;
int rt;
pcmdt_list = cmd_main_list();
list_size = cmd_main_listlen();
rt = do_help(ptty, pcmdt_list, list_size);
return rt;
}
int do_clear(struct tty *ptty)
{
ptty->printf("\033[2J\033[0;0H");
return 0;
}
cmdt cmd_main_help = {"help", do_main_help, "The help information about the command is displayed",
"help: Displays all current commands and their introduction\r\n"
"help cmd:Displays all current commands and their introduction\r\n",
"[name]"};
cmdt cmd_clear = {"clear", do_clear, "cls",
"clear: Clear screen (not supported by some terminals)\r\n"," "};
int do_cmdset(struct tty *ptty)
{
struct USER_SDIO_CMD52 cmd52;
int func_num =0;
if(ptty->argc>2){
if(strcmp(ptty->argv[1],"w")==0){
cmd52.s_addr =1;
}else if(strcmp(ptty->argv[1],"r")==0){
cmd52.s_addr =2;
}else{
ptty->printf("undefine cmd!\r\n");
return 0;
}
cmd52.value = strtoul(ptty->argv[2],NULL,16);
}else{
ptty->printf("undefine cmd!\r\n");
return 0;
}
int *fd = (int *)ptty->vpt;
if(ioctl(*fd,USER_SDIO_CMD_s,&cmd52)==-1){
ptty->printf(" ioctl fail !\r\n");
}else{
ptty->printf("cmd52 %s s_addr=0x%08x \r\n",ptty->argv[1],cmd52.value);
}
return 0;
}
cmdt cmd_cmdset = {"cmdset", do_cmdset, "cmdset [w/r] [data{5000-5FFF,6000-6FFF,}]",
"cmdset: write SDIO w/r addr start\r\n"," "};
int do_cmd52w(struct tty *ptty)
{
struct USER_SDIO_CMD52 cmd52;
int func_num =0;
if(ptty->argc>3){
func_num = atoi(ptty->argv[1]);
cmd52.s_addr =strtoul(ptty->argv[2],NULL,16);
cmd52.value = strtoul(ptty->argv[3],NULL,16);
}else{
ptty->printf("undefine cmd!\r\n");
return 0;
}
cmd52.w_r_flag=1;
cmd52.w_r_flag|=func_num&0x1?(1<<31):0;//func
int *fd = (int *)ptty->vpt;
if(ioctl(*fd,USER_SDIO_CMD_b,&cmd52)==-1){
ptty->printf("1 ioctl fail !\r\n");
}else{
ptty->printf("1 cmd52 addr=0x%08x ,data=0x%02x \r\n",cmd52.s_addr,cmd52.value);
}
return 0;
}
cmdt cmd_cmd52w = {"cmd52.w", do_cmd52w, "cmd52.w [func{0-1}] [addr{00000-1FFFF}] [data{00-FF}]",
"cmd52.w: write function 1 SDIO byte\r\n"," "};
int do_cmd52r(struct tty *ptty)
{
struct USER_SDIO_CMD52 cmd52;
int func_num =0;
if(ptty->argc>2){
func_num = atoi(ptty->argv[1]);
cmd52.s_addr =strtoul(ptty->argv[2],NULL,16);
}else{
ptty->printf("undefine cmd!\r\n");
return 0;
}
cmd52.w_r_flag=0;
cmd52.w_r_flag|=func_num&0x1?(1<<31):0;//func
int *fd = (int *)ptty->vpt;
if(ioctl(*fd,USER_SDIO_CMD_b,&cmd52)==-1){
ptty->printf("r ioctl fail !\r\n");
}else{
ptty->printf("r cmd52 addr=0x%08x ,data=0x%02x \r\n",cmd52.s_addr,cmd52.value);
}
return 0;
}
cmdt cmd_cmd52r = {"cmd52.r", do_cmd52r, "cmd52.r [func{0-1}] [addr{00000-1FFFF}]",
"cmd52.r: read function 1 SDIO byte\r\n"," "};
int do_sdiow(struct tty *ptty)
{
uint8_t start_data =0;
uint16_t length =0;
if(ptty->argc>2){
start_data =strtoul(ptty->argv[1],NULL,16);
length =strtoul(ptty->argv[2],NULL,16);
}else{
ptty->printf("undefine cmd!\r\n");
return 0;
}
uint8_t *write_buffer =malloc(4096);
for(int i=0;i<4096;i++){
write_buffer[i]=(start_data+i)%0xff;
}
int *fd = (int *)ptty->vpt;
uint16_t bytes_len = write(*fd, write_buffer, length);
free(write_buffer);
ptty->printf("sdio write ,start=0x%02x ,wlen=0x%02x \r\n",start_data,bytes_len);
return 0;
}
cmdt cmd_sdiow = {"sdio.w", do_sdiow, "sdio.w [start{00-FF}] [length{1-4096}]",
"sdio.w: write function 1 SDIO n byte\r\n"," "};
int do_sdior(struct tty *ptty)
{
uint16_t length =0;
if(ptty->argc>1){
length =strtoul(ptty->argv[1],NULL,16);
}else{
ptty->printf("undefine cmd!\r\n");
return 0;
}
uint8_t *read_buffer =malloc(4096);
int *fd = (int *)ptty->vpt;
uint16_t bytes_len = read(*fd, read_buffer, length);
ptty->printf("sdio read ,rlen=0x%04x => list 32byte\r\n",bytes_len);
uint8_t *p =read_buffer;
for(int i=0;i<32;i++){
ptty->printf("%02x ",*p++);
}
free(read_buffer);
return 0;
}
cmdt cmd_sdior = {"sdio.r", do_sdior, "sdio.r [length{1-4096}]",
"sdio.r: read function 1 SDIO n byte\r\n"," "};