/* * sdio_adapter.c * * linux sdio driver implement. * * Copyright (c) 2020-2021 Huawei Device Co., Ltd. * * This software is licensed under the terms of the GNU General Public * License version 2, as published by the Free Software Foundation, and * may be copied, distributed, and modified under those terms. * * 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. * */ #include #include #include #include #include "device_resource_if.h" #include "mmc_corex.h" #include "mmc_sdio.h" #define HDF_LOG_TAG sdio_adapter_c #define DATA_LEN_ONE_BYTE 1 #define DATA_LEN_TWO_BYTES 2 #define DATA_LEN_FOUR_BYTES 4 #define MMC_SLOT_NUM 3 #define SDIO_RESCAN_WAIT_TIME 40 struct mmc_host *GetMmcHost(int32_t slot); void SdioRescan(int32_t slot); enum SleepTime { MS_10 = 10, MS_50 = 50, }; static struct sdio_func *LinuxSdioGetFunc(struct SdioDevice *dev) { if (dev == NULL) { HDF_LOGE("LinuxSdioGetFunc: dev is null."); return NULL; } return (struct sdio_func *)dev->sd.mmc.priv; } static int32_t LinuxSdioIncrAddrReadBytes(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size) { struct sdio_func *func = LinuxSdioGetFunc(dev); int32_t ret = HDF_SUCCESS; uint16_t *output16 = NULL; uint32_t *output32 = NULL; if (func == NULL) { HDF_LOGE("LinuxSdioIncrAddrReadBytes: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if ((data == NULL) || (size == 0)) { HDF_LOGE("LinuxSdioIncrAddrReadBytes: data or size is invalid."); return HDF_ERR_INVALID_PARAM; } if (size == DATA_LEN_ONE_BYTE) { *data = sdio_readb(func, addr, &ret); return ret; } if (size == DATA_LEN_TWO_BYTES) { output16 = (uint16_t *)data; *output16 = sdio_readw(func, addr, &ret); return ret; } if (size == DATA_LEN_FOUR_BYTES) { output32 = (uint32_t *)data; *output32 = sdio_readl(func, addr, &ret); return ret; } return sdio_memcpy_fromio(func, data, addr, size); } static int32_t LinuxSdioIncrAddrWriteBytes(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size) { int32_t ret = HDF_SUCCESS; struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioIncrAddrWriteBytes: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if ((data == NULL) || (size == 0)) { HDF_LOGE("LinuxSdioIncrAddrWriteBytes: data or size is invalid."); return HDF_ERR_INVALID_PARAM; } if (size == DATA_LEN_ONE_BYTE) { sdio_writeb(func, *data, addr, &ret); return ret; } if (size == DATA_LEN_TWO_BYTES) { sdio_writew(func, *(uint16_t *)data, addr, &ret); return ret; } if (size == DATA_LEN_FOUR_BYTES) { sdio_writel(func, *(uint32_t *)data, addr, &ret); return ret; } return sdio_memcpy_toio(func, addr, data, size); } static int32_t LinuxSdioFixedAddrReadBytes(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioFixedAddrReadBytes: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if ((data == NULL) || (size == 0)) { HDF_LOGE("LinuxSdioFixedAddrReadBytes: data or size is invalid."); return HDF_ERR_INVALID_PARAM; } if (scatterLen > 0) { HDF_LOGE("LinuxSdioFixedAddrReadBytes: not support!"); return HDF_ERR_NOT_SUPPORT; } return sdio_readsb(func, data, addr, size); } static int32_t LinuxSdioFixedAddrWriteBytes(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioFixedAddrWriteBytes: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if ((data == NULL) || (size == 0)) { HDF_LOGE("LinuxSdioFixedAddrReadBytes: data or size is invalid."); return HDF_ERR_INVALID_PARAM; } if (scatterLen > 0) { HDF_LOGE("LinuxSdioFixedAddrWriteBytes: not support!"); return HDF_ERR_NOT_SUPPORT; } return sdio_writesb(func, addr, data, size); } static int32_t LinuxSdioFunc0ReadBytes(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size) { int32_t ret = HDF_SUCCESS; struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioFunc0ReadBytes: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if ((data == NULL) || (size == 0)) { HDF_LOGE("LinuxSdioFunc0ReadBytes: data or size is invalid."); return HDF_ERR_INVALID_PARAM; } *data = sdio_f0_readb(func, addr, &ret); return ret; } static int32_t LinuxSdioFunc0WriteBytes(struct SdioDevice *dev, uint8_t *data, uint32_t addr, uint32_t size) { int32_t ret = HDF_SUCCESS; struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioFunc0WriteBytes: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if ((data == NULL) || (size == 0)) { HDF_LOGE("LinuxSdioFunc0WriteBytes: data or size is invalid."); return HDF_ERR_INVALID_PARAM; } sdio_f0_writeb(func, *data, addr, &ret); return ret; } static int32_t LinuxSdioSetBlockSize(struct SdioDevice *dev, uint32_t blockSize) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioSetBlockSize, func is NULL."); return HDF_ERR_INVALID_OBJECT; } return sdio_set_block_size(func, blockSize); } static int32_t LinuxSdioGetCommonInfo(struct SdioDevice *dev, SdioCommonInfo *info, uint32_t infoType) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioGetCommonInfo: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if (info == NULL) { HDF_LOGE("LinuxSdioGetCommonInfo: info is null."); return HDF_ERR_INVALID_PARAM; } if (infoType != SDIO_FUNC_INFO) { HDF_LOGE("LinuxSdioGetCommonInfo: cur type %d is not support.", infoType); return HDF_ERR_NOT_SUPPORT; } if (func->card == NULL) { HDF_LOGE("LinuxSdioGetCommonInfo fail, card is null."); return HDF_ERR_INVALID_PARAM; } if (func->card->host == NULL) { HDF_LOGE("LinuxSdioGetCommonInfo fail, host is null."); return HDF_ERR_INVALID_PARAM; } info->funcInfo.enTimeout = func->enable_timeout; info->funcInfo.maxBlockNum = func->card->host->max_blk_count; info->funcInfo.maxBlockSize = func->card->host->max_blk_size; info->funcInfo.maxRequestSize = func->card->host->max_req_size; info->funcInfo.funcNum = func->num; info->funcInfo.irqCap = func->card->host->caps & MMC_CAP_SDIO_IRQ; info->funcInfo.data = func; return HDF_SUCCESS; } static int32_t LinuxSdioSetCommonInfo(struct SdioDevice *dev, SdioCommonInfo *info, uint32_t infoType) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioSetCommonInfo: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if (info == NULL) { HDF_LOGE("LinuxSdioSetCommonInfo: info is null."); return HDF_ERR_INVALID_PARAM; } if (infoType != SDIO_FUNC_INFO) { HDF_LOGE("LinuxSdioSetCommonInfo: cur type %d is not support.", infoType); return HDF_ERR_NOT_SUPPORT; } if (func->card == NULL) { HDF_LOGE("LinuxSdioSetCommonInfo fail, card is null."); return HDF_ERR_INVALID_PARAM; } if (func->card->host == NULL) { HDF_LOGE("LinuxSdioSetCommonInfo fail, host is null."); return HDF_ERR_INVALID_PARAM; } func->enable_timeout = info->funcInfo.enTimeout; func->card->host->max_blk_count = info->funcInfo.maxBlockNum; func->card->host->max_blk_size = info->funcInfo.maxBlockSize; func->card->host->max_req_size = info->funcInfo.maxRequestSize; func->num = info->funcInfo.funcNum; return HDF_SUCCESS; } static int32_t LinuxSdioFlushData(struct SdioDevice *dev) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioFlushData: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if (func->card == NULL) { HDF_LOGE("LinuxSdioFlushData: card is NULL."); return HDF_ERR_INVALID_OBJECT; } return mmc_sw_reset(func->card->host); } static int32_t LinuxSdioClaimHost(struct SdioDevice *dev) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioClaimHost: func is NULL."); return HDF_ERR_INVALID_OBJECT; } sdio_claim_host(func); return HDF_SUCCESS; } static int32_t LinuxSdioReleaseHost(struct SdioDevice *dev) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioReleaseHost: func is NULL."); return HDF_ERR_INVALID_OBJECT; } sdio_release_host(func); return HDF_SUCCESS; } static int32_t LinuxSdioEnableFunc(struct SdioDevice *dev) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioEnableFunc: func is NULL."); return HDF_ERR_INVALID_OBJECT; } return sdio_enable_func(func); } static int32_t LinuxSdioDisableFunc(struct SdioDevice *dev) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioDisableFunc: func is NULL."); return HDF_ERR_INVALID_OBJECT; } return sdio_disable_func(func); } static int32_t LinuxSdioClaimIrq(struct SdioDevice *dev, SdioIrqHandler *handler) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioClaimIrq: func is NULL."); return HDF_ERR_INVALID_OBJECT; } if (handler == NULL) { HDF_LOGE("LinuxSdioClaimIrq: handler is null."); return HDF_ERR_INVALID_PARAM; } return sdio_claim_irq(func, (sdio_irq_handler_t *)handler); } static int32_t LinuxSdioReleaseIrq(struct SdioDevice *dev) { struct sdio_func *func = LinuxSdioGetFunc(dev); if (func == NULL) { HDF_LOGE("LinuxSdioReleaseIrq: func is NULL."); return HDF_ERR_INVALID_OBJECT; } return sdio_release_irq(func); } static struct sdio_func *LinuxSdioSearchFunc(uint32_t funcNum, uint16_t vendorId, uint16_t deviceId) { struct mmc_card *card = NULL; struct mmc_host *host = NULL; struct sdio_func *func = NULL; uint32_t i, j; for (i = 0; i < MMC_SLOT_NUM; i++) { host = GetMmcHost(i); if (host == NULL) { continue; } card = host->card; if (card == NULL) { continue; } for (j = 0; j <= card->sdio_funcs; j++) { func = card->sdio_func[j]; if ((func != NULL) && (func->num == funcNum) && (func->vendor == vendorId) && (func->device == deviceId)) { HDF_LOGD("LinuxSdioSearchFunc: find func!"); return func; } } } HDF_LOGE("LinuxSdioSearchFunc: get sdio func fail!"); return NULL; } static int32_t LinuxSdioFindFunc(struct SdioDevice *dev, struct SdioFunctionConfig *configData) { if (dev == NULL || configData == NULL) { HDF_LOGE("LinuxSdioFindFunc: dev or configData is NULL."); return HDF_ERR_INVALID_OBJECT; } dev->sd.mmc.priv = LinuxSdioSearchFunc(configData->funcNr, configData->vendorId, configData->deviceId); if (dev->sd.mmc.priv == NULL) { HDF_LOGE("LinuxSdioFindFunc: LinuxSdioSearchFunc fail."); return HDF_ERR_NOT_SUPPORT; } return HDF_SUCCESS; } static struct SdioDeviceOps g_sdioDeviceOps = { .incrAddrReadBytes = LinuxSdioIncrAddrReadBytes, .incrAddrWriteBytes = LinuxSdioIncrAddrWriteBytes, .fixedAddrReadBytes = LinuxSdioFixedAddrReadBytes, .fixedAddrWriteBytes = LinuxSdioFixedAddrWriteBytes, .func0ReadBytes = LinuxSdioFunc0ReadBytes, .func0WriteBytes = LinuxSdioFunc0WriteBytes, .setBlockSize = LinuxSdioSetBlockSize, .getCommonInfo = LinuxSdioGetCommonInfo, .setCommonInfo = LinuxSdioSetCommonInfo, .flushData = LinuxSdioFlushData, .enableFunc = LinuxSdioEnableFunc, .disableFunc = LinuxSdioDisableFunc, .claimIrq = LinuxSdioClaimIrq, .releaseIrq = LinuxSdioReleaseIrq, .findFunc = LinuxSdioFindFunc, .claimHost = LinuxSdioClaimHost, .releaseHost = LinuxSdioReleaseHost, }; static bool LinuxSdioRescanFinish(struct MmcCntlr *cntlr) { struct mmc_host *host = NULL; struct mmc_card *card = NULL; host = GetMmcHost(cntlr->index); if (host == NULL) { return false; } card = host->card; if (card == NULL) { return false; } if (card->sdio_funcs > 0) { return true; } return false; } static int32_t LinuxSdioRescan(struct MmcCntlr *cntlr) { bool rescanFinish = false; uint32_t count = 0; if (cntlr == NULL) { HDF_LOGE("LinuxSdioRescan: cntlr is NULL."); return HDF_ERR_INVALID_OBJECT; } SdioRescan(cntlr->index); while (rescanFinish == false && count < SDIO_RESCAN_WAIT_TIME) { OsalMSleep(MS_50); count++; rescanFinish = LinuxSdioRescanFinish(cntlr); } if (rescanFinish == false) { HDF_LOGE("LinuxSdioRescan: fail!"); return HDF_FAILURE; } OsalMSleep(MS_10); return HDF_SUCCESS; } struct MmcCntlrOps g_sdioCntlrOps = { .rescanSdioDev = LinuxSdioRescan, }; static void LinuxSdioDeleteCntlr(struct MmcCntlr *cntlr) { if (cntlr == NULL) { return; } if (cntlr->curDev != NULL) { MmcDeviceRemove(cntlr->curDev); OsalMemFree(cntlr->curDev); } MmcCntlrRemove(cntlr); OsalMemFree(cntlr); } static int32_t LinuxSdioCntlrParse(struct MmcCntlr *cntlr, struct HdfDeviceObject *obj) { const struct DeviceResourceNode *node = NULL; struct DeviceResourceIface *drsOps = NULL; int32_t ret; if (obj == NULL || cntlr == NULL) { HDF_LOGE("LinuxSdioCntlrParse: input para is NULL."); return HDF_FAILURE; } node = obj->property; if (node == NULL) { HDF_LOGE("LinuxSdioCntlrParse: drs node is NULL."); return HDF_FAILURE; } drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); if (drsOps == NULL || drsOps->GetUint16 == NULL || drsOps->GetUint32 == NULL) { HDF_LOGE("LinuxSdioCntlrParse: invalid drs ops fail!"); return HDF_FAILURE; } ret = drsOps->GetUint16(node, "hostId", &(cntlr->index), 0); if (ret != HDF_SUCCESS) { HDF_LOGE("LinuxSdioCntlrParse: read hostIndex fail!"); return ret; } ret = drsOps->GetUint32(node, "devType", &(cntlr->devType), 0); if (ret != HDF_SUCCESS) { HDF_LOGE("LinuxSdioCntlrParse: read devType fail!"); return ret; } HDF_LOGD("LinuxSdioCntlrParse: hostIndex = %d, devType = %d.", cntlr->index, cntlr->devType); return HDF_SUCCESS; } static int32_t LinuxSdioBind(struct HdfDeviceObject *obj) { struct MmcCntlr *cntlr = NULL; int32_t ret; if (obj == NULL) { HDF_LOGE("LinuxSdioBind: Fail, obj is NULL."); return HDF_ERR_INVALID_OBJECT; } cntlr = (struct MmcCntlr *)OsalMemCalloc(sizeof(struct MmcCntlr)); if (cntlr == NULL) { HDF_LOGE("LinuxSdioBind: no mem for MmcCntlr."); return HDF_ERR_MALLOC_FAIL; } cntlr->ops = &g_sdioCntlrOps; cntlr->hdfDevObj = obj; obj->service = &cntlr->service; /* init cntlr. */ ret = LinuxSdioCntlrParse(cntlr, obj); if (ret != HDF_SUCCESS) { HDF_LOGE("LinuxSdioBind: cntlr parse fail."); goto _ERR; } ret = MmcCntlrAdd(cntlr); if (ret != HDF_SUCCESS) { HDF_LOGE("LinuxSdioBind: cntlr add fail."); goto _ERR; } ret = MmcCntlrAllocDev(cntlr, (enum MmcDevType)cntlr->devType); if (ret != HDF_SUCCESS) { HDF_LOGE("LinuxSdioBind: alloc dev fail."); goto _ERR; } MmcDeviceAddOps(cntlr->curDev, &g_sdioDeviceOps); HDF_LOGD("LinuxSdioBind: Success!"); return HDF_SUCCESS; _ERR: LinuxSdioDeleteCntlr(cntlr); HDF_LOGE("LinuxSdioBind: Fail!"); return HDF_FAILURE; } static int32_t LinuxSdioInit(struct HdfDeviceObject *obj) { (void)obj; HDF_LOGD("LinuxSdioInit: Success!"); return HDF_SUCCESS; } static void LinuxSdioRelease(struct HdfDeviceObject *obj) { if (obj == NULL) { return; } LinuxSdioDeleteCntlr((struct MmcCntlr *)obj->service); } struct HdfDriverEntry g_sdioDriverEntry = { .moduleVersion = 1, .Bind = LinuxSdioBind, .Init = LinuxSdioInit, .Release = LinuxSdioRelease, .moduleName = "HDF_PLATFORM_SDIO", }; HDF_INIT(g_sdioDriverEntry);