/******************************************************************************
 *
 * Copyright(c) 2007 - 2011 Realtek Corporation. All rights reserved.
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of version 2 of the GNU General Public License as
 * published by the Free Software Foundation.
 *
 * This program is distributed in the hope that it will be useful, but WITHOUT
 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
 * more details.
 *
 * You should have received a copy of the GNU General Public License along with
 * this program; if not, write to the Free Software Foundation, Inc.,
 * 51 Franklin Street, Fifth Floor, Boston, MA 02110, USA
 *
 *
 ******************************************************************************/
#define _HAL_SDIO_C_

#include <drv_types.h>
#include <hal_data.h>

#ifndef RTW_HALMAC
static void dump_sdio_f0(PADAPTER padapter)
{
	char str_out[128];
	char str_val[8];
	char *p = NULL;
	int index = 0, i = 0;
	u8 val8 = 0, len = 0;

	RTW_ERR("Dump SDIO function_0 register:\n");
	for (index = 0 ; index < 0x100 ; index += 16) {
		p = &str_out[0];
		len = snprintf(str_val, sizeof(str_val),
			       "0x%02x: ", index);
		strncpy(str_out, str_val, len);
		p += len;

		for (i = 0 ; i < 16 ; i++) {
			len = snprintf(str_val, sizeof(str_val), "%02x ",
				       rtw_sd_f0_read8(padapter, index + i));
			strncpy(p, str_val, len);
			p += len;
		}
		RTW_INFO("%s\n", str_out);
		_rtw_memset(&str_out, '\0', sizeof(str_out));
	}
}

static void dump_sdio_local(PADAPTER padapter)
{
	char str_out[128];
	char str_val[8];
	char *p = NULL;
	int index = 0, i = 0;
	u8 val8 = 0, len = 0;

	RTW_ERR("Dump SDIO local register:\n");
	for (index = 0 ; index < 0x100 ; index += 16) {
		p = &str_out[0];
		len = snprintf(str_val, sizeof(str_val),
			       "0x%02x: ", index);
		strncpy(str_out, str_val, len);
		p += len;

		for (i = 0 ; i < 16 ; i++) {
			len = snprintf(str_val, sizeof(str_val), "%02x ",
				       rtw_read8(padapter, (0x1025 << 16) | (index + i)));
			strncpy(p, str_val, len);
			p += len;
		}
		RTW_INFO("%s\n", str_out);
		_rtw_memset(&str_out, '\0', sizeof(str_out));
	}
}

static void dump_mac_page0(PADAPTER padapter)
{
	char str_out[128];
	char str_val[8];
	char *p = NULL;
	int index = 0, i = 0;
	u8 val8 = 0, len = 0;

	RTW_ERR("Dump MAC Page0 register:\n");
	for (index = 0 ; index < 0x100 ; index += 16) {
		p = &str_out[0];
		len = snprintf(str_val, sizeof(str_val),
			       "0x%02x: ", index);
		strncpy(str_out, str_val, len);
		p += len;

		for (i = 0 ; i < 16 ; i++) {
			len = snprintf(str_val, sizeof(str_val), "%02x ",
				       rtw_read8(padapter, index + i));
			strncpy(p, str_val, len);
			p += len;
		}
		RTW_INFO("%s\n", str_out);
		_rtw_memset(&str_out, '\0', sizeof(str_out));
	}
}

/*
 * Description:
 *	Call this function to make sure power on successfully
 *
 * Return:
 *	_SUCCESS	enable success
 *	_FAIL	enable fail
 */
bool sdio_power_on_check(PADAPTER padapter) {
	u32 val_offset0, val_offset1, val_offset2, val_offset3;
	u32 val_mix = 0;
	u32 res = 0;
	bool ret = _FAIL;
	int index = 0;

	val_offset0 = rtw_read8(padapter, REG_CR);
	val_offset1 = rtw_read8(padapter, REG_CR + 1);
	val_offset2 = rtw_read8(padapter, REG_CR + 2);
	val_offset3 = rtw_read8(padapter, REG_CR + 3);

	if (val_offset0 == 0xEA || val_offset1 == 0xEA ||
	    val_offset2 == 0xEA || val_offset3 == 0xEA) {
		RTW_INFO("%s: power on fail, do Power on again\n", __func__);
		goto _exit;
	}

	val_mix = val_offset3 << 24 | val_mix;
	val_mix = val_offset2 << 16 | val_mix;
	val_mix = val_offset1 << 8 | val_mix;
	val_mix = val_offset0 | val_mix;

	res = rtw_read32(padapter, REG_CR);

	RTW_INFO("%s: val_mix:0x%08x, res:0x%08x\n", __func__, val_mix, res);

	while (index < 100) {
		if (res == val_mix) {
			RTW_INFO("%s: 0x100 the result of cmd52 and cmd53 is the same.\n", __func__);
			ret = _SUCCESS;
			break;
		} else {
			RTW_INFO("%s: 0x100 cmd52 and cmd53 is not the same(index:%d).\n", __func__, index);
			res = rtw_read32(padapter, REG_CR);
			index++;
			ret = _FAIL;
		}
	}

	if (ret) {
		index = 0;
		while (index < 100) {
			rtw_write32(padapter, 0x1B8, 0x12345678);
			res = rtw_read32(padapter, 0x1B8);
			if (res == 0x12345678) {
				RTW_INFO("%s: 0x1B8 test Pass.\n", __func__);
				ret = _SUCCESS;
				break;
			} else {
				index++;
				RTW_INFO("%s: 0x1B8 test Fail(index: %d).\n", __func__, index);
				ret = _FAIL;
			}
		}
	} else
		RTW_INFO("%s: fail at cmd52, cmd53.\n", __func__);

_exit:
	if (ret == _FAIL) {
		dump_sdio_f0(padapter);
		dump_sdio_local(padapter);
		dump_mac_page0(padapter);
	}

	return ret;
}

u8 rtw_hal_sdio_max_txoqt_free_space(_adapter *padapter)
{
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);

	if (pHalData->SdioTxOQTMaxFreeSpace < 8)
		pHalData->SdioTxOQTMaxFreeSpace = 8;

	return pHalData->SdioTxOQTMaxFreeSpace;
}

u8 rtw_hal_sdio_query_tx_freepage(_adapter *padapter, u8 PageIdx, u8 RequiredPageNum)
{
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);

	if ((pHalData->SdioTxFIFOFreePage[PageIdx] + pHalData->SdioTxFIFOFreePage[PUBLIC_QUEUE_IDX]) >= (RequiredPageNum))
		return _TRUE;
	else
		return _FALSE;
}

void rtw_hal_sdio_update_tx_freepage(_adapter *padapter, u8 PageIdx, u8 RequiredPageNum)
{
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);
	u8	DedicatedPgNum = 0;
	u8	RequiredPublicFreePgNum = 0;
	/* _irqL irql; */

	/* _enter_critical_bh(&pHalData->SdioTxFIFOFreePageLock, &irql); */

	DedicatedPgNum = pHalData->SdioTxFIFOFreePage[PageIdx];
	if (RequiredPageNum <= DedicatedPgNum)
		pHalData->SdioTxFIFOFreePage[PageIdx] -= RequiredPageNum;
	else {
		pHalData->SdioTxFIFOFreePage[PageIdx] = 0;
		RequiredPublicFreePgNum = RequiredPageNum - DedicatedPgNum;
		pHalData->SdioTxFIFOFreePage[PUBLIC_QUEUE_IDX] -= RequiredPublicFreePgNum;
	}

	/* _exit_critical_bh(&pHalData->SdioTxFIFOFreePageLock, &irql); */
}

void rtw_hal_set_sdio_tx_max_length(PADAPTER padapter, u8 numHQ, u8 numNQ, u8 numLQ, u8 numPubQ)
{
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);
	u32	page_size;
	u32	lenHQ, lenNQ, lenLQ;

	rtw_hal_get_def_var(padapter, HAL_DEF_TX_PAGE_SIZE, &page_size);

	lenHQ = ((numHQ + numPubQ) >> 1) * page_size;
	lenNQ = ((numNQ + numPubQ) >> 1) * page_size;
	lenLQ = ((numLQ + numPubQ) >> 1) * page_size;

	pHalData->sdio_tx_max_len[HI_QUEUE_IDX] = (lenHQ > MAX_XMITBUF_SZ) ? MAX_XMITBUF_SZ : lenHQ;
	pHalData->sdio_tx_max_len[MID_QUEUE_IDX] = (lenNQ > MAX_XMITBUF_SZ) ? MAX_XMITBUF_SZ : lenNQ;
	pHalData->sdio_tx_max_len[LOW_QUEUE_IDX] = (lenLQ > MAX_XMITBUF_SZ) ? MAX_XMITBUF_SZ : lenLQ;
}

u32 rtw_hal_get_sdio_tx_max_length(PADAPTER padapter, u8 queue_idx)
{
	struct dvobj_priv	*pdvobjpriv = adapter_to_dvobj(padapter);
	HAL_DATA_TYPE	*pHalData = GET_HAL_DATA(padapter);
	u32	deviceId, max_len;


	deviceId = ffaddr2deviceId(pdvobjpriv, queue_idx);
	switch (deviceId) {
	case WLAN_TX_HIQ_DEVICE_ID:
		max_len = pHalData->sdio_tx_max_len[HI_QUEUE_IDX];
		break;

	case WLAN_TX_MIQ_DEVICE_ID:
		max_len = pHalData->sdio_tx_max_len[MID_QUEUE_IDX];
		break;

	case WLAN_TX_LOQ_DEVICE_ID:
		max_len = pHalData->sdio_tx_max_len[LOW_QUEUE_IDX];
		break;

	default:
		max_len = pHalData->sdio_tx_max_len[MID_QUEUE_IDX];
		break;
	}

	return max_len;
}

#ifdef CONFIG_FW_C2H_REG
void sd_c2h_hisr_hdl(_adapter *adapter)
{
	u8 c2h_evt[C2H_REG_LEN] = {0};
	u8 id, seq, plen;
	u8 *payload;

	if (rtw_hal_c2h_evt_read(adapter, c2h_evt) != _SUCCESS)
		goto exit;

	if (rtw_hal_c2h_reg_hdr_parse(adapter, c2h_evt, &id, &seq, &plen, &payload) != _SUCCESS)
		goto exit;
		
	if (rtw_hal_c2h_id_handle_directly(adapter, id, seq, plen, payload)) {
		/* Handle directly */
		rtw_hal_c2h_handler(adapter, id, seq, plen, payload);
		goto exit;
	}

	if (rtw_c2h_reg_wk_cmd(adapter, c2h_evt) != _SUCCESS)
		RTW_ERR("%s rtw_c2h_reg_wk_cmd fail\n", __func__);

exit:
	return;
}
#endif

#endif /* !RTW_HALMAC */
