/*
* Copyright (C) 2011-2014 MediaTek Inc.
*
* This program is free software: you can redistribute it and/or modify it under the terms of the
* GNU General Public License version 2 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, see .
*/
#ifdef DFT_TAG
#undef DFT_TAG
#endif
#define DFT_TAG "[SDIO-DETECT]"
#include "wmt_detect.h"
#if MTK_HIF_SDIO_AUTOK_ENABLED
#include
#endif
unsigned int gComboChipId = -1;
struct sdio_func *g_func = NULL;
MTK_WCN_HIF_SDIO_CHIP_INFO gChipInfoArray[] = {
/* MT6620 *//* Not an SDIO standard class device */
{{SDIO_DEVICE(0x037A, 0x020A)}, 0x6620}, /* SDIO1:FUNC1:WIFI */
{{SDIO_DEVICE(0x037A, 0x020B)}, 0x6620}, /* SDIO2:FUNC1:BT+FM+GPS */
{{SDIO_DEVICE(0x037A, 0x020C)}, 0x6620}, /* 2-function (SDIO2:FUNC1:BT+FM+GPS, FUNC2:WIFI) */
/* MT6628 *//* SDIO1: Wi-Fi, SDIO2: BGF */
{{SDIO_DEVICE(0x037A, 0x6628)}, 0x6628},
/* MT6630 *//* SDIO1: Wi-Fi, SDIO2: BGF */
{{SDIO_DEVICE(0x037A, 0x6630)}, 0x6630},
};
/* Supported SDIO device table */
static const struct sdio_device_id mtk_sdio_id_tbl[] = {
/* MT6618 *//* Not an SDIO standard class device */
{SDIO_DEVICE(0x037A, 0x018A)}, /* SDIO1:WIFI */
{SDIO_DEVICE(0x037A, 0x018B)}, /* SDIO2:FUNC1:BT+FM */
{SDIO_DEVICE(0x037A, 0x018C)}, /* 2-function (SDIO2:FUNC1:BT+FM, FUNC2:WIFI) */
/* MT6619 *//* Not an SDIO standard class device */
{SDIO_DEVICE(0x037A, 0x6619)}, /* SDIO2:FUNC1:BT+FM+GPS */
/* MT6620 *//* Not an SDIO standard class device */
{SDIO_DEVICE(0x037A, 0x020A)}, /* SDIO1:FUNC1:WIFI */
{SDIO_DEVICE(0x037A, 0x020B)}, /* SDIO2:FUNC1:BT+FM+GPS */
{SDIO_DEVICE(0x037A, 0x020C)}, /* 2-function (SDIO2:FUNC1:BT+FM+GPS, FUNC2:WIFI) */
/* MT5921 *//* Not an SDIO standard class device */
{SDIO_DEVICE(0x037A, 0x5921)},
/* MT6628 *//* SDIO1: Wi-Fi, SDIO2: BGF */
{SDIO_DEVICE(0x037A, 0x6628)},
/* MT6630 *//* SDIO1: Wi-Fi, SDIO2: BGF */
{SDIO_DEVICE(0x037A, 0x6630)},
{ /* end: all zeroes */ },
};
static int sdio_detect_probe(struct sdio_func *func, const struct sdio_device_id *id);
static void sdio_detect_remove(struct sdio_func *func);
static struct sdio_driver mtk_sdio_client_drv = {
.name = "mtk_sdio_client", /* MTK SDIO Client Driver */
.id_table = mtk_sdio_id_tbl, /* all supported struct sdio_device_id table */
.probe = sdio_detect_probe,
.remove = sdio_detect_remove,
};
static int hif_sdio_match_chipid_by_dev_id(const struct sdio_device_id *id);
int hif_sdio_is_chipid_valid(int chipId)
{
int index = -1;
int left = 0;
int middle = 0;
int right = sizeof(gChipInfoArray) / sizeof(gChipInfoArray[0]) - 1;
if ((chipId < gChipInfoArray[left].chipId) || (chipId > gChipInfoArray[right].chipId))
return index;
middle = (left + right) / 2;
while (left <= right) {
if (chipId > gChipInfoArray[middle].chipId) {
left = middle + 1;
} else if (chipId < gChipInfoArray[middle].chipId) {
right = middle - 1;
} else {
index = middle;
break;
}
middle = (left + right) / 2;
}
if (0 > index)
WMT_DETECT_ERR_FUNC("no supported chipid found\n");
else
WMT_DETECT_INFO_FUNC("index:%d, chipId:0x%x\n", index, gChipInfoArray[index].chipId);
return index;
}
int hif_sdio_match_chipid_by_dev_id(const struct sdio_device_id *id)
{
int maxIndex = sizeof(gChipInfoArray) / sizeof(gChipInfoArray[0]);
int index = 0;
struct sdio_device_id *localId = NULL;
int chipId = -1;
for (index = 0; index < maxIndex; index++) {
localId = &(gChipInfoArray[index].deviceId);
if ((localId->vendor == id->vendor) && (localId->device == id->device)) {
chipId = gChipInfoArray[index].chipId;
WMT_DETECT_INFO_FUNC
("valid chipId found, index(%d), vendor id(0x%x), device id(0x%x), chip id(0x%x)\n", index,
localId->vendor, localId->device, chipId);
gComboChipId = chipId;
mtk_wcn_wmt_set_chipid(gComboChipId);
break;
}
}
if (0 > chipId) {
WMT_DETECT_ERR_FUNC("No valid chipId found, vendor id(0x%x), device id(0x%x)\n", id->vendor,
id->device);
}
return chipId;
}
int sdio_detect_query_chipid(int waitFlag)
{
unsigned int timeSlotMs = 200;
unsigned int maxTimeSlot = 15;
unsigned int counter = 0;
/* gComboChipId = 0x6628; */
if (0 == waitFlag)
return gComboChipId;
if (0 <= hif_sdio_is_chipid_valid(gComboChipId))
return gComboChipId;
while (counter < maxTimeSlot) {
if (0 <= hif_sdio_is_chipid_valid(gComboChipId))
break;
msleep(timeSlotMs);
counter++;
}
return gComboChipId;
}
int sdio_detect_do_autok(int chipId)
{
int i_ret = 0;
#if MTK_HIF_SDIO_AUTOK_ENABLED
#if 0
BOOTMODE boot_mode;
boot_mode = get_boot_mode();
if (boot_mode == META_BOOT) {
WMT_DETECT_INFO_FUNC("omit autok in meta mode\n");
return 0;
}
#endif
if (0x6630 == chipId) {
#ifdef CONFIG_SDIOAUTOK_SUPPORT
if (NULL != g_func) {
WMT_DETECT_INFO_FUNC("wait_sdio_autok_ready++\n");
i_ret = wait_sdio_autok_ready(g_func->card->host);
WMT_DETECT_INFO_FUNC("wait_sdio_autok_ready--\n");
if (0 == i_ret) {
WMT_DETECT_INFO_FUNC("wait_sdio_autok_ready return success\n");
} else {
WMT_DETECT_INFO_FUNC("wait_sdio_autok_ready return fail, i_ret:%d\n", i_ret);
gComboChipId = -1;
}
} else {
WMT_DETECT_INFO_FUNC("g_func NULL, omit autok\n");
}
#else
i_ret = 0;
WMT_DETECT_INFO_FUNC("MTK_SDIOAUTOK_SUPPORT not defined\n");
#endif
} else {
WMT_DETECT_INFO_FUNC("MT%x does not support SDIO3.0 autoK is not needed\n", chipId);
}
#else
i_ret = 0;
WMT_DETECT_INFO_FUNC("MTK_HIF_SDIO_AUTOK_ENABLED is not defined\n");
#endif
return i_ret;
}
/*!
* \brief hif_sdio probe function
*
* hif_sdio probe function called by mmc driver when any matched SDIO function
* is detected by it.
*
* \param func
* \param id
*
* \retval 0 register successfully
* \retval < 0 list error code here
*/
static int sdio_detect_probe(struct sdio_func *func, const struct sdio_device_id *id)
{
int chipId = 0;
WMT_DETECT_INFO_FUNC("vendor(0x%x) device(0x%x) num(0x%x)\n", func->vendor, func->device, func->num);
chipId = hif_sdio_match_chipid_by_dev_id(id);
if ((0x6630 == chipId) && (1 == func->num)) {
int ret = 0;
g_func = func;
WMT_DETECT_INFO_FUNC("autok function detected, func:0x%p\n", g_func);
sdio_claim_host(func);
ret = sdio_enable_func(func);
sdio_release_host(func);
if (ret)
WMT_DETECT_ERR_FUNC("sdio_enable_func failed!\n");
}
return 0;
}
static void sdio_detect_remove(struct sdio_func *func)
{
if (g_func == func) {
sdio_claim_host(func);
sdio_disable_func(func);
sdio_release_host(func);
g_func = NULL;
}
WMT_DETECT_INFO_FUNC("do sdio remove\n");
}
int sdio_detect_init(void)
{
int ret = -1;
/* register to mmc driver */
ret = sdio_register_driver(&mtk_sdio_client_drv);
WMT_DETECT_INFO_FUNC("sdio_register_driver() ret=%d\n", ret);
return 0;
}
int sdio_detect_exit(void)
{
g_func = NULL;
/* register to mmc driver */
sdio_unregister_driver(&mtk_sdio_client_drv);
WMT_DETECT_INFO_FUNC("sdio_unregister_driver\n");
return 0;
}