Ejemplo n.º 1
0
/*****************************************************************************
* FUNCTION
*  hal_dma_get_ava_room
* DESCRIPTION
*  get tx available room
* PARAMETERS
* p_dma_info   [IN]        pointer to BTIF dma channel's information
* RETURNS
*  available room  size
*****************************************************************************/
int hal_dma_get_ava_room(P_MTK_DMA_INFO_STR p_dma_info)
{
	int i_ret = -1;
	unsigned long base = p_dma_info->base;

/*read vFIFO's left size*/
	i_ret = BTIF_READ32(TX_DMA_VFF_LEFT_SIZE(base));
	BTIF_DBG_FUNC("DMA tx ava room (%d).\n", i_ret);
	if (0 == i_ret)
		BTIF_INFO_FUNC("DMA tx vfifo is full.\n");

	return i_ret;
}
static int hal_tx_dma_dump_reg(P_MTK_DMA_INFO_STR p_dma_info,
			       ENUM_BTIF_REG_ID flag)
{
	int i_ret = -1;
	unsigned int base = p_dma_info->base;
	unsigned int int_flag = 0;
	unsigned int enable = 0;
	unsigned int stop = 0;
	unsigned int flush = 0;
	unsigned int wpt = 0;
	unsigned int rpt = 0;
	unsigned int int_buf = 0;
	unsigned int valid_size = 0;
	/*unsigned long irq_flag = 0;*/

	/*spin_lock_irqsave(&(g_clk_cg_spinlock), irq_flag);*/
	if (0 == clock_is_on(MTK_BTIF_APDMA_CLK_CG)) {
		/*spin_unlock_irqrestore(&(g_clk_cg_spinlock), irq_flag);*/
		BTIF_ERR_FUNC("%s: clock is off, this should never happen!!!\n",
			      __FILE__);
		return i_ret;
	}

	int_flag = BTIF_READ32(TX_DMA_INT_FLAG(base));
	enable = BTIF_READ32(TX_DMA_EN(base));
	stop = BTIF_READ32(TX_DMA_STOP(base));
	flush = BTIF_READ32(TX_DMA_FLUSH(base));
	wpt = BTIF_READ32(TX_DMA_VFF_WPT(base));
	rpt = BTIF_READ32(TX_DMA_VFF_RPT(base));
	int_buf = BTIF_READ32(TX_DMA_INT_BUF_SIZE(base));
	valid_size = BTIF_READ32(TX_DMA_VFF_VALID_SIZE(base));
	/*spin_unlock_irqrestore(&(g_clk_cg_spinlock), irq_flag);*/

	BTIF_INFO_FUNC("DMA's clock is on\n");
	BTIF_INFO_FUNC("Tx DMA's base address: 0x%x\n", base);

	if (REG_TX_DMA_ALL == flag) {
		BTIF_INFO_FUNC("TX_EN(:0x%x\n", enable);
		BTIF_INFO_FUNC("INT_FLAG:0x%x\n", int_flag);
		BTIF_INFO_FUNC("TX_STOP:0x%x\n", stop);
		BTIF_INFO_FUNC("TX_FLUSH:0x%x\n", flush);
		BTIF_INFO_FUNC("TX_WPT:0x%x\n", wpt);
		BTIF_INFO_FUNC("TX_RPT:0x%x\n", rpt);
		BTIF_INFO_FUNC("INT_BUF_SIZE:0x%x\n", int_buf);
		BTIF_INFO_FUNC("VALID_SIZE:0x%x\n", valid_size);
		BTIF_INFO_FUNC("INT_EN:0x%x\n",
			       BTIF_READ32(TX_DMA_INT_EN(base)));
		BTIF_INFO_FUNC("TX_RST:0x%x\n", BTIF_READ32(TX_DMA_RST(base)));
		BTIF_INFO_FUNC("VFF_ADDR:0x%x\n",
			       BTIF_READ32(TX_DMA_VFF_ADDR(base)));
		BTIF_INFO_FUNC("VFF_LEN:0x%x\n",
			       BTIF_READ32(TX_DMA_VFF_LEN(base)));
		BTIF_INFO_FUNC("TX_THRE:0x%x\n",
			       BTIF_READ32(TX_DMA_VFF_THRE(base)));
		BTIF_INFO_FUNC("W_INT_BUF_SIZE:0x%x\n",
			       BTIF_READ32(TX_DMA_W_INT_BUF_SIZE(base)));
		BTIF_INFO_FUNC("LEFT_SIZE:0x%x\n",
			       BTIF_READ32(TX_DMA_VFF_LEFT_SIZE(base)));
		BTIF_INFO_FUNC("DBG_STATUS:0x%x\n",
			       BTIF_READ32(TX_DMA_DEBUG_STATUS(base)));
		i_ret = 0;
	} else {
		BTIF_WARN_FUNC("unknown flag:%d\n", flag);
	}
	BTIF_INFO_FUNC("tx dma %s\n", (enable & DMA_EN_BIT) &&
		       (!(stop && DMA_STOP_BIT)) ? "enabled" : "stoped");
	BTIF_INFO_FUNC("data in tx dma is %s sent by HW\n",
		       ((wpt == rpt) &&
			(int_buf == 0)) ? "completely" : "not completely");

	return i_ret;
}
/*****************************************************************************
* FUNCTION
*  hal_dma_send_data
* DESCRIPTION
*  send data through btif in DMA mode
* PARAMETERS
* p_dma_info   [IN]        pointer to BTIF dma channel's information
* p_buf     [IN]        pointer to rx data buffer
* max_len  [IN]        tx buffer length
* RETURNS
*  0 means success, negative means fail
*****************************************************************************/
int hal_dma_send_data(P_MTK_DMA_INFO_STR p_dma_info,
		      const unsigned char *p_buf, const unsigned int buf_len)
{
	unsigned int i_ret = -1;
	unsigned int base = p_dma_info->base;
	P_DMA_VFIFO p_vfifo = p_dma_info->p_vfifo;
	unsigned int len_to_send = buf_len;
	unsigned int ava_len = 0;
	unsigned int wpt = 0;
	unsigned int last_wpt_wrap = 0;
	unsigned int vff_size = 0;
	unsigned char *p_data = (unsigned char *)p_buf;
	P_MTK_BTIF_DMA_VFIFO p_mtk_vfifo = container_of(p_vfifo,
							MTK_BTIF_DMA_VFIFO,
							vfifo);

	BTIF_TRC_FUNC();
	if ((NULL == p_buf) || (0 == buf_len)) {
		i_ret = ERR_INVALID_PAR;
		BTIF_ERR_FUNC("invalid parameters, p_buf:0x%08x, buf_len:%d\n",
			      p_buf, buf_len);
		return i_ret;
	}
/*check if tx dma in flush operation? if yes, should wait until DMA finish flush operation*/
/*currently uplayer logic will make sure this pre-condition*/
/*disable Tx IER, in case Tx irq happens, flush bit may be set in irq handler*/
	btif_tx_dma_ier_ctrl(p_dma_info, false);

	vff_size = p_mtk_vfifo->vfifo.vfifo_size;
	ava_len = BTIF_READ32(TX_DMA_VFF_LEFT_SIZE(base));
	wpt = BTIF_READ32(TX_DMA_VFF_WPT(base)) & DMA_WPT_MASK;
	last_wpt_wrap = BTIF_READ32(TX_DMA_VFF_WPT(base)) & DMA_WPT_WRAP;

/*copy data to vFIFO, Note: ava_len should always large than buf_len, otherwise common logic layer will not call hal_dma_send_data*/
	if (buf_len > ava_len) {
		BTIF_ERR_FUNC
		    ("length to send:(%d) < length available(%d), abnormal!!!---!!!\n",
		     buf_len, ava_len);
		BUG_ON(buf_len > ava_len);	/* this will cause kernel panic */
	}

	len_to_send = buf_len < ava_len ? buf_len : ava_len;
	if (len_to_send + wpt >= vff_size) {
		unsigned int tail_len = vff_size - wpt;
		memcpy((p_mtk_vfifo->vfifo.p_vir_addr + wpt), p_data, tail_len);
		p_data += tail_len;
		memcpy(p_mtk_vfifo->vfifo.p_vir_addr,
		       p_data, len_to_send - tail_len);
/*make sure all data write to memory area tx vfifo locates*/
		dsb();

/*calculate WPT*/
		wpt = wpt + len_to_send - vff_size;
		last_wpt_wrap ^= DMA_WPT_WRAP;
	} else {
		memcpy((p_mtk_vfifo->vfifo.p_vir_addr + wpt),
		       p_data, len_to_send);
/*make sure all data write to memory area tx vfifo locates*/
		dsb();

/*calculate WPT*/
		wpt += len_to_send;
	}
	p_mtk_vfifo->wpt = wpt;
	p_mtk_vfifo->last_wpt_wrap = last_wpt_wrap;

/*make sure tx dma is allowed(tx flush bit is not set) to use before update WPT*/
	if (hal_dma_is_tx_allow(p_dma_info)) {
/*make sure tx dma enabled*/
		hal_btif_dma_ctrl(p_dma_info, DMA_CTRL_ENABLE);

/*update WTP to Tx DMA controller's control register*/
		btif_reg_sync_writel(wpt | last_wpt_wrap, TX_DMA_VFF_WPT(base));

		if ((8 > BTIF_READ32(TX_DMA_VFF_VALID_SIZE(base))) &&
		    (0 < BTIF_READ32(TX_DMA_VFF_VALID_SIZE(base)))) {
/*0 < valid size in Tx vFIFO < 8 && TX Flush is not in process<should always be done>? if yes, set flush bit to DMA*/
			_tx_dma_flush(p_dma_info);
		}
		i_ret = len_to_send;
	} else {
/*TODO: print error log*/
		BTIF_ERR_FUNC
		    ("Tx DMA flush operation is in process, this case should never happen, please check if tx operation is allowed before call this API\n");
/*if flush operation is in process , we will return 0*/
		i_ret = 0;
	}

/*Enable Tx IER*/
	btif_tx_dma_ier_ctrl(p_dma_info, true);

	BTIF_TRC_FUNC();
	return i_ret;
}
/*****************************************************************************
* FUNCTION
*  hal_tx_dma_irq_handler
* DESCRIPTION
*  lower level tx interrupt handler
* PARAMETERS
* p_dma_info   [IN]        pointer to BTIF dma channel's information
* RETURNS
*  0 means success, negative means fail
*****************************************************************************/
int hal_tx_dma_irq_handler(P_MTK_DMA_INFO_STR p_dma_info)
{
#define MAX_CONTINIOUS_TIMES 512
	unsigned int i_ret = -1;
	unsigned int valid_size = 0;
	unsigned int vff_len = 0;
	unsigned int left_len = 0;
	unsigned int base = p_dma_info->base;
	static int flush_irq_counter;
	static struct timeval start_timer;
	static struct timeval end_timer;
	unsigned long flag = 0;
	spin_lock_irqsave(&(g_clk_cg_spinlock), flag);
	if (0 == clock_is_on(MTK_BTIF_APDMA_CLK_CG)) {
		spin_unlock_irqrestore(&(g_clk_cg_spinlock), flag);
		BTIF_ERR_FUNC
		    ("%s: clock is off before irq status clear done!!!\n",
		     __FILE__);
		return i_ret;
	}
/*check if Tx VFF Left Size equal to VFIFO size or not*/
	vff_len = BTIF_READ32(TX_DMA_VFF_LEN(base));
	valid_size = BTIF_READ32(TX_DMA_VFF_VALID_SIZE(base));
	left_len = BTIF_READ32(TX_DMA_VFF_LEFT_SIZE(base));
	if (0 == flush_irq_counter) {
		do_gettimeofday(&start_timer);
	}
	if ((0 < valid_size) && (8 > valid_size)) {
		i_ret = _tx_dma_flush(p_dma_info);
		flush_irq_counter++;
		if (MAX_CONTINIOUS_TIMES <= flush_irq_counter) {
			do_gettimeofday(&end_timer);
/*when btif tx fifo cannot accept any data and counts of bytes left in tx vfifo < 8 for a while
we assume that btif cannot send data for a long time
in order not to generate interrupt continiously, which may effect system's performance.
we clear tx flag and disable btif tx interrupt
*/
/*clear interrupt flag*/
			BTIF_CLR_BIT(TX_DMA_INT_FLAG(base),
				     TX_DMA_INT_FLAG_MASK);
/*vFIFO data has been read by DMA controller, just disable tx dma's irq*/
			i_ret = hal_btif_dma_ier_ctrl(p_dma_info, false);
			BTIF_ERR_FUNC
			    ("**********************ERROR, ERROR, ERROR**************************\n");
			BTIF_ERR_FUNC
			    ("BTIF Tx IRQ happened %d times (continiously), between %d.%d and %d.%d\n",
			     MAX_CONTINIOUS_TIMES, start_timer.tv_sec,
			     start_timer.tv_usec, end_timer.tv_usec,
			     end_timer.tv_usec);
		}
	} else if (vff_len == left_len) {
		flush_irq_counter = 0;
/*clear interrupt flag*/
		BTIF_CLR_BIT(TX_DMA_INT_FLAG(base), TX_DMA_INT_FLAG_MASK);
/*vFIFO data has been read by DMA controller, just disable tx dma's irq*/
		i_ret = hal_btif_dma_ier_ctrl(p_dma_info, false);
	} else {
#if 0
		BTIF_ERR_FUNC
		    ("**********************WARNING**************************\n");
		BTIF_ERR_FUNC("invalid irq condition, dump register\n");
		hal_dma_dump_reg(p_dma_info, REG_TX_DMA_ALL);
#endif
		BTIF_DBG_FUNC
		    ("superious IRQ occurs, vff_len(%d), valid_size(%d), left_len(%d)\n",
		     vff_len, valid_size, left_len);
	}
	spin_unlock_irqrestore(&(g_clk_cg_spinlock), flag);
	return i_ret;
}