W806 SDIO 设备 扩展 荔枝派 V3s IO 使用

全志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"," "};

 

 

  • 1
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 7
    评论
评论 7
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值