diff --git a/support/platform/include/common/platform.h b/support/platform/include/common/platform.h new file mode 100644 index 0000000000000000000000000000000000000000..dd3a5563798380d6adb7f296e3f37839ea246ed5 --- /dev/null +++ b/support/platform/include/common/platform.h @@ -0,0 +1,28 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ +#ifndef PLATFORM_H +#define PLATFORM_H + +#include "platform_common.h" +#include "platform_device.h" +#include "platform_manager.h" +#include "platform_queue.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* PLATFORM_H */ diff --git a/support/platform/include/common/platform_common.h b/support/platform/include/common/platform_common.h new file mode 100644 index 0000000000000000000000000000000000000000..a3d78b6b3d140819fcb239d519cd85b1967f91d7 --- /dev/null +++ b/support/platform/include/common/platform_common.h @@ -0,0 +1,90 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef PLATFORM_COMMON_H +#define PLATFORM_COMMON_H + +#include "hdf_base.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +enum PlatformModuleType { + PLATFORM_MODULE_GPIO, + PLATFORM_MODULE_I2C, + PLATFORM_MODULE_SPI, + PLATFORM_MODULE_PIN, + PLATFORM_MODULE_CLOCK, + PLATFORM_MODULE_REGULATOR, + PLATFORM_MODULE_MIPI_DSI, + PLATFORM_MODULE_UART, + PLATFORM_MODULE_SDIO, + PLATFORM_MODULE_MDIO, + PLATFORM_MODULE_APB, + PLATFORM_MODULE_PCIE, + PLATFORM_MODULE_PCM, + PLATFORM_MODULE_I2S, + PLATFORM_MODULE_PWM, + PLATFORM_MODULE_DMA, + PLATFORM_MODULE_ADC, + PLATFORM_MODULE_RTC, + PLATFORM_MODULE_WDT, + PLATFORM_MODULE_I3C, + PLATFORM_MODULE_CAN, + PLATFORM_MODULE_HDMI, + PLATFORM_MODULE_MMC, + PLATFORM_MODULE_MTD, + PLATFORM_MODULE_DEFAULT, + PLATFORM_MODULE_MAX, +}; + +struct PlatformModuleInfo { + enum PlatformModuleType moduleType; + const char *moduleName; + void *priv; +}; + +struct PlatformModuleInfo *PlatformModuleInfoGet(enum PlatformModuleType moduleType); +int32_t PlatformModuleInfoCount(void); + +enum PlatformErrno { +#define HDF_PLT_ERR_START HDF_BSP_ERR_START /**< Error number start of platform. */ +#define HDF_PLT_ERR_NUM(v) (HDF_PLT_ERR_START + (v)) + HDF_PLT_ERR_OS_API = HDF_ERR_BSP_PLT_API_ERR, + HDF_PLT_ERR_OPEN_DEV = HDF_PAL_ERR_DEV_CREATE, /**< Failed to open a device. */ + HDF_PLT_ERR_INNER = HDF_PAL_ERR_INNER, /**< Internal error of platform framework. */ + + HDF_PLT_ERR_NO_DEV = HDF_PLT_ERR_NUM(-5), /**< There is no device present. */ + HDF_PLT_ERR_DEV_TYPE = HDF_PLT_ERR_NUM(-6), /**< invalid device type */ + HDF_PLT_ERR_DEV_GET = HDF_PLT_ERR_NUM(-7), /**< err on device get */ + HDF_PLT_ERR_DEV_ADD = HDF_PLT_ERR_NUM(-8), /**< err on device add */ + HDF_PLT_ERR_DEV_FULL = HDF_PLT_ERR_NUM(-9), /**< id number conflict */ + HDF_PLT_ERR_ID_REPEAT = HDF_PLT_ERR_NUM(-10), /**< id number conflict */ +#define HDF_MMC_ERR_START (HDF_PLT_ERR_START - 50) /**< Error number start for mmc. */ +#define HDF_MMC_ERR_NUM(v) (HDF_MMC_ERR_START + (v)) + HDF_MMC_ERR_SWITCH_FAIL = HDF_MMC_ERR_NUM(-1), + HDF_MMC_ERR_OTHER_CMD_IS_RUNNING = HDF_MMC_ERR_NUM(-2), + HDF_MMC_ERR_ILLEGAL_SEQ = HDF_MMC_ERR_NUM(-3), +}; + +void PlatformGlobalLock(void); +void PlatformGlobalUnlock(void); + +/* Os adapt */ +bool PlatInIrqContext(void); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* PLATFORM_COMMON_H */ diff --git a/support/platform/include/common/platform_device.h b/support/platform/include/common/platform_device.h new file mode 100644 index 0000000000000000000000000000000000000000..d5363b9785c675d41f1e95cd4c8c611400e06218 --- /dev/null +++ b/support/platform/include/common/platform_device.h @@ -0,0 +1,170 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef PLATFORM_DEVICE_H +#define PLATFORM_DEVICE_H + +#include "hdf_base.h" +#include "hdf_dlist.h" +#include "hdf_sref.h" +#include "osal_sem.h" +#include "osal_spinlock.h" +#include "platform_common.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +struct PlatformManager; + +struct PlatformDevice { + struct HdfDeviceObject *hdfDev; /* releated to an hdf device object */ + struct PlatformManager *manager; /* the platform manager it belongs to */ + uint32_t magic; /* magic number of the device instance */ + const char *name; /* name of the device instance */ + struct HdfSRef ref; /* used for reference count */ + struct DListHead node; /* linked to the list of a manager */ + struct DListHead notifiers; /* list of notifier nodes */ + bool ready; /* indicates whether initialized */ + OsalSpinlock spin; /* for member protection */ + struct OsalSem released; /* for death notification */ +}; + +enum PlatformEventType { + PLAT_EVENT_DEAD = 0, /* a platform device going to die */ +}; + +struct PlatformNotifier { + void *data; /* private data u can take */ + /** + * @brief the event handle funciton of this notifier. + * + * make sure it can be called in irq context. + * + * @param device Indicates the pointer to the platform device. + * @param event Indicates the event type of this handling process. + * + * @since 1.0 + */ + void (*handle)(struct PlatformDevice *device, enum PlatformEventType event, void *data); +}; + +struct PlatformNotifierNode { + struct DListHead node; /* link to notfifier node list */ + struct PlatformNotifier *notifier; /* pointer to the notifier instance */ +}; + +/** + * @brief Initialize a platform device. + * + * Initialize members of a platform device. + * + * @param device Indicates the pointer to the platform device. + * + * @since 1.0 + */ +void PlatformDeviceInit(struct PlatformDevice *device); + +/** + * @brief Uninitialize a platform device. + * + * Uninitialize members of a platform device. + * + * @param device Indicates the pointer to the platform device. + * + * @since 1.0 + */ +void PlatformDeviceUninit(struct PlatformDevice *device); + +/** + * @brief Increase reference count for a platform device. + * + * @param device Indicates the pointer to the platform device. + * + * @return Returns the pointer to the paltform device on success; returns NULL otherwise. + * @since 1.0 + */ +struct PlatformDevice *PlatformDeviceGet(struct PlatformDevice *device); + +/** + * @brief Decrease reference count for a platform device. + * + * @param device Indicates the pointer to the platform device. + * + * @since 1.0 + */ +void PlatformDevicePut(struct PlatformDevice *device); + +/** + * @brief Register a notifier to a platform device. + * + * Subscribe the events of a platform device by registering a notfier. + * + * @param device Indicates the pointer to the platform device. + * @param notifier Indicates the pointer to the platform notifier. + * + * @return Returns 0 if the notifier registered successfully; returns a negative value otherwise. + * @since 1.0 + */ +int32_t PlatformDeviceRegNotifier(struct PlatformDevice *device, struct PlatformNotifier *notifier); + +/** + * @brief Unregister a notifier to a platform device. + * + * Unsubscribe the events of a platform device by unregistering the notfier. + * + * @param device Indicates the pointer to the platform device. + * @param notifier Indicates the pointer to the platform notifier. + * + * @since 1.0 + */ +void PlatformDeviceUnregNotifier(struct PlatformDevice *device, struct PlatformNotifier *notifier); + +/** + * @brief Unregister all notifiers to a platform device. + * + * Unsubscribe all the events of a platform device by clearing the notfiers. + * + * @param device Indicates the pointer to the platform device. + * + * @since 1.0 + */ +void PlatformDeviceClearNotifier(struct PlatformDevice *device); + +/** + * @brief Add a platform device by module type. + * + * do not call in irq context cause can sleep + * + * @param device Indicates the pointer to the platform device. + * + * @return Returns 0 if add successfully; returns a negative value otherwise. + * @since 1.0 + */ +int32_t PlatformDeviceAdd(struct PlatformDevice *device); + +/** + * @brief Remove a platform device by module type. + * + * do not call in irq context cause can sleep + * + * @param device Indicates the pointer to the platform device. + * + * @since 1.0 + */ +void PlatformDeviceDel(struct PlatformDevice *device); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* PLATFORM_DEVICE_H */ diff --git a/support/platform/include/common/platform_errno.h b/support/platform/include/common/platform_errno.h new file mode 100644 index 0000000000000000000000000000000000000000..edfdbbe5d6a50c00e7e539a0241216203bed1556 --- /dev/null +++ b/support/platform/include/common/platform_errno.h @@ -0,0 +1,46 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef PLATFORM_ERRNO_H +#define PLATFORM_ERRNO_H + +#include "hdf_base.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +enum PlatformErrno { +#define HDF_PLT_ERR_START HDF_BSP_ERR_START /**< Error number start of platform. */ +#define HDF_PLT_ERR_NUM(v) (HDF_PLT_ERR_START + (v)) + HDF_PLT_ERR_OS_API = HDF_ERR_BSP_PLT_API_ERR, + HDF_PLT_ERR_OPEN_DEV = HDF_PAL_ERR_DEV_CREATE, /**< Failed to open a device. */ + HDF_PLT_ERR_INNER = HDF_PAL_ERR_INNER, /**< Internal error of platform framework. */ + + HDF_PLT_ERR_NO_DEV = HDF_PLT_ERR_NUM(-5), /**< There is no device present. */ + HDF_PLT_ERR_DEV_TYPE = HDF_PLT_ERR_NUM(-6), /**< invalid device type */ + HDF_PLT_ERR_DEV_GET = HDF_PLT_ERR_NUM(-7), /**< err on device get */ + HDF_PLT_ERR_DEV_ADD = HDF_PLT_ERR_NUM(-8), /**< err on device add */ + HDF_PLT_ERR_DEV_FULL = HDF_PLT_ERR_NUM(-9), /**< id number conflict */ + HDF_PLT_ERR_ID_REPEAT = HDF_PLT_ERR_NUM(-10), /**< id number conflict */ +#define HDF_MMC_ERR_START (HDF_PLT_ERR_START - 50) /**< Error number start for mmc. */ +#define HDF_MMC_ERR_NUM(v) (HDF_MMC_ERR_START + (v)) + HDF_MMC_ERR_SWITCH_FAIL = HDF_MMC_ERR_NUM(-1), + HDF_MMC_ERR_OTHER_CMD_IS_RUNNING = HDF_MMC_ERR_NUM(-2), + HDF_MMC_ERR_ILLEGAL_SEQ = HDF_MMC_ERR_NUM(-3), +}; + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* PLATFORM_ERRNO_H */ diff --git a/support/platform/include/common/platform_manager.h b/support/platform/include/common/platform_manager.h new file mode 100644 index 0000000000000000000000000000000000000000..8bb1c25dc90847127be4eb82e0c6fca577937f8b --- /dev/null +++ b/support/platform/include/common/platform_manager.h @@ -0,0 +1,112 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef PLATFORM_MANAGER_H +#define PLATFORM_MANAGER_H + +#include "hdf_base.h" +#include "hdf_dlist.h" +#include "osal_spinlock.h" +#include "platform_device.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +struct PlatformManager { + const char *name; /* name of the manager */ + struct DListHead devices; /* list to keep all it's device instances */ + OsalSpinlock spin; /* for member protection */ +}; + +/** + * @brief Create a platform manager. + * + * Create a platform manager with member initialized. + * + * @param name Indicates the name of the manager. + * + * @return Returns the pointer to the paltform manager on success; returns NULL otherwise. + * @since 1.0 + */ +struct PlatformManager *PlatformManagerCreate(const char *name); + +/** + * @brief Get a platform manager by module type. + * + * @param module Indicates the module type of the manager. + * + * @return Returns the pointer to the paltform manager on success; returns NULL otherwise. + * @since 1.0 + */ +struct PlatformManager *PlatformManagerGet(enum PlatformModuleType module); + +/** + * @brief Add a platform device to a platform manager. + * + * Add a platform device to make it managed by platform core. + * + * @param manager Indicates the pointer to the platform manager. + * @param device Indicates the pointer to the platform device. + * + * @return Returns 0 if the devcie added successfully; returns a negative value otherwise. + * @since 1.0 + */ +int32_t PlatformManagerAddDevice(struct PlatformManager *manager, struct PlatformDevice *device); + +/** + * @brief Remove a platform device from a platform manager. + * + * Remove a platform device to make it away from management of platform core. + * + * @param manager Indicates the pointer to the platform manager. + * @param device Indicates the pointer to the platform device. + * + * @since 1.0 + */ +void PlatformManagerDelDevice(struct PlatformManager *manager, struct PlatformDevice *device); + +/** + * @brief Find a particular device from the manager. + * + * Locate a particular device from the manager by a matching function, witch will be called for + * each device, untill it returns true indicatting a device is "found". + * The device found will be returned with reference count increased. + * + * @param manager Indicates the pointer to the platform manager. + * @param data Indicates the pointer to the data passed to match function. + * @param match Indicates the pointer to the match function. + * + * @return Returns the pointer to the paltform device on success; returns NULL otherwise. + * @since 1.0 + */ +struct PlatformDevice *PlatformManagerFindDevice(struct PlatformManager *manager, void *data, + bool (*match)(struct PlatformDevice *pdevice, void *data)); + +/** + * @brief Get a platform device from the manager by magic. + * + * The device got will be returned with reference count increased. + * + * @param manager Indicates the pointer to the platform manager. + * @param magic Indicates the magic number of the target platform device. + * + * @return Returns the pointer to the paltform device on success; returns NULL otherwise. + * @since 1.0 + */ +struct PlatformDevice *PlatformManagerGetDeviceByMagic(struct PlatformManager *manager, uint32_t magic); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* PLATFORM_MANAGER_H */ diff --git a/support/platform/include/common/platform_queue.h b/support/platform/include/common/platform_queue.h new file mode 100644 index 0000000000000000000000000000000000000000..00b6839e2cc2c25531a3e3f7be67e1844541526e --- /dev/null +++ b/support/platform/include/common/platform_queue.h @@ -0,0 +1,61 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef PLATFORM_QUEUE_H +#define PLATFORM_QUEUE_H + +#include "hdf_dlist.h" +#include "osal_thread.h" +#include "osal_sem.h" +#include "osal_spinlock.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +struct PlatformMsg; +struct PlatformQueue; + +struct PlatformMsg { + struct DListHead node; + struct OsalSem sem; + int32_t code; + int32_t error; + bool block; /* whether need to block thread */ + void *data; +}; + +int32_t PlatformMsgWait(struct PlatformMsg *msg); +typedef int32_t (*PlatformMsgHandle)(struct PlatformQueue *queue, struct PlatformMsg *msg); + +struct PlatformQueue { + const char *name; + OsalSpinlock spin; + struct OsalSem sem; + struct DListHead msgs; + struct OsalThread thread; /* the worker thread of this queue */ + PlatformMsgHandle handle; + void *data; +}; + +void PlatformQueueAddMsg(struct PlatformQueue *queue, struct PlatformMsg *msg); +struct PlatformQueue *PlatformQueueCreate(PlatformMsgHandle handle, const char *name, void *data); +void PlatformQueueDestroy(struct PlatformQueue *queue); +int32_t PlatformQueueStart(struct PlatformQueue *queue); +int32_t PlatformQueueSuspend(struct PlatformQueue *queue); +int32_t PlatformQueueResume(struct PlatformQueue *queue); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* PLATFORM_QUEUE_H */ diff --git a/support/platform/include/mmc/mmc_block.h b/support/platform/include/mmc/mmc_block.h new file mode 100644 index 0000000000000000000000000000000000000000..bec2bb785c3e1b8a73fb816c99ca64dd5193e679 --- /dev/null +++ b/support/platform/include/mmc/mmc_block.h @@ -0,0 +1,29 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef MMC_BLOCK_H +#define MMC_BLOCK_H + +#include "mmc_corex.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +int32_t MmcBlockAdd(struct MmcDevice *mmc); +void MmcBlockDel(struct MmcDevice *mmc); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* MMC_BLOCK_H */ diff --git a/support/platform/include/mmc/mmc_caps.h b/support/platform/include/mmc/mmc_caps.h new file mode 100644 index 0000000000000000000000000000000000000000..7f90d0638f32e0707761f5aff9645ead58b0cb90 --- /dev/null +++ b/support/platform/include/mmc/mmc_caps.h @@ -0,0 +1,104 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef MMC_CAPS_H +#define MMC_CAPS_H + +#include "hdf_base.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +enum MmcVolt { VOLT_3V3 = 0, VOLT_1V8, VOLT_1V2 }; + +enum MmcPowerMode { MMC_POWER_MODE_POWER_OFF = 0, MMC_POWER_MODE_POWER_UP, MMC_POWER_MODE_POWER_ON }; + +enum MmcBusWidth { BUS_WIDTH1 = 0, BUS_WIDTH4 = 2, BUS_WIDTH8 = 3 }; + +enum MmcBusTiming { + BUS_TIMING_MMC_DS = 0, + BUS_TIMING_MMC_HS, + BUS_TIMING_SD_HS, + BUS_TIMING_UHS_SDR12, + BUS_TIMING_UHS_SDR25, + BUS_TIMING_UHS_SDR50, + BUS_TIMING_UHS_SDR104, + BUS_TIMING_UHS_DDR50, + BUS_TIMING_UHS_DDR52, + BUS_TIMING_MMC_HS200, /* for emmc */ + BUS_TIMING_MMC_HS400, /* for emmc */ +}; + +union MmcCaps { + uint32_t capsData; + struct CapsBitsData { + uint32_t cap4Bit : 1; /* bit:0 support 4 bit transfer */ + uint32_t cap8Bit : 1; /* bit:1 support 8 bit transfer */ + uint32_t highSpeed : 1; /* bit:2 support high-speed timing */ + uint32_t sdioIrq : 1; /* bit:3 signal pending SDIO irqs */ + uint32_t onlySpi : 1; /* bit:4 only support spi protocols */ + uint32_t needPoll : 1; /* bit:5 need polling for card-detection */ + uint32_t nonremovable : 1; /* bit:6 Nonremoveable eg. eMMC */ + uint32_t waitWhileBusy : 1; /* bit:7 waits while card is busy */ + uint32_t erase : 1; /* bit:8 allow erase */ + uint32_t ddr1v8 : 1; /* bit:9 support ddr mode at 1.8V */ + uint32_t ddr1v2 : 1; /* bit:10 support ddr mode at 1.2V */ + uint32_t powerOffCard : 1; /* bit:11 support power off after boot */ + uint32_t busWidthTest : 1; /* bit:12 CMD14/CMD19 bus width ok */ + uint32_t uhsSdr12 : 1; /* bit:13 support UHS SDR12 mode */ + uint32_t uhsSdr25 : 1; /* bit:14 support UHS SDR25 mode */ + uint32_t uhsSdr50 : 1; /* bit:15 support UHS SDR50 mode */ + uint32_t uhsSdr104 : 1; /* bit:16 support UHS SDR104 mode */ + uint32_t uhsDdr50 : 1; /* bit:17 support UHS DDR50 mode */ + uint32_t xpc330 : 1; /* bit:18 support >150mA current at 3.3V */ + uint32_t xpc300 : 1; /* bit:19 support >150mA current at 3.0V */ + uint32_t xpc180 : 1; /* bit:20 support >150mA current at 1.8V */ + uint32_t driverTypeA : 1; /* bit:21 support driver type A */ + uint32_t driverTypeC : 1; /* bit:22 support driver type C */ + uint32_t driverTypeD : 1; /* bit:23 support driver type D */ + uint32_t maxCurrentLimit200 : 1; /* bit:24 max current limit 200mA */ + uint32_t maxCurrentLimit400 : 1; /* bit:25 max current limit 400mA */ + uint32_t maxCurrentLimit600 : 1; /* bit:26 max current limit 600mA */ + uint32_t maxCurrentLimit800 : 1; /* bit:27 max current limit 800mA */ + uint32_t cmd23 : 1; /* bit:28 support CMD23 */ + uint32_t hardwareReset : 1; /* bit:29 support hardware reset */ + uint32_t sdSupportProtocol3 : 1; /* bit:30 SD support Protocol 3.0 */ + uint32_t cmdStop : 1; /* bit:31 support cmd stop */ + } bits; +}; + +union MmcCaps2 { + uint32_t caps2Data; + struct Caps2BitsData { + uint32_t bootPartNoAcc : 1; /* bit:0 boot partition no access */ + uint32_t cacheCtrl : 1; /* bit:1 allow cache control */ + uint32_t poweroffNotify : 1; /* bit:2 support notify power off */ + uint32_t noMultiRead : 1; /* bit:3 not support multiblock read */ + uint32_t noSleepCmd : 1; /* bit:4 not support sleep command */ + uint32_t hs200Sdr1v8 : 1; /* bit:5 support hs200 sdr 1.8V */ + uint32_t hs200Sdr1v2 : 1; /* bit:6 support sdr hs200 1.2V */ + uint32_t brokenVoltage : 1; /* bit:7 use broken voltage */ + uint32_t detectNoErr : 1; /* bit:8 I/O err check card removal */ + uint32_t hcEraseSize : 1; /* bit:9 High-capacity erase size */ + uint32_t hs400Support1v8 : 1; /* bit:10 support hs400 1.8V */ + uint32_t hs400Support1v2 : 1; /* bit:11 support hs400 1.2V */ + uint32_t hs400EnhancedStrobe : 1; /* bit:12 support hs400 enhanced strobe */ + uint32_t reserved : 18; /* bits:13~31, reserved */ + } bits; +}; + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* _MMC_CAPS_H */ diff --git a/support/platform/include/mmc/mmc_corex.h b/support/platform/include/mmc/mmc_corex.h new file mode 100644 index 0000000000000000000000000000000000000000..6cdd2d0a666ebf6c9748b96fb3433b3656155b25 --- /dev/null +++ b/support/platform/include/mmc/mmc_corex.h @@ -0,0 +1,318 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef MMC_CORE_H +#define MMC_CORE_H + +#include "hdf_base.h" +#include "hdf_device_desc.h" +#include "hdf_log.h" +#include "mmc_caps.h" +#include "mmc_protocol.h" +#include "osal_mem.h" +#include "osal_mutex.h" +#include "osal_time.h" +#include "platform.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +#define MMC_CNTLR_NR_MAX 3 +#define MMC_DEV_NR_MAX 3 +#define MMC_SEC_SIZE 512 + +#define MMC_MIN(x, y) (((x) < (y)) ? (x) : (y)) +#define MMC_MAX(x, y) (((x) < (y)) ? (y) : (x)) + +struct MmcCntlr; +struct MmcCntlrOps; +struct MmcDevice; +struct MmcMsg; +struct MmcData; +struct MmcCmd; +enum MmcMsgCode; +enum MmcCmdCode; +union MmcDevState; + +enum MmcDevType { + MMC_DEV_EMMC = 0, + MMC_DEV_SD, + MMC_DEV_SDIO, + MMC_DEV_COMBO, + MMC_DEV_INVALID +}; + +struct MmcCntlr { + struct IDeviceIoService service; + struct HdfDeviceObject *hdfDevObj; + struct PlatformDevice device; + struct OsalMutex mutex; + struct OsalSem released; + uint32_t devType; + struct MmcDevice *curDev; + struct MmcCntlrOps *ops; + struct PlatformQueue *msgQueue; + uint16_t index; + uint16_t voltDef; + uint32_t vddBit; + uint32_t freqMin; + uint32_t freqMax; + uint32_t freqDef; + union MmcOcr ocrDef; + union MmcCaps caps; + union MmcCaps2 caps2; + uint32_t maxBlkNum; + uint32_t maxBlkSize; + uint32_t maxReqSize; + bool devPluged; + bool detecting; + void *priv; +}; + +struct MmcCntlrOps { + int32_t (*request)(struct MmcCntlr *cntlr, struct MmcCmd *cmd); + int32_t (*setClock)(struct MmcCntlr *cntlr, uint32_t clock); + int32_t (*setPowerMode)(struct MmcCntlr *cntlr, enum MmcPowerMode mode); + int32_t (*setBusWidth)(struct MmcCntlr *cntlr, enum MmcBusWidth width); + int32_t (*setBusTiming)(struct MmcCntlr *cntlr, enum MmcBusTiming timing); + int32_t (*setSdioIrq)(struct MmcCntlr *cntlr, bool enable); + int32_t (*hardwareReset)(struct MmcCntlr *cntlr); + int32_t (*systemInit)(struct MmcCntlr *cntlr); + int32_t (*setEnhanceSrobe)(struct MmcCntlr *cntlr, bool enable); + int32_t (*switchVoltage)(struct MmcCntlr *cntlr, enum MmcVolt volt); + bool (*devReadOnly)(struct MmcCntlr *cntlr); + bool (*devPluged)(struct MmcCntlr *cntlr); + bool (*devBusy)(struct MmcCntlr *cntlr); + int32_t (*tune)(struct MmcCntlr *cntlr, uint32_t cmdCode); + int32_t (*rescanSdioDev)(struct MmcCntlr *cntlr); +}; + +/* controller management */ +int32_t MmcCntlrAdd(struct MmcCntlr *cntlr); +void MmcCntlrRemove(struct MmcCntlr *cntlr); +struct MmcDevice *MmcCntlrGetDevice(struct MmcCntlr *cntlr); + +static inline struct MmcCntlr *MmcCntlrGet(struct MmcCntlr *cntlr) +{ + if (cntlr != NULL && PlatformDeviceGet(&cntlr->device) != NULL) { + return cntlr; + } + return NULL; +} + +static inline struct MmcCntlr *MmcCntlrGetByNr(uint16_t number) +{ + struct PlatformDevice *device = PlatformManagerGetDeviceByMagic(PlatformManagerGet(PLATFORM_MODULE_MMC), number); + + if (device == NULL) { + return NULL; + } + return CONTAINER_OF(device, struct MmcCntlr, device); +} + +static inline void MmcCntlrPut(struct MmcCntlr *cntlr) +{ + if (cntlr != NULL) { + PlatformDevicePut(&cntlr->device); + } +} + +static inline bool MmcCntlrDevPresent(struct MmcCntlr *cntlr) +{ + return (cntlr != NULL && cntlr->curDev != NULL); +} + +static inline void MmcCntlrLock(struct MmcCntlr *cntlr) +{ + if (cntlr != NULL) { + (void)OsalMutexLock(&cntlr->mutex); + } +} + +static inline void MmcCntlrUnlock(struct MmcCntlr *cntlr) +{ + if (cntlr != NULL) { + (void)OsalMutexUnlock(&cntlr->mutex); + } +} + +/* controller common bussiness */ +int32_t MmcCntlrDoRequest(struct MmcCntlr *cntlr, struct MmcCmd *cmd); +int32_t MmcCntlrAddMsgToQueue(struct MmcCntlr *cntlr, struct MmcCmd *cmd, int32_t code, bool block); +int32_t MmcCntlrAddRequestMsgToQueue(struct MmcCntlr *cntlr, struct MmcCmd *cmd); +int32_t MmcCntlrAddDetectMsgToQueue(struct MmcCntlr *cntlr); +int32_t MmcCntlrAddPlugMsgToQueue(struct MmcCntlr *cntlr); +int32_t MmcCntlrAddSdioRescanMsgToQueue(struct MmcCntlr *cntlr); +int32_t MmcCntlrParse(struct MmcCntlr *cntlr, struct HdfDeviceObject *obj); +int32_t MmcCntlrCreatSdioIrqThread(struct MmcCntlr *cntlr); +void MmcCntlrDestroySdioIrqThread(struct MmcCntlr *cntlr); +void MmcCntlrNotifySdioIrqThread(struct MmcCntlr *cntlr); + +/* controller private bussiness */ +void MmcCntlrSetClock(struct MmcCntlr *cntlr, uint32_t clock); +void MmcCntlrSetBusWidth(struct MmcCntlr *cntlr, enum MmcBusWidth width); +void MmcCntlrSetBusTiming(struct MmcCntlr *cntlr, enum MmcBusTiming timing); +void MmcCntlrSetEnhanceSrobe(struct MmcCntlr *cntlr, bool enable); +int32_t MmcCntlrSwitchVoltage(struct MmcCntlr *cntlr, enum MmcVolt voltage); +bool MmcCntlrDevReadOnly(struct MmcCntlr *cntlr); +bool MmcCntlrDevBusy(struct MmcCntlr *cntlr); +bool MmcCntlrDevPluged(struct MmcCntlr *cntlr); +int32_t MmcCntlrTune(struct MmcCntlr *cntlr, uint32_t cmdCode); +void MmcCntlrSelectWorkVoltage(struct MmcCntlr *cntlr, union MmcOcr *ocr); +void MmcCntlrPowerUp(struct MmcCntlr *cntlr); +void MmcCntlrPowerOff(struct MmcCntlr *cntlr); +int32_t MmcCntlrAllocDev(struct MmcCntlr *cntlr, enum MmcDevType devType); +void MmcCntlrFreeDev(struct MmcCntlr *cntlr); +bool MmcCntlrSupportUhs(struct MmcCntlr *cntlr); +bool MmcCntlrSupportHighSpeed400EnhancedStrobe(struct MmcCntlr *cntlr); +bool MmcCntlrSupportHighSpeed400(struct MmcCntlr *cntlr); +bool MmcCntlrSupportHighSpeed200(struct MmcCntlr *cntlr); +bool MmcCntlrSdSupportCmd23(struct MmcCntlr *cntlr); +bool MmcCntlrEmmcSupportCmd23(struct MmcCntlr *cntlr); + +struct MmcDeviceWorkParam { + uint32_t clock; + enum MmcBusWidth width; + enum MmcBusTiming timing; +}; + +struct MmcRegister { + union MmcOcr ocr; + uint32_t rca; + uint32_t rawCid[CID_LEN]; + uint32_t rawCsd[CSD_LEN]; + struct MmcCid cid; + struct MmcCsd csd; +}; + +union MmcDevState { + uint32_t stateData; + struct StateBitsData { + uint32_t present : 1; + uint32_t readonly : 1; + uint32_t highSpeed : 1; + uint32_t blockAddr : 1; + uint32_t ddrMode : 1; + uint32_t highSpeedDdr : 1; + uint32_t uhs : 1; + uint32_t sdxc : 1; + uint32_t hs200 : 1; + uint32_t sleep : 1; + uint32_t removeable : 1; + uint32_t blkszForByteMode : 1; + uint32_t hs400 : 1; + uint32_t hs400es : 1; + uint32_t reserverd : 17; + } bits; +}; + +struct MmcDevice { + struct PlatformDevice device; + struct MmcCntlr *cntlr; + struct MmcRegister reg; + struct MmcDeviceWorkParam workPara; + union MmcDevState state; + enum MmcDevType type; + size_t secSize; // by bytes + size_t capacity; // by sectors + size_t eraseSize; // by sectors + struct StorageBlock *sb; + void *priv; +}; + +/* device management */ +int32_t MmcDeviceAdd(struct MmcDevice *mmc); +void MmcDeviceAddOps(struct MmcDevice *mmc, void *ops); +void MmcDeviceRemove(struct MmcDevice *mmc); +struct MmcDevice *MmcDeviceGet(struct MmcDevice *mmc); +void MmcDevicePut(struct MmcDevice *mmc); + +/* device business */ +ssize_t MmcDeviceRead(struct MmcDevice *mmc, uint8_t *buf, size_t startSec, size_t nSec); +ssize_t MmcDeviceWrite(struct MmcDevice *mmc, uint8_t *buf, size_t startSec, size_t nSec); +ssize_t MmcDeviceErase(struct MmcDevice *mmc, size_t startSec, size_t nSec); + +static inline bool MmcDeviceIsPresent(struct MmcDevice *mmc) +{ + return (mmc != NULL && mmc->state.bits.present); +} + +struct MmcCmd { + uint32_t cmdCode; + uint32_t argument; + uint32_t resp[MMC_CMD_RESP_SIZE]; + uint32_t respType; + int32_t returnError; + struct MmcData *data; +}; + +#define DATA_WRITE (0x1 << 0) +#define DATA_READ (0x1 << 1) +#define DATA_STREAM (0x1 << 2) +struct MmcData { + uint32_t blockSize; /* data block size, byte */ + uint32_t blockNum; + int32_t returnError; + uint8_t *dataBuffer; + void *scatter; + uint32_t scatterLen; + uint32_t dataFlags; + struct MmcCmd stopCmd; /* stop command */ + bool sendStopCmd; +}; + +enum MmcMsgCode { + MMC_MSG_REQUEST, + MMC_MSG_PLUG, + MMC_MSG_UNPLUG, + MMC_MSG_SDIO_RESCAN, +}; + +struct MmcMsg { + struct PlatformMsg msg; + struct MmcCmd *mmcCmd; +}; + +struct MmcRwData { + uint8_t *buf; + bool writeFlag; + size_t startSector; + size_t sectors; +}; + +int32_t MmcDoDetect(struct MmcCntlr *cntlr); +void MmcDeleteDev(struct MmcCntlr *cntlr); +int32_t MmcSendStatus(struct MmcCntlr *cntlr, uint32_t *status); +int32_t MmcStopTransmission(struct MmcCntlr *cntlr, bool writeFlag, uint32_t *stopStatus); +int32_t MmcSendTuning(struct MmcCntlr *cntlr, uint32_t cmdCode, bool sendStop); +int32_t MmcSendErase(struct MmcCntlr *cntlr, uint32_t startSec, uint32_t nSec); +void MmcSetupReadWriteBlocksCmd(struct MmcDevice *mmc, struct MmcCmd *cmd, struct MmcRwData *info); +int32_t MmcSendReadWriteBlocks(struct MmcCntlr *cntlr, struct MmcRwData *info); +int32_t SdioReinit(struct MmcCntlr *cntlr); +int32_t SdioReadCccrIoEnable(struct MmcCntlr *cntlr, uint8_t *val); +int32_t SdioCccrIntEnable(struct MmcCntlr *cntlr); +int32_t SdioCccrIntDisable(struct MmcCntlr *cntlr); +int32_t SdioCccrIoEnable(struct MmcCntlr *cntlr); +int32_t SdioCccrIoDisable(struct MmcCntlr *cntlr); +int32_t SdioReadCccrIntPending(struct MmcCntlr *cntlr, uint8_t *val); +int32_t SdioReadCccrIoReady(struct MmcCntlr *cntlr, uint8_t *val); +int32_t SdioSetFbrIoBlockSize(struct MmcCntlr *cntlr, uint32_t blkSize); +int32_t SdioReadWriteByte(struct MmcCntlr *cntlr, bool writeFlag, + uint32_t funcNum, uint32_t addr, uint8_t *data); +int32_t SdioReadWriteBlock(struct MmcCntlr *cntlr, struct SdioRwBlockInfo *info); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* MMC_CORE_H */ diff --git a/support/platform/include/mmc/mmc_dispatch.h b/support/platform/include/mmc/mmc_dispatch.h new file mode 100644 index 0000000000000000000000000000000000000000..51db247a773bbc961d02c4b5027cb1e90856e8e7 --- /dev/null +++ b/support/platform/include/mmc/mmc_dispatch.h @@ -0,0 +1,36 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef MMC_DISPATCH_H +#define MMC_DISPATCH_H + +#ifndef __USER__ +#include "devsvc_manager_clnt.h" +#endif + +#ifdef __USER__ +#include "hdf_io_service_if.h" +#endif + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +#ifndef __USER__ +int32_t MmcIoDispatch(struct HdfDeviceIoClient *client, int cmd, struct HdfSBuf *data, struct HdfSBuf *reply); +#endif + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* MMC_DISPATCH_H */ diff --git a/support/platform/include/mmc/mmc_emmc.h b/support/platform/include/mmc/mmc_emmc.h new file mode 100644 index 0000000000000000000000000000000000000000..dadfec8a372e2646da7941da559b05c2d35b2017 --- /dev/null +++ b/support/platform/include/mmc/mmc_emmc.h @@ -0,0 +1,44 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef MMC_EMMC_H +#define MMC_EMMC_H + +#include "emmc_if.h" +#include "mmc_corex.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +struct EmmcRegister { + struct EmmcExtCsd extCsd; +}; + +struct EmmcDevice { + struct MmcDevice mmc; + struct EmmcDeviceOps *emmcOps; + struct EmmcRegister emmcReg; +}; + +struct EmmcDeviceOps { + int32_t (*getCid)(struct EmmcDevice *, uint8_t *, uint32_t); +}; + +int32_t EmmcDeviceGetCid(struct EmmcDevice *dev, uint8_t *cid, uint32_t len); +void EmmcDeviceAddOps(struct EmmcDevice *dev, void *ops); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* MMC_EMMC_H */ diff --git a/support/platform/include/mmc/mmc_protocol.h b/support/platform/include/mmc/mmc_protocol.h new file mode 100644 index 0000000000000000000000000000000000000000..7bab66d510d73fd5d9880fd0317315593f5df5a8 --- /dev/null +++ b/support/platform/include/mmc/mmc_protocol.h @@ -0,0 +1,1191 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef MMC_PROTOCOL_H +#define MMC_PROTOCOL_H + +#include "hdf_base.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +#define CID_LEN 4 +#define CID_BITS 128 +#define CID_BYTES_LEN 16 +#define CID_PNM_LEN 8 +#define CSD_LEN 4 +#define CSD_BITS 128 +#define EXT_CSD_BYTES_LEN 512 +#define BITS_PER_BYTE 8 +#define SCR_LEN 2 +#define SCR_BYTES_LEN 8 +#define SCR_BITS 64 +#define DSR_BITS 16 +#define SSR_LEN 16 +#define SSR_BYTES_LEN 64 +#define SSR_BITS 512 + +/* The basic unit data transfer is a block whose maximum size is always 512 bytes. */ +#define BYTES_PER_BLOCK 512 +#define MMC_MAX_BLOCKSIZE_SHIFT 9 + +#define SDIO_BLOCK_TRANSFER_MAX_BLKNUM 511 + +/* mmc commands */ +enum MmcCmdCode { + GO_IDLE_STATE = 0, /* resets all cards to idle state */ + SEND_OP_COND = 1, /* + * asks all cards in idle state to send their operation conditions register contents + * in the response on the CMD line. + */ + ALL_SEND_CID = 2, /* asks all cards to send their CID numbers on the CMD line. */ + SET_RELATIVE_ADDR = 3, /* assigns relative address to the card. */ + SET_DSR = 4, /* programs the DSR of all cards. */ + SWITCH = 6, /* Switches the mode of operation of the selected card or modifies the EXT_CSD registers. */ + SELECT_CARD = 7, /* + * command toggles a card between the stand-by and transfer states or between the programming and + * disconnect states. In both cases the card is selected by its own relative address and gets + * deselected by any other address; address 0 deselects all. + */ + SEND_EXT_CSD = 8, /* The card sends its EXT_CSD register as a block of data. */ + SEND_CSD = 9, /* addressed card sends its card-specific data (CSD) on the CMD line. */ + SEND_CID = 10, /* addressed card sends its card identification (CID) on CMD the line. */ + READ_DAT_UNTIL_STOP = 11, /* + * reads data stream from the card, starting at the given address, + * until a STOP_TRANSMISSION follows. + */ + STOP_TRANSMISSION = 12, /* + * Terminates a read/write stream/multiple block operation. + * When CMD12 is used to terminate a read transaction the card will respond with R1. + * When it is used to stop a write transaction the card will respondwith R1b. + */ + SEND_STATUS = 13, /* addressed card sends its status register. */ + GO_INACTIVE_STATE = 15, /* + * sets the card to inactive state in order to protect the card stack against + * communication breakdowns. + */ + SET_BLOCKLEN = 16, /* + * sets the block length (in bytes) for all following block commands (read and write). + * Default block length is specified in the CSD. + */ + READ_SINGLE_BLOCK = 17, /* reads a block of the size selected by the SET_BLOCKLEN command. */ + READ_MULTIPLE_BLOCK = 18, /* + * continuously transfers data blocks from card to host until interrupted + * by a stop command or the requested number of data block transmitted. + */ + WRITE_DAT_UNTIL_STOP = 20, /* + * writes data stream from the host, starting at the given address, + * until a STOP_TRANSMISSION follows. + */ + SEND_TUNING_BLOCK_HS200 = 21, /* + * Dedicated for HS200 mode, used to optimize host sampling points. + * The host sends the CMD21 command, and the device sends the data block in tuning + * mode. The host collects data at different sampling points to find the best + * sampling point. + */ + SET_BLOCK_COUNT = 23, /* + * Defines the number of blocks which are going to be transferred in the immediately + * succeeding multiple block read or write command. + */ + WRITE_BLOCK = 24, /* writes a block of the size selected by the SET_BLOCKLEN command. */ + WRITE_MULTIPLE_BLOCK = 25, /* + * continuously writes blocks of data until a STOP_TRANSMISSION follows + * or the requested number of block received. + */ + PROGRAM_CID = 26, /* + * programming of the card identification register. This command shall be issued only once + * per card. The card contains hardware to prevent this operation after the first programming. + * Normally this command is reserved for the manufacturer. + */ + PROGRAM_CSD = 27, /* programming of the programmable bits of the CSD. */ + SET_WRITE_PROT = 28, /* + * if the card has write protection features, + * this command sets the write protection bit of the addressed group. + * The properties of write protection are coded in the card specific data (WP_GRP_SIZE). + */ + CLR_WRITE_PROT = 29, /* + * if the card provides write protection features, + * this command clears the write protection bit of the addressed group. + */ + SEND_WRITE_PROT = 30, /* + * if the card provides write protection features, + * this command asks the card to send the status of the write protection bits. + */ + ERASE_GROUP_START = 35, /* sets the address of the first erase group within a range to be selected for erase. */ + ERASE_GROUP_END = 36, /* + * sets the address of the last erase group within a continuous + * range to be selected for erase. + */ + MMC_ERASE = 38, /* erases all previously selected write blocks. */ + MMC_FAST_IO = 39, /* + * used to write and read 8 bit (register) data fields. The command addresses a card and + * a register and provides the data for writing if the write flag is set. The R4 response + * contains data read from the addressed register. This command accesses application + * dependent registers which are not defined in the MultiMediaCard standard. + */ + GO_IRQ_STATE = 40, /* Sets the system into interrupt mode. */ + LOCK_UNLOCK = 42, /* + * Used to set/reset the password or lock/ unlock the card. + * The size of the data block is set by the SET_BLOCK_LEN command. + */ + APP_CMD = 55, /* + * Indicates to the card that the next command is an application specific command + * rather than a standard command. + */ + GEN_CMD = 56, /* + * Used either to transfer a data block to the card or to get a data block from the card + * for general purpose / application specific commands. + * The size of the data block shall be set by the SET_BLOCK_LEN command. + */ +}; + +#define MMC_CARD_BUSY_STATUS 0x80000000 /* Card Power up status bit */ + +/* OCR bit definitions */ +enum MmcOcrBitsMark { + MMC_OCR_1V65_1V95 = 0x00000080, /* VDD 1.65V ~ 1.95V */ + MMC_OCR_2V0_2V1 = 0x00000100, /* VDD 2.0V ~ 2.1V */ + MMC_OCR_2V1_2V2 = 0x00000200, /* VDD 2.1V ~ 2.2V */ + MMC_OCR_2V2_2V3 = 0x00000400, /* VDD 2.2V ~ 2.3V */ + MMC_OCR_2V3_2V4 = 0x00000800, /* VDD 2.3V ~ 2.4V */ + MMC_OCR_2V4_2V5 = 0x00001000, /* VDD 2.4V ~ 2.5V */ + MMC_OCR_2V5_2V6 = 0x00002000, /* VDD 2.5V ~ 2.6V */ + MMC_OCR_2V6_2V7 = 0x00004000, /* VDD 2.6V ~ 2.7V */ + MMC_OCR_2V7_2V8 = 0x00008000, /* VDD 2.7V ~ 2.8V */ + MMC_OCR_2V8_2V9 = 0x00010000, /* VDD 2.8V ~ 2.9V */ + MMC_OCR_2V9_3V0 = 0x00020000, /* VDD 2.9V ~ 3.0V */ + MMC_OCR_3V0_3V1 = 0x00040000, /* VDD 3.0V ~ 3.1V */ + MMC_OCR_3V1_3V2 = 0x00080000, /* VDD 3.1V ~ 3.2V */ + MMC_OCR_3V2_3V3 = 0x00100000, /* VDD 3.2V ~ 3.3V */ + MMC_OCR_3V3_3V4 = 0x00200000, /* VDD 3.3V ~ 3.4V */ + MMC_OCR_3V4_3V5 = 0x00400000, /* VDD 3.4V ~ 3.5V */ + MMC_OCR_3V5_3V6 = 0x00800000, /* VDD 3.5V ~ 3.6V */ +}; + +/* ocr register describe */ +union MmcOcr { + uint32_t ocrData; + struct OcrBits { + uint32_t reserved : 7; + uint32_t vdd1v65To1v95 : 1; /* bit:7 voltage 1.65 ~ 1.95 */ + uint32_t vdd2v0To2v1 : 1; /* bit:8 voltage 2.0 ~ 2.1 */ + uint32_t vdd2v1To2v2 : 1; /* bit:9 voltage 2.1 ~ 2.2 */ + uint32_t vdd2v2To2v3 : 1; /* bit:10 voltage 2.2 ~ 2.3 */ + uint32_t vdd2v3To2v4 : 1; /* bit:11 voltage 2.3 ~ 2.4 */ + uint32_t vdd2v4To2v5 : 1; /* bit:12 voltage 2.4 ~ 2.5 */ + uint32_t vdd2v5To2v6 : 1; /* bit:13 voltage 2.5 ~ 2.6 */ + uint32_t vdd2v6To2v7 : 1; /* bit:14 voltage 2.6 ~ 2.7 */ + uint32_t vdd2v7To2v8 : 1; /* bit:15 voltage 2.7 ~ 2.8 */ + uint32_t vdd2v8To2v9 : 1; /* bit:16 voltage 2.8 ~ 2.9 */ + uint32_t vdd2v9To3v0 : 1; /* bit:17 voltage 2.9 ~ 3.0 */ + uint32_t vdd3v0To3v1 : 1; /* bit:18 voltage 3.0 ~ 3.1 */ + uint32_t vdd3v1To3v2 : 1; /* bit:19 voltage 3.1 ~ 3.2 */ + uint32_t vdd3v2To3v3 : 1; /* bit:20 voltage 3.2 ~ 3.3 */ + uint32_t vdd3v3To3v4 : 1; /* bit:21 voltage 3.3 ~ 3.4 */ + uint32_t vdd3v4To3v5 : 1; /* bit:22 voltage 3.4 ~ 3.5 */ + uint32_t vdd3v5To3v6 : 1; /* bit:23 voltage 3.5 ~ 3.6 */ + uint32_t s18r : 1; /* bit:24 switch to 1.8v accepted */ + uint32_t reserved2 : 2; + uint32_t sdioMemoryPreset : 1; /* bit:27 sdio memory present */ + uint32_t sdXpc : 1; /* bit:28 XPC for ACMD41 */ + uint32_t sdUhsII : 1; /* bit:29 UHSII for resp of ACMD41 */ + uint32_t hcs : 1; /* bit:30 support high capacity */ + uint32_t busy : 1; /* bit:31 This bit is set to LOW if the card has not finished the power up routine */ + } bits; +}; + +/* SEND_STATUS rsp, card status bits */ +enum MmcRspCardStatus { + OUT_OF_RANGE = (1 << 31), /* The command's argument was out of the allowed range for this card. */ + ADDRESS_ERROR = (1 << 30), /* A misaligned address which did not match the block length was used in the command. */ + BLOCK_LEN_ERROR = (1 << 29), /* The transferred block length is not allowed for this card, + * or the number of transferred bytes does not match the block length. + */ + ERASE_SEQ_ERROR = (1 << 28), /* An error in the sequence of erase commands occurred. */ + ERASE_PARAM = (1 << 27), /* An invalid selection of erase groups for erase occurred. */ + WP_VIOLATION = (1 << 26), /* Attempt to program a write protected block. */ + CARD_IS_LOCKED = (1 << 25), /* When set, signals that the card is locked by the host. */ + LOCK_UNLOCK_FAILED = (1 << 24), /* Set when a sequence or password error has been detected in lock/unlock card + * command or if there was an attempt to access a locked card. + */ + COM_CRC_ERROR = (1 << 23), /* The CRC check of the previous command failed. */ + ILLEGAL_COMMAND = (1 << 22), /* Command not legal for the card state. */ + CARD_ECC_FAILED = (1 << 21), /* Card internal ECC was applied but failed to correct the data. */ + CC_ERROR = (1 << 20), /* Internal card controller error. */ + GENERAL_ERROR = (1 << 19), /* A general or an unknown error occurred during the operation. */ + UNDERRUN = (1 << 18), /* The card could not sustain data transfer in stream read mode. */ + OVERRUN = (1 << 17), /* The card could not sustain data programming in stream write mode. */ + CID_CSD_OVERWRITE = (1 << 16), /* can be either one of the following errors: + * - The CID register has been already written and can not be overwritten + * - The read only section of the CSD does not match the card content. + * - An attempt to reverse the copy (set as original) or permanent WP (unprotected) + * bits was made. + */ + WP_ERASE_SKIP = (1 << 15), /* Only partial address space was erased due to existing write protected blocks. */ + CARD_ECC_DISABLED = (1 << 14), /* The command has been executed without using the internal ECC. */ + ERASE_RESET = (1 << 13), /* An erase sequence was cleared before executing because an out of erase sequence + * command was received. + */ + READY_FOR_DATA = (1 << 8), /* corresponds to buffer empty signalling on the bus. */ + SWITCH_ERROR = (1 << 7), /* If set, the card did not switch to the expected mode as requested by + * the SWITCH command. + */ + URGENT_BKOPS = (1 << 6), /* If set, device needs to perform background operations urgently. + * Host can check EXT_CSD field BKOPS_STATUS for the detailed level. + */ + IS_APP_CMD = (1 << 5), /* The card will expect ACMD, or indication that the command has been + * interpreted as ACMD. + */ +}; + +/* + * The state of the card when receiving the command. If the command execution causes a + * state change, it will be visible to the host in the response to the next command. + * The four bits([12:9]) are interpreted as a binary coded number between 0 and 15. + */ +#define MMC_CARD_CURRENT_STATE(x) ((x & 0x00001E00) >> 9) /* sx, b (4 bits) */ +enum MmcCardCurrentState { + STATE_IDLE = 0, + STATE_READY = 1, + STATE_IDENT = 2, + STATE_STBY = 3, + STATE_TRAN = 4, + STATE_DATA = 5, + STATE_RCV = 6, + STATE_PRG = 7, + STATE_DIS = 8, +}; + +/* addressed (point-to-point) commands (ac) sent on CMD line, response on CMD line. */ +#define MMC_CMD_TYPE_AC (0x0 << 5) + +/* + * addressed (point-to-point) data transfer commands (adtc) sent on CMD line, response on CMD line, + * data transfer on DAT line. + */ +#define MMC_CMD_TYPE_ADTC (0x1 << 5) + +/* broadcast commands (bc) sent on CMD line, no response. */ +#define MMC_CMD_TYPE_BC (0x2 << 5) + +/* broadcast commands with response (bcr) sent on CMD line, response (all cards simultaneously) on CMD line. */ +#define MMC_CMD_TYPE_BCR (0x3 << 5) + +#define MMC_CMD_RESP_SIZE 4 +/* cmd resp type */ +#define RESP_PRESENT (1 << 0) +#define RESP_136 (1 << 1) /* 136 bit response */ +#define RESP_CRC (1 << 2) /* expect valid crc */ +#define RESP_BUSY (1 << 3) /* card may send busy */ +#define RESP_CMDCODE (1 << 4) /* response contains opcode */ + +#define MMC_RESP_NONE 0 +#define MMC_RESP_R1 (RESP_PRESENT | RESP_CMDCODE | RESP_CRC) +#define MMC_RESP_R1B (RESP_PRESENT | RESP_CMDCODE | RESP_BUSY | RESP_CRC) +#define MMC_RESP_R2 (RESP_PRESENT | RESP_136 | RESP_CRC) +#define MMC_RESP_R3 (RESP_PRESENT) +#define MMC_RESP_R4 (RESP_PRESENT) +#define MMC_RESP_R5 (RESP_PRESENT | RESP_CMDCODE | RESP_CRC) +#define MMC_RESP_R6 (RESP_PRESENT | RESP_CMDCODE | RESP_CRC) +#define MMC_RESP_R7 (RESP_PRESENT | RESP_CMDCODE | RESP_CRC) + +#define MMC_RESP_TYPE(cmd) ((cmd)->respType & (RESP_PRESENT | RESP_136 | RESP_CRC | RESP_BUSY | RESP_CMDCODE)) + +#define MMC_RESP_SPI_S1 (1 << 7) +#define MMC_RESP_SPI_S2 (1 << 8) +#define MMC_RESP_SPI_B4 (1 << 9) +#define MMC_RESP_SPI_BUSY (1 << 10) + +#define MMC_RESP_SPI_R1 (MMC_RESP_SPI_S1) +#define MMC_RESP_SPI_R1B (MMC_RESP_SPI_S1 | MMC_RESP_SPI_BUSY) +#define MMC_RESP_SPI_R2 (MMC_RESP_SPI_S1 | MMC_RESP_SPI_S2) +#define MMC_RESP_SPI_R3 (MMC_RESP_SPI_S1 | MMC_RESP_SPI_B4) +#define MMC_RESP_SPI_R4 (MMC_RESP_SPI_S1 | MMC_RESP_SPI_B4) +#define MMC_RESP_SPI_R5 (MMC_RESP_SPI_S1 | MMC_RESP_SPI_S2) +#define MMC_RESP_SPI_R7 (MMC_RESP_SPI_S1 | MMC_RESP_SPI_B4) + +enum MmcCsdStructure { + MMC_CSD_STRUCTURE_VER_1_0 = 0, + MMC_CSD_STRUCTURE_VER_1_1 = 1, + MMC_CSD_STRUCTURE_VER_1_2 = 2, /* Specification Version 4.1-4.2-4.3 */ + MMC_CSD_STRUCTURE_VER_OTHER = 3, /* Version is coded int the CSD_STRUCTURE byte in the EXT_CSD register. */ +}; + +enum MmcCsdSpecVersion { + MMC_CSD_SPEC_VER_0 = 0, + MMC_CSD_SPEC_VER_1 = 1, + MMC_CSD_SPEC_VER_2 = 2, + MMC_CSD_SPEC_VER_3 = 3, + MMC_CSD_SPEC_VER_4 = 4, +}; + +enum MmcCsdCardCmdClass { + MMC_CSD_CCC_BASIC = (1 << 0), + MMC_CSD_CCC_BLOCK_READ = (1 << 2), + MMC_CSD_CCC_BLOCK_WRITE = (1 << 4), + MMC_CSD_CCC_ERASE = (1 << 5), + MMC_CSD_CCC_WRITE_PROT = (1 << 6), + MMC_CSD_CCC_LOCK_CARD = (1 << 7), + MMC_CSD_CCC_APP_SPEC = (1 << 8), + MMC_CSD_CCC_IO_MODE = (1 << 9), + MMC_CSD_CCC_SWITCH = (1 << 10), +}; + +enum MmcBusMode { + MMC_BUS_MODE_NULL = 0, + MMC_1_2V_DDR_MODE = 1, + MMC_1_8V_DDR_MODE = 2, + MMC_1_2V_SDR_MODE = 3, + MMC_1_8V_SDR_MODE = 4, +}; + +/* CID register define */ +struct MmcCid { + uint32_t mid; /* Manufacturer ID */ + char pnm[CID_PNM_LEN]; /* Product name */ + uint32_t psn; /* Product serial number */ + uint16_t oid; /* OEM/Application ID */ + uint16_t year; /* Manufacturing date: The y field */ + uint8_t month; /* Manufacturing date: The m field */ + /* + * The product revision is composed of two Binary Coded Decimal (BCD) digits, four bits each, + * representing an "n.m" revision number. + */ + uint8_t hwPrv; /* Product revision: n */ + uint8_t fwPrv; /* Product revision: m */ +}; + +/* CSD register define */ +struct MmcCsd { + uint8_t structure; /* CSD_STRUCTURE: [127:126] */ + uint8_t specVers; /* emmc-->SPEC_VERS: [125:122] */ + uint16_t ccc; /* card command classes: [95:84] */ + uint16_t taccClks; /* data read access-time-2 in CLK(NSAC)[111:104]: The unit for NSAC is 100 clock cycles. */ + uint32_t taccNs; /* data read access-time-1(TAAC)[119:112]: time unit[2:0] * time value[6:3] */ + uint32_t cSize; /* device size(C_SIZE): [73:62] */ + uint32_t r2wFactor; /* write speed factor(R2W_FACTOR): [28:26] */ + uint32_t maxDtr; /* max data transfer rate[103:96]: transfer rate unit[2:0] * time value[6:3] */ + uint32_t eraseSize; /* erase sector size: [45:39], emmc and sd are different. */ + uint32_t readBlkLen; /* max read data block length(READ_BL_LEN): [83:80] */ + uint32_t writeBlkLen; /* max write data block length(WRITE_BL_LEN): [25:22] */ + uint32_t capacity; /* see CSD Field C_SIZE. */ + uint32_t rdPartial : 1, /* partial blocks for read allowed: [79] */ + rdMisalign : 1, /* read block misalignment: [77] */ + wrPartial : 1, /* partial blocks for write allowed: [21] */ + wrMisalign : 1; /* write block misalignment: [78] */ +}; + +/* Emmc Extended CSD fields */ +enum EmmcExtendedCsdField { + EMMC_EXT_CSD_ENH_START_ADDR = 136, /* R/W, 4 bytes */ + EMMC_EXT_CSD_ENH_SIZE_MULT = 140, /* R/W, 3 bytes */ + EMMC_EXT_CSD_GP_SIZE_MULT = 143, /* R/W, 12 bytes */ + EMMC_EXT_CSD_PARTITIONS_ATTRIBUTE = 156, /* R/W */ + EMMC_EXT_CSD_PARTITIONING_SUPPORT = 160, /* RO */ + EMMC_EXT_CSD_HPI_MGMT = 161, /* R/W */ + EMMC_EXT_CSD_RST_N_FUNCTION = 162, /* R/W */ + EMMC_EXT_CSD_WR_REL_PARAM = 166, /* RO */ + EMMC_EXT_CSD_BOOT_WP = 173, /* R/W */ + EMMC_EXT_CSD_ERASE_GROUP_DEF = 175, /* R/W */ + EMMC_EXT_CSD_PARTITION_CONFIG = 179, /* R/W */ + EMMC_EXT_CSD_ERASED_MEM_CONT = 181, /* RO */ + EMMC_EXT_CSD_BUS_WIDTH = 183, /* R/W */ + EMMC_EXT_CSD_STROBE_SUPPORT = 184, /* RO */ + EMMC_EXT_CSD_HS_TIMING = 185, /* R/W */ + EMMC_EXT_CSD_POWER_CLASS = 187, /* R/W */ + EMMC_EXT_CSD_REV = 192, /* RO */ + EMMC_EXT_CSD_STRUCTURE = 194, /* RO */ + EMMC_EXT_CSD_CARD_TYPE = 196, /* RO */ + EMMC_EXT_CSD_OUT_OF_INTERRUPT_TIME = 198, /* RO */ + EMMC_EXT_CSD_PARTITION_SWITCH_TIME = 199, /* RO */ + EMMC_EXT_CSD_PWR_CL_52_195 = 200, /* RO */ + EMMC_EXT_CSD_PWR_CL_26_195 = 201, /* RO */ + EMMC_EXT_CSD_PWR_CL_52_360 = 202, /* RO */ + EMMC_EXT_CSD_PWR_CL_26_360 = 203, /* RO */ + EMMC_EXT_CSD_SEC_CNT = 212, /* RO, 4 bytes */ + EMMC_EXT_CSD_S_A_TIMEOUT = 217, /* RO */ + EMMC_EXT_CSD_HC_WP_GRP_SIZE = 221, /* RO */ + EMMC_EXT_CSD_REL_WR_SEC_C = 222, /* RO */ + EMMC_EXT_CSD_ERASE_TIMEOUT_MULT = 223, /* RO */ + EMMC_EXT_CSD_HC_ERASE_GRP_SIZE = 224, /* RO */ + EMMC_EXT_CSD_BOOT_MULTI = 226, /* RO */ + EMMC_EXT_CSD_SEC_TRIM_MULT = 229, /* RO */ + EMMC_EXT_CSD_SEC_ERASE_MULT = 230, /* RO */ + EMMC_EXT_CSD_SEC_FEATURE_SUPPORT = 231, /* RO */ + EMMC_EXT_CSD_TRIM_MULT = 232, /* RO */ + EMMC_EXT_CSD_PWR_CL_200_195 = 236, /* RO */ + EMMC_EXT_CSD_PWR_CL_200_360 = 237, /* RO */ + EMMC_EXT_CSD_PWR_CL_DDR_52_195 = 238, /* RO */ + EMMC_EXT_CSD_PWR_CL_DDR_52_360 = 239, /* RO */ + EMMC_EXT_CSD_HPI_FEATURES = 503, /* RO */ +}; + +#define EMMC_EXT_CSD_SEC_CNT_BYTES 4 +#define EMMC_EXT_CSD_ENH_START_ADDR_BYTES 4 +#define EMMC_EXT_CSD_GP_SIZE_MULT_BYTES 12 +#define EMMC_EXT_CSD_ENH_SIZE_MULT_BYTES 3 + +/* PARTITION_CONFIG[179], bit2-bit0: PARTITION_ACCESS. */ +#define EMMC_EXT_CSD_PART_CONFIG_ACCESS_MASK (0x7) + +enum EmmcExtCsdStructure { + EMMC_EXT_CSD_STRUCTURE_VER_1_0 = 0, + EMMC_EXT_CSD_STRUCTURE_VER_1_1 = 1, + EMMC_EXT_CSD_STRUCTURE_VER_1_2 = 2, /* Specification Version 4.1-4.2-4.3-4.4 */ + EMMC_EXT_CSD_STRUCTURE_VER_OTHER = 3, /* Reserved for future use. */ +}; + +enum EmmcExtCsdRev { + EMMC_EXT_CSD_REV_1_0 = 0, /* Revision 1.0(for MMC v4.0) */ + EMMC_EXT_CSD_REV_1_1 = 1, /* Revision 1.1(for MMC v4.1) */ + EMMC_EXT_CSD_REV_1_2 = 2, /* Revision 1.2(for MMC v4.2) */ + EMMC_EXT_CSD_REV_1_3 = 3, /* Revision 1.3(for MMC v4.3) */ + EMMC_EXT_CSD_REV_1_4 = 4, /* Revision 1.4(Obsolete) */ + EMMC_EXT_CSD_REV_1_5 = 5, /* Revision 1.5(for MMC v4.4.1) */ + EMMC_EXT_CSD_REV_OTHER = 6, /* Reserved */ +}; + +#define EMMC_EXT_CSD_CARD_TYPE_MASK 0x3F /* Mask out reserved bits */ + +enum EmmcExtCsdCardType { + EMMC_EXT_CSD_CARD_TYPE_26 = 0x01, /* Card can run at 26MHz */ + EMMC_EXT_CSD_CARD_TYPE_52 = 0x02, /* Card can run at 52MHz */ + EMMC_EXT_CSD_CARD_TYPE_DDR_1_8V = 0x04, /* DDR mode @1.8V or 3V I/O, Card can run at 52MHz */ + EMMC_EXT_CSD_CARD_TYPE_DDR_1_2V = 0x08, /* DDR mode @1.2V I/O, Card can run at 52MHz */ + EMMC_EXT_CSD_CARD_TYPE_DDR_52 = 0x0C, /* EMMC_EXT_CSD_CARD_TYPE_DDR_1_8V | EMMC_EXT_CSD_CARD_TYPE_DDR_1_2V */ + EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V = 0x10, /* Card can run at 200MHz */ + EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V_ALL = 0x13, + EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V_ALL_DDR_1_8V = 0x17, + EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V_ALL_DDR_1_2V = 0x1B, + EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V_ALL_DDR_52 = 0x1F, + EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V = 0x20, /* SDR mode @1.2V I/O, Card can run at 200MHz */ + EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V_ALL = 0x23, + EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V_ALL_DDR_1_8V = 0x27, + EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V_ALL_DDR_1_2V = 0x2B, + EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V_ALL_DDR_52 = 0x2F, + EMMC_EXT_CSD_CARD_TYPE_SDR_200 = 0x30, + EMMC_EXT_CSD_CARD_TYPE_SDR_ALL = 0x33, + EMMC_EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V = 0x37, + EMMC_EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V = 0x3B, + EMMC_EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52 = 0x3F, + EMMC_EXT_CSD_CARD_TYPE_HS400_1_8V = 0x40, /* Card can run at 200MHz DDR, 1.8V */ + EMMC_EXT_CSD_CARD_TYPE_HS400_1_2V = 0x80, /* Card can run at 200MHz DDR, 1.2V */ + EMMC_EXT_CSD_CARD_TYPE_HS400 = 0xC0, + EMMC_EXT_CSD_CARD_TYPE_HS400ES = 0x100, /* Card can run at HS400ES */ +}; + +enum EmmcExtCsdHighSpeedMaxDtr { + EMMC_EXT_CSD_HIGH_SPEED_26 = 26000000, /* 26M */ + EMMC_EXT_CSD_HIGH_SPEED_52 = 52000000, /* 52M */ + EMMC_EXT_CSD_HIGH_SPEED_200 = 200000000, /* 200M */ +}; + +/* High Priority Interrupt */ +enum EmmcExtCsdHpiFeatures { + EMMC_EXT_CSD_HPI_SUPPORT = (1 << 0), /* HPI mechanism supported flag. */ + EMMC_EXT_CSD_HPI_IMPLEMENTATION = (1 << 1), /* 0x0 : HPI mechanism implementation based on CMD13. + * 0x1 : HPI mechanism implementation based on CMD12. + */ +}; + +/* EXT_CSD access modes */ +enum EmmcExtCsdAccessMode { + EMMC_EXT_CSD_CMD_SET = 0x00, /* The command set is changed according to the Cmd Set field of the argument. */ + EMMC_EXT_CSD_SET_BITS = 0x01, /* + * The bits in the pointed byte are set, + * according to the '1' bits in the Value field. + */ + EMMC_EXT_CSD_CLEAR_BITS = 0x02, /* + * The bits in the pointed byte are cleared, + * according to the '1' bits in the Value field. + */ + EMMC_EXT_CSD_WRITE_BYTE = 0x03, /* The Value field is written into the pointed byte. */ +}; + +enum EmmcExtCsdBusWidth { + EMMC_EXT_CSD_BUS_WIDTH_1 = 0, /* 1 bit mode */ + EMMC_EXT_CSD_BUS_WIDTH_4 = 1, /* 4 bit mode */ + EMMC_EXT_CSD_BUS_WIDTH_8 = 2, /* 8 bit mode */ + EMMC_EXT_CSD_DDR_BUS_WIDTH_4 = 5, /* 4 bit DDR mode */ + EMMC_EXT_CSD_DDR_BUS_WIDTH_8 = 6, /* 8 bit DDR mode */ + EMMC_EXT_CSD_BUS_WIDTH_STROBE = (1 << 7), /* Enhanced strobe mode */ +}; + +enum EmmcExtCsdBusTiming { + EMMC_EXT_CSD_BUS_TIMING_HS = 1, /* High speed */ + EMMC_EXT_CSD_BUS_TIMING_HS200 = 2, /* HS200 */ + EMMC_EXT_CSD_BUS_TIMING_HS400 = 3, /* HS400 */ + EMMC_EXT_CSD_BUS_DRV_STR_SHIFT = 4, /* Driver Strength shift */ +}; + +enum EmmcExtCsdCmdSet { + EMMC_EXT_CSD_CMD_SET_NORMAL = (1 << 0), + EMMC_EXT_CSD_CMD_SET_SECURE = (1 << 1), + EMMC_EXT_CSD_CMD_SET_CPSECURE = (1 << 2), +}; + +/* + * PWR_CL_ff_vvv, PWR_CL_DDR_ff_vvv. + * Bits [7:4] code the current consumption for the 8 bit bus configuration. + * Bits [3:0] code the current consumption for the 4 bit bus configuration. + */ +#define EMMC_EXT_CSD_PWR_CL_8BIT_MASK 0xF0 /* 8 bit PWR CLS */ +#define EMMC_EXT_CSD_PWR_CL_4BIT_MASK 0x0F /* 8 bit PWR CLS */ +#define EMMC_EXT_CSD_PWR_CL_8BIT_SHIFT 4 +#define EMMC_EXT_CSD_PWR_CL_4BIT_SHIFT 0 + +/* + * S_A_TIMEOUT [217]. + * Max register value defined is 0x17 which equals 838.86ms timeout. Values between 0x18 and 0xFF are reserved. + */ +#define MAX_S_A_TIMEOUT_VALUE 0x17 + +/* + * PARTITIONING_SUPPORT [160]. + * Bit[7:2]: Reserved. + * Bit[1]: ENH_ATTRIBUTE_EN. + * 0x0: Device can not have enhanced technological features in partitions and user data area. + * 0x1: Device can have enhanced technological features in partitions and user data area. + * Bit[0]: PARTITIONING_EN. + * 0x0: Device does not support partitioning features. + * 0x1: Device supports partitioning features. + * Partitioning feature is optional for device. + */ +#define PARTITIONING_SUPPORT_ENH_ATTRIBUTE_EN (1 << 1) +#define PARTITIONING_SUPPORT_PARTITIONING_EN (1 << 0) + +/* + * PARTITIONS_ATTRIBUTE [156]. + * Bit[7:5]: Reserved. + * Bit[4]: ENH_4. 0x0: Default; 0x1: Set Enhanced attribute in General Purpose partition 4. + * Bit[3]: ENH_3. 0x0: Default; 0x1: Set Enhanced attribute in General Purpose partition 3. + * Bit[2]: ENH_2. 0x0: Default; 0x1: Set Enhanced attribute in General Purpose partition 2. + * Bit[1]: ENH_1. 0x0: Default; 0x1: Set Enhanced attribute in General Purpose partition 1. + * Bit[0]: ENH_USR. 0x0: Default; 0x1: Set Enhanced attribute in User Data Area. + */ +#define PARTITIONS_ATTRIBUTE_ENH_4 (1 << 4) +#define PARTITIONS_ATTRIBUTE_ENH_3 (1 << 3) +#define PARTITIONS_ATTRIBUTE_ENH_2 (1 << 2) +#define PARTITIONS_ATTRIBUTE_ENH_1 (1 << 1) +#define PARTITIONS_ATTRIBUTE_ENH_USR (1 << 0) + +/* + * The Extended CSD register defines the card properties and selected modes. It is 512 bytes long. The most + * significant 320 bytes are the Properties segment, which defines the card capabilities and cannot be modified + * by the host. The lower 192 bytes are the Modes segment, which defines the configuration the card is + * working in. These modes can be changed by the host by means of the SWITCH command. + */ +struct EmmcExtCsd { + uint8_t rev; /* EXT_CSD_REV: [192] */ + uint8_t eraseGroupDef; /* ERASE_GROUP_DEF: [175] */ + uint8_t secFeatureSupport; /* SEC_FEATURE_SUPPORT: [231] */ + uint8_t relWrSecorCount; /* Reliable write sector count(REL_WR_SEC_C): [222] */ + uint8_t wrRelParam; /* Write reliability parameter register(WR_REL_PARAM): [166] */ + uint8_t partConfig; /* PARTITION_CONFIG: [179] */ + uint32_t partSwitchTime; /* PARTITION_SWITCH_TIME:[199], ms */ + uint32_t saTimeout; /* Sleep/Awake Timeout = 100ns * 2^S_A_timeout, S_A_TIMEOUT: [217], Units: 100ns */ + uint32_t hsMaxDtr; /* high-speed max data transfer rate, according to cardType */ + uint32_t sectors; /* SEC_COUNT: [215:212], 512B/secter */ + uint32_t cardType; /* see rawCardType */ + uint32_t hcEraseSize; /* High-capacity erase unit size: [224] */ + uint32_t hcEraseTimeout; /* High-capacity erase timeout: [223], ms */ + uint32_t secTrimMult; /* Secure TRIM Multiplier: [229] */ + uint32_t secEraseMult; /* Secure Erase Multiplier: [230] */ + uint32_t trimTimeout; /* TRIM Multiplier: [232], TRIM Timeout = 300ms * TRIM_MULT */ + bool enhAreaEnable; /* enable bit */ + uint64_t enhAreaOffset; /* Enhanced User Data Start Address(ENH_START_ADDR): [139:136] */ + uint32_t enhAreaSize; /* Enhanced User Data Area Size(ENH_SIZE_MULT): [142:140] */ + uint32_t cache_size; /* Units: KB */ + bool hpiEnable; /* HPI enable bit */ + bool hpi; /* HPI_FEATURES: [503], Bit[0]: HPI_SUPPORT */ + uint32_t hpiCmd; /* HPI_FEATURES: [503], Bit[1]: HPI_IMPLEMENTATION, cmd used as HPI */ + uint32_t dataSectorSize; /* 512 bytes or 4KB */ + uint32_t data_tag_unit_size; /* DATA TAG UNIT size */ + uint32_t boot_ro_lock; /* ro lock support */ + uint32_t bootSize; /* Boot Partition size = 128Kbytes * BOOT_SIZE_MULT, BOOT_SIZE_MULT: [226] */ + uint8_t pwrClF52V195; /* Power class for 52MHz at 1.95V: [200] */ + uint8_t pwrClF26V195; /* Power class for 26MHz at 1.95V: [201] */ + uint8_t pwrClF52V360; /* Power class for 52MHz at 3.6V: [202] */ + uint8_t pwrClF26V360; /* Power class for 26MHz at 3.6V: [203] */ + uint8_t pwrClF200V195; /* Power class for 200MHz at 1.95V: [236] */ + uint8_t pwrClF200V360; /* Power class for 200MHz at 1.95V: [237] */ + uint8_t pwrClDdrF52V195; /* Power class for 52MHz, DDR at 3.6V: [238] */ + uint8_t pwrClDdrF52V360; /* Power class for 52MHz, DDR at 3.6V: [239] */ + uint8_t rawPartitionSupport; /* PARTITIONING_SUPPORT: [160] */ + uint8_t rawErasedMemCount; /* ERASED_MEM_CONT: [181] */ + uint8_t strobeSupport; /* strobe Support:[184] */ + uint8_t rawCsdStructure; /* CSD_STRUCTURE: [194] */ + uint8_t rawCardType; /* CARD_TYPE: [196] */ + uint8_t outOfInterruptTime; /* OUT_OF_INTERRUPT_TIME: [198], Units: 10ms */ + uint8_t rawSaTimeout; /* S_A_TIMEOUT: [217], 100ns */ + uint8_t rawHcWpGrpSize; /* High-capacity write protect group size(HC_WP_GRP_SIZE): [221] */ + uint8_t rawEraseTimeoutMult; /* ERASE_TIMEOUT_MULT: [223] */ + uint8_t rawHcEraseGrpSize; /* HC_ERASE_GRP_SIZE: [224] */ + uint8_t rawSecTrimMult; /* SEC_TRIM_MULT: [229] */ + uint8_t rawSecEraseMult; /* SEC_ERASE_MULT: [230] */ + uint8_t rawSecFeatureSupport; /* SEC_FEATURE_SUPPORT: [231] */ + uint8_t rawTrimMult; /* TRIM_MULT: [232] */ + uint8_t rawSectors[EMMC_EXT_CSD_SEC_CNT_BYTES]; /* SEC_COUNT: [215:212] */ +}; + +/* SD_SWITCH_FUNC mode */ +#define SD_SWITCH_FUNC_CHECK 0 +#define SD_SWITCH_FUNC_SET 1 + +/* SD_SWITCH_FUNC function groups */ +#define SD_SWITCH_FUNC_GRP_ACCESS 0 + +/* SD_SWITCH_FUNC access modes */ +#define SD_SWITCH_FUNC_ACCESS_DEF 0 +#define SD_SWITCH_FUNC_ACCESS_HS 1 + +/* + * As a response to the switch function command, the SD Memory Card will send R1 response on the CMD line, + * and 512 bits of status on the DAT lines. + */ +#define SD_SWITCH_FUNCTION_STATUS_BYTES_LEN 64 + +enum SdCmdCode { + SD_CMD_SEND_RELATIVE_ADDR = 3, /* ask the card to publish a new relative address (RCA). */ + SD_CMD_SWITCH_FUNC = 6, /* Checks switchable function (mode 0) and switchs card function (mode 1). */ + SD_CMD_SEND_IF_COND = 8, /* + * Sends SD Memory Card interface condition, which includes host supply voltage + * information and asks the card whether card supports voltage. + */ + SD_CMD_SWITCH_VOLTAGE = 11, /* Switch to 1.8V bus signaling level. */ + SD_CMD_SEND_TUNING_BLOCK = 19, /* 64 bytes tuning pattern is sent for SDR50 and SDR104. */ + SD_CMD_ERASE_WR_BLK_START = 32, /* sets the address of the first writeblock to be erased. */ + SD_CMD_ERASE_WR_BLK_END = 33, /* sets the address of the last write block of the continuous range to be erased. */ + SD_ACMD_SET_BUS_WIDTH = 6, /* + * Defines the data bus width ('00' =1bit or '10' =4 bits bus) to be used for data + * transfer. The allowed data bus widths are given in SCR register. + */ + SD_ACMD_SD_STATUS = 13, /* Send the SD Memory Card status. */ + SD_ACMD_SEND_NUM_WR_BLKS = 22, /* Send the number of the written (without errors) write blocks. */ + SD_ACMD_OP_COND = 41, /* + * Asks the accessed card to send its operating condition register(OCR) content + * in the response on the CMD line. + */ + SD_ACMD_SEND_SCR = 51, /* Reads the SD Configuration Register(SCR). */ +}; + +enum SdSwitchFuncMode { + SD_SWITCH_FUNC_MODE_CHECK = 0, /* + * Check function is used to query if the card supports + * a specific function or functions. + */ + SD_SWITCH_FUNC_MODE_SET = 1, /* Set function is uesd to switch the functionality of the card. */ +}; + +enum SdSwitchFuncGroup { + SD_SWITCH_FUNC_GROUP_1 = 0, /* Function Group 1 is defined as Bus Speed Mode switch. */ + SD_SWITCH_FUNC_GROUP_2 = 1, /* Function Group 2 is defined for Command System extension. */ + SD_SWITCH_FUNC_GROUP_3 = 2, /* Function Group 3 is defined as driver strength selection for UHS-I modes. */ + SD_SWITCH_FUNC_GROUP_4 = 3, /* Function Group 4 is defined as current limit switch for each UHS-I mode. */ + SD_SWITCH_FUNC_GROUP_5 = 4, /* reserved. */ + SD_SWITCH_FUNC_GROUP_6 = 5, /* reserved. */ +}; + +struct SdSwitchFuncParam { + uint32_t mode; + uint32_t group; + uint32_t value; +}; + +enum SdBusSpeedMode { + SD_BUS_SPEED_MODE_DS = 0, /* Default Speed up to 25MHz 3.3V signaling */ + SD_BUS_SPEED_MODE_UHS_SDR12 = 0, /* SDR up to 25MHz 1.8V signaling */ + SD_BUS_SPEED_MODE_HS = 1, /* High Speed 50MHz 3.3V signaling */ + SD_BUS_SPEED_MODE_UHS_SDR25 = 1, /* SDR up to 50MHz 1.8V signaling */ + SD_BUS_SPEED_MODE_UHS_SDR50 = 2, /* SDR up to 100MHz 1.8V signaling */ + SD_BUS_SPEED_MODE_UHS_SDR104 = 3, /* SDR up to 208MHz 1.8V signaling */ + SD_BUS_SPEED_MODE_UHS_DDR50 = 4, /* DDR up to 50MHz 1.8V signaling */ +}; + +/* sd max data transfer rate */ +enum SdMaxDtr { + SD_HIGH_SPEED_MAX_DTR = 50000000, + SD_UHS_SDR104_MAX_DTR = 208000000, + SD_UHS_SDR50_MAX_DTR = 100000000, + SD_UHS_DDR50_MAX_DTR = 50000000, + SD_UHS_SDR25_MAX_DTR = 50000000, + SD_UHS_SDR12_MAX_DTR = 25000000, +}; + +enum SdDrvType { + SD_DRV_TYPE_B = 0x01, + SD_DRV_TYPE_A = 0x02, + SD_DRV_TYPE_C = 0x04, + SD_DRV_TYPE_D = 0x08, +}; + +enum SdMaxCurrentLimitBit { + SD_MAX_CURRENT_LIMIT_200 = (1 << 0), /* 200mA bit */ + SD_MAX_CURRENT_LIMIT_400 = (1 << 1), /* 400mA bit */ + SD_MAX_CURRENT_LIMIT_600 = (1 << 2), /* 600mA bit */ + SD_MAX_CURRENT_LIMIT_800 = (1 << 3), /* 800mA bit */ +}; + +enum SdMaxCurrentLimitValue { + SD_MAX_CURRENT_LIMIT_200_VALUE = 0, /* 200mA */ + SD_MAX_CURRENT_LIMIT_400_VALUE = 1, /* 400mA */ + SD_MAX_CURRENT_LIMIT_600_VALUE = 2, /* 600mA */ + SD_MAX_CURRENT_LIMIT_800_VALUE = 3, /* 800mA */ +}; + +enum SdScrSpec { + SD_SCR_SPEC_0 = 0, + SD_SCR_SPEC_1 = 1, + SD_SCR_SPEC_2 = 2, +}; + +enum SdScrBusWidths { + SD_SCR_BUS_WIDTHS_1 = (1 << 0), + SD_SCR_BUS_WIDTHS_4 = (1 << 2), +}; + +#define SD_SCR_SPEED_CLASS_CONTROL_SUPPORT (0x1 << 0) /* cmd20 */ +#define SD_SCR_SET_BLOCK_COUNT_SUPPORT (0x1 << 1) /* cmd23 */ + +/* SD Configuration register */ +struct SdScr { + uint8_t sdSpec; + uint8_t sdSpec3; + uint8_t sdBusWidths; + uint8_t cmdSupport; +}; + +/* + * SPEED_CLASS Code Field, SEPPD_CLASS : VAL. + * 0 : 0, 1 : 2, 2 : 4, 3 : 6, 4 : 10, 5-FF : reversed. + */ +enum SdSsrSpeedClass { + SD_SSR_SPEED_CLASS_0 = 0, + SD_SSR_SPEED_CLASS_1 = 1, + SD_SSR_SPEED_CLASS_2 = 2, + SD_SSR_SPEED_CLASS_3 = 3, + SD_SSR_SPEED_CLASS_4 = 4, + SD_SSR_SPEED_CLASS_REV = 5, +}; + +enum SdSsrSpeedClassVal { + SD_SSR_SPEED_CLASS_0_VAL = 0, + SD_SSR_SPEED_CLASS_1_VAL = 2, + SD_SSR_SPEED_CLASS_2_VAL = 4, + SD_SSR_SPEED_CLASS_3_VAL = 6, + SD_SSR_SPEED_CLASS_4_VAL = 10, + SD_SSR_SPEED_CLASS_REV_VAL = 20, +}; + +enum SdSsrUhsSpeedGrade { + SD_SSR_UHS_SPEED_GRADE_0 = 0, /* Less than 10MB/sec */ + SD_SSR_UHS_SPEED_GRADE_1 = 1, /* 10MB/sec and above */ + SD_SSR_UHS_SPEED_GRADE_2 = 2, /* Reversed */ + SD_SSR_UHS_SPEED_GRADE_3 = 3, /* 30MB/sec and above */ + SD_SSR_UHS_SPEED_GRADE_4 = 4, /* 4h-Fh, Reversed */ +}; + +/* SD Status register */ +struct SdSsr { + uint8_t speedClass; + uint8_t auSize; + uint16_t eraseSize; + uint8_t eraseTimeout; + uint8_t eraseOffset; + uint8_t uhsSpeedGrade; + uint32_t auValue; +}; + +union SdSpec3BusMode { + uint32_t data; + struct dataBits { + uint32_t uhsSdr12 : 1; + uint32_t uhsSdr25 : 1; + uint32_t uhsSdr50 : 1; + uint32_t uhsSdr104 : 1; + uint32_t uhsDdr50 : 1; + + uint32_t reserved : 27; + } bits; +}; + +struct SdSwitchCaps { + enum SdMaxDtr hsMaxDtr; + enum SdMaxDtr uhsMaxDtr; + union SdSpec3BusMode sdSpec3BusMode; + enum SdDrvType sdSpec3DrvType; + enum SdMaxCurrentLimitBit sdSpec3CurrLimit; +}; + +enum SdioCmdCode { + /* + * It is similar to the operation of ACMD41 for SD memory cards. + * It is used to inquire about the valtage range needed by the I/O card. + */ + SDIO_SEND_OP_COND = 5, + /* + * It is the simplest means to access a single register within the total 128K of register space in any I/O function, + * including the common I/O area (CIA). This command reads or writes 1 byte using only 1 command/response pair. + * A common use is to initialize registers or monitor status values for I/O functions. This command is the fastest + * means to read or write single I/O registers, as it requires only a single command/response pair. + */ + SDIO_RW_DIRECT = 52, + /* + * This command allows the reading or writing of a large number of I/O registers with a single command. + * Since this is a data transfer command, it provides the highest possible transfer rate. + */ + SDIO_RW_EXTENDED = 53, +}; + +/* Card Common Control Registers (CCCR), Register Name--Address. */ +enum SdioCccrAddr { + CCCR_SDIO_REVISION = 0x00, /* bit3-bit0: CCCR_REVISION, bit7-bit4: SDIO_REVISION. */ + SD_SPECIFICATION_REVISION = 0x01, /* bit3-bit0: SD_REVISION, bit7-bit4: RFU. */ + IO_ENABLE = 0x02, /* bit7-bit1: IOEx, bit0: RFU. */ + IO_READY = 0x03, /* bit7-bit1: IORx, bit0: RFU. */ + INT_ENABLE = 0x04, /* bit7-bit1: IENx, bit0: IENM. */ + INT_PENDING = 0x05, /* bit7-bit1: INTx, bit0: RFU. */ + IO_ABORT = 0x06, /* bit2-bit0: ASx, bit3: RES, bit7-bit4: RFU. */ + BUS_INTERFACE_CONTROL = 0x07, /* + * bit1-bit0: Bus Width, bit2: S8B, bit3: RFU, bit4:RFU, bit5: ECSI, + * bit6: SCSI, bit7: CD Disable. + */ + CARD_CAPBILITY = 0x08, /* + * bit0: SDC, bit1: SMB, bit2: SRW, bit3: SBS, bit4: S4MI, bit5: E4MI, + * bit6: LSC, bit7: 4BLS. + */ + COMMON_CIS_POINTER = 0x09, /* 09h-0Bh, Pointer to card's common CIS. */ + BUS_SUSPEND = 0x0C, /* bit0: BS, bit1: BR, bit7-bit2: RFU. */ + FUNCTION_SELECT = 0x0D, /* bit3-bit0: FSx, bit6-bit4: RFU, bit7: DF. */ + EXEC_FLAG = 0x0E, /* bit7-bit0: EXx. */ + FN0_BLOCK_SIZE = 0x10, /* 11h-10h, I/O block size for Function 0. */ + POWER_CONTROL = 0x12, /* bit0: SMPC, bit1: EMPC, bit7-bit2: RFU. */ + BUS_SPEED_SELECT = 0x13, /* bit0: SHS, bit3-bit1: BSSx, bit7-bit4: RFU. */ + UHS_I_SUPPORT = 0x14, /* bit0: SSDR50, bit1: SSDR104, bit2: SDDR50, bit7-bit3: RFU. */ + DRIVER_STRENGTH = 0x15, /* bit0: SDTA, bit1: SDTC, bit2: SDTD, bit3: RFU, bit4: DTS0, bit5: DTS1, bit7-6: RFU. */ + INTERRUPT_EXTENSION = 0x16, /* bit0: SAI, bit1: EAI, bit7-bit2: RFU. */ +}; + +/* CCCR_SDIO_REVISION-->bit3-bit0: CCCR_REVISION. */ +enum SdioCccrRev { + SDIO_CCCR_VERSION_1_00 = 0x00, /* CCCR/FBR Version 1.00 */ + SDIO_CCCR_VERSION_1_10 = 0x01, /* CCCR/FBR Version 1.10 */ + SDIO_CCCR_VERSION_1_20 = 0x02, /* CCCR/FBR Version 1.20 */ + SDIO_CCCR_VERSION_3_00 = 0x03, /* CCCR/FBR Version 3.00 */ +}; + +/* CCCR_SDIO_REVISION-->bit7-bit4: SDIO_REVISION. */ +enum SdioCccrSdioRev { + SDIO_CCCR_SDIO_REV_1_00 = 0x00, /* SDIO Spec Version 1.00 */ + SDIO_CCCR_SDIO_REV_1_10 = 0x01, /* SDIO Spec Version 1.10 */ + SDIO_CCCR_SDIO_REV_1_20 = 0x02, /* SDIO Spec Version 1.20 */ + SDIO_CCCR_SDIO_REV_2_00 = 0x03, /* SDIO Spec Version 2.00 */ + SDIO_CCCR_SDIO_REV_3_00 = 0x04, /* SDIO Spec Version 3.00 */ +}; + +/* SD_SPECIFICATION_REVISION-->bit3-bit0: SD_REVISION. */ +enum SdioCccrSdRev { + SDIO_CCCR_SD_REV_1_01 = 0x00, /* SD Physical Spec Version 1.01 */ + SDIO_CCCR_SD_REV_1_10 = 0x01, /* SD Physical Spec Version 1.10 */ + SDIO_CCCR_SD_REV_2_00 = 0x02, /* SD Physical Spec Version 2.00 */ + SDIO_CCCR_SD_REV_3_0x = 0x03, /* SD Physical Spev Version 3.0x */ +}; + +/* IO_ABORT-->bit3: RES(I/O Card Reset). + * Setting the RES to 1 shall cause I/O functions perform a soft reset. This does not affect the current card + * protocol selection and CD Disable. It is auto cleared, so there is no need to rewrite a value of 0. + */ +#define SDIO_CCCR_RES 0x08 + +/* BUS_INTERFACE_CONTROL-->bit1-bit0: Bus Width. */ +enum SdioCccrWidth { + SDIO_CCCR_WIDTH_1BIT = 0x00, /* 1-bit */ + SDIO_CCCR_WIDTH_4BIT = 0x02, /* 4-bit bus */ + SDIO_CCCR_WIDTH_8BIT = 0x03 /* 8-bit bus(only for embedded SDIO) */ +}; + +/* BUS_INTERFACE_CONTROL-->bit5: ECSI. Enable Continuous SPI Interrupt. */ +#define SDIO_CCCR_ECSI 0x20 +/* BUS_INTERFACE_CONTROL-->bit6: SCSI. Support Continuous SPI Interrupt. */ +#define SDIO_CCCR_SCSI 0x40 +/* + * BUS_INTERFACE_CONTROL-->bit7: CD Disable. Card Detect Disable. + * Connect[0]/DisConnect[1] the 10K-90K ohm pull-up resistor on CD/DAT[3](pin1) of the card. + * The pull-up may be used for card detection. This bit shall be set to 1 before issuing CMD53. + */ +#define SDIO_CCCR_CD_DISABLE 0x80 + +/* CARD_CAPBILITY */ +enum SdioCccrCapbility { + SDIO_CCCR_CAP_SDC = 0x01, /* Support Direct Command(CMD52). If set, all functions shall accept and execute + * the CMD52 while data transfer is underway on the DAT[x] lines. + */ + SDIO_CCCR_CAP_SMB = 0x02, /* Support Multiple Block Transfer. If set, all functions shall accept and execute + * CMD53 with the optional block mode bit set. + */ + SDIO_CCCR_CAP_SRW = 0x04, /* Support Read Wait. If set, all functions on the card are able to accept + * the wait signal on DAT[2]. + */ + SDIO_CCCR_CAP_SBS = 0x08, /* Support Bus Control. If set, all functions except 0 shall accept a request to + * suspend operations and resume under host control. + */ + SDIO_CCCR_CAP_S4MI = 0x10, /* Support Block Gap Interrupt. Support bit of interrupt between blocks of data + * in 4-bit SD mode. If set, the SDIO card is able to signal an interrupt between + * blocks while data transfer is in progress. + */ + SDIO_CCCR_CAP_E4MI = 0x20, /* Enable Block Gap Interrupt. If set, the card shall generate interrupts + * during 4 bit multi-block data transfer. + */ + SDIO_CCCR_CAP_LSC = 0x40, /* Low-Speed Card. If set, it indicates that the SDIO card is a Low-Speed Card. + * if this bit is clear, The SDIO card is Full-Speed card. + */ + SDIO_CCCR_CAP_4BLS = 0x80, /* 4-bit Mode Support for Low-Speed Card. if the SDIO card is a Low-Speed Card and + * it supports 4-bit data transfer, then this bit shall be set. + */ +}; + +/* POWER_CONTROL-->bit0: SMPC, bit1: EMPC */ +enum SdioCccrPower { + SDIO_CCCR_POWER_SMPC = 0x01, /* Support Master Power Control. */ + SDIO_CCCR_POWER_EMPC = 0x02, /* Enable Master Power Control. */ +}; + +/* BUS_SPEED_SELECT-->bit0: SHS. Support High-Speed. */ +#define SDIO_CCCR_BUS_SPEED_SHS 0x01 +/* + * BUS_SPEED_SELECT-->BSS0. BSS0 = 0, Default Speed(25MHz); BSS0 = 1, High Speed(50MHz). + * If the card is initialized 3.3V signaling, then BSS0 is effective. When SHS is set to 0, + * writing to this bit is ignored and always indicates 0. The card operates in High-Speed timing mode + * with a clock rate up to 50MHz. + */ +#define SDIO_CCCR_BUS_SPEED_EHS 0x02 + +/* + * BUS_SPEED_SELECT-->bit3-bit1: BSSx. + * If the card is initialized 1.8V signaling, then BSS[2:0] is effective. A card that supports UHS-I shall + * support High Speed mode and SHS shall be set to 1. + */ +enum SdioCccrBusSpeed { + SDIO_CCCR_BUS_SPEED_SDR12 = 0, /* Max Clock Freq: 25MHz. */ + SDIO_CCCR_BUS_SPEED_SDR25 = 1, /* Max Clock Freq: 50MHz. */ + SDIO_CCCR_BUS_SPEED_SDR50 = 2, /* Max Clock Freq: 100MHz. */ + SDIO_CCCR_BUS_SPEED_SDR104 = 3, /* Max Clock Freq: 208MHz. */ + SDIO_CCCR_BUS_SPEED_DDR50 = 4, /* Max Clock Freq: 50MHz. */ +}; + +/* UHS_I_SUPPORT-->bit0: SSDR50, bit1: SSDR104, bit2: SDDR50. */ +enum SdioCccrUhsISupport { + SDIO_CCCR_UHS_I_SSDR50 = (1 << 0), /* Support SDR50. If this bit set to 1, SDR12 and SDR25 shall be supported. */ + SDIO_CCCR_UHS_I_SSDR104 = (1 << 1), /* + * Support SDR104. If this bit set to 1, + * SDR12, SDR25 and SDR50 shall be supported. + */ + SDIO_CCCR_UHS_I_SDDR50 = (1 << 2), /* + * Support DDR50. If this bit set to 1, + * SDR12, SDR25 and SDR50 shall be supported. + */ +}; + +/* DRIVER_STRENGTH-->bit0: SDTA, bit1: SDTC, bit2: SDTD, bit3: RFU, bit4: DTS0, bit5: DTS1, bit7-bit6: RFU. */ +enum SdioCccrDriveTypeSupport { + SDIO_CCCR_DRIVE_SDTA = (1 << 0), + SDIO_CCCR_DRIVE_SDTC = (1 << 1), + SDIO_CCCR_DRIVE_SDTD = (1 << 2), +}; + +/* DRIVER_STRENGTH-->bit4: DTS0, bit5: DTS1. */ +enum SdioCccrDriveTypeSelect { + SDIO_CCCR_DTS_TYPE_B = (0 << 4), + SDIO_CCCR_DTS_TYPE_A = (1 << 4), + SDIO_CCCR_DTS_YPE_C = (2 << 4), + SDIO_CCCR_DTS_TYPE_D = (3 << 4), +}; + +/* INTERRUPT_EXTENSION-->bit0: SAI, bit1: EAI. */ +#define SDIO_CCCR_SAI 0x01 /* Support Asynchronous Interrupt. */ +#define SDIO_CCCR_EAI 0x02 /* Enable Asynchronous Interrupt. */ + +/* Function Basic Registers (FBR), Register Name--Address. */ +enum SdioFbrAddr { + SDIO_FBR_STD_FUNCTION_INTERFACE_CODE = 0x00, /* + * bit7: CSA enable, bit6: support CSA, bit5-bit4L: RFU, + * bit3-bit0: Standard SDIO Function Interface Code. + */ + SDIO_FBR_EXT_STD_FUNCTION_INTERFACE_CODE = 0x01, /* Extended Standard SDIO Function Interface Code. */ + SDIO_FBR_POWER_SELECTION = 0x02, /* bit0: SPS, bit1: EPS, bit3-bit2: RFU, bit7-bit4: PSx. */ + SDIO_FBR_POINTER_CIS = 0x09, /* n09h-n0Bh: (Card Information Structure)CIS pointer */ + SDIO_FBR_POINTER_CSA = 0x0C, /* n0Ch-n0Eh: (Code Storage Area)CSA pointer */ + SDIO_FBR_CSA_DATA_ACCESS = 0x0F, /* Data access window to CSA. */ + SDIO_FBR_IO_BLOCK_SIZE = 0x10, /* I/O block size. */ +}; + +/* The address of FBR is from 00n00h to 00nFFh where n is the function number(1 to 7). */ +#define SDIO_FBR_BASE_ADDR(f) ((f) * 0x100) + +/* SDIO_FBR_STD_FUNCTION_INTERFACE_CODE-->bit3-bit0: Standard SDIO Function Interface Code. */ +enum SdioFbrStdFuncIfCode { + SDIO_FBR_NO_STD_IF = 0x00, /* No SDIO standard interface supported by this function. */ + SDIO_FBR_STD_UART = 0x01, /* This function support SDIO standard UART. */ + SDIO_FBR_STD_BLUETOOTH_TYPE_A = 0x02, /* This function support SDIO Bluetooth Type-A standard interface. */ + SDIO_FBR_STD_BLUETOOTH_TYPE_B = 0x03, /* This function support SDIO Bluetooth Type-B standard interface. */ + SDIO_FBR_STD_GPS = 0x04, /* This function support SDIO GPS standard interface. */ + SDIO_FBR_STD_CAMERA = 0x05, /* This function support SDIO Camera standard interface. */ + SDIO_FBR_STD_PHS = 0x06, /* This function support SDIO PHS standard interface. */ + SDIO_FBR_STD_WLAN = 0x07, /* This function support SDIO WLAN interface. */ + SDIO_FBR_STD_EMBEDDED_SDIO_ATA = 0x08, /* This function support Embedded SDIO-ATA standard interface. */ + SDIO_FBR_STD_BLUETOOTH_TYPE_A_AMP = 0x09, /* This function support SDIO Bluetooth Type-A AMP standard interface. */ + SDIO_FBR_STD_SDIO_IF = 0x0F, /* + * This function support an SDIO standard interface number greater than Eh. + * In this case, the value in SDIO_FBR_EXT_STD_FUNCTION_INTERFACE_CODE identifies + * the standard SDIO interfaces type. + */ +}; + +/* SDIO_FBR_STD_FUNCTION_INTERFACE_CODE-->bit6: support CSA. */ +#define SDIO_FBR_SUPPORTS_CSA 0x40 +/* + * SDIO_FBR_STD_FUNCTION_INTERFACE_CODE-->bit7: CSA enable. + * If this function does not support CSA, then this bit shall be R/O and always read as 0. + */ +#define SDIO_FBR_CSA_ENABLE 0x80 + +/* + * SDIO_FBR_POWER_SELECTION-->bit0: SPS(Support Power Selection). + * SPS = 0: This function has no Power Selection. EPS shall be zero. + * SPS = 1: This function has 2 Power modes which are selected by EPS. + * This bit is effective when EMPC = 1 in CCCR and PS[3:0] = 0. + */ +#define SDIO_FBR_POWER_SPS 0x01 +/* + * SDIO_FBR_POWER_SELECTION-->bit1: EPS(Enable Power Selection). + * Enable (low) Power Selection. + */ +#define SDIO_FBR_POWER_EPS 0x02 +/* Sdio function 1-7. */ +#define SDIO_MAX_FUNCTION_NUMBER 7 + +/* SDIO CMD5 response R4, [24]S18A(Switching to 1.8V Accepted). */ +#define SDIO_R4_S18A (1 << 24) +/* R4, [27]Memory Present. Set to 1 if the card also contains SD memory. Set to 0 if the card is I/O only. */ +#define SDIO_R4_MEMORY_PRESENT (1 << 27) + +/* R5: [15:8]Response flags, indicating the status of the SDIO card. */ +enum SdioR5CardStatusBits { + SDIO_R5_COM_CRC_ERROR = (1 << 15), /* The CRC chack of the previous command failed. */ + SDIO_R5_ILLEGAL_COMMAND = (1 << 14), /* Command not legal for the card State. */ + SDIO_R5_ERROR = (1 << 11), /* A general or an unknuwn error occurred during the operation. */ + SDIO_R5_FUNCTION_NUMBER = (1 << 9), /* An invalid function number was requested. */ + SDIO_R5_OUT_OF_RANGE = (1 << 8), /* The command's argument was out of the allowed range for this card. */ +}; + +/* R5: [13:12]IO_CURRENT_STATE. */ +enum SdioCurrentState { + SDIO_CURRENT_STATE_DIS = 0, /* Disable. Initialize, Standby and Inactive States(card not selected). */ + SDIO_CURRENT_STATE_CMD = 1, /* DAT lines free. Command waiting, Executing CMD52 in CMD State. */ + SDIO_CURRENT_STATE_TRN = 2, /* Transfer. Command executing with data transfer using DAT[0] or DAT[3:0] lines. */ +}; + +/* Card Common Control Registers(CCCR). */ +struct SdioCccr { + uint32_t sdioRev; + uint32_t sdRev; + uint32_t multiBlock : 1; + uint32_t lowSpeed : 1; + uint32_t lowSpeed4Bit : 1; + uint32_t highPower : 1; + uint32_t highSpeed : 1; + uint32_t disableCd : 1; +}; + +/* + * n09-n0B, these three bytes make up a 24-bit pointer(only the lower 17 bits are used) to the start of the + * Card Information Structure(CIS) that is associated with each function. + */ +#define SDIO_CCCR_CIS_START_ADDR_BYTES 3 + +/* + * There are 2 tuples with only a tuple code, the CISTPL_NULL and the CISTPL_END. These tuples do not have any + * additional bytes. For all other tuples, bytes 1 of each tuple contains a link to the next tuple in the chain. + * If the link field is 0, the the tuple body is empty. + * If the link contains FFh, the this tuple is the last tuple in its chain. + */ +enum SdioCisTupleCode { + SDIO_CIS_TPL_NULL = 0x00, /* Null tuple. */ + SDIO_CIS_TPL_CHECKSUM = 0x10, /* Checksum Control. */ + SDIO_CIS_TPL_VERS_1 = 0x15, /* Level 1 version/product-information. */ + SDIO_CIS_TPL_ALTSTR = 0x16, /* The Alternate Language String Tuple. */ + SDIO_CIS_TPL_MANFID = 0x20, /* Manufacturer Identification String Tuple. */ + SDIO_CIS_TPL_FUNCID = 0x21, /* Function Identification Tuple. */ + SDIO_CIS_TPL_FUNCE = 0x22, /* Function Extensions. */ + SDIO_CIS_TPL_SDIO_STD = 0x91, /* Additional information for functions built to support application + * specifications for standard SDIO functions. + */ + SDIO_CIS_TPL_SDIO_EXT = 0x92, /* Reserved for future use with SDIO devices. */ + SDIO_CIS_TPL_END = 0xFF, /* The End-0f-chain Tuple. */ +}; + +#define SDIO_CIS_TPL_MANFID_MIN_SIZE 0x04 +#define SDIO_CIS_TPL_FUNCE_COMMON_MIN_SIZE 0x04 +#define SDIO_CIS_TPL_FUNCE_FUNCTION_PC_MIN_SIZE 0x1C +#define SDIO_CIS_TPL_FUNCE_FUNCTION_PC_MAX_SIZE 0x2A +#define SDIO_CIS_TPLFE_ENABLE_TIMEOUT_VAL_DEF 100 + +enum SdioCisTupleFunceType { + SDIO_CIS_TPL_FUNCE_COMMON = 0, /* This tuple gives the host common information about the card. */ + SDIO_CIS_TPL_FUNCE_FUNCTION_PC = 1, /* Extended Data 01h is defined for Power Control. */ + SDIO_CIS_TPL_FUNCE_FUNCTION_PS = 2, /* Extended of this tuple indicates that the card supports the Power State. */ +}; + +/* Card Information Structure(CIS). */ +struct SdioCis { + uint16_t vendorId; + uint16_t deviceId; + uint16_t blkSize; + uint32_t maxDtr; +}; + +/* + * The Card Information Structure is one or more chains(or linked lists) of data blocks or tuples. + * Basic Tuple Format: TPL_CODE, TPL_LINK, The tuple body. + */ +struct SdioCisTuple { + uint8_t tplCode; /* Tuple code: CISTPL_XXX */ + uint8_t tplLink; /* TPL_LINK Offset to next tuple in chains. This is the number of bytes in the tuple body. */ + uint8_t tplBody[0]; /* The tuple body(n bytes). */ +}; + +struct SdioCmdParam { + uint32_t funcNum; + uint32_t regAddr; + uint8_t writeData; + bool incrAddrFlag; + bool writeflag; + bool scatterFlag; + uint32_t scatterLen; +}; + +struct SdioRwBlockInfo { + uint32_t addr; + uint8_t *buf; + uint32_t size; + uint32_t scatterLen; + bool writeFlag; + bool incrAddrFlag; + bool scatterFlag; +}; + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* _MMC_PROTOCOL_H */ diff --git a/support/platform/include/mmc/mmc_sd.h b/support/platform/include/mmc/mmc_sd.h new file mode 100644 index 0000000000000000000000000000000000000000..451566ccc860d38070013385e05caf113da43bbb --- /dev/null +++ b/support/platform/include/mmc/mmc_sd.h @@ -0,0 +1,40 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef MMC_SD_H +#define MMC_SD_H + +#include "mmc_corex.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +struct SdRegister { + uint32_t rawScr[SCR_LEN]; + uint16_t rawDsr; + struct SdScr scr; + struct SdSsr ssr; + struct SdSwitchCaps swCaps; +}; + +struct SdDevice { + struct MmcDevice mmc; + enum SdBusSpeedMode busSpeedMode; + struct SdRegister reg; +}; + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* MMC_SD_H */ diff --git a/support/platform/include/mmc/mmc_sdio.h b/support/platform/include/mmc/mmc_sdio.h new file mode 100644 index 0000000000000000000000000000000000000000..9ee57952c497544b051eede96c3fff572822bfa9 --- /dev/null +++ b/support/platform/include/mmc/mmc_sdio.h @@ -0,0 +1,106 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef MMC_SDIO_H +#define MMC_SDIO_H + +#include "mmc_sd.h" +#include "osal_thread.h" +#include "sdio_if.h" + +#ifdef __cplusplus +#if __cplusplus +extern "C" { +#endif +#endif /* __cplusplus */ + +struct SdioFunction { + struct SdioDevice *dev; + uint32_t maxBlkSize; + uint32_t curBlkSize; + uint32_t funcNum; + uint8_t funcClass; + uint16_t vendorId; + uint16_t deviceId; + uint32_t timeOut; + SdioIrqHandler *irqHandler; +}; + +struct SdioRegister { + struct SdioCccr cccr; + struct SdioCis cis; +}; + +struct SdioDevice { + struct SdDevice sd; + struct SdioDeviceOps *sdioOps; + struct SdioRegister sdioReg; + uint32_t functions; + struct SdioFunction *sdioFunc[SDIO_MAX_FUNCTION_NUMBER]; + struct SdioFunction *curFunction; + struct OsalThread thread; /* irq thread */ + struct OsalSem sem; + bool irqPending; + bool threadRunning; +}; + +struct SdioDeviceOps { + int32_t (*incrAddrReadBytes)(struct SdioDevice *, uint8_t *, uint32_t, uint32_t); + int32_t (*incrAddrWriteBytes)(struct SdioDevice *, uint8_t *, uint32_t, uint32_t); + int32_t (*fixedAddrReadBytes)(struct SdioDevice *, uint8_t *, uint32_t, uint32_t, uint32_t); + int32_t (*fixedAddrWriteBytes)(struct SdioDevice *, uint8_t *, uint32_t, uint32_t, uint32_t); + int32_t (*func0ReadBytes)(struct SdioDevice *, uint8_t *, uint32_t, uint32_t); + int32_t (*func0WriteBytes)(struct SdioDevice *, uint8_t *, uint32_t, uint32_t); + int32_t (*setBlockSize)(struct SdioDevice *, uint32_t); + int32_t (*getCommonInfo)(struct SdioDevice *, SdioCommonInfo *, uint32_t); + int32_t (*setCommonInfo)(struct SdioDevice *, SdioCommonInfo *, uint32_t); + int32_t (*flushData)(struct SdioDevice *); + int32_t (*enableFunc)(struct SdioDevice *); + int32_t (*disableFunc)(struct SdioDevice *); + int32_t (*claimIrq)(struct SdioDevice *, SdioIrqHandler *); + int32_t (*releaseIrq)(struct SdioDevice *); + int32_t (*findFunc)(struct SdioDevice *, struct SdioFunctionConfig *); + int32_t (*claimHost)(struct SdioDevice *); + int32_t (*releaseHost)(struct SdioDevice *); +}; + +int32_t SdioDeviceFindFunction(struct SdioDevice *sdio, struct SdioFunctionConfig *config); +int32_t SdioDeviceIncrAddrReadBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size); +int32_t SdioDeviceIncrAddrWriteBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size); +int32_t SdioDeviceFixedAddrReadBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen); +int32_t SdioDeviceFixedAddrWriteBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen); +int32_t SdioDeviceFunc0ReadBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size); +int32_t SdioDeviceFunc0WriteBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size); +int32_t SdioDeviceSetBlockSize(struct SdioDevice *sdio, uint32_t blockSize); +int32_t SdioDeviceGetCommonInfo(struct SdioDevice *sdio, + SdioCommonInfo *info, SdioCommonInfoType infoType); +int32_t SdioDeviceSetCommonInfo(struct SdioDevice *sdio, + SdioCommonInfo *info, SdioCommonInfoType infoType); +int32_t SdioDeviceFlushData(struct SdioDevice *sdio); +int32_t SdioDeviceClaimHost(struct SdioDevice *sdio); +int32_t SdioDeviceReleaseHost(struct SdioDevice *sdio); +int32_t SdioDeviceEnableFunc(struct SdioDevice *sdio); +int32_t SdioDeviceDisableFunc(struct SdioDevice *sdio); +int32_t SdioDeviceClaimIrq(struct SdioDevice *sdio, SdioIrqHandler *irqHandler); +int32_t SdioDeviceReleaseIrq(struct SdioDevice *sdio); + +void SdioDeviceAddOps(struct SdioDevice *sdio, void *ops); + +#ifdef __cplusplus +#if __cplusplus +} +#endif +#endif /* __cplusplus */ + +#endif /* MMC_SDIO_H */ diff --git a/support/platform/src/common/platform_common.c b/support/platform/src/common/platform_common.c new file mode 100644 index 0000000000000000000000000000000000000000..694bda3116e0544e85de53843f923fbef5684b48 --- /dev/null +++ b/support/platform/src/common/platform_common.c @@ -0,0 +1,196 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "hdf_log.h" +#include "osal_spinlock.h" +#include "platform_common.h" + +static struct PlatformModuleInfo g_platformModules[] = { +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_GPIO) || defined(CONFIG_DRIVERS_HDF_PLATFORM_GPIO) + { + .moduleType = PLATFORM_MODULE_GPIO, + .moduleName = "PLATFORM_MODULE_GPIO", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_I2C) || defined(CONFIG_DRIVERS_HDF_PLATFORM_I2C) + { + .moduleType = PLATFORM_MODULE_I2C, + .moduleName = "PLATFORM_MODULE_I2C", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_SPI) || defined(CONFIG_DRIVERS_HDF_PLATFORM_SPI) + { + .moduleType = PLATFORM_MODULE_SPI, + .moduleName = "PLATFORM_MODULE_SPI", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_PIN) || defined(CONFIG_DRIVERS_HDF_PLATFORM_PIN) + { + .moduleType = PLATFORM_MODULE_PIN, + .moduleName = "PLATFORM_MODULE_PIN", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_CLOCK) || defined(CONFIG_DRIVERS_HDF_PLATFORM_CLOCK) + { + .moduleType = PLATFORM_MODULE_CLOCK, + .moduleName = "PLATFORM_MODULE_CLOCK", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_REGULATOR) || defined(CONFIG_DRIVERS_HDF_PLATFORM_REGULATOR) + { + .moduleType = PLATFORM_MODULE_REGULATOR, + .moduleName = "PLATFORM_MODULE_REGULATOR", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_MIPI_DSI) || defined(CONFIG_DRIVERS_HDF_PLATFORM_MIPI_DSI) + { + .moduleType = PLATFORM_MODULE_MIPI_DSI, + .moduleName = "PLATFORM_MODULE_MIPI_DSI", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_UART) || defined(CONFIG_DRIVERS_HDF_PLATFORM_UART) + { + .moduleType = PLATFORM_MODULE_UART, + .moduleName = "PLATFORM_MODULE_UART", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_SDIO) || defined(CONFIG_DRIVERS_HDF_PLATFORM_SDIO) + { + .moduleType = PLATFORM_MODULE_SDIO, + .moduleName = "PLATFORM_MODULE_SDIO", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_MDIO) || defined(CONFIG_DRIVERS_HDF_PLATFORM_MDIO) + { + .moduleType = PLATFORM_MODULE_MDIO, + .moduleName = "PLATFORM_MODULE_MDIO", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_APB) || defined(CONFIG_DRIVERS_HDF_PLATFORM_APB) + { + .moduleType = PLATFORM_MODULE_APB, + .moduleName = "PLATFORM_MODULE_APB", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_PCIE) || defined(CONFIG_DRIVERS_HDF_PLATFORM_PCIE) + { + .moduleType = PLATFORM_MODULE_PCIE, + .moduleName = "PLATFORM_MODULE_PCIE", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_PCM) || defined(CONFIG_DRIVERS_HDF_PLATFORM_PCM) + { + .moduleType = PLATFORM_MODULE_PCM, + .moduleName = "PLATFORM_MODULE_PCM", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_I2S) || defined(CONFIG_DRIVERS_HDF_PLATFORM_I2S) + { + .moduleType = PLATFORM_MODULE_I2S, + .moduleName = "PLATFORM_MODULE_I2S", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_PWM) || defined(CONFIG_DRIVERS_HDF_PLATFORM_PWM) + { + .moduleType = PLATFORM_MODULE_PWM, + .moduleName = "PLATFORM_MODULE_PWM", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_DMA) || defined(CONFIG_DRIVERS_HDF_PLATFORM_DMA) + { + .moduleType = PLATFORM_MODULE_DMA, + .moduleName = "PLATFORM_MODULE_DMA", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_ADC) || defined(CONFIG_DRIVERS_HDF_PLATFORM_ADC) + { + .moduleType = PLATFORM_MODULE_ADC, + .moduleName = "PLATFORM_MODULE_ADC", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_RTC) || defined(CONFIG_DRIVERS_HDF_PLATFORM_RTC) + { + .moduleType = PLATFORM_MODULE_RTC, + .moduleName = "PLATFORM_MODULE_RTC", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_WDT) || defined(CONFIG_DRIVERS_HDF_PLATFORM_WDT) + { + .moduleType = PLATFORM_MODULE_WDT, + .moduleName = "PLATFORM_MODULE_WDT", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_I3C) || defined(CONFIG_DRIVERS_HDF_PLATFORM_I3C) + { + .moduleType = PLATFORM_MODULE_I3C, + .moduleName = "PLATFORM_MODULE_I3C", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_CAN) || defined(CONFIG_DRIVERS_HDF_PLATFORM_CAN) + { + .moduleType = PLATFORM_MODULE_CAN, + .moduleName = "PLATFORM_MODULE_CAN", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_HDMI) || defined(CONFIG_DRIVERS_HDF_PLATFORM_HDMI) + { + .moduleType = PLATFORM_MODULE_HDMI, + .moduleName = "PLATFORM_MODULE_HDMI", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_MMC) || defined(CONFIG_DRIVERS_HDF_PLATFORM_MMC) + { + .moduleType = PLATFORM_MODULE_MMC, + .moduleName = "PLATFORM_MODULE_MMC", + }, +#endif +#if defined(LOSCFG_DRIVERS_HDF_PLATFORM_MTD) || defined(CONFIG_DRIVERS_HDF_PLATFORM_MTD) + { + .moduleType = PLATFORM_MODULE_MTD, + .moduleName = "PLATFORM_MODULE_MTD", + }, +#endif + { + .moduleType = PLATFORM_MODULE_DEFAULT, + .moduleName = "PLATFORM_MODULE_DEFAULT", + }, +}; + +static OsalSpinlock g_platformSpin; +static bool g_platformReady; + +void PlatformGlobalLock(void) +{ + if (g_platformReady == false) { + (void)OsalSpinInit(&g_platformSpin); + g_platformReady = true; + } + (void)OsalSpinLock(&g_platformSpin); +} + +void PlatformGlobalUnlock(void) +{ + (void)OsalSpinUnlock(&g_platformSpin); +} + +struct PlatformModuleInfo *PlatformModuleInfoGet(enum PlatformModuleType moduleType) +{ + int32_t i; + + for (i = 0; i < PlatformModuleInfoCount(); i++) { + if (g_platformModules[i].moduleType == moduleType) { + return &g_platformModules[i]; + } + } + return NULL; +} + +int32_t PlatformModuleInfoCount(void) +{ + return sizeof(g_platformModules) / sizeof(g_platformModules[0]); +} diff --git a/support/platform/src/common/platform_device.c b/support/platform/src/common/platform_device.c new file mode 100644 index 0000000000000000000000000000000000000000..0e70cddfc7fb2643055132c6290add7332dd6c7e --- /dev/null +++ b/support/platform/src/common/platform_device.c @@ -0,0 +1,216 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "hdf_log.h" +#include "osal_mem.h" +#include "platform_common.h" +#include "platform_device.h" +#include "platform_manager.h" + +#define PLATFORM_DEV_NAME_DEFAULT "platform_device" + +static void PlatformDeviceOnFirstGet(struct HdfSRef *sref) +{ + (void)sref; +} + +static void PlatformDeviceOnLastPut(struct HdfSRef *sref) +{ + struct PlatformDevice *device = NULL; + struct PlatformNotifierNode *pos = NULL; + struct PlatformNotifierNode *tmp = NULL; + + if (sref == NULL) { + return; + } + + device = CONTAINER_OF(sref, struct PlatformDevice, ref); + if (device == NULL) { + HDF_LOGE("PlatformDeviceOnLastPut: get device is NULL!"); + return; + } + (void)OsalSpinLock(&device->spin); + device->ready = false; + + DLIST_FOR_EACH_ENTRY_SAFE(pos, tmp, &device->notifiers, struct PlatformNotifierNode, node) { + if (pos != NULL && pos->notifier != NULL && pos->notifier->handle != NULL) { + pos->notifier->handle(device, PLAT_EVENT_DEAD, pos->notifier->data); + } + } + + (void)OsalSpinUnlock(&device->spin); +} + +struct IHdfSRefListener g_platObjListener = { + .OnFirstAcquire = PlatformDeviceOnFirstGet, + .OnLastRelease = PlatformDeviceOnLastPut, +}; + +static void PlatformEventHandle(struct PlatformDevice *device, enum PlatformEventType event, void *data) +{ + if (device == NULL) { + return; + } + if (event == PLAT_EVENT_DEAD) { + (void)OsalSemPost(&device->released); + } +} + +static struct PlatformNotifier g_platformEventNotifier = { + .data = NULL, + .handle = PlatformEventHandle, +}; + +void PlatformDeviceInit(struct PlatformDevice *device) +{ + if (device == NULL) { + HDF_LOGW("PlatformDeviceInit: device is NULL"); + return; + } + + if (device->name == NULL) { + device->name = PLATFORM_DEV_NAME_DEFAULT; + } + + (void)OsalSpinInit(&device->spin); + (void)OsalSemInit(&device->released, 0); + DListHeadInit(&device->node); + DListHeadInit(&device->notifiers); + HdfSRefConstruct(&device->ref, &g_platObjListener); + + if (PlatformDeviceRegNotifier(device, &g_platformEventNotifier) != HDF_SUCCESS) { + HDF_LOGE("PlatformDeviceInit: reg notifier fail"); + } + device->ready = true; +} + +void PlatformDeviceUninit(struct PlatformDevice *device) +{ + if (device == NULL) { + HDF_LOGW("PlatformDevice: device is NULL"); + return; + } + device->ready = false; + + /* make sure no reference anymore before exit. */ + (void)OsalSemWait(&device->released, HDF_WAIT_FOREVER); + PlatformDeviceClearNotifier(device); + (void)OsalSemDestroy(&device->released); + (void)OsalSpinDestroy(&device->spin); +} + +struct PlatformDevice *PlatformDeviceGet(struct PlatformDevice *device) +{ + if (device == NULL) { + HDF_LOGE("PlatformDeviceGet: device is NULL"); + return NULL; + } + + HdfSRefAcquire(&device->ref); + return device; +} + +void PlatformDevicePut(struct PlatformDevice *device) +{ + if (device != NULL) { + HdfSRefRelease(&device->ref); + } +} + +int32_t PlatformDeviceRegNotifier(struct PlatformDevice *device, struct PlatformNotifier *notifier) +{ + struct PlatformNotifierNode *pNode = NULL; + + if (device == NULL || notifier == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + pNode = (struct PlatformNotifierNode *)OsalMemCalloc(sizeof(*pNode)); + if (pNode == NULL) { + HDF_LOGE("PlatformDeviceRegNotifier: alloc pNode fail"); + return HDF_ERR_MALLOC_FAIL; + } + pNode->notifier = notifier; + (void)OsalSpinLock(&device->spin); + DListInsertTail(&pNode->node, &device->notifiers); + (void)OsalSpinUnlock(&device->spin); + return HDF_SUCCESS; +} + +static void PlatformDeviceRemoveNotifier(struct PlatformDevice *device, struct PlatformNotifier *notifier) +{ + struct PlatformNotifierNode *pos = NULL; + struct PlatformNotifierNode *tmp = NULL; + + if (device->notifiers.next == NULL || device->notifiers.prev == NULL) { + HDF_LOGD("PlatformDeviceRemoveNotifier: notifiers not init."); + return; + } + + (void)OsalSpinLock(&device->spin); + DLIST_FOR_EACH_ENTRY_SAFE(pos, tmp, &device->notifiers, struct PlatformNotifierNode, node) { + if (pos != NULL && pos->notifier != NULL) { + /* if notifier not set, we remove all the notifier nodes. */ + if (notifier == NULL || notifier == pos->notifier) { + DListRemove(&pos->node); + OsalMemFree(pos); + } + if (notifier == pos->notifier) { + break; + } + } + } + (void)OsalSpinUnlock(&device->spin); +} + +void PlatformDeviceUnregNotifier(struct PlatformDevice *device, struct PlatformNotifier *notifier) +{ + if (device == NULL || notifier == NULL) { + return; + } + PlatformDeviceRemoveNotifier(device, notifier); +} + +void PlatformDeviceClearNotifier(struct PlatformDevice *device) +{ + if (device == NULL) { + return; + } + PlatformDeviceRemoveNotifier(device, NULL); +} + +int32_t PlatformDeviceAdd(struct PlatformDevice *device) +{ + struct PlatformManager *manager = NULL; + + if (device == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + PlatformDeviceInit(device); + manager = device->manager; + if (manager == NULL) { + manager = PlatformManagerGet(PLATFORM_MODULE_DEFAULT); + } + return PlatformManagerAddDevice(manager, device); +} + +void PlatformDeviceDel(struct PlatformDevice *device) +{ + struct PlatformManager *manager = NULL; + + if (device == NULL) { + return; + } + + manager = device->manager; + if (manager == NULL) { + manager = PlatformManagerGet(PLATFORM_MODULE_DEFAULT); + } + PlatformManagerDelDevice(manager, device); + PlatformDeviceUninit(device); +} diff --git a/support/platform/src/common/platform_manager.c b/support/platform/src/common/platform_manager.c new file mode 100644 index 0000000000000000000000000000000000000000..0f3ccf66290608c5b91ab74cf3112de09c469e4b --- /dev/null +++ b/support/platform/src/common/platform_manager.c @@ -0,0 +1,155 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "hdf_log.h" +#include "osal_mem.h" +#include "osal_sem.h" +#include "platform_common.h" +#include "platform_device.h" +#include "platform_manager.h" + +#define PLATFORM_MANAGER_NAME_DEFAULT "PlatformManagerDefault" + +static void PlatformManagerInit(struct PlatformManager *manager) +{ + if (manager->name == NULL) { + manager->name = PLATFORM_MANAGER_NAME_DEFAULT; + } + + OsalSpinInit(&manager->spin); + DListHeadInit(&manager->devices); +} + +struct PlatformManager *PlatformManagerCreate(const char *name) +{ + struct PlatformManager *manager = NULL; + + manager = (struct PlatformManager *)OsalMemCalloc(sizeof(*manager)); + if (manager == NULL) { + HDF_LOGE("PlatformManagerCreate: malloc fail!"); + return NULL; + } + manager->name = name; + PlatformManagerInit(manager); + return manager; +} + +struct PlatformManager *PlatformManagerGet(enum PlatformModuleType module) +{ + struct PlatformManager *manager = NULL; + struct PlatformModuleInfo *info = NULL; + + info = PlatformModuleInfoGet(module); + if (info == NULL) { + HDF_LOGE("PlatformManagerGet: get module(%d) info failed", module); + return NULL; + } + + PlatformGlobalLock(); + if (info->priv == NULL) { + manager = PlatformManagerCreate(info->moduleName); + info->priv = manager; + } else { + manager = (struct PlatformManager *)info->priv; + } + PlatformGlobalUnlock(); + + return manager; +} + +int32_t PlatformManagerAddDevice(struct PlatformManager *manager, struct PlatformDevice *device) +{ + struct PlatformDevice *tmp = NULL; + bool repeatId = false; + + if (manager == NULL || device == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + device->manager = manager; + + if (PlatformDeviceGet(device) == NULL) { // keep a reference by manager + return HDF_PLT_ERR_DEV_GET; + } + + (void)OsalSpinLock(&manager->spin); + DLIST_FOR_EACH_ENTRY(tmp, &manager->devices, struct PlatformDevice, node) { + if (tmp != NULL && tmp->magic == device->magic) { + repeatId = true; + HDF_LOGE("PlatformManagerAddDevice: repeated magic:%u!", device->magic); + break; + } + } + if (!repeatId) { + DListInsertTail(&device->node, &manager->devices); + } + (void)OsalSpinUnlock(&manager->spin); + + if (repeatId) { + PlatformDevicePut(device); + } + return repeatId ? HDF_PLT_ERR_ID_REPEAT : HDF_SUCCESS; +} + +void PlatformManagerDelDevice(struct PlatformManager *manager, struct PlatformDevice *device) +{ + if (manager == NULL || device == NULL) { + return; + } + + if (device->manager != manager) { + HDF_LOGE("PlatformManagerDelDevice: manager mismatch!"); + return; + } + + (void)OsalSpinLock(&manager->spin); + if (!DListIsEmpty(&device->node)) { + DListRemove(&device->node); + } + (void)OsalSpinUnlock(&manager->spin); + PlatformDevicePut(device); // put the reference hold by manager +} + +struct PlatformDevice *PlatformManagerFindDevice(struct PlatformManager *manager, void *data, + bool (*match)(struct PlatformDevice *pdevice, void *data)) +{ + struct PlatformDevice *tmp = NULL; + struct PlatformDevice *pdevice = NULL; + + if (manager == NULL || match == NULL) { + return NULL; + } + if (manager->devices.prev == NULL || manager->devices.next == NULL) { + HDF_LOGD("PlatformManagerFindDevice: devices not init."); + return NULL; + } + + (void)OsalSpinLock(&manager->spin); + DLIST_FOR_EACH_ENTRY(tmp, &manager->devices, struct PlatformDevice, node) { + if (tmp != NULL && match(tmp, data)) { + pdevice = PlatformDeviceGet(tmp); + } + } + (void)OsalSpinUnlock(&manager->spin); + + return pdevice; +} + +static bool PlatformDeviceMatchByMagic(struct PlatformDevice *device, void *data) +{ + uint32_t magic = (uint32_t)(uintptr_t)data; + + return (device != NULL && device->magic == magic); +} + +struct PlatformDevice *PlatformManagerGetDeviceByMagic(struct PlatformManager *manager, uint32_t magic) +{ + if (manager == NULL) { + return NULL; + } + return PlatformManagerFindDevice(manager, (void *)(uintptr_t)magic, PlatformDeviceMatchByMagic); +} diff --git a/support/platform/src/common/platform_queue.c b/support/platform/src/common/platform_queue.c new file mode 100644 index 0000000000000000000000000000000000000000..3a83ee017774a6de4066bb0fbad85ad94175c09d --- /dev/null +++ b/support/platform/src/common/platform_queue.c @@ -0,0 +1,157 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "platform_queue.h" +#include "hdf_log.h" +#include "osal_mem.h" +#include "osal_mutex.h" +#include "osal_thread.h" + +#define MMC_QUEUE_THREAD_STAK 20000 + +static int32_t PlatformQueueThreadWorker(void *data) +{ + int32_t ret; + struct PlatformQueue *queue = (struct PlatformQueue *)data; + struct PlatformMsg *msg = NULL; + + while (true) { + /* wait envent */ + ret = OsalSemWait(&queue->sem, HDF_WAIT_FOREVER); + if (ret != HDF_SUCCESS) { + continue; + } + + (void)OsalSpinLock(&queue->spin); + if (DListIsEmpty(&queue->msgs)) { + msg = NULL; + } else { + msg = DLIST_FIRST_ENTRY(&queue->msgs, struct PlatformMsg, node); + DListRemove(&msg->node); + } + (void)OsalSpinUnlock(&queue->spin); + /* message process */ + if (msg != NULL) { + (void)(queue->handle(queue, msg)); + if (msg->block == true) { + (void)OsalSemPost(&msg->sem); + } + } + } + return HDF_SUCCESS; +} + +struct PlatformQueue *PlatformQueueCreate(PlatformMsgHandle handle, const char *name, void *data) +{ + int32_t ret; + struct PlatformQueue *queue = NULL; + + if (handle == NULL) { + return NULL; + } + + queue = (struct PlatformQueue *)OsalMemCalloc(sizeof(*queue)); + if (queue == NULL) { + HDF_LOGE("PlatformQueueCreate: alloc queue fail!"); + return NULL; + } + (void)OsalSpinInit(&queue->spin); + (void)OsalSemInit(&queue->sem, 0); + DListHeadInit(&queue->msgs); + + ret = OsalThreadCreate(&queue->thread, (OsalThreadEntry)PlatformQueueThreadWorker, (void *)queue); + if (ret != HDF_SUCCESS) { + HDF_LOGE("PlatformQueueCreate: create thread fail!"); + OsalMemFree(queue); + return NULL; + } + + queue->name = (name == NULL) ? "PlatformWorkerThread" : name; + queue->handle = handle; + queue->data = data; + return queue; +} + +void PlatformQueueDestroy(struct PlatformQueue *queue) +{ + if (queue == NULL) { + return; + } + + (void)OsalThreadDestroy(&queue->thread); + (void)OsalSemDestroy(&queue->sem); + (void)OsalSpinDestroy(&queue->spin); + OsalMemFree(queue); +} + +int32_t PlatformQueueStart(struct PlatformQueue *queue) +{ + int32_t ret; + struct OsalThreadParam cfg; + + if (queue == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + cfg.name = (char *)queue->name; + cfg.priority = OSAL_THREAD_PRI_HIGHEST; + cfg.stackSize = MMC_QUEUE_THREAD_STAK; + ret = OsalThreadStart(&queue->thread, &cfg); + if (ret != HDF_SUCCESS) { + HDF_LOGE("PlatformQueueStart: start thread fail:%d", ret); + return ret; + } + + return HDF_SUCCESS; +} + +int32_t PlatformQueueSuspend(struct PlatformQueue *queue) +{ + if (queue == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + return OsalThreadSuspend(&queue->thread); +} + +int32_t PlatformQueueResume(struct PlatformQueue *queue) +{ + if (queue == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + return OsalThreadResume(&queue->thread); +} + +void PlatformQueueAddMsg(struct PlatformQueue *queue, struct PlatformMsg *msg) +{ + if (queue == NULL || msg == NULL) { + return; + } + + if (msg->block == true) { + (void)OsalSemInit(&msg->sem, 0); + } + DListHeadInit(&msg->node); + msg->error = HDF_SUCCESS; + (void)OsalSpinLock(&queue->spin); + DListInsertTail(&msg->node, &queue->msgs); + (void)OsalSpinUnlock(&queue->spin); + /* notify the worker thread */ + (void)OsalSemPost(&queue->sem); +} + +int32_t PlatformMsgWait(struct PlatformMsg *msg) +{ + if (msg == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + if (msg->block == false) { + return HDF_SUCCESS; + } + return OsalSemWait(&msg->sem, HDF_WAIT_FOREVER); +} diff --git a/support/platform/src/mmc/emmc_if.c b/support/platform/src/mmc/emmc_if.c new file mode 100644 index 0000000000000000000000000000000000000000..b3c9f206143032da4de12d4fbfdb59868ef6955f --- /dev/null +++ b/support/platform/src/mmc/emmc_if.c @@ -0,0 +1,159 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#ifndef __USER__ +#include "devsvc_manager_clnt.h" +#include "mmc_emmc.h" +#endif +#include "emmc_if.h" +#include "hdf_base.h" +#ifdef __USER__ +#include "hdf_io_service_if.h" +#endif +#include "hdf_log.h" +#include "osal_mem.h" +#include "securec.h" + +#define HDF_LOG_TAG emmc_if_c + +#ifdef __USER__ +static int32_t EmmcGetCidReadReplyData(struct HdfSBuf *reply, uint8_t *cid, uint32_t size) +{ + uint32_t rLen; + const void *rBuf = NULL; + + if (HdfSbufReadBuffer(reply, &rBuf, &rLen) == false) { + HDF_LOGE("EmmcGetCidReadReplyData: read rBuf fail!"); + return HDF_ERR_IO; + } + if (size != rLen) { + HDF_LOGE("EmmcGetCidReadReplyData: err len:%u, rLen:%u", size, rLen); + if (rLen > size) { + rLen = size; + } + } + + if (memcpy_s(cid, size, rBuf, rLen) != EOK) { + HDF_LOGE("EmmcGetCidReadReplyData: memcpy rBuf fail!"); + return HDF_ERR_IO; + } + return HDF_SUCCESS; +} + +int32_t EmmcServiceGetCid(struct HdfIoService *service, uint8_t *cid, uint32_t size) +{ + int32_t ret; + struct HdfSBuf *reply = NULL; + + reply = HdfSBufObtainDefaultSize(); + if (reply == NULL) { + HDF_LOGE("EmmcServiceGetCid: failed to obtain reply!"); + ret = HDF_ERR_MALLOC_FAIL; + goto __EXIT; + } + + if (service->dispatcher == NULL || service->dispatcher->Dispatch == NULL) { + HDF_LOGE("EmmcServiceGetCid: dispatcher or Dispatch is NULL!"); + ret = HDF_ERR_NOT_SUPPORT; + goto __EXIT; + } + + ret = service->dispatcher->Dispatch(&service->object, EMMC_CMD_GET_CID, NULL, reply); + if (ret != HDF_SUCCESS) { + HDF_LOGE("EmmcServiceGetCid: failed to send service call:%d", ret); + goto __EXIT; + } + + ret = EmmcGetCidReadReplyData(reply, cid, size); + if (ret != HDF_SUCCESS) { + goto __EXIT; + } + HDF_LOGD("EmmcServiceGetCid: success"); + +__EXIT: + if (reply != NULL) { + HdfSBufRecycle(reply); + } + return ret; +} + +#else +static int32_t EmmcDeviceGetFromHandle(DevHandle handle, struct EmmcDevice **emmc) +{ + struct MmcDevice *mmc = NULL; + + if (handle == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + if (emmc == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + mmc = MmcCntlrGetDevice((struct MmcCntlr *)handle); + if (mmc == NULL) { + return HDF_PLT_ERR_NO_DEV; + } + if (mmc->type != MMC_DEV_EMMC) { + MmcDevicePut(mmc); + return HDF_PLT_ERR_DEV_TYPE; + } + + *emmc = (struct EmmcDevice *)mmc; + return HDF_SUCCESS; +} + +#endif + +int32_t EmmcGetCid(DevHandle handle, uint8_t *cid, uint32_t size) +{ + if (handle == NULL) { + HDF_LOGE("EmmcGetCid: handle is NULL!"); + return HDF_ERR_INVALID_OBJECT; + } + + if (cid == NULL || size < EMMC_CID_LEN) { + HDF_LOGE("EmmcGetCid: error parms!"); + return HDF_ERR_INVALID_PARAM; + } +#ifdef __USER__ + return EmmcServiceGetCid((struct HdfIoService *)handle, cid, size); +#else + struct EmmcDevice *emmc = NULL; + int32_t ret; + + if (EmmcDeviceGetFromHandle(handle, &emmc) != HDF_SUCCESS) { + return HDF_ERR_INVALID_OBJECT; + } + ret = EmmcDeviceGetCid(emmc, cid, size); + MmcDevicePut((struct MmcDevice *)emmc); + return ret; +#endif +} + +void EmmcGetHuid(uint8_t *cid, uint32_t size) +{ + DevHandle handle = NULL; + if (cid == NULL || size == 0) { + HDF_LOGE("EmmcGetUdid: error parms!"); + return; + } + if (memset_s(cid, sizeof(uint8_t) * size, 0, sizeof(uint8_t) * size) != EOK) { + HDF_LOGE("EmmcGetUdid: memset_s fail!"); + return; + } + handle = EmmcOpen(0); + if (handle == NULL) { + HDF_LOGD("EmmcGetUdid: open fail, use default value!"); + return; + } + if (EmmcGetCid(handle, cid, size) != HDF_SUCCESS) { + HDF_LOGD("EmmcGetUdid: get fail, use default value!"); + } + EmmcClose(handle); +} diff --git a/support/platform/src/mmc/mmc_block.c b/support/platform/src/mmc/mmc_block.c new file mode 100644 index 0000000000000000000000000000000000000000..2917a2842919be037a7a171690a26b2b66c1d719 --- /dev/null +++ b/support/platform/src/mmc/mmc_block.c @@ -0,0 +1,166 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "storage_block.h" +#include "hdf_base.h" +#include "hdf_log.h" +#include "mmc_corex.h" +#include "mmc_sd.h" +#include "osal_mem.h" +#include "securec.h" + +static ssize_t MmcBlockRead(struct StorageBlock *sb, uint8_t *buf, size_t secStart, size_t nSec) +{ + if (sb == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (sb->type == MEDIA_MMC) { + return MmcDeviceRead((struct MmcDevice *)sb->media, buf, secStart, nSec); + } + return HDF_ERR_NOT_SUPPORT; +} + +static ssize_t MmcBlockWrite(struct StorageBlock *sb, const uint8_t *buf, size_t secStart, size_t nSec) +{ + if (sb == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (sb->type == MEDIA_MMC) { + return MmcDeviceWrite((struct MmcDevice *)sb->media, (uint8_t *)buf, secStart, nSec); + } + return HDF_ERR_NOT_SUPPORT; +} + +static ssize_t MmcBlockErase(struct StorageBlock *sb, size_t secStart, size_t nSec) +{ + if (sb == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (sb->type == MEDIA_MMC) { + return MmcDeviceErase((struct MmcDevice *)sb->media, secStart, nSec); + } + return HDF_ERR_NOT_SUPPORT; +} + +static uint32_t MmcBlockGetAuSize(struct StorageBlock *sb) +{ + struct MmcDevice *mmc = NULL; + struct SdDevice *sd = NULL; + + if (sb == NULL || sb->media == NULL) { + HDF_LOGE("MmcBlockGetAuSize: invalid sb or media!"); + return 0; + } + + if (sb->type != MEDIA_MMC) { + HDF_LOGE("MmcBlockGetAuSize: media is not mmc!"); + return 0; + } + + mmc = (struct MmcDevice *)sb->media; + if (mmc->type != MMC_DEV_SD) { + HDF_LOGE("MmcBlockGetAuSize: media is not sd!"); + return 0; + } + sd = (struct SdDevice *)mmc; + + return sd->reg.ssr.auSize; +} + +static bool MmcBlockIsPresent(struct StorageBlock *sb) +{ + return (sb != NULL && sb->type == MEDIA_MMC && + MmcDeviceIsPresent((struct MmcDevice *)sb->media)); +} + +static struct StorageBlockMethod g_mmcBlockOps = { + .read = MmcBlockRead, + .write = MmcBlockWrite, + .erase = MmcBlockErase, + .getAuSize = MmcBlockGetAuSize, + .isPresent = MmcBlockIsPresent, +}; + +static int32_t MmcBlockInit(struct StorageBlock *sb, struct MmcDevice *mmc) +{ + int32_t ret; + size_t nameSize; + + if (PlatformDeviceGet(&mmc->device) == NULL) { + return HDF_PLT_ERR_DEV_GET; + } + mmc->sb = sb; + sb->type = MEDIA_MMC; + sb->media = mmc; + sb->secSize = mmc->secSize; + sb->capacity = mmc->capacity; + sb->removeable = (mmc->state.bits.removeable == 0) ? false : true; + sb->ops = &g_mmcBlockOps; + nameSize = sizeof(sb->name); + ret = snprintf_s(sb->name, nameSize, nameSize - 1, "/dev/mmcblk%0d", mmc->cntlr->index); + if (ret <= 0) { + PlatformDevicePut(&mmc->device); + } + + return HDF_SUCCESS; +} + +static void MmcBlockUninit(struct StorageBlock *sb) +{ + struct MmcDevice *mmc = (struct MmcDevice *)sb->media; + + mmc->sb = NULL; + PlatformDevicePut(&mmc->device); +} + +int32_t MmcBlockAdd(struct MmcDevice *mmc) +{ + int32_t ret; + struct StorageBlock *sb = NULL; + + if (mmc == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + sb = (struct StorageBlock *)OsalMemCalloc(sizeof(*sb)); + if (sb == NULL) { + return HDF_ERR_MALLOC_FAIL; + } + + ret = MmcBlockInit(sb, mmc); + if (ret != HDF_SUCCESS) { + OsalMemFree(sb); + return ret; + } + + ret = StorageBlockAdd(sb); + if (ret != HDF_SUCCESS) { + MmcBlockUninit(sb); + OsalMemFree(sb); + return ret; + } + + return HDF_SUCCESS; +} + +void MmcBlockDel(struct MmcDevice *mmc) +{ + struct StorageBlock *sb = NULL; + + if (mmc == NULL) { + return; + } + sb = mmc->sb; + if (sb == NULL) { + return; + } + + StorageBlockDel(sb); + MmcBlockUninit(sb); + OsalMemFree(sb); +} diff --git a/support/platform/src/mmc/mmc_core.c b/support/platform/src/mmc/mmc_core.c new file mode 100644 index 0000000000000000000000000000000000000000..6d2670b1ae9f4f8d171de83141be8718afb98999 --- /dev/null +++ b/support/platform/src/mmc/mmc_core.c @@ -0,0 +1,1195 @@ + /* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "device_resource_if.h" +#include "hdf_dlist.h" +#include "hdf_log.h" +#include "mmc_block.h" +#include "mmc_corex.h" +#include "mmc_dispatch.h" +#include "mmc_emmc.h" +#include "mmc_sdio.h" +#include "osal_mem.h" +#include "osal_time.h" + +#define HDF_LOG_TAG mmc_core_c + +#define MMC_POWER_ON_DELAY_TIME 10 /* 10ms */ +#define MMC_OCR_MAX_VOLTAGE_BIT 23 +#define MMC_OCR_MAX_SHIFT_BITS 3 +#define MMC_DETECET_RETRY 5 +#define MMC_REQUEST_RETRY 3 +#define MMC_MAX_SECTOR_NUM 2048 +#define MMC_MAX_ERASE_SECTOR 0x100000 /* 512M */ +#define SDIO_IRQ_TASK_STACK_SIZE 0x2000 + +int32_t MmcCntlrDoRequest(struct MmcCntlr *cntlr, struct MmcCmd *cmd) +{ + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (cmd == NULL) { + return HDF_ERR_INVALID_PARAM; + } + if (cntlr->ops == NULL || cntlr->ops->request == NULL) { + return HDF_ERR_NOT_SUPPORT; + } + return cntlr->ops->request(cntlr, cmd); +} + +static int32_t MmcCntlrExecRequest(struct MmcCntlr *cntlr, struct MmcCmd *cmd) +{ + uint32_t i; + int32_t ret; + + for (i = 0; i < MMC_REQUEST_RETRY; i++) { + MmcCntlrLock(cntlr); + ret = MmcCntlrDoRequest(cntlr, cmd); + MmcCntlrUnlock(cntlr); + if (ret != HDF_SUCCESS) { + continue; + } + if (cmd->returnError != HDF_SUCCESS) { + continue; + } + if (cmd->data != NULL && cmd->data->returnError != HDF_SUCCESS) { + continue; + } + break; + } + return ret; +} + +static int32_t MmcCntlrPlug(struct MmcCntlr *cntlr) +{ + uint32_t i; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (MmcCntlrDevPluged(cntlr) != true) { + HDF_LOGD("MmcCntlrPlug: dev is not plugged!"); + return HDF_SUCCESS; + } + + /* dev not delete. */ + if (cntlr->curDev != NULL) { + MmcCntlrLock(cntlr); + MmcDeleteDev(cntlr); + MmcCntlrUnlock(cntlr); + } + + if (cntlr->ops != NULL && cntlr->ops->systemInit != NULL) { + if (cntlr->ops->systemInit(cntlr) != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrPlug: system init fail!"); + return HDF_FAILURE; + } + } + + MmcCntlrLock(cntlr); + for (i = 0; i < MMC_DETECET_RETRY; i++) { + cntlr->detecting = true; + if (MmcDoDetect(cntlr) == HDF_SUCCESS) { + MmcDeviceAddOps(cntlr->curDev, NULL); + cntlr->detecting = false; + MmcCntlrUnlock(cntlr); + return HDF_SUCCESS; + } + } + cntlr->detecting = false; + MmcCntlrUnlock(cntlr); + + return HDF_FAILURE; +} + +static int32_t MmcCntlrUnplug(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL) { + HDF_LOGE("MmcCntlrUnplug: cntlr is null!"); + return HDF_ERR_INVALID_OBJECT; + } + + MmcCntlrLock(cntlr); + MmcDeleteDev(cntlr); + MmcCntlrUnlock(cntlr); + HDF_LOGD("host%d: a dev is removed!", cntlr->index); + return HDF_SUCCESS; +} + +static int32_t MmcCntlrSdioRescanHandle(struct MmcCntlr *cntlr) +{ + uint8_t val; + int32_t error; + + if (cntlr == NULL) { + HDF_LOGE("MmcCntlrSdioRescanHandle: cntlr is null."); + return HDF_ERR_INVALID_OBJECT; + } + + if (cntlr->curDev != NULL && cntlr->curDev->state.bits.present > 0) { + if (cntlr->curDev->type == MMC_DEV_SDIO || cntlr->curDev->type == MMC_DEV_COMBO) { + error = SdioReadCccrIoEnable(cntlr, &val); + if (error != HDF_SUCCESS) { + HDF_LOGD("re-detect sdio."); + MmcCntlrPlug(cntlr); + } else { + HDF_LOGD("sdio no need to rescan."); + return HDF_SUCCESS; + } + } else { + HDF_LOGD("sd/emmc rescan does not support."); + } + } else { + HDF_LOGD("init detect fail, re-detect sdio."); + MmcCntlrPlug(cntlr); + } + + if (cntlr->curDev == NULL) { + HDF_LOGE("sdio rescan fail, please reset card!"); + return HDF_ERR_DEVICE_BUSY; + } + return HDF_SUCCESS; +} + +static int32_t MmcMsgHandleDefault(struct PlatformQueue *queue, struct PlatformMsg *msg) +{ + int32_t ret; + struct MmcCntlr *cntlr = NULL; + struct MmcMsg *mmcMsg = NULL; + + if (queue == NULL || msg == NULL) { + HDF_LOGE("MmcMsgHandleDefault: msg or queue is null!"); + return HDF_ERR_INVALID_OBJECT; + } + + cntlr = (struct MmcCntlr *)queue->data; + if (cntlr == NULL) { + HDF_LOGE("MmcMsgHandleDefault: cntlr is null!"); + return HDF_ERR_INVALID_OBJECT; + } + mmcMsg = (struct MmcMsg *)msg; + switch (msg->code) { + case MMC_MSG_PLUG: + ret = MmcCntlrPlug(cntlr); + break; + case MMC_MSG_UNPLUG: + ret = MmcCntlrUnplug(cntlr); + break; + case MMC_MSG_REQUEST: + ret = MmcCntlrExecRequest(cntlr, mmcMsg->mmcCmd); + break; + case MMC_MSG_SDIO_RESCAN: + ret = MmcCntlrSdioRescanHandle(cntlr); + break; + default: + ret = HDF_ERR_NOT_SUPPORT; + break; + } + + if (mmcMsg->msg.block == false) { + OsalMemFree(mmcMsg); + } + return ret; +} + +static int32_t MmcCntlrInit(struct MmcCntlr *cntlr) +{ + int32_t ret; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + if (cntlr->index >= MMC_CNTLR_NR_MAX) { + HDF_LOGE("MmcCntlrInit: invalid cntlr index:%u", cntlr->index); + return HDF_ERR_INVALID_PARAM; + } + + if (cntlr->hdfDevObj == NULL) { + HDF_LOGE("MmcCntlrInit: no HdfDeviceObject attached!"); + return HDF_ERR_INVALID_OBJECT; + } + + ret = OsalMutexInit(&cntlr->mutex); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrInit: mutex init fail!"); + return ret; + } + + cntlr->msgQueue = PlatformQueueCreate(MmcMsgHandleDefault, NULL, cntlr); + if (cntlr->msgQueue == NULL) { + HDF_LOGE("MmcCntlrInit: failed to create msg queue!"); + return HDF_PLT_ERR_OS_API; + } + ret = PlatformQueueStart(cntlr->msgQueue); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrInit: failed to start msg queue!"); + PlatformQueueDestroy(cntlr->msgQueue); + return ret; + } + + cntlr->service.Dispatch = MmcIoDispatch; + cntlr->hdfDevObj->service = &(cntlr->service); + cntlr->device.magic = cntlr->index; + cntlr->device.hdfDev = cntlr->hdfDevObj; + return HDF_SUCCESS; +} + +static void MmcCntlrUninit(struct MmcCntlr *cntlr) +{ + if (cntlr != NULL) { + (void)OsalMutexDestroy(&cntlr->mutex); + PlatformQueueDestroy(cntlr->msgQueue); + } +} + +int32_t MmcCntlrAdd(struct MmcCntlr *cntlr) +{ + int32_t ret; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + ret = MmcCntlrInit(cntlr); + if (ret != HDF_SUCCESS) { + return ret; + } + + cntlr->device.manager = PlatformManagerGet(PLATFORM_MODULE_MMC); + ret = PlatformDeviceAdd(&cntlr->device); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrAdd: device add fail!"); + MmcCntlrUninit(cntlr); + return ret; + } + return HDF_SUCCESS; +} + +void MmcCntlrRemove(struct MmcCntlr *cntlr) +{ + if (cntlr != NULL) { + MmcCntlrUninit(cntlr); + PlatformDeviceDel(&cntlr->device); + } +} + +void MmcCntlrSetDevice(struct MmcCntlr *cntlr, struct MmcDevice *mmc) +{ + if (cntlr == NULL || mmc == NULL) { + return; + } + cntlr->curDev = mmc; +} + +struct MmcDevice *MmcCntlrGetDevice(struct MmcCntlr *cntlr) +{ + struct MmcDevice *mmc = NULL; + + if (cntlr != NULL) { + mmc = cntlr->curDev; + } + return MmcDeviceGet(mmc); +} + +static int32_t MmcCntlrPostMsg(struct MmcCntlr *cntlr, struct MmcMsg *mmcMsg) +{ + int32_t ret; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (mmcMsg == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + PlatformQueueAddMsg(cntlr->msgQueue, &mmcMsg->msg); + ret = PlatformMsgWait(&mmcMsg->msg); + if (mmcMsg->msg.block == true) { + (void)OsalSemDestroy(&mmcMsg->msg.sem); + OsalMemFree(mmcMsg); + } + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrPostMsg: wait mmc msg fail!"); + return ret; + } + return HDF_SUCCESS; +} + +void MmcCntlrSetClock(struct MmcCntlr *cntlr, uint32_t clock) +{ + if (cntlr == NULL || cntlr->ops == NULL || cntlr->ops->setClock == NULL) { + return; + } + cntlr->curDev->workPara.clock = clock; + cntlr->ops->setClock(cntlr, clock); +} + +void MmcCntlrSetBusWidth(struct MmcCntlr *cntlr, enum MmcBusWidth width) +{ + if (cntlr == NULL || cntlr->ops == NULL || cntlr->ops->setBusWidth == NULL) { + return; + } + cntlr->curDev->workPara.width = width; + cntlr->ops->setBusWidth(cntlr, width); +} + +void MmcCntlrSetBusTiming(struct MmcCntlr *cntlr, enum MmcBusTiming timing) +{ + if (cntlr == NULL || cntlr->ops == NULL || cntlr->ops->setBusTiming == NULL) { + return; + } + cntlr->curDev->workPara.timing = timing; + cntlr->ops->setBusTiming(cntlr, timing); +} + +void MmcCntlrSetEnhanceSrobe(struct MmcCntlr *cntlr, bool enable) +{ + if (cntlr == NULL || cntlr->ops == NULL || cntlr->ops->setEnhanceSrobe == NULL) { + return; + } + cntlr->ops->setEnhanceSrobe(cntlr, enable); +} + +int32_t MmcCntlrSwitchVoltage(struct MmcCntlr *cntlr, enum MmcVolt voltage) +{ + if (cntlr == NULL || cntlr->ops == NULL || cntlr->ops->switchVoltage == NULL) { + return HDF_ERR_INVALID_PARAM; + } + return cntlr->ops->switchVoltage(cntlr, voltage); +} + +bool MmcCntlrDevReadOnly(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL || cntlr->ops == NULL || cntlr->ops->devReadOnly == NULL) { + return false; + } + return cntlr->ops->devReadOnly(cntlr); +} + +bool MmcCntlrDevPluged(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL || cntlr->ops == NULL || cntlr->ops->devPluged == NULL) { + return false; + } + + if (cntlr->caps.bits.nonremovable > 0) { + return true; + } + return cntlr->ops->devPluged(cntlr); +} + +bool MmcCntlrDevBusy(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL || cntlr->ops == NULL || cntlr->ops->devBusy == NULL) { + return true; + } + + return cntlr->ops->devBusy(cntlr); +} + +int32_t MmcCntlrTune(struct MmcCntlr *cntlr, uint32_t cmdCode) +{ + if (cntlr == NULL || cntlr->ops == NULL || cntlr->ops->tune == NULL) { + return HDF_ERR_INVALID_PARAM; + } + return cntlr->ops->tune(cntlr, cmdCode); +} + +void MmcCntlrSelectWorkVoltage(struct MmcCntlr *cntlr, union MmcOcr *ocr) +{ + uint32_t tmpOcr; + uint32_t i; + + /* ignore reveserd. */ + if ((ocr->ocrData & 0x7F) > 0) { + ocr->ocrData &= (~0x7F); + } + /* use low voltage shuould both host and dev support. */ + if (cntlr->ocrDef.bits.vdd1v65To1v95 == 0) { + ocr->bits.vdd1v65To1v95 = 0; + } + + /* + * Based on the voltage range supported by the host and the voltage range read from the OCR register, + * obtain the voltage range supported by both the host and the OCR register, + * and then select the minimum voltage value. + */ + tmpOcr = ((ocr->ocrData) & (cntlr->ocrDef.ocrData)); + for (i = 0; i <= MMC_OCR_MAX_VOLTAGE_BIT; i++) { + if ((tmpOcr & (1 << i)) > 0) { + break; + } + } + + if (i > 0 && i <= MMC_OCR_MAX_VOLTAGE_BIT) { + tmpOcr &= (MMC_OCR_MAX_SHIFT_BITS << i); + cntlr->vddBit = i; + } else { + tmpOcr = 0; + } + cntlr->curDev->reg.ocr.ocrData = tmpOcr; +} + +void MmcCntlrPowerUp(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL || cntlr->ops == NULL) { + return; + } + + if (cntlr->ops->setEnhanceSrobe != NULL) { + cntlr->ops->setEnhanceSrobe(cntlr, false); + } + /* disable clock. */ + if (cntlr->ops->setClock != NULL) { + cntlr->ops->setClock(cntlr, 0); + } + /* power up. */ + if (cntlr->ops->setPowerMode != NULL) { + cntlr->ops->setPowerMode(cntlr, MMC_POWER_MODE_POWER_UP); + } + if (cntlr->ops->setBusWidth != NULL) { + cntlr->ops->setBusWidth(cntlr, BUS_WIDTH1); + } + if (cntlr->ops->setBusTiming != NULL) { + cntlr->ops->setBusTiming(cntlr, BUS_TIMING_MMC_DS); + } + OsalMDelay(MMC_POWER_ON_DELAY_TIME); + + /* enable clock. */ + if (cntlr->ops->setClock != NULL) { + cntlr->ops->setClock(cntlr, cntlr->freqDef); + } + /* power on. */ + if (cntlr->ops->setPowerMode != NULL) { + cntlr->ops->setPowerMode(cntlr, MMC_POWER_MODE_POWER_ON); + } + OsalMDelay(MMC_POWER_ON_DELAY_TIME); + + if (cntlr->ops->hardwareReset != NULL && cntlr->caps.bits.hardwareReset > 0) { + cntlr->ops->hardwareReset(cntlr); + } + /* init voltage 3.3v. */ + if (cntlr->ops->switchVoltage != NULL) { + cntlr->ops->switchVoltage(cntlr, cntlr->voltDef); + } +} + +void MmcCntlrPowerOff(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL || cntlr->ops == NULL) { + return; + } + + /* disable clock. */ + if (cntlr->ops->setClock != NULL) { + cntlr->ops->setClock(cntlr, 0); + } + /* power up. */ + if (cntlr->ops->setPowerMode != NULL) { + cntlr->ops->setPowerMode(cntlr, MMC_POWER_MODE_POWER_OFF); + } + if (cntlr->ops->setBusWidth != NULL) { + cntlr->ops->setBusWidth(cntlr, BUS_WIDTH1); + } +} + +int32_t MmcCntlrAllocDev(struct MmcCntlr *cntlr, enum MmcDevType devType) +{ + uint32_t len = 0; + struct MmcDevice *mmc = NULL; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + if (devType == MMC_DEV_SDIO || devType == MMC_DEV_COMBO) { + len = (uint32_t)sizeof(struct SdioDevice); + } else if (devType == MMC_DEV_SD) { + len = (uint32_t)sizeof(struct SdDevice); + } else if (devType == MMC_DEV_EMMC) { + len = (uint32_t)sizeof(struct EmmcDevice); + } + + if (len == 0) { + HDF_LOGE("MmcCntlrAllocDev: len is 0, type = %d!", (uint32_t)devType); + return HDF_ERR_INVALID_PARAM; + } + + mmc = (struct MmcDevice *)OsalMemCalloc(len); + if (mmc == NULL) { + HDF_LOGE("MmcCntlrAllocDev: OsalMemCalloc fail!"); + return HDF_ERR_MALLOC_FAIL; + } + mmc->type = devType; + mmc->cntlr = cntlr; + MmcCntlrSetDevice(cntlr, mmc); + return 0; +} + +void MmcCntlrFreeDev(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL || cntlr->curDev == NULL) { + return; + } + + OsalMemFree(cntlr->curDev); + cntlr->curDev = NULL; +} + +bool MmcCntlrSupportUhs(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL) { + return false; + } + + if (cntlr->caps.bits.uhsSdr12 > 0 || + cntlr->caps.bits.uhsSdr25 > 0 || + cntlr->caps.bits.uhsSdr50 > 0 || + cntlr->caps.bits.uhsSdr104 > 0 || + cntlr->caps.bits.uhsDdr50 > 0) { + return true; + } + return false; +} + +bool MmcCntlrSupportHighSpeed400EnhancedStrobe(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL) { + return false; + } + + if (cntlr->caps2.bits.hs400EnhancedStrobe > 0 && + cntlr->caps.bits.cap8Bit > 0 && + cntlr->caps2.bits.hs400Support1v2 > 0 && + cntlr->caps2.bits.hs400Support1v8 > 0) { + return true; + } + return false; +} + +bool MmcCntlrSupportHighSpeed400(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL) { + return false; + } + + if (cntlr->caps.bits.cap8Bit > 0 && + cntlr->caps2.bits.hs400Support1v2 > 0 && + cntlr->caps2.bits.hs400Support1v8 > 0) { + return true; + } + return false; +} + +bool MmcCntlrSupportHighSpeed200(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL) { + return false; + } + + if (cntlr->caps2.bits.hs200Sdr1v8 > 0 || + cntlr->caps2.bits.hs200Sdr1v2 > 0) { + return true; + } + return false; +} + +bool MmcCntlrSdSupportCmd23(struct MmcCntlr *cntlr) +{ + struct SdDevice *dev = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return false; + } + if (cntlr->curDev->type != MMC_DEV_SD && cntlr->curDev->type != MMC_DEV_COMBO) { + return false; + } + + dev = (struct SdDevice *)cntlr->curDev; + if ((dev->reg.scr.cmdSupport & SD_SCR_SET_BLOCK_COUNT_SUPPORT) > 0) { + return true; + } + return false; +} + +bool MmcCntlrEmmcSupportCmd23(struct MmcCntlr *cntlr) +{ + struct EmmcDevice *dev = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return false; + } + if (cntlr->curDev->type != MMC_DEV_EMMC) { + return false; + } + + dev = (struct EmmcDevice *)cntlr->curDev; + if ((dev->mmc.reg.csd.ccc & MMC_CSD_CCC_BLOCK_READ) > 0 || + (dev->mmc.reg.csd.ccc & MMC_CSD_CCC_BLOCK_WRITE) > 0) { + return true; + } + return false; +} + +int32_t MmcCntlrAddMsgToQueue(struct MmcCntlr *cntlr, struct MmcCmd *cmd, + int32_t code, bool block) +{ + struct MmcMsg *msg = NULL; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + msg = (struct MmcMsg *)OsalMemCalloc(sizeof(struct MmcMsg)); + if (msg == NULL) { + HDF_LOGE("MmcCntlrAddMsgToQueue: OsalMemCalloc fail!n"); + return HDF_ERR_MALLOC_FAIL; + } + msg->msg.code = code; + msg->msg.data = (void *)cntlr; + msg->msg.block = block; + msg->mmcCmd = cmd; + return MmcCntlrPostMsg(cntlr, msg); +} + +int32_t MmcCntlrAddRequestMsgToQueue(struct MmcCntlr *cntlr, struct MmcCmd *cmd) +{ + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (cntlr->ops == NULL || cntlr->ops->devPluged == NULL) { + return HDF_PLT_ERR_NO_DEV; + } + if (cntlr->ops->devPluged(cntlr) == false) { + HDF_LOGE("MmcCntlrAddRequestMsgToQueue: host%d dev is unplug!", cntlr->index); + return HDF_PLT_ERR_NO_DEV; + } + + return MmcCntlrAddMsgToQueue(cntlr, cmd, MMC_MSG_REQUEST, true); +} + +int32_t MmcCntlrAddDetectMsgToQueue(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (cntlr->ops == NULL || cntlr->ops->devPluged == NULL) { + return HDF_PLT_ERR_NO_DEV; + } + + if (cntlr->ops->devPluged(cntlr) == false) { + HDF_LOGD("MmcCntlrAddDetectMsgToQueue: host%d dev is not plugged!", cntlr->index); + return HDF_PLT_ERR_NO_DEV; + } + cntlr->devPluged = true; + return MmcCntlrAddMsgToQueue(cntlr, NULL, MMC_MSG_PLUG, false); +} + +int32_t MmcCntlrAddPlugMsgToQueue(struct MmcCntlr *cntlr) +{ + bool curStatus = false; + int32_t code; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (cntlr->ops == NULL || cntlr->ops->devPluged == NULL) { + return HDF_PLT_ERR_NO_DEV; + } + + curStatus = cntlr->ops->devPluged(cntlr); + if (curStatus == cntlr->devPluged) { + HDF_LOGD("MmcCntlrAddPlugMsgToQueue: dev plug srtatus not change."); + return HDF_SUCCESS; + } + + if (curStatus == true) { + cntlr->devPluged = true; + code = MMC_MSG_PLUG; + HDF_LOGD("host%d: a dev is plugged!", cntlr->index); + } else { + cntlr->devPluged = false; + code = MMC_MSG_UNPLUG; + HDF_LOGD("host%d: a dev is unplugged!", cntlr->index); + } + return MmcCntlrAddMsgToQueue(cntlr, NULL, code, false); +} + +int32_t MmcCntlrAddSdioRescanMsgToQueue(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (cntlr->devType != MMC_DEV_SDIO) { + HDF_LOGD("MmcCntlrAddSdioRescanMsgToQueue: not sdio card, do not handle rescan!"); + return HDF_FAILURE; + } + + return MmcCntlrAddMsgToQueue(cntlr, NULL, MMC_MSG_SDIO_RESCAN, true); +} + +static void SdioHandlePendingIrq(struct MmcCntlr *cntlr, struct SdioDevice *dev) +{ + uint8_t val; + int32_t ret; + uint32_t i; + struct SdioFunction *func = dev->curFunction; + + if (func == NULL) { + HDF_LOGE("MmcCntlrHandleSdioPendingIrq: cur func is NULL."); + return; + } + if (dev->irqPending == true && func->irqHandler != NULL) { + func->irqHandler(func); + return; + } + + ret = SdioReadCccrIntPending(cntlr, &val); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrHandleSdioPendingIrq: read cccr int pending fail! ret = %d.", ret); + return; + } + + for (i = 0; i < SDIO_MAX_FUNCTION_NUMBER; i++) { + if ((val & (1 << (i + 1))) == 0) { + continue; + } + func = dev->sdioFunc[i]; + if (func == NULL) { + HDF_LOGD("MmcCntlrHandleSdioPendingIrq: function%d not exist.", i + 1); + continue; + } + if (func->irqHandler == NULL) { + HDF_LOGD("MmcCntlrHandleSdioPendingIrq: function%d irq handler not exist.", i + 1); + continue; + } + func->irqHandler(func); + } +} + +static int32_t SdioIrqThreadWorker(void *data) +{ + int32_t ret; + struct SdioDevice *dev = (struct SdioDevice *)data; + struct MmcCntlr *cntlr = NULL; + + if (dev == NULL) { + HDF_LOGE("SdioIrqThreadWorker: data is NULL."); + return HDF_FAILURE; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioIrqThreadWorker: cntlr is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + while (true) { + /* wait envent */ + ret = OsalSemWait(&dev->sem, HDF_WAIT_FOREVER); + if (ret != HDF_SUCCESS) { + continue; + } + SdioHandlePendingIrq(cntlr, dev); + dev->irqPending = false; + if (cntlr->caps.bits.sdioIrq > 0 && cntlr->ops != NULL && cntlr->ops->setSdioIrq != NULL) { + (void)cntlr->ops->setSdioIrq(cntlr, true); + } + } + + return HDF_SUCCESS; +} + +int32_t MmcCntlrCreatSdioIrqThread(struct MmcCntlr *cntlr) +{ + int32_t ret; + struct OsalThreadParam config = {0}; + struct SdioDevice *dev = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + dev = (struct SdioDevice *)cntlr->curDev; + ret = OsalSemInit(&dev->sem, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrCreatSdioIrqThread: sem init fail."); + return ret; + } + + ret = OsalThreadCreate(&dev->thread, (OsalThreadEntry)SdioIrqThreadWorker, dev); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrCreatSdioIrqThread: thread create fail."); + (void)OsalSemDestroy(&dev->sem); + return ret; + } + + config.name = "SdioIrqTask"; + config.priority = OSAL_THREAD_PRI_HIGHEST; + config.stackSize = SDIO_IRQ_TASK_STACK_SIZE; + ret = OsalThreadStart(&dev->thread, &config); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrCreatSdioIrqThread: thread start fail."); + OsalThreadDestroy(&dev->thread); + (void)OsalSemDestroy(&dev->sem); + return ret; + } + dev->threadRunning = true; + if (cntlr->caps.bits.sdioIrq > 0 && cntlr->ops != NULL && cntlr->ops->setSdioIrq != NULL) { + (void)cntlr->ops->setSdioIrq(cntlr, true); + } + return HDF_SUCCESS; +} + +void MmcCntlrDestroySdioIrqThread(struct MmcCntlr *cntlr) +{ + struct SdioDevice *dev = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return; + } + + dev = (struct SdioDevice *)cntlr->curDev; + (void)OsalThreadDestroy(&dev->thread); + (void)OsalSemDestroy(&dev->sem); + dev->threadRunning = true; +} + +void MmcCntlrNotifySdioIrqThread(struct MmcCntlr *cntlr) +{ + struct SdioDevice *dev = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + HDF_LOGD("MmcCntlrNotifySdioIrqThread: cntlr or curDev is null!"); + return; + } + if (cntlr->curDev->type != MMC_DEV_SDIO && cntlr->curDev->type != MMC_DEV_COMBO) { + return; + } + dev = (struct SdioDevice *)cntlr->curDev; + if (dev->threadRunning == true) { + dev->irqPending = true; + /* notify the worker thread */ + (void)OsalSemPost(&dev->sem); + } +} + +static void MmcCntlrParseCapability(const struct DeviceResourceNode *node, + struct DeviceResourceIface *drsOps, struct MmcCntlr *cntlr) +{ + int32_t ret; + + ret = drsOps->GetUint16(node, "voltDef", &(cntlr->voltDef), 0); + if (ret != HDF_SUCCESS) { + cntlr->voltDef = 0; + } + ret = drsOps->GetUint32(node, "freqMin", &(cntlr->freqMin), 0); + if (ret != HDF_SUCCESS) { + cntlr->freqMin = 0; + } + ret = drsOps->GetUint32(node, "freqMax", &(cntlr->freqMax), 0); + if (ret != HDF_SUCCESS) { + cntlr->freqMax = 0; + } + ret = drsOps->GetUint32(node, "freqDef", &(cntlr->freqDef), 0); + if (ret != HDF_SUCCESS) { + cntlr->freqDef = 0; + } + ret = drsOps->GetUint32(node, "ocrDef", &(cntlr->ocrDef.ocrData), 0); + if (ret != HDF_SUCCESS) { + cntlr->ocrDef.ocrData = 0; + } + + ret = drsOps->GetUint32(node, "caps", &(cntlr->caps.capsData), 0); + if (ret != HDF_SUCCESS) { + cntlr->caps.capsData = 0; + } + ret = drsOps->GetUint32(node, "caps2", &(cntlr->caps2.caps2Data), 0); + if (ret != HDF_SUCCESS) { + cntlr->caps2.caps2Data = 0; + } + + ret = drsOps->GetUint32(node, "maxBlkNum", &(cntlr->maxBlkNum), 0); + if (ret != HDF_SUCCESS) { + cntlr->maxBlkNum = 0; + } + ret = drsOps->GetUint32(node, "maxBlkSize", &(cntlr->maxBlkSize), 0); + if (ret != HDF_SUCCESS) { + cntlr->maxBlkSize = 0; + } + cntlr->maxReqSize = cntlr->maxBlkNum * cntlr->maxBlkSize; +} + +int32_t MmcCntlrParse(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("MmcCntlrParse: input param is NULL."); + return HDF_FAILURE; + } + + node = obj->property; + if (node == NULL) { + HDF_LOGE("MmcCntlrParse: drs node is NULL."); + return HDF_FAILURE; + } + drsOps = DeviceResourceGetIfaceInstance(HDF_CONFIG_SOURCE); + if (drsOps == NULL || drsOps->GetUint16 == NULL || drsOps->GetUint32 == NULL) { + HDF_LOGE("MmcCntlrParse: invalid drs ops fail!"); + return HDF_FAILURE; + } + + ret = drsOps->GetUint16(node, "hostId", &(cntlr->index), 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrParse: read hostId fail!"); + return ret; + } + HDF_LOGD("MmcCntlrParse: hostId = %d", cntlr->index); + + ret = drsOps->GetUint32(node, "devType", &(cntlr->devType), 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCntlrParse: read devType fail!"); + return ret; + } + HDF_LOGD("MmcCntlrParse: devType = %d", cntlr->devType); + + MmcCntlrParseCapability(node, drsOps, cntlr); + return HDF_SUCCESS; +} + +int32_t MmcDeviceAdd(struct MmcDevice *mmc) +{ + int32_t ret; + + if (mmc == NULL || mmc->cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (mmc->type >= MMC_DEV_INVALID) { + return HDF_PLT_ERR_DEV_TYPE; + } + if (mmc->secSize == 0) { + mmc->secSize = MMC_SEC_SIZE; + } else { + HDF_LOGE("MmcDeviceAdd: invalid sector size:%u", mmc->secSize); + return HDF_ERR_INVALID_PARAM; + } + if (mmc->type != MMC_DEV_SDIO && mmc->capacity == 0) { + HDF_LOGE("MmcDeviceAdd: invalid capacity:%u", mmc->capacity); + return HDF_ERR_INVALID_PARAM; + } + if (mmc->type != MMC_DEV_SDIO && mmc->eraseSize == 0) { + HDF_LOGE("MmcDeviceAdd: invalid erase size:%u", mmc->eraseSize); + return HDF_ERR_INVALID_PARAM; + } + mmc->device.magic = mmc->cntlr->device.magic + MMC_CNTLR_NR_MAX; + + if (MmcCntlrGet(mmc->cntlr) == NULL) { + return HDF_PLT_ERR_DEV_GET; + } + + mmc->device.manager = PlatformManagerGet(PLATFORM_MODULE_MMC); + ret = PlatformDeviceAdd(&mmc->device); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcDeviceAdd: mmc device add fail"); + MmcCntlrPut(mmc->cntlr); + return ret; + } + + if (mmc->type == MMC_DEV_EMMC || mmc->type == MMC_DEV_SD || mmc->type == MMC_DEV_COMBO) { + ret = MmcBlockAdd(mmc); + if (ret != HDF_SUCCESS) { + MmcCntlrPut(mmc->cntlr); + PlatformDeviceDel(&mmc->device); + return ret; + } + } + return HDF_SUCCESS; +} + +void MmcDeviceRemove(struct MmcDevice *mmc) +{ + if (mmc == NULL) { + return; + } + if (mmc->type == MMC_DEV_EMMC || mmc->type == MMC_DEV_SD || mmc->type == MMC_DEV_COMBO) { + MmcBlockDel(mmc); + } + PlatformDeviceDel(&mmc->device); + MmcCntlrPut(mmc->cntlr); +} + +struct MmcDevice *MmcDeviceGet(struct MmcDevice *mmc) +{ + if (mmc == NULL) { + return NULL; + } + + if (PlatformDeviceGet(&mmc->device) == NULL) { + HDF_LOGE("MmcDeviceAdd: get device ref fail!"); + return NULL; + } + return mmc; +} + +void MmcDevicePut(struct MmcDevice *mmc) +{ + if (mmc == NULL) { + return; + } + PlatformDevicePut(&mmc->device); +} + +void MmcDeviceAddOps(struct MmcDevice *mmc, void *ops) +{ + if (mmc->type >= MMC_DEV_INVALID) { + HDF_LOGE("MmcDeviceAddOps: dev type error!"); + return; + } + + if (mmc->type == MMC_DEV_SDIO || mmc->type == MMC_DEV_COMBO) { + SdioDeviceAddOps((struct SdioDevice *)mmc, ops); + return; + } + + if (mmc->type == MMC_DEV_EMMC) { + EmmcDeviceAddOps((struct EmmcDevice *)mmc, ops); + } +} + +static void MmcDeviceFillRwInfo(struct MmcRwData *info, uint8_t *buf, + bool writeFlag, size_t startSec, size_t nSec) +{ + info->buf = buf; + info->writeFlag = writeFlag; + info->startSector = startSec; + info->sectors = nSec; +} + +ssize_t MmcDeviceRead(struct MmcDevice *mmc, uint8_t *buf, size_t startSec, size_t nSec) +{ + struct MmcCmd cmd = {0}; + struct MmcData data = {0}; + struct MmcRwData info = {0}; + size_t curSec = nSec; + size_t curStartSec = startSec; + size_t readSec; + ssize_t ret; + + if (mmc == NULL) { + HDF_LOGE("MmcDeviceRead: mmc is null!"); + return HDF_ERR_INVALID_OBJECT; + } + if (buf == NULL) { + HDF_LOGE("MmcDeviceRead: buf is null!"); + return HDF_ERR_INVALID_PARAM; + } + + do { + if (curSec >= MMC_MAX_SECTOR_NUM) { + readSec = MMC_MAX_SECTOR_NUM; + } else { + readSec = curSec; + } + MmcDeviceFillRwInfo(&info, buf, false, curStartSec, readSec); + if (mmc->cntlr->detecting == true) { + ret = MmcSendReadWriteBlocks(mmc->cntlr, &info); + } else { + cmd.data = &data; + MmcSetupReadWriteBlocksCmd(mmc, &cmd, &info); + ret = MmcCntlrAddRequestMsgToQueue(mmc->cntlr, &cmd); + } + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcDeviceRead: fail!"); + return ret; + } + curSec -= readSec; + curStartSec += readSec; + buf += (readSec * BYTES_PER_BLOCK); + } while (curSec > 0); + + return nSec; +} + +ssize_t MmcDeviceWrite(struct MmcDevice *mmc, uint8_t *buf, size_t startSec, size_t nSec) +{ + struct MmcCmd cmd = {0}; + struct MmcData data = {0}; + struct MmcRwData info = {0}; + size_t curSec = nSec; + size_t curStartSec = startSec; + size_t writeSec; + ssize_t ret; + + if (mmc == NULL) { + HDF_LOGE("MmcDeviceWrite: mmc is null!"); + return HDF_ERR_INVALID_OBJECT; + } + if (buf == NULL) { + HDF_LOGE("MmcDeviceWrite: buf is null!"); + return HDF_ERR_INVALID_PARAM; + } + + do { + if (curSec >= MMC_MAX_SECTOR_NUM) { + writeSec = MMC_MAX_SECTOR_NUM; + } else { + writeSec = curSec; + } + MmcDeviceFillRwInfo(&info, buf, true, curStartSec, writeSec); + if (mmc->cntlr->detecting == true) { + ret = MmcSendReadWriteBlocks(mmc->cntlr, &info); + } else { + cmd.data = &data; + MmcSetupReadWriteBlocksCmd(mmc, &cmd, &info); + ret = MmcCntlrAddRequestMsgToQueue(mmc->cntlr, &cmd); + } + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcDeviceWrite: fail!"); + return ret; + } + curSec -= writeSec; + curStartSec += writeSec; + buf += (writeSec * BYTES_PER_BLOCK); + } while (curSec > 0); + + return nSec; +} + +ssize_t MmcDeviceErase(struct MmcDevice *mmc, size_t startSec, size_t nSec) +{ + size_t curSec = nSec; + size_t curStartSec = startSec; + size_t eraseSec; + ssize_t ret; + + if (mmc == NULL) { + HDF_LOGE("MmcDeviceErase: mmc is null!"); + return HDF_ERR_INVALID_OBJECT; + } + + do { + if (curSec > MMC_MAX_ERASE_SECTOR) { + eraseSec = MMC_MAX_ERASE_SECTOR; + } else { + eraseSec = curSec; + } + ret = MmcSendErase(mmc->cntlr, curStartSec, eraseSec); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcDeviceErase: fail!"); + return ret; + } + curSec -= eraseSec; + curStartSec += eraseSec; + } while (curSec > 0); + + return HDF_SUCCESS; +} diff --git a/support/platform/src/mmc/mmc_dispatch.c b/support/platform/src/mmc/mmc_dispatch.c new file mode 100644 index 0000000000000000000000000000000000000000..2013b8ab37174fe183f17eed7d078d8c7b9fb096 --- /dev/null +++ b/support/platform/src/mmc/mmc_dispatch.c @@ -0,0 +1,118 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "mmc_dispatch.h" +#include "hdf_log.h" +#include "mmc_corex.h" +#include "mmc_emmc.h" +#include "mmc_sdio.h" + +static int32_t MmcCmdDevPresent(struct MmcCntlr *cntlr, struct HdfSBuf *data, struct HdfSBuf *reply) +{ + (void)data; + if (reply == NULL) { + return HDF_ERR_INVALID_PARAM; + } + if (!HdfSbufWriteUint8(reply, MmcCntlrDevPresent(cntlr))) { + HDF_LOGE("MmcCmdDevPresent: write reply sbuf fail!"); + return HDF_ERR_IO; + } + return HDF_SUCCESS; +} + +static int32_t MmcDispatch(struct MmcCntlr *cntlr, int cmd, struct HdfSBuf *data, struct HdfSBuf *reply) +{ + int32_t ret; + + switch (cmd) { + case MMC_CMD_DEV_PRESENT: + ret = MmcCmdDevPresent(cntlr, data, reply); + break; + default: + ret = HDF_ERR_NOT_SUPPORT; + break; + } + return ret; +} + +static int32_t EmmcCmdGetCid(struct MmcCntlr *cntlr, struct HdfSBuf *reply) +{ + int32_t ret; + uint8_t cid[EMMC_CID_LEN] = {0}; + + if (reply == NULL || cntlr == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + ret = EmmcDeviceGetCid((struct EmmcDevice *)cntlr->curDev, cid, EMMC_CID_LEN); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcCmdGetCid: get cid fail!"); + return ret; + } + + if (HdfSbufWriteBuffer(reply, cid, EMMC_CID_LEN) == false) { + HDF_LOGE("MmcCmdGetCid: write back cid fail!"); + return HDF_ERR_IO; + } + + return HDF_SUCCESS; +} + +static int32_t EmmcDispatch(struct MmcCntlr *cntlr, int cmd, struct HdfSBuf *data, struct HdfSBuf *reply) +{ + int32_t ret; + (void)data; + + switch (cmd) { + case EMMC_CMD_GET_CID: + ret = EmmcCmdGetCid(cntlr, reply); + break; + default: + ret = HDF_ERR_NOT_SUPPORT; + break; + } + return ret; +} + +static int32_t SdioDispatch(struct MmcCntlr *cntlr, int cmd, struct HdfSBuf *data, struct HdfSBuf *reply) +{ + (void)cntlr; + (void)cmd; + (void)data; + (void)reply; + return HDF_ERR_NOT_SUPPORT; +} + +int32_t MmcIoDispatch(struct HdfDeviceIoClient *client, int cmd, struct HdfSBuf *data, struct HdfSBuf *reply) +{ + int32_t ret; + struct MmcCntlr *cntlr = NULL; + + if (client == NULL || client->device == NULL) { + HDF_LOGE("MmcIoDispatch: client or hdf dev obj is NULL"); + return HDF_ERR_INVALID_OBJECT; + } + + cntlr = (struct MmcCntlr *)client->device->service; + if (cntlr == NULL) { + HDF_LOGE("MmcIoDispatch: service is NULL"); + return HDF_ERR_INVALID_OBJECT; + } + + if (cmd < MMC_CMD_MAX) { + ret = MmcDispatch(cntlr, cmd, data, reply); + } else if (cmd < EMMC_CMD_MAX) { + ret = EmmcDispatch(cntlr, cmd, data, reply); + } else if (cmd < SDIO_CMD_MAX) { + ret = SdioDispatch(cntlr, cmd, data, reply); + } else { + ret = HDF_ERR_NOT_SUPPORT; + } + + return ret; +} diff --git a/support/platform/src/mmc/mmc_emmc.c b/support/platform/src/mmc/mmc_emmc.c new file mode 100644 index 0000000000000000000000000000000000000000..e4495e3c2d493d756e25ab7bd6deaecfc6021a2d --- /dev/null +++ b/support/platform/src/mmc/mmc_emmc.c @@ -0,0 +1,57 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "mmc_emmc.h" +#include "securec.h" + +#define HDF_LOG_TAG mmc_emmc_c + +static int32_t EmmcDeviceDefaultGetCid(struct EmmcDevice *dev, uint8_t *cid, uint32_t len) +{ + struct MmcDevice *mmc = (struct MmcDevice *)dev; + + if (memcpy_s(cid, sizeof(uint8_t) * len, (uint8_t *)(mmc->reg.rawCid), + sizeof(mmc->reg.rawCid)) != EOK) { + HDF_LOGE("EmmcDeviceDefaultGetCid: memcpy_s fail, size = %d!", len); + return HDF_FAILURE; + } + return HDF_SUCCESS; +} + +static struct EmmcDeviceOps g_emmcOps = { + .getCid = EmmcDeviceDefaultGetCid, +}; + +int32_t EmmcDeviceGetCid(struct EmmcDevice *dev, uint8_t *cid, uint32_t len) +{ + if (dev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + if (cid == NULL || len == 0) { + return HDF_ERR_INVALID_PARAM; + } + if (dev->emmcOps == NULL || dev->emmcOps->getCid == NULL) { + HDF_LOGE("EmmcDeviceGetCid: ops or getCid is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + return dev->emmcOps->getCid(dev, cid, len); +} + +void EmmcDeviceAddOps(struct EmmcDevice *dev, void *ops) +{ + if (dev == NULL) { + HDF_LOGE("EmmcDeviceAddOps: dev is NULL."); + return; + } + if (ops == NULL) { + dev->emmcOps = &g_emmcOps; + HDF_LOGD("EmmcDeviceAddOps: use default ops."); + } else { + dev->emmcOps = (struct EmmcDeviceOps *)ops; + } +} diff --git a/support/platform/src/mmc/mmc_if.c b/support/platform/src/mmc/mmc_if.c new file mode 100644 index 0000000000000000000000000000000000000000..8b37d0d798a416b45946bf7629dd6343e4db5f8a --- /dev/null +++ b/support/platform/src/mmc/mmc_if.c @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "mmc_if.h" +#ifndef __USER__ +#include "devsvc_manager_clnt.h" +#include "mmc_corex.h" +#endif +#include "hdf_base.h" +#ifdef __USER__ +#include "hdf_io_service_if.h" +#endif +#include "hdf_log.h" +#include "osal_mem.h" +#include "securec.h" + +#define HDF_LOG_TAG mmc_if_c + +#define MMC_SVC_NAME_LEN 32 + +static void *MmcCntlrObjGetByNumber(int16_t id) +{ + void *object = NULL; + char *serviceName = NULL; + + if (id < 0) { + HDF_LOGE("MmcCntlrObjGetByNumber: invalid id:%d", id); + return NULL; + } + serviceName = OsalMemCalloc(MMC_SVC_NAME_LEN + 1); + if (serviceName == NULL) { + HDF_LOGE("MmcCntlrObjGetByNumber: OsalMemCalloc fail!"); + return NULL; + } + if (snprintf_s(serviceName, MMC_SVC_NAME_LEN + 1, MMC_SVC_NAME_LEN, + "HDF_PLATFORM_MMC_%d", id) < 0) { + HDF_LOGE("MmcCntlrObjGetByNumber: format service name fail!"); + goto __ERR; + } + +#ifdef __USER__ + object = (void *)HdfIoServiceBind(serviceName); +#else + object = (void *)MmcCntlrGetByNr(id); +#endif + if (object == NULL) { + HDF_LOGE("MmcCntlrObjGetByNumber: get service fail!"); + } else { + HDF_LOGD("MmcCntlrObjGetByNumber: success"); + } + +__ERR: + OsalMemFree(serviceName); + return object; +} + +DevHandle MmcOpen(int16_t id) +{ + return (DevHandle)MmcCntlrObjGetByNumber(id); +} + +void MmcClose(DevHandle handle) +{ + if (handle != NULL) { +#ifdef __USER__ + HdfIoServiceRecycle((struct HdfIoService *)handle); +#endif + } +} + +bool MmcDevPresent(DevHandle handle) +{ + if (handle == NULL) { + return false; + } +#ifdef __USER__ + int32_t ret; + uint8_t present = 0; + struct HdfSBuf *reply = NULL; + struct HdfIoService *service = (struct HdfIoService *)handle; + + reply = HdfSBufObtainDefaultSize(); + if (reply == NULL) { + HDF_LOGE("MmcDevPresent: failed to obtain reply!"); + return false; + } + + if (service->dispatcher == NULL || service->dispatcher->Dispatch == NULL) { + HDF_LOGE("MmcDevPresent: dispatcher or Dispatch is NULL!"); + goto __EXIT; + } + ret = service->dispatcher->Dispatch(&service->object, MMC_CMD_DEV_PRESENT, + NULL, reply); + if (ret != HDF_SUCCESS) { + HDF_LOGE("MmcDevPresent: failed to send service call:%d", ret); + goto __EXIT; + } + + if (HdfSbufReadUint8(reply, &present) == false) { + HDF_LOGE("MmcDevPresent: read present fail!"); + goto __EXIT; + } +__EXIT: + HdfSBufRecycle(reply); + return (present != 0); +#else + return MmcCntlrDevPresent((struct MmcCntlr *)handle); +#endif +} diff --git a/support/platform/src/mmc/mmc_protocol.c b/support/platform/src/mmc/mmc_protocol.c new file mode 100644 index 0000000000000000000000000000000000000000..304c3e12783f05bb801b73e4832cad5c41181bd2 --- /dev/null +++ b/support/platform/src/mmc/mmc_protocol.c @@ -0,0 +1,4119 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "mmc_emmc.h" +#include "mmc_sd.h" +#include "mmc_sdio.h" +#include "securec.h" + +#define HDF_LOG_TAG mmc_protocol_c + +#define INIT_CMD_RETRY_TIMES 100 +#define MMC_CMD_DEFAULT_RETRY_TIMES 3 +#define SEND_STATUS_CMD_RETRY_TIMES 100 +#define STOP_TRANSMISSION_CMD_RETRY_TIMES 5 +#define BITS_NUMBER_OF_4_BYTES 32 + +/* TRAN_SPEED: Frequency unit 0 = 100KHz, 1 = 1MHz, 2 = 10MHz, 3 = 100MHz, 4...7 = reserved */ +uint32_t g_tranSpeedUnit[] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; + +/* TAAC: Time unit 0 = 1ns, 1 = 10ns, 2 = 100ns, 3 = 1us, 4 = 10us, 5 = 100us, 6 = 1ms, 7 = 10ms */ +uint32_t g_taccUnit[] = { 1, 10, 100, 1000, 10000, 100000, 1000000, 10000000 }; + +/* + * TRAN_SPEED/TAAC: Multiplier factor 0 = reserved, 1 = 1.0, 2 = 1.2, 3 = 1.3, 4 = 1.5, 5 = 2.0, 6 = 2.5, + * 7 = 3.0, 8 = 3.5, 9 = 4.0, A = 4.5, B = 5.0, C = 5.5, D = 6.0, E = 7.0, F = 8.0 + */ +uint32_t g_commFactor[] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; + +/* Little-endian and Big-endian interconversion. */ +static uint32_t MmcEndianConversion(uint32_t x) +{ + uint32_t val = x; + + return (uint32_t)( + (((uint32_t)(val) & (uint32_t)0x000000ffUL) << 24) | + (((uint32_t)(val) & (uint32_t)0x0000ff00UL) << 8) | + (((uint32_t)(val) & (uint32_t)0x00ff0000UL) >> 8) | + (((uint32_t)(val) & (uint32_t)0xff000000UL) >> 24)); +} + +/* Decoding algorithm */ +static uint32_t MmcParseBits(uint32_t *data, uint32_t bitsLen, uint32_t start, uint32_t size) +{ + uint32_t index; + uint32_t shift; + uint32_t ret; + + if (start >= bitsLen || size == 0) { + HDF_LOGE("MmcParseBits: input invalid!"); + return 0; + } + if (size > BITS_NUMBER_OF_4_BYTES) { + HDF_LOGE("MmcParseBits: not support!"); + return 0; + } + + index = (bitsLen / BITS_NUMBER_OF_4_BYTES) - (start / BITS_NUMBER_OF_4_BYTES) - 1; + shift = start & (BITS_NUMBER_OF_4_BYTES - 1); + ret = data[index] >> shift; + if (size + shift > BITS_NUMBER_OF_4_BYTES) { + ret |= data[index - 1] << (BITS_NUMBER_OF_4_BYTES - shift); + } + + if (size < BITS_NUMBER_OF_4_BYTES) { + return (ret & ((1u << size) - 1)); + } + return (ret & 0xFFFFFFFF); +} + +static int32_t MmcSendCmd(struct MmcCntlr *cntlr, struct MmcCmd *cmd, struct MmcData *data, uint32_t retryTimes) +{ + uint32_t i; + int32_t ret; + + if (cntlr == NULL || cmd == NULL || retryTimes == 0) { + return HDF_ERR_INVALID_PARAM; + } + if (cntlr->ops == NULL || cntlr->ops->request == NULL) { + return HDF_ERR_NOT_SUPPORT; + } + + cmd->data = data; + for (i = 0; i < retryTimes; i++) { + ret = MmcCntlrDoRequest(cntlr, cmd); + if (ret != HDF_SUCCESS) { + continue; + } + if (cmd->returnError != HDF_SUCCESS) { + continue; + } + if (data != NULL && data->returnError != HDF_SUCCESS) { + continue; + } + break; + } + if (data == NULL) { + return cmd->returnError; + } + if (cmd->returnError != HDF_SUCCESS) { + return cmd->returnError; + } + if (data->returnError != HDF_SUCCESS) { + return data->returnError; + } + return HDF_SUCCESS; +} + +static void MmcGoIdleState(struct MmcCntlr *cntlr) +{ + struct MmcCmd cmd = {0}; + + /* Command GO_IDLE_STATE (CMD0) is the software reset command and sets all cards into Idle State. */ + cmd.cmdCode = GO_IDLE_STATE; + /* [31:0] stuff bits. */ + cmd.argument = 0; + /* Broadcast Commands (bc), no response. */ + cmd.respType = MMC_RESP_SPI_R1 | MMC_RESP_NONE | MMC_CMD_TYPE_BC; + (void)MmcSendCmd(cntlr, &cmd, NULL, 1); + OsalMDelay(1); +} + +static int32_t MmcSendOpCond(struct MmcCntlr *cntlr, uint32_t arg, uint32_t *ocr) +{ + struct MmcCmd cmd = {0}; + int32_t err; + uint32_t i; + + /* + * The SEND_OP_COND (CMD1) command is designed to provide MultiMediaCard hosts with a mechanism + * to identify and reject cards which do not match the VDD range desired by the host. + */ + cmd.cmdCode = SEND_OP_COND; + /* [31:0] OCR. */ + cmd.argument = arg; + cmd.respType = MMC_RESP_SPI_R1 | MMC_RESP_R3 | MMC_CMD_TYPE_BCR; + /* + * The host must poll the card (by repeatedly sending CMD1 or ACMD41) until the 'in-idle-state' bit in + * the card response indicates (by being set to 0) that the card completed its initialization processes + * and is ready for the next command. + */ + for (i = 0; i < INIT_CMD_RETRY_TIMES; i++) { + err = MmcSendCmd(cntlr, &cmd, NULL, 1); + if (err != HDF_SUCCESS) { + break; + } + if (arg == 0 || (cmd.resp[0] & MMC_CARD_BUSY_STATUS) > 0) { + break; + } + + err = HDF_ERR_TIMEOUT; + OsalMDelay(10); + } + + if (ocr != NULL) { + *ocr = cmd.resp[0]; + } + return err; +} + +static int32_t MmcAllSendCid(struct MmcCntlr *cntlr) +{ + struct MmcCmd cmd = {0}; + uint32_t *cid = NULL; + int32_t err; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + cmd.cmdCode = ALL_SEND_CID; + /* [31:0] stuff bits. */ + cmd.argument = 0; + /* Broadcast Commands with Response(bcr). */ + cmd.respType = MMC_RESP_R2 | MMC_CMD_TYPE_BCR; + err = MmcSendCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); + if (err != HDF_SUCCESS) { + return err; + } + cid = cntlr->curDev->reg.rawCid; + if (memcpy_s(cid, sizeof(cntlr->curDev->reg.rawCid), cmd.resp, sizeof(cmd.resp)) != EOK) { + HDF_LOGE("MmcAllSendCid: memcpy_s fail!"); + return HDF_FAILURE; + } + return err; +} + +static int32_t MmcSetRelativeAddr(struct MmcCntlr *cntlr) +{ + struct MmcCmd cmd = {0}; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + cmd.cmdCode = SET_RELATIVE_ADDR; + /* [31:16] RCA, [15:0] stuff bits. */ + cmd.argument = (cntlr->curDev->reg.rca << 16); + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_AC; + return MmcSendCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); +} + +static int32_t MmcSelectCard(struct MmcCntlr *cntlr) +{ + struct MmcCmd cmd = {0}; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + cmd.cmdCode = SELECT_CARD; + /* [31:16] RCA, [15:0] stuff bits. */ + cmd.argument = (cntlr->curDev->reg.rca << 16); + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_AC; + return MmcSendCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); +} + +static int32_t MmcSendExtCsd(struct MmcCntlr *cntlr, uint8_t *extCsd, uint32_t len) +{ + struct MmcCmd cmd = {0}; + struct MmcData data = {0}; + + cmd.cmdCode = SEND_EXT_CSD; + cmd.argument = 0; + cmd.respType = MMC_RESP_SPI_R1 | MMC_RESP_R1 | MMC_CMD_TYPE_ADTC; + + /* The card sends the EXT_CSD register as a block of data, 512 bytes long. */ + data.blockSize = len; + data.blockNum = 1; + data.dataFlags = DATA_READ; + data.dataBuffer = extCsd; + + return MmcSendCmd(cntlr, &cmd, &data, 1); +} + +static int32_t MmcSendCsd(struct MmcCntlr *cntlr) +{ + struct MmcCmd cmd = {0}; + uint32_t *csd = NULL; + int32_t err; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + cmd.cmdCode = SEND_CSD; + cmd.argument = (cntlr->curDev->reg.rca << 16); + cmd.respType = MMC_RESP_R2 | MMC_CMD_TYPE_AC; + err = MmcSendCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); + if (err != HDF_SUCCESS) { + return err; + } + csd = cntlr->curDev->reg.rawCsd; + if (memcpy_s(csd, sizeof(cntlr->curDev->reg.rawCsd), cmd.resp, sizeof(cmd.resp)) != EOK) { + HDF_LOGE("memcpy_s fail!"); + return HDF_FAILURE; + } + return err; +} + +int32_t MmcSendStatus(struct MmcCntlr *cntlr, uint32_t *status) +{ + struct MmcCmd cmd = {0}; + int32_t error; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + cmd.cmdCode = SEND_STATUS; + /* [31:16] RCA, [15:0] stuff bits. */ + cmd.argument = (cntlr->curDev->reg.rca << 16); + cmd.respType = MMC_RESP_SPI_R2 | MMC_RESP_R1 | MMC_CMD_TYPE_AC; + error = MmcSendCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); + if (error != HDF_SUCCESS) { + return error; + } + if (status != NULL) { + *status = cmd.resp[0]; + } + return error; +} + +static int32_t MmcSwitch(struct MmcCntlr *cntlr, uint8_t set, uint8_t index, uint8_t value) +{ + int32_t error; + struct MmcCmd cmd = {0}; + uint32_t status; + + cmd.cmdCode = SWITCH; + /* + * [31:26] Set to 0; [25:24] Access; [23:16] Index; + * [15:8] Value; [7:3] Set to 0; [2:0] Cmd Set. + */ + cmd.argument = (EMMC_EXT_CSD_WRITE_BYTE << 24) + | (index << 16) + | (value << 8) + | set; + cmd.respType = MMC_RESP_SPI_R1B | MMC_RESP_R1B | MMC_CMD_TYPE_AC; + error = MmcSendCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); + if (error != HDF_SUCCESS) { + HDF_LOGE("MmcSwitch: send cmd fail!"); + return error; + } + + /* + * The SWITCH command response is of type R1b, therefore, the host should read the card status, using + * SEND_STATUS command, after the busy signal is de-asserted, to check the result of the SWITCH operation. + */ + do { + error = MmcSendStatus(cntlr, &status); + if (error != HDF_SUCCESS) { + HDF_LOGE("MmcSwitch: send status cmd fail!"); + return error; + } + } while ((!(status & READY_FOR_DATA)) || MmcCntlrDevBusy(cntlr) == true); + + /* + * When the host tries to access a partition which has not been created before, the devices sets the + * SWITCH_ERROR bit in the status register and will not change the PARTITION_ACCESS bits. + */ + if ((status & SWITCH_ERROR) > 0) { + return HDF_MMC_ERR_SWITCH_FAIL; + } + return error; +} + +static int32_t MmcAppCmd(struct MmcCntlr *cntlr, uint32_t acmd) +{ + int32_t err; + struct MmcCmd cmd = {0}; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + /* + * After the bus is activated the host will request the cards to send their valid operation conditions + * (ACMD41 preceding with APP_CMD - CMD55 with RCA = 0x0000). + */ + cmd.cmdCode = APP_CMD; + if (acmd != SD_ACMD_OP_COND) { + /* [31:16] RCA, [15:0] stuff bits */ + cmd.argument = (cntlr->curDev->reg.rca << 16); + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_AC; + } else { + cmd.argument = 0; + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_BCR; + } + err = MmcSendCmd(cntlr, &cmd, NULL, 1); + if (err != HDF_SUCCESS) { + return err; + } + if (!(cmd.resp[0] & IS_APP_CMD)) { + return HDF_FAILURE; + } + return err; +} + +static int32_t MmcWaitCardReady(struct MmcCntlr *cntlr) +{ + int error; + uint32_t status; + int32_t retryTimes = SEND_STATUS_CMD_RETRY_TIMES; + + do { + error = MmcSendStatus(cntlr, &status); + if (error != HDF_SUCCESS) { + return error; + } + + if (retryTimes >= 0) { + retryTimes--; + OsalMSleep(10); + } else { + return HDF_ERR_TIMEOUT; + } + } while (!(status & READY_FOR_DATA) || (MMC_CARD_CURRENT_STATE(status) == STATE_PRG)); + return error; +} + +int32_t MmcStopTransmission(struct MmcCntlr *cntlr, bool writeFlag, uint32_t *stopStatus) +{ + int32_t err; + struct MmcCmd cmd = {0}; + + cmd.cmdCode = STOP_TRANSMISSION; + /* R1 for read cases and R1b for write cases. */ + if (writeFlag == true) { + cmd.respType = MMC_RESP_R1B | MMC_CMD_TYPE_AC; + } else { + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_AC; + } + err = MmcSendCmd(cntlr, &cmd, NULL, STOP_TRANSMISSION_CMD_RETRY_TIMES); + if (err != HDF_SUCCESS) { + return err; + } + + *stopStatus = cmd.resp[0]; + /* No need to check card status in case of read. */ + if (writeFlag == false) { + return err; + } + return MmcWaitCardReady(cntlr); +} + +int32_t MmcSendTuning(struct MmcCntlr *cntlr, uint32_t cmdCode, bool sendStop) +{ + struct MmcCmd cmd = {0}; + struct MmcData data = {0}; + /* Tuning Block Pattern UHS-I. */ + uint8_t tuningBlk4bit[] = { + 0xFF, 0x0F, 0xFF, 0x00, 0xFF, 0xCC, 0xC3, 0xCC, 0xC3, 0x3C, 0xCC, 0xFF, + 0xFE, 0xFF, 0xFE, 0xEF, 0xFF, 0xDF, 0xFF, 0xDD, 0xFF, 0xFB, 0xFF, 0xFB, + 0xBF, 0xFF, 0x7F, 0xFF, 0x77, 0xF7, 0xBD, 0xEF, 0xFF, 0xF0, 0xFF, 0xF0, + 0x0F, 0xFC, 0xCC, 0x3C, 0xCC, 0x33, 0xCC, 0xCF, 0xFF, 0xEF, 0xFF, 0xEE, + 0xFF, 0xFD, 0xFF, 0xFD, 0xDF, 0xFF, 0xBF, 0xFF, 0xBB, 0xFF, 0xF7, 0xFF, + 0xF7, 0x7f, 0x7B, 0xDE, + }; + /* Tuning block pattern for 8 bit mode for HS200. */ + uint8_t tuningBlk8bit[] = { + 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, 0x00, 0xFF, 0xFF, 0xCC, 0xCC, + 0xCC, 0x33, 0xCC, 0xCC, 0xCC, 0x33, 0x33, 0xCC, 0xCC, 0xCC, 0xFF, 0xFF, + 0xFF, 0xEE, 0xFF, 0xFF, 0xFF, 0xEE, 0xEE, 0xFF, 0xFF, 0xFF, 0xDD, 0xFF, + 0xFF, 0xFF, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, 0xBB, 0xFF, 0xFF, 0xFF, 0xBB, + 0xBB, 0xFF, 0xFF, 0xFF, 0x77, 0xFF, 0xFF, 0xFF, 0x77, 0x77, 0xFF, 0x77, + 0xBB, 0xDD, 0xEE, 0xFF, 0xFF, 0xFF, 0xFF, 0x00, 0xFF, 0xFF, 0xFF, 0x00, + 0x00, 0xFF, 0xFF, 0xCC, 0xCC, 0xCC, 0x33, 0xCC, 0xCC, 0xCC, 0x33, 0x33, + 0xCC, 0xCC, 0xCC, 0xFF, 0xFF, 0xFF, 0xEE, 0xFF, 0xFF, 0xFF, 0xEE, 0xEE, + 0xFF, 0xFF, 0xFF, 0xDD, 0xFF, 0xFF, 0xFF, 0xDD, 0xDD, 0xFF, 0xFF, 0xFF, + 0xBB, 0xFF, 0xFF, 0xFF, 0xBB, 0xBB, 0xFF, 0xFF, 0xFF, 0x77, 0xFF, 0xFF, + 0xFF, 0x77, 0x77, 0xFF, 0x77, 0xBB, 0xDD, 0xEE, + }; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + cmd.cmdCode = cmdCode; + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_ADTC; + + if (cntlr->curDev->workPara.width == BUS_WIDTH4) { + data.blockSize = sizeof(tuningBlk4bit); + data.dataBuffer = tuningBlk4bit; + } else if (cntlr->curDev->workPara.width == BUS_WIDTH8) { + data.blockSize = sizeof(tuningBlk8bit); + data.dataBuffer = tuningBlk8bit; + } else { + HDF_LOGE("MmcSendTuning: work width is 1, can not tune!"); + return HDF_FAILURE; + } + data.blockNum = 1; + data.dataFlags = DATA_READ; + if (sendStop == true) { + data.sendStopCmd = true; + data.stopCmd.cmdCode = STOP_TRANSMISSION; + data.stopCmd.respType = MMC_RESP_R1B | MMC_CMD_TYPE_AC; + } + + return MmcSendCmd(cntlr, &cmd, &data, 1); +} + +static int32_t MmcSendEraseStartCmd(struct MmcCntlr *cntlr, uint32_t arg) +{ + struct MmcCmd cmd = {0}; + struct MmcDevice *dev = cntlr->curDev; + + if (dev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + /* SD: CMD32; EMMC: CMD35 */ + if (dev->type == MMC_DEV_SD) { + cmd.cmdCode = SD_CMD_ERASE_WR_BLK_START; + } else { + cmd.cmdCode = ERASE_GROUP_START; + } + /* [31:0]data address. */ + cmd.argument = arg; + /* + * Data address for media =<2GB is a 32bit byte address and data address for media > 2GB is + * a 32bit sector (512B) address. + */ + if (dev->state.bits.blockAddr == 0) { + cmd.argument <<= MMC_MAX_BLOCKSIZE_SHIFT; + } + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_AC; + return MmcSendCmd(cntlr, &cmd, NULL, 1); +} + +static int32_t MmcSendEraseEndCmd(struct MmcCntlr *cntlr, uint32_t arg) +{ + struct MmcCmd cmd = {0}; + struct MmcDevice *dev = cntlr->curDev; + + if (dev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + /* SD: CMD33; EMMC: CMD36 */ + if (dev->type == MMC_DEV_SD) { + cmd.cmdCode = SD_CMD_ERASE_WR_BLK_END; + } else { + cmd.cmdCode = ERASE_GROUP_END; + } + /* [31:0]data address. */ + cmd.argument = arg; + /* + * Data address for media =<2GB is a 32bit byte address and data address for media > 2GB is + * a 32bit sector (512B) address. + */ + if (dev->state.bits.blockAddr == 0) { + cmd.argument <<= MMC_MAX_BLOCKSIZE_SHIFT; + } + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_AC; + return MmcSendCmd(cntlr, &cmd, NULL, 1); +} + +static int32_t MmcSendEraseCmd(struct MmcCntlr *cntlr, uint32_t arg) +{ + struct MmcCmd cmd = {0}; + struct MmcDevice *dev = cntlr->curDev; + + if (dev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + /* ERASE cmd38 */ + cmd.cmdCode = MMC_ERASE; + cmd.argument = arg; + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_AC; + return MmcSendCmd(cntlr, &cmd, NULL, 1); +} + +static int32_t MmcAlignEraseSize(struct MmcCntlr *cntlr, uint32_t *start, uint32_t *end, uint32_t size) +{ + uint32_t curSize = size; + uint32_t tmp; + + if (cntlr->curDev->eraseSize == 0) { + return HDF_ERR_NOT_SUPPORT; + } + + /* align start. */ + tmp = (*start) % cntlr->curDev->eraseSize; + if (tmp > 0) { + tmp = cntlr->curDev->eraseSize - tmp; + (*start) += tmp; + if (curSize > tmp) { + curSize -= tmp; + } else { + return HDF_ERR_NOT_SUPPORT; + } + } + + /* align size. */ + tmp = curSize % cntlr->curDev->eraseSize; + if (tmp > 0) { + curSize -= tmp; + } + if (curSize == 0) { + return HDF_ERR_NOT_SUPPORT; + } + + (*end) = (*start) + curSize; + return HDF_SUCCESS; +} + +int32_t MmcSendErase(struct MmcCntlr *cntlr, uint32_t startSec, uint32_t nSec) +{ + int32_t ret; + uint32_t start = startSec; + uint32_t end = start + nSec; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + if (cntlr->curDev->eraseSize == 0 || + (cntlr->curDev->reg.csd.ccc & MMC_CSD_CCC_ERASE) == 0) { + return HDF_ERR_NOT_SUPPORT; + } + + ret = MmcAlignEraseSize(cntlr, &start, &end, nSec); + if (ret != HDF_SUCCESS) { + /* after align, no need to erase. */ + return HDF_SUCCESS; + } + + /* + * emmc: + * Starting the erase process is a three steps sequence. + * First the host defines the start address of the range using the ERASE_GROUP_START (CMD35) command, + * next it defines the last address of the range using the ERASE_GROUP_END (CMD36) command and + * finally it starts the erase process by issuing the ERASE (CMD38) command with argument bits set to zero. + * sd: + * The host should adhere to the following command sequence: ERASE_WR_BLK_START(cmd32), ERASE_WR_BLK_END(cmd33) + * and ERASE(CMD38). + */ + ret = MmcSendEraseStartCmd(cntlr, start); + if (ret != HDF_SUCCESS) { + HDF_LOGE("send erase start cmd fail, err = %d!", ret); + return ret; + } + + ret = MmcSendEraseEndCmd(cntlr, end); + if (ret != HDF_SUCCESS) { + HDF_LOGE("send erase end cmd fail, err = %d!", ret); + return ret; + } + + ret = MmcSendEraseCmd(cntlr, 0); + if (ret != HDF_SUCCESS) { + HDF_LOGE("send erase cmd fail, err = %d!", ret); + return ret; + } + + return MmcWaitCardReady(cntlr); +} + +void MmcSetupReadWriteBlocksCmd(struct MmcDevice *mmc, struct MmcCmd *cmd, struct MmcRwData *info) +{ + cmd->data->blockSize = BYTES_PER_BLOCK; + cmd->data->blockNum = info->sectors; + cmd->data->dataBuffer = info->buf; + + cmd->respType = MMC_RESP_R1 | MMC_CMD_TYPE_ADTC; + /* [31:0]data address. */ + cmd->argument = info->startSector; + if (mmc->state.bits.blockAddr == 0) { + /* SDSC Card uses byte unit address and SDHC and SDXC Cards use block unit address(512 Bytes unit). */ + cmd->argument <<= MMC_MAX_BLOCKSIZE_SHIFT; + } + if (info->writeFlag == true) { + cmd->data->dataFlags = DATA_WRITE; + cmd->cmdCode = WRITE_BLOCK; + if (info->sectors > 1) { + cmd->cmdCode = WRITE_MULTIPLE_BLOCK; + } + } else { + cmd->data->dataFlags = DATA_READ; + cmd->cmdCode = READ_SINGLE_BLOCK; + if (info->sectors > 1) { + cmd->cmdCode = READ_MULTIPLE_BLOCK; + } + } + if ((info->sectors > 1) && (mmc->cntlr->caps.bits.cmdStop > 0)) { + cmd->data->stopCmd.cmdCode = STOP_TRANSMISSION; + cmd->data->sendStopCmd = true; + } +} + +int32_t MmcSendReadWriteBlocks(struct MmcCntlr *cntlr, struct MmcRwData *info) +{ + struct MmcCmd cmd = {0}; + struct MmcData data = {0}; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + cmd.data = &data; + MmcSetupReadWriteBlocksCmd(cntlr->curDev, &cmd, info); + return MmcSendCmd(cntlr, &cmd, &data, 1); +} + +static int32_t EmmcDecodeCsd(struct MmcCntlr *cntlr) +{ + struct MmcCsd *csd = NULL; + uint32_t unit, factor; + uint32_t *rawCsd = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + rawCsd = cntlr->curDev->reg.rawCsd; + csd = &(cntlr->curDev->reg.csd); + + /* CSD_STRUCTURE: [127:126]. */ + csd->structure = MmcParseBits(rawCsd, CSD_BITS, 126, 2); + if (csd->structure == 0) { + HDF_LOGE("EmmcDecodeCsd: structure is invalid!"); + return HDF_ERR_INVALID_PARAM; + } + + /* SPEC_VERS: [125:122]. */ + csd->specVers = MmcParseBits(rawCsd, CSD_BITS, 122, 4); + /* TAAC: [119:112]; TAAC bit position-->Time unit: [2:0], Multiplier factor: [6:3]. */ + factor = MmcParseBits(rawCsd, CSD_BITS, 115, 4); + unit = MmcParseBits(rawCsd, CSD_BITS, 112, 3); + csd->taccNs = (g_taccUnit[unit] * g_commFactor[factor] + 9) / 10; + /* NSAC: [111:104] */ + csd->taccClks = MmcParseBits(rawCsd, CSD_BITS, 104, 8) * 100; + + /* TRAN_SPEED: [103:96]; TRAN_SPEED bit-->Frequency unit: [2:0], Multiplier factor: [6:3]. */ + factor = MmcParseBits(rawCsd, CSD_BITS, 99, 4); + unit = MmcParseBits(rawCsd, CSD_BITS, 96, 3); + csd->maxDtr = g_tranSpeedUnit[unit] * g_commFactor[factor]; + csd->ccc = MmcParseBits(rawCsd, CSD_BITS, 84, 12); + + /* C_SIZE: [73:62] */ + factor = MmcParseBits(rawCsd, CSD_BITS, 62, 12); + /* C_SIZE_MULT: [49:47] */ + unit = MmcParseBits(rawCsd, CSD_BITS, 47, 3); + csd->capacity = (1 + factor) << (unit + 2); + /* READ_BL_LEN: [83:80]. */ + csd->readBlkLen = MmcParseBits(rawCsd, CSD_BITS, 80, 4); + /* READ_BL_PARTIAL: [79:79] */ + csd->rdPartial = MmcParseBits(rawCsd, CSD_BITS, 79, 1); + /* WRITE_BLK_MISALIGN: [78:78] */ + csd->wrMisalign = MmcParseBits(rawCsd, CSD_BITS, 78, 1); + /* READ_BLK_MISALIGN: [77:77] */ + csd->rdMisalign = MmcParseBits(rawCsd, CSD_BITS, 77, 1); + /* Write speed factor(R2W_FACTOR) :[28:26] */ + csd->r2wFactor = MmcParseBits(rawCsd, CSD_BITS, 26, 3); + /* WRITE_BL_LEN: [25:22] */ + csd->writeBlkLen = MmcParseBits(rawCsd, CSD_BITS, 22, 4); + /* WRITE_BL_PARTIAL: [21:21] */ + csd->wrPartial = MmcParseBits(rawCsd, CSD_BITS, 21, 1); + + /* + * Note that the support for 512B read access is mandatory for all cards. + * And that the cards has to be in 512B block length mode by default after power-on, or software reset. + */ + if (csd->writeBlkLen >= MMC_MAX_BLOCKSIZE_SHIFT) { + /* ERASE_GRP_SIZE: [46:42] */ + unit = MmcParseBits(rawCsd, CSD_BITS, 42, 5); + /* ERASE_GRP_MULT: [41:37] */ + factor = MmcParseBits(rawCsd, CSD_BITS, 37, 5); + /* size of erasable unit = (ERASE_GRP_SIZE + 1) * (ERASE_GRP_MULT + 1) */ + csd->eraseSize = (unit + 1) * (factor + 1); + csd->eraseSize <<= (csd->writeBlkLen - MMC_MAX_BLOCKSIZE_SHIFT); + } + return HDF_SUCCESS; +} + +static int32_t EmmcDecodeCid(struct MmcCntlr *cntlr) +{ + uint32_t i; + struct MmcCid *cid = NULL; + uint32_t *rawCid = NULL; + uint8_t specVers, cbx; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + rawCid = cntlr->curDev->reg.rawCid; + cid = &(cntlr->curDev->reg.cid); + specVers = cntlr->curDev->reg.csd.specVers; + if (specVers > MMC_CSD_SPEC_VER_4) { + HDF_LOGE("EmmcDecodeCid: specVers is invalid!"); + return HDF_ERR_NOT_SUPPORT; + } + + /* + * The manufacturing date is composed of two hexadecimal digits, four bits each, + * representing a two digits date code m/y; + */ + cid->month = MmcParseBits(rawCid, CID_BITS, 12, 4); + /* The "y" field, least significant nibble, is the year code. 0 is 1997. */ + cid->year = MmcParseBits(rawCid, CID_BITS, 8, 4) + 1997; + + if (specVers == MMC_CSD_SPEC_VER_0 || specVers == MMC_CSD_SPEC_VER_1) { + cid->mid = MmcParseBits(rawCid, CID_BITS, 104, 24); + for (i = 0; i < 7; i++) { + cid->pnm[i] = MmcParseBits(rawCid, CID_BITS, 96 - (i * 8), 8); + } + cid->pnm[7] = '\0'; + cid->hwPrv = MmcParseBits(rawCid, CID_BITS, 44, 4); + cid->fwPrv = MmcParseBits(rawCid, CID_BITS, 40, 4); + cid->psn = MmcParseBits(rawCid, CID_BITS, 16, 24); + } else { + /* Manufacturer ID(MID): [127:120] */ + cid->mid = MmcParseBits(rawCid, CID_BITS, 120, 8); + /* OEM/Application ID(OID): [119:104] */ + cid->oid = MmcParseBits(rawCid, CID_BITS, 104, 16); + /* Product name(PNM): [103:56] */ + for (i = 0; i < 6; i++) { + cid->pnm[i] = MmcParseBits(rawCid, CID_BITS, 96 - (i * 8), 8); + } + cid->pnm[6] = '\0'; + /* Product serial number(PSN): [47:16] */ + cid->psn = MmcParseBits(rawCid, CID_BITS, 16, 32); + /* + * Card or BGA(CBX): [113:112] + * 00: Card (removable); 01: BGA (Discrete embedded); 10: POP; 11: Reserved. + */ + cbx = MmcParseBits(rawCid, CID_BITS, 112, 2); + if (cbx == 0) { + cntlr->curDev->state.bits.removeable = 1; + HDF_LOGD("Emmc is removeable!"); + } + } + return HDF_SUCCESS; +} + +static void EmmcDecodeExtCsdSector(struct EmmcDevice *emmcDev, struct EmmcExtCsd *extCsd) +{ + uint32_t i; + uint32_t shift = 0; + + extCsd->sectors = 0; + for (i = 0; i < EMMC_EXT_CSD_SEC_CNT_BYTES; i++) { + extCsd->sectors |= (uint32_t)(extCsd->rawSectors[i] << shift); + shift += BITS_PER_BYTE; + } + /* Device density > 2GiB are sector addressed. */ + if (extCsd->sectors > (2u * 1024 * 1024 * 1024) / 512) { + emmcDev->mmc.state.bits.blockAddr = 1; + } +} + +static void EmmcDecodeExtCsdCardType(struct EmmcExtCsd *extCsd) +{ + uint8_t cardType = extCsd->rawCardType & EMMC_EXT_CSD_CARD_TYPE_MASK; + + switch (cardType) { + case EMMC_EXT_CSD_CARD_TYPE_SDR_ALL: + case EMMC_EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_8V: + case EMMC_EXT_CSD_CARD_TYPE_SDR_ALL_DDR_1_2V: + case EMMC_EXT_CSD_CARD_TYPE_SDR_ALL_DDR_52: + extCsd->hsMaxDtr = EMMC_EXT_CSD_HIGH_SPEED_200; + extCsd->cardType = EMMC_EXT_CSD_CARD_TYPE_SDR_200; + break; + case EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V_ALL: + case EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V_ALL_DDR_1_8V: + case EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V_ALL_DDR_1_2V: + case EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V_ALL_DDR_52: + extCsd->hsMaxDtr = EMMC_EXT_CSD_HIGH_SPEED_200; + extCsd->cardType = EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V; + break; + case EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V_ALL: + case EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V_ALL_DDR_1_8V: + case EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V_ALL_DDR_1_2V: + case EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V_ALL_DDR_52: + extCsd->hsMaxDtr = EMMC_EXT_CSD_HIGH_SPEED_200; + extCsd->cardType = EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V; + break; + case (EMMC_EXT_CSD_CARD_TYPE_DDR_52 | EMMC_EXT_CSD_CARD_TYPE_52 | EMMC_EXT_CSD_CARD_TYPE_26): + extCsd->hsMaxDtr = EMMC_EXT_CSD_HIGH_SPEED_52; + extCsd->cardType = EMMC_EXT_CSD_CARD_TYPE_DDR_52; + break; + case (EMMC_EXT_CSD_CARD_TYPE_DDR_1_2V | EMMC_EXT_CSD_CARD_TYPE_52 | EMMC_EXT_CSD_CARD_TYPE_26): + extCsd->hsMaxDtr = EMMC_EXT_CSD_HIGH_SPEED_52; + extCsd->cardType = EMMC_EXT_CSD_CARD_TYPE_DDR_1_2V; + break; + case (EMMC_EXT_CSD_CARD_TYPE_DDR_1_8V | EMMC_EXT_CSD_CARD_TYPE_52 | EMMC_EXT_CSD_CARD_TYPE_26): + extCsd->hsMaxDtr = EMMC_EXT_CSD_HIGH_SPEED_52; + extCsd->cardType = EMMC_EXT_CSD_CARD_TYPE_DDR_1_8V; + break; + case (EMMC_EXT_CSD_CARD_TYPE_52 | EMMC_EXT_CSD_CARD_TYPE_26): + extCsd->hsMaxDtr = EMMC_EXT_CSD_HIGH_SPEED_52; + break; + case EMMC_EXT_CSD_CARD_TYPE_26: + extCsd->hsMaxDtr = EMMC_EXT_CSD_HIGH_SPEED_26; + break; + default: + HDF_LOGD("EmmcDecodeExtCsdCardType: not support high-speed!"); + break; + } +} + +static void EmmcDecodeExtCsdRev13Field(struct EmmcExtCsd *extCsd, uint8_t *rawExtCsd) +{ + uint8_t shift = rawExtCsd[EMMC_EXT_CSD_S_A_TIMEOUT]; + + extCsd->eraseGroupDef = rawExtCsd[EMMC_EXT_CSD_ERASE_GROUP_DEF]; + extCsd->partConfig = rawExtCsd[EMMC_EXT_CSD_PARTITION_CONFIG]; + extCsd->partSwitchTime = 10 * rawExtCsd[EMMC_EXT_CSD_PARTITION_SWITCH_TIME]; + + if (shift > 0 && shift <= MAX_S_A_TIMEOUT_VALUE) { + extCsd->saTimeout = 1 << shift; + } + extCsd->relWrSecorCount = rawExtCsd[EMMC_EXT_CSD_REL_WR_SEC_C]; + extCsd->hcEraseTimeout = 300 * rawExtCsd[EMMC_EXT_CSD_ERASE_TIMEOUT_MULT]; + extCsd->hcEraseSize = (extCsd->rawHcEraseGrpSize) << 10; + extCsd->bootSize = rawExtCsd[EMMC_EXT_CSD_BOOT_MULTI] << 17; +} + +static void EmmcDecodeExtCsdEnhanceArea(struct EmmcDevice *emmcDev, + struct EmmcExtCsd *extCsd, uint8_t *rawExtCsd) +{ + uint32_t i; + uint32_t shift = 0; + + extCsd->enhAreaEnable = true; + extCsd->enhAreaOffset = 0; + for (i = 0; i < EMMC_EXT_CSD_ENH_START_ADDR_BYTES; i++) { + extCsd->enhAreaOffset |= ((uint64_t)rawExtCsd[EMMC_EXT_CSD_ENH_START_ADDR + i] << shift); + shift += BITS_PER_BYTE; + } + /* + * Start address of the Enhanced User Data Area segment in the User Data Area + * (expressedin bytes or in sectors in case of high capacity devices). + */ + if (emmcDev->mmc.state.bits.blockAddr == 1) { + extCsd->enhAreaOffset <<= MMC_MAX_BLOCKSIZE_SHIFT; + } + + shift = 0; + extCsd->enhAreaSize = 0; + for (i = 0; i < EMMC_EXT_CSD_ENH_SIZE_MULT_BYTES; i++) { + extCsd->enhAreaSize |= (rawExtCsd[EMMC_EXT_CSD_ENH_SIZE_MULT + i] << shift); + shift += BITS_PER_BYTE; + } + /* Max Enhanced Area = MAX_ENH_SIZE_MULT * HC_WP_GRP_SIZE * HC_ERASE_GPR_SIZE * 512kBytes */ + extCsd->enhAreaSize *= (uint32_t)(extCsd->rawHcEraseGrpSize * rawExtCsd[EMMC_EXT_CSD_HC_WP_GRP_SIZE]); + extCsd->enhAreaSize <<= MMC_MAX_BLOCKSIZE_SHIFT; +} + +static void EmmcDecodeExtCsdPowerClassValue(struct EmmcExtCsd *extCsd, uint8_t *rawExtCsd) +{ + extCsd->pwrClF52V195 = rawExtCsd[EMMC_EXT_CSD_PWR_CL_52_195]; + extCsd->pwrClF26V195 = rawExtCsd[EMMC_EXT_CSD_PWR_CL_26_195]; + extCsd->pwrClF52V360 = rawExtCsd[EMMC_EXT_CSD_PWR_CL_52_360]; + extCsd->pwrClF26V360 = rawExtCsd[EMMC_EXT_CSD_PWR_CL_26_360]; + extCsd->pwrClF200V195 = rawExtCsd[EMMC_EXT_CSD_PWR_CL_200_195]; + extCsd->pwrClF200V360 = rawExtCsd[EMMC_EXT_CSD_PWR_CL_200_360]; + extCsd->pwrClDdrF52V195 = rawExtCsd[EMMC_EXT_CSD_PWR_CL_DDR_52_195]; + extCsd->pwrClDdrF52V360 = rawExtCsd[EMMC_EXT_CSD_PWR_CL_DDR_52_360]; +} + +static void EmmcDecodeExtCsdRev14Field(struct EmmcDevice *emmcDev, + struct EmmcExtCsd *extCsd, uint8_t *rawExtCsd) +{ + if ((rawExtCsd[EMMC_EXT_CSD_PARTITIONING_SUPPORT] & PARTITIONING_SUPPORT_ENH_ATTRIBUTE_EN) > 0 && + (rawExtCsd[EMMC_EXT_CSD_PARTITIONS_ATTRIBUTE] & PARTITIONS_ATTRIBUTE_ENH_USR) > 0) { + EmmcDecodeExtCsdEnhanceArea(emmcDev, extCsd, rawExtCsd); + } + extCsd->secTrimMult = extCsd->rawSecTrimMult; + extCsd->secEraseMult = extCsd->rawSecEraseMult; + /* TRIM Timeout = 300ms x TRIM_MULT. */ + extCsd->trimTimeout = 300 * extCsd->rawTrimMult; + EmmcDecodeExtCsdPowerClassValue(extCsd, rawExtCsd); +} + +static void EmmcDecodeExtCsdRev15Field(struct EmmcExtCsd *extCsd, uint8_t *rawExtCsd) +{ + /* whether the eMMC card supports HPI. */ + if ((rawExtCsd[EMMC_EXT_CSD_HPI_FEATURES] & EMMC_EXT_CSD_HPI_SUPPORT) > 0) { + extCsd->hpi = true; + if ((rawExtCsd[EMMC_EXT_CSD_HPI_FEATURES] & EMMC_EXT_CSD_HPI_IMPLEMENTATION) > 0) { + extCsd->hpiCmd = STOP_TRANSMISSION; + } else { + extCsd->hpiCmd = SEND_STATUS; + } + /* + * This field indicates the maximum timeout to close a command interrupted by HPI –time between the end + * bit of CMD12/13 to the DAT0 release by the device. + * Time is expressed in units of 10-milliseconds. + */ + extCsd->outOfInterruptTime = rawExtCsd[EMMC_EXT_CSD_OUT_OF_INTERRUPT_TIME] * 10; + } + extCsd->wrRelParam = rawExtCsd[EMMC_EXT_CSD_WR_REL_PARAM]; +} + +static int32_t EmmcDecodeExtCsd(struct MmcCntlr *cntlr, uint8_t *rawExtCsd, uint32_t len) +{ + struct EmmcDevice *emmcDev = (struct EmmcDevice *)cntlr->curDev; + struct EmmcExtCsd *extCsd = &(emmcDev->emmcReg.extCsd); + uint32_t i; + + extCsd->rawCsdStructure = rawExtCsd[EMMC_EXT_CSD_STRUCTURE]; + if (emmcDev->mmc.reg.csd.structure == MMC_CSD_STRUCTURE_VER_OTHER) { + if (extCsd->rawCsdStructure > EMMC_EXT_CSD_STRUCTURE_VER_1_2) { + HDF_LOGE("EmmcDecodeExtCsd: rawCsdStructure is invalid!"); + return HDF_ERR_INVALID_PARAM; + } + } + extCsd->rev = rawExtCsd[EMMC_EXT_CSD_REV]; + for (i = 0; i < EMMC_EXT_CSD_SEC_CNT_BYTES; i++) { + extCsd->rawSectors[i] = rawExtCsd[EMMC_EXT_CSD_SEC_CNT + i]; + } + if (extCsd->rev >= EMMC_EXT_CSD_REV_1_2) { + EmmcDecodeExtCsdSector(emmcDev, extCsd); + } + + extCsd->strobeSupport = rawExtCsd[EMMC_EXT_CSD_STROBE_SUPPORT]; + extCsd->rawCardType = rawExtCsd[EMMC_EXT_CSD_CARD_TYPE]; + EmmcDecodeExtCsdCardType(extCsd); + + extCsd->rawSaTimeout = rawExtCsd[EMMC_EXT_CSD_S_A_TIMEOUT]; + extCsd->rawEraseTimeoutMult = rawExtCsd[EMMC_EXT_CSD_ERASE_TIMEOUT_MULT]; + extCsd->rawHcEraseGrpSize = rawExtCsd[EMMC_EXT_CSD_HC_ERASE_GRP_SIZE]; + if (extCsd->rev >= EMMC_EXT_CSD_REV_1_3) { + EmmcDecodeExtCsdRev13Field(extCsd, rawExtCsd); + } + + extCsd->rawSecTrimMult = rawExtCsd[EMMC_EXT_CSD_SEC_TRIM_MULT]; + extCsd->rawSecEraseMult = rawExtCsd[EMMC_EXT_CSD_SEC_ERASE_MULT]; + extCsd->rawSecFeatureSupport = rawExtCsd[EMMC_EXT_CSD_SEC_FEATURE_SUPPORT]; + extCsd->rawTrimMult = rawExtCsd[EMMC_EXT_CSD_TRIM_MULT]; + if (extCsd->rev >= EMMC_EXT_CSD_REV_1_4) { + EmmcDecodeExtCsdRev14Field(emmcDev, extCsd, rawExtCsd); + } + + if (extCsd->rev >= EMMC_EXT_CSD_REV_1_5) { + EmmcDecodeExtCsdRev15Field(extCsd, rawExtCsd); + } + return HDF_SUCCESS; +} + +static void EmmcSetBlockCapacity(struct MmcCntlr *cntlr) +{ + struct EmmcDevice *emmcDev = (struct EmmcDevice *)cntlr->curDev; + uint32_t gibVal, mibVal; + + /* + * ERASE_GROUP_DEF Bit0: ENABLE: + * 0x0 : Use old erase group size and write protect group size definition (default) + * 0x1 : Use high-capacity erase unit size, high capacity erase timeout, and high-capacity write protect + * group size definition. + */ + if (emmcDev->emmcReg.extCsd.eraseGroupDef == 1) { + emmcDev->mmc.eraseSize = emmcDev->emmcReg.extCsd.hcEraseSize; + } else { + emmcDev->mmc.eraseSize = emmcDev->mmc.reg.csd.eraseSize; + } + + if (emmcDev->mmc.state.bits.blockAddr > 0) { + emmcDev->mmc.capacity = emmcDev->emmcReg.extCsd.sectors; + } else { + emmcDev->mmc.capacity = (size_t)emmcDev->mmc.reg.csd.capacity << + (emmcDev->mmc.reg.csd.readBlkLen - MMC_MAX_BLOCKSIZE_SHIFT); + } + + gibVal = emmcDev->mmc.capacity >> 21; + mibVal = (emmcDev->mmc.capacity & ~(gibVal << 21)) >> 11; + HDF_LOGD("Emmc dev capacity %d.%d Gib", gibVal, mibVal); +} + +static int32_t EmmcCheckExtCsd(struct MmcCntlr *cntlr, enum MmcBusWidth width) +{ + uint8_t extCsd[EXT_CSD_BYTES_LEN] = {0}; + + if (width == BUS_WIDTH1) { + return 0; + } + + return MmcSendExtCsd(cntlr, extCsd, EXT_CSD_BYTES_LEN); +} + +static int32_t EmmcSwitchVoltage(struct MmcCntlr *cntlr, struct EmmcDevice *emmcDev) +{ + uint32_t cardType = emmcDev->emmcReg.extCsd.cardType; + + /* Host Voltage Switch. */ + if (cntlr->caps2.bits.hs200Sdr1v2 == 1 && (cardType & EMMC_EXT_CSD_CARD_TYPE_SDR_1_2V) > 0) { + (void)MmcCntlrSwitchVoltage(cntlr, VOLT_1V2); + } + if (cntlr->caps2.bits.hs200Sdr1v8 && (cardType & EMMC_EXT_CSD_CARD_TYPE_SDR_1_8V) > 0) { + (void)MmcCntlrSwitchVoltage(cntlr, VOLT_1V8); + } + return HDF_SUCCESS; +} + +static int32_t EmmcSelectHighSpeedBusWidth(struct MmcCntlr *cntlr) +{ + int err = HDF_FAILURE; + enum MmcBusWidth width = BUS_WIDTH1; + + if (cntlr->caps.bits.cap8Bit > 0) { + err = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_BUS_WIDTH, EMMC_EXT_CSD_BUS_WIDTH_8); + if (err != HDF_SUCCESS) { + HDF_LOGD("EmmcSelectHighSpeedBusWidth: switch 8 bit bus width fail!"); + } else { + MmcCntlrSetBusWidth(cntlr, BUS_WIDTH8); + width = BUS_WIDTH8; + } + } + if (err != HDF_SUCCESS) { + err = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_BUS_WIDTH, EMMC_EXT_CSD_BUS_WIDTH_4); + if (err != HDF_SUCCESS) { + HDF_LOGD("EmmcSelectHighSpeedBusWidth: switch 4 bit bus width fail!"); + } else { + MmcCntlrSetBusWidth(cntlr, BUS_WIDTH4); + width = BUS_WIDTH4; + } + } + if (err != HDF_SUCCESS) { + return err; + } + + return EmmcCheckExtCsd(cntlr, width); +} + +static int32_t EmmcCheckSwitchStatus(struct MmcCntlr *cntlr) +{ + int err; + uint32_t status; + + err = MmcSendStatus(cntlr, &status); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcCheckSwitchStatus: send status cmd fail!"); + return err; + } + if ((status & SWITCH_ERROR) > 0) { + return HDF_MMC_ERR_SWITCH_FAIL; + } + return HDF_SUCCESS; +} + +static int32_t EmmcSwitchHighSpeed(struct MmcCntlr *cntlr) +{ + int err; + + /* + * Switch dev to HS mode. + * HS_TIMING must be set to "0x1" before setting BUS_WIDTH for dual data rate operation (values 5 or 6). + */ + err = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_HS_TIMING, EMMC_EXT_CSD_BUS_TIMING_HS); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSwitchHighSpeed: switch hs fail!"); + return err; + } + /* change host freq to 52M according to JEDEC Standard No.84-B51, Page49. */ + MmcCntlrSetClock(cntlr, EMMC_EXT_CSD_HIGH_SPEED_52); + /* Set host controller to HS timing. */ + MmcCntlrSetBusTiming(cntlr, BUS_TIMING_MMC_HS); + return HDF_SUCCESS; +} + +static int32_t EmmcSelectHs400(struct MmcCntlr *cntlr) +{ + int err; + + err = EmmcSwitchHighSpeed(cntlr); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectHs400: switch hs fail!"); + return err; + } + + /* Switch dev to DDR. */ + err = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_BUS_WIDTH, EMMC_EXT_CSD_DDR_BUS_WIDTH_8); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectHs400: switch to ddr fail!"); + return err; + } + /* Switch dev to HS400. */ + err = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_HS_TIMING, EMMC_EXT_CSD_BUS_TIMING_HS400); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectHs400: switch to hs400 fail!"); + return err; + } + MmcCntlrSetClock(cntlr, EMMC_EXT_CSD_HIGH_SPEED_200); + /* Set host controller to HS400 timing and frequency. */ + MmcCntlrSetBusTiming(cntlr, BUS_TIMING_MMC_HS400); + return EmmcCheckSwitchStatus(cntlr); +} + +static int32_t EmmcSelectHs400es(struct MmcCntlr *cntlr, struct EmmcDevice *emmcDev) +{ + int err; + + err = EmmcSwitchVoltage(cntlr, emmcDev); + if (err != HDF_SUCCESS) { + return err; + } + + err = EmmcSelectHighSpeedBusWidth(cntlr); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectHs400es: select hs width fail!"); + return err; + } + + err = EmmcSwitchHighSpeed(cntlr); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectHs400es: switch hs fail!"); + return err; + } + + /* Switch dev to DDR with strobe bit. */ + err = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_BUS_WIDTH, + EMMC_EXT_CSD_DDR_BUS_WIDTH_8 | EMMC_EXT_CSD_BUS_WIDTH_STROBE); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectHs400es: switch to ddr fail!"); + return err; + } + /* Switch dev to HS400. */ + err = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_HS_TIMING, EMMC_EXT_CSD_BUS_TIMING_HS400); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectHs400es: switch to hs400 fail!"); + return err; + } + MmcCntlrSetClock(cntlr, EMMC_EXT_CSD_HIGH_SPEED_200); + /* Set host controller to HS400 timing and frequency. */ + MmcCntlrSetBusTiming(cntlr, BUS_TIMING_MMC_HS400); + MmcCntlrSetEnhanceSrobe(cntlr, true); + return EmmcCheckSwitchStatus(cntlr); +} + +static int32_t EmmcSelectHs200(struct MmcCntlr *cntlr, struct EmmcDevice *emmcDev) +{ + int err; + + err = EmmcSwitchVoltage(cntlr, emmcDev); + if (err != HDF_SUCCESS) { + return err; + } + + err = EmmcSelectHighSpeedBusWidth(cntlr); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectHs200: select hs width fail!"); + return err; + } + + /* Switch dev to HS200. */ + err = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_HS_TIMING, EMMC_EXT_CSD_BUS_TIMING_HS200); + if (err != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectHs200: switch to hs200 fail!"); + return err; + } + MmcCntlrSetBusTiming(cntlr, BUS_TIMING_MMC_HS200); + return EmmcCheckSwitchStatus(cntlr); +} + +static uint32_t EmmcGetPowerClassValue(struct MmcCntlr *cntlr, struct EmmcExtCsd *extCsd) +{ + uint32_t val = 0; + uint32_t vdd, busWidthBit, clock; + + busWidthBit = (cntlr->curDev->workPara.width == BUS_WIDTH8) ? + EMMC_EXT_CSD_BUS_WIDTH_8 : EMMC_EXT_CSD_BUS_WIDTH_4; + vdd = 1 << (cntlr->vddBit); + clock = cntlr->curDev->workPara.clock; + + if (vdd == MMC_OCR_1V65_1V95) { + if (clock <= EMMC_EXT_CSD_HIGH_SPEED_26) { + val = extCsd->pwrClF26V195; + } else if (clock <= EMMC_EXT_CSD_HIGH_SPEED_52) { + val = extCsd->pwrClF52V195; + } else if (clock <= EMMC_EXT_CSD_HIGH_SPEED_200) { + val = extCsd->pwrClF200V195; + } + } else if (vdd >= MMC_OCR_2V7_2V8 && vdd <= MMC_OCR_3V5_3V6) { + if (clock <= EMMC_EXT_CSD_HIGH_SPEED_26) { + val = extCsd->pwrClF26V360; + } else if (clock <= EMMC_EXT_CSD_HIGH_SPEED_52) { + val = extCsd->pwrClF52V360; + } else if (clock <= EMMC_EXT_CSD_HIGH_SPEED_200) { + val = extCsd->pwrClF200V360; + } + } else { + return 0; + } + + if (busWidthBit == EMMC_EXT_CSD_BUS_WIDTH_8) { + val = (val & EMMC_EXT_CSD_PWR_CL_8BIT_MASK) >> EMMC_EXT_CSD_PWR_CL_8BIT_SHIFT; + } else { + val = (val & EMMC_EXT_CSD_PWR_CL_4BIT_MASK) >> EMMC_EXT_CSD_PWR_CL_4BIT_SHIFT; + } + return val; +} + +static int32_t EmmcSelectPowerClass(struct MmcCntlr *cntlr, struct EmmcExtCsd *extCsd) +{ + int32_t error = 0; + uint32_t val; + + if (cntlr->curDev->reg.csd.specVers < MMC_CSD_SPEC_VER_4) { + return HDF_SUCCESS; + } + + val = EmmcGetPowerClassValue(cntlr, extCsd); + if (val > 0) { + error = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_POWER_CLASS, val); + } + return error; +} + +static int32_t EmmcSelectActivateHighSpeed(struct MmcCntlr *cntlr, + struct EmmcDevice *emmcDev, struct EmmcExtCsd *extCsd) +{ + int32_t error; + + if (extCsd->hsMaxDtr > 0) { + error = HDF_SUCCESS; + if (MmcCntlrSupportHighSpeed200(cntlr) == true && + extCsd->hsMaxDtr > EMMC_EXT_CSD_HIGH_SPEED_52) { + error = EmmcSelectHs200(cntlr, emmcDev); + if (error == HDF_SUCCESS) { + emmcDev->mmc.state.bits.hs200 = 1; + } + } else if (cntlr->caps.bits.highSpeed == 1) { + error = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_HS_TIMING, + EMMC_EXT_CSD_BUS_TIMING_HS); + if (error == HDF_SUCCESS) { + emmcDev->mmc.state.bits.highSpeed = 1; + MmcCntlrSetBusTiming(cntlr, BUS_TIMING_MMC_HS); + } + } + if (error != HDF_SUCCESS && error != HDF_MMC_ERR_SWITCH_FAIL) { + return error; + } + } + + return HDF_SUCCESS; +} + +static int32_t EmmcSelectBusSpeedMode(struct MmcCntlr *cntlr, struct EmmcDevice *emmcDev, struct EmmcExtCsd *extCsd) +{ + int32_t error; + + /* HS400ES mode requires 8-bit bus width. */ + if (extCsd->strobeSupport > 0 && MmcCntlrSupportHighSpeed400EnhancedStrobe(cntlr) == true) { + error = EmmcSelectHs400es(cntlr, emmcDev); + if (error != HDF_SUCCESS) { + return error; + } + emmcDev->mmc.state.bits.hs400es = 1; + } else { + /* Activate high speed if supported. */ + error = EmmcSelectActivateHighSpeed(cntlr, emmcDev, extCsd); + if (error != HDF_SUCCESS) { + return error; + } + /* host set clock. */ + if (emmcDev->mmc.state.bits.hs200 == 1 || emmcDev->mmc.state.bits.highSpeed == 1) { + if (extCsd->hsMaxDtr > 0) { + MmcCntlrSetClock(cntlr, extCsd->hsMaxDtr); + } + } else if (emmcDev->mmc.reg.csd.maxDtr > 0) { + MmcCntlrSetClock(cntlr, emmcDev->mmc.reg.csd.maxDtr); + } + } + + if (emmcDev->mmc.state.bits.hs400es == 1) { + error = EmmcSelectPowerClass(cntlr, extCsd); + if (error != HDF_SUCCESS) { + HDF_LOGD("EmmcSelectBusSpeedMode: hs400es select power class fail!"); + } + } else if (emmcDev->mmc.state.bits.hs200 == 1) { + if ((cntlr->caps2.bits.hs200Sdr1v8 | cntlr->caps2.bits.hs200Sdr1v2) > 0) { + error = MmcCntlrTune(cntlr, SEND_TUNING_BLOCK_HS200); + if (error != HDF_SUCCESS) { + return error; + } + } + + if ((extCsd->rawCardType & EMMC_EXT_CSD_CARD_TYPE_HS400) > 0 && + MmcCntlrSupportHighSpeed400(cntlr) == true) { + error = EmmcSelectHs400(cntlr); + if (error != HDF_SUCCESS) { + return error; + } + emmcDev->mmc.state.bits.hs400 = 1; + emmcDev->mmc.state.bits.hs200 = 0; + } + + error = EmmcSelectPowerClass(cntlr, extCsd); + if (error != HDF_SUCCESS) { + HDF_LOGD("EmmcSelectBusSpeedMode: hs200 select power class fail!"); + } + } + + return HDF_SUCCESS; +} + +static uint32_t EmmcGetDdrMode(struct MmcCntlr *cntlr, struct EmmcDevice *emmcDev, struct EmmcExtCsd *extCsd) +{ + uint32_t ddrMode = MMC_BUS_MODE_NULL; + + /* Indicate DDR mode (if supported). */ + if (emmcDev->mmc.state.bits.highSpeed == 1) { + if ((extCsd->cardType & EMMC_EXT_CSD_CARD_TYPE_DDR_1_8V) > 0 && + (cntlr->caps.bits.ddr1v8 > 0) && + (cntlr->caps.bits.uhsDdr50 > 0)) { + ddrMode = MMC_1_8V_DDR_MODE; + } else if ((extCsd->cardType & EMMC_EXT_CSD_CARD_TYPE_DDR_1_2V) > 0 && cntlr->caps.bits.ddr1v2 > 0) { + ddrMode = MMC_1_2V_DDR_MODE; + } + } + return ddrMode; +} + +static int32_t EmmcSwitchDdrMode(struct MmcCntlr *cntlr, uint32_t ddrMode, + enum MmcBusWidth width, uint32_t widthBit) +{ + int32_t error; + + error = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_BUS_WIDTH, widthBit); + if (error != HDF_SUCCESS) { + HDF_LOGE("EmmcSwitchDdrMode: switch BUS_WIDTH fail!"); + return error; + } + + if (ddrMode == MMC_1_2V_DDR_MODE) { + error = MmcCntlrSwitchVoltage(cntlr, VOLT_1V2); + if (error != HDF_SUCCESS) { + HDF_LOGE("EmmcSwitchDdrMode: switch 1.2V fail!"); + return error; + } + } + cntlr->curDev->state.bits.ddrMode = 1; + MmcCntlrSetBusTiming(cntlr, BUS_TIMING_UHS_DDR50); + MmcCntlrSetBusWidth(cntlr, width); + return HDF_SUCCESS; +} + +static int32_t EmmcSelectSwitchDdrMode(struct MmcCntlr *cntlr, struct EmmcDevice *emmcDev, + struct EmmcExtCsd *extCsd) +{ + int32_t error; + uint32_t index, ddrMode; + enum MmcBusWidth width; + enum MmcBusWidth busWidths[] = { BUS_WIDTH8, BUS_WIDTH4, BUS_WIDTH1 }; + uint32_t busWidthBit[][2] = { + { EMMC_EXT_CSD_BUS_WIDTH_8, EMMC_EXT_CSD_DDR_BUS_WIDTH_8 }, + { EMMC_EXT_CSD_BUS_WIDTH_4, EMMC_EXT_CSD_DDR_BUS_WIDTH_4 }, + { EMMC_EXT_CSD_BUS_WIDTH_1, EMMC_EXT_CSD_BUS_WIDTH_1 }, + }; + + ddrMode = EmmcGetDdrMode(cntlr, emmcDev, extCsd); + if (emmcDev->mmc.state.bits.hs400es == 0 && + emmcDev->mmc.state.bits.hs400 == 0 && + emmcDev->mmc.state.bits.hs200 == 0 && + cntlr->curDev->reg.csd.specVers >= MMC_CSD_SPEC_VER_4 && + (cntlr->caps.bits.cap4Bit | cntlr->caps.bits.cap8Bit) > 0) { + index = 1; + if (cntlr->caps.bits.cap8Bit > 0) { + index = 0; + } + for (; index < sizeof(busWidthBit) / sizeof(busWidthBit[0]); index++) { + width = busWidths[index]; + /* no DDR for 1-bit width. */ + if (width == BUS_WIDTH1) { + ddrMode = MMC_BUS_MODE_NULL; + } + error = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_BUS_WIDTH, busWidthBit[index][0]); + if (error != HDF_SUCCESS) { + continue; + } + MmcCntlrSetBusWidth(cntlr, width); + error = EmmcCheckExtCsd(cntlr, width); + if (error == HDF_SUCCESS) { + break; + } + } + /* switch DDR mode. */ + if (error == HDF_SUCCESS && ddrMode > MMC_BUS_MODE_NULL) { + error = EmmcSwitchDdrMode(cntlr, ddrMode, width, busWidthBit[index][1]); + } + if (error != HDF_SUCCESS) { + HDF_LOGE("EmmcSelectSwitchDdrMode: switch ddr mode fail!"); + return error; + } + } + return HDF_SUCCESS; +} + +static int32_t EmmcActivateHpiMechanism(struct MmcCntlr *cntlr, struct EmmcExtCsd *extCsd) +{ + int32_t error; + + if (extCsd->hpi == true) { + error = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_HPI_MGMT, 1); + if (error == HDF_SUCCESS) { + extCsd->hpiEnable = true; + } else if (error != HDF_MMC_ERR_SWITCH_FAIL) { + HDF_LOGE("EmmcActivateHpiMechanism: switch HPI_MGMT fail!"); + return error; + } + } + return HDF_SUCCESS; +} + +static int32_t EmmcSwitchOperationMode(struct MmcCntlr *cntlr) +{ + struct EmmcDevice *emmcDev = (struct EmmcDevice *)cntlr->curDev; + struct EmmcExtCsd *extCsd = &(emmcDev->emmcReg.extCsd); + int32_t error; + + if (extCsd->enhAreaEnable == true) { + error = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_ERASE_GROUP_DEF, 1); + if (error == HDF_SUCCESS) { + extCsd->eraseGroupDef = 1; + } else if (error != HDF_MMC_ERR_SWITCH_FAIL) { + HDF_LOGE("EmmcSwitchOperationMode: switch ERASE_GROUP_DEF fail!"); + return error; + } + } + + if ((extCsd->partConfig & EMMC_EXT_CSD_PART_CONFIG_ACCESS_MASK) > 0) { + /* No access to boot partition (default). */ + extCsd->partConfig &= ~EMMC_EXT_CSD_PART_CONFIG_ACCESS_MASK; + error = MmcSwitch(cntlr, EMMC_EXT_CSD_CMD_SET_NORMAL, EMMC_EXT_CSD_PARTITION_CONFIG, extCsd->partConfig); + if (error != HDF_SUCCESS && error != HDF_MMC_ERR_SWITCH_FAIL) { + HDF_LOGE("EmmcSwitchOperationMode: switch PARTITION_CONFIG fail!"); + return error; + } + } + + error = EmmcSelectBusSpeedMode(cntlr, emmcDev, extCsd); + if (error != HDF_SUCCESS) { + HDF_LOGE("EmmcSwitchOperationMode: select bus speed mode fail!"); + return error; + } + + error = EmmcSelectSwitchDdrMode(cntlr, emmcDev, extCsd); + if (error != HDF_SUCCESS) { + HDF_LOGE("EmmcSwitchOperationMode: select switch ddr mode fail!"); + return error; + } + return EmmcActivateHpiMechanism(cntlr, extCsd); +} + +static int32_t EmmcReadExtCsd(struct MmcCntlr *cntlr) +{ + uint8_t extCsd[EXT_CSD_BYTES_LEN] = {0}; + int32_t error; + + if (cntlr->curDev->reg.csd.specVers < MMC_CSD_SPEC_VER_4) { + return HDF_SUCCESS; + } + + error = MmcSendExtCsd(cntlr, extCsd, EXT_CSD_BYTES_LEN); + if (error != HDF_SUCCESS) { + HDF_LOGE("EmmcReadExtCsd: send cmd8 fail, error = %d.", error); + return error; + } + error = EmmcDecodeExtCsd(cntlr, extCsd, EXT_CSD_BYTES_LEN); + if (error != HDF_SUCCESS) { + HDF_LOGE("EmmcReadExtCsd: decode ext csd fail, error = %d.", error); + return error; + } + + return error; +} + +static int32_t EmmcSelect(struct MmcCntlr *cntlr, union MmcOcr ocr) +{ + union MmcOcr curOcr; + int32_t error; + + MmcGoIdleState(cntlr); + ocr.bits.hcs = 1; + /* set dev work voltage. */ + error = MmcSendOpCond(cntlr, ocr.ocrData, &curOcr.ocrData); + if (error != HDF_SUCCESS) { + HDF_LOGE("Emmc cmd1(set voltage) fail, error = %d.", error); + return error; + } + + error = MmcAllSendCid(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("Emmc cmd2(get cid) fail, error = %d.", error); + return error; + } + + /* card is identified. */ + cntlr->curDev->reg.rca = 1; + error = MmcSetRelativeAddr(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("Emmc cmd3(set rca) fail, error = %d.", error); + return error; + } + + error = MmcSendCsd(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("Emmc send cmd9(get csd) fail, error = %d.", error); + return error; + } + error = EmmcDecodeCsd(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("Emmc decode csd fail, error = %d.", error); + return error; + } + error = EmmcDecodeCid(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("Emmc decode cid fail, error = %d.", error); + return error; + } + error = MmcSelectCard(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("Emmc send cmd7 fail, error = %d.", error); + return error; + } + error = EmmcReadExtCsd(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("Emmc read ext csd fail, error = %d.", error); + return error; + } + + return EmmcSwitchOperationMode(cntlr); +} + +static int32_t EmmcDeviceAdd(struct MmcCntlr *cntlr) +{ + EmmcSetBlockCapacity(cntlr); + /* add dev. */ + if (MmcDeviceAdd(cntlr->curDev) != HDF_SUCCESS) { + HDF_LOGE("EmmcDeviceAdd: Add device fail!"); + return HDF_FAILURE; + } + cntlr->curDev->state.bits.present = 1; + return HDF_SUCCESS; +} + +static int32_t EmmcInit(struct MmcCntlr *cntlr) +{ + int32_t error; + union MmcOcr ocr = {0}; + + /* cmd1, detect emmc dev and get the voltage range. */ + error = MmcSendOpCond(cntlr, 0, &(ocr.ocrData)); + if (error != HDF_SUCCESS) { + HDF_LOGE("cmd1(detect emmc) fail, error = %d.", error); + return error; + } + + MmcCntlrSelectWorkVoltage(cntlr, &ocr); + if (cntlr->curDev->reg.ocr.ocrData == 0) { + HDF_LOGE("Emmc select work voltage fail!"); + return HDF_ERR_INVALID_PARAM; + } + /* work voltage is low voltage, host should switch. */ + if (cntlr->curDev->reg.ocr.bits.vdd1v65To1v95 > 0) { + HDF_LOGD("Emmc switch to 1.8V!"); + MmcCntlrSwitchVoltage(cntlr, VOLT_1V8); + } + + error = EmmcSelect(cntlr, cntlr->curDev->reg.ocr); + if (error != HDF_SUCCESS) { + return error; + } + + return EmmcDeviceAdd(cntlr); +} + +static int32_t EmmcDetect(struct MmcCntlr *cntlr) +{ + int32_t ret; + + HDF_LOGD("Detect emmc dev start..."); + MmcGoIdleState(cntlr); + ret = EmmcInit(cntlr); + if (ret == HDF_SUCCESS) { + HDF_LOGD("Detect emmc dev success! %s dev at address 0x%x!", + cntlr->curDev->state.bits.uhs ? "Ultra high speed" : + (cntlr->curDev->state.bits.highSpeed ? "High speed" : ""), + cntlr->curDev->reg.rca); + } + return ret; +} + +static int32_t SdSendAppCmd(struct MmcCntlr *cntlr, struct MmcCmd *cmd, + struct MmcData *data, uint32_t retryTimes) +{ + uint32_t i; + int32_t err; + + if (cntlr == NULL || cmd == NULL || retryTimes == 0) { + return HDF_ERR_INVALID_PARAM; + } + + for (i = 0; i <= retryTimes; i++) { + err = MmcAppCmd(cntlr, cmd->cmdCode); + if (err != HDF_SUCCESS) { + continue; + } + err = MmcSendCmd(cntlr, cmd, data, 1); + if (err == HDF_SUCCESS) { + break; + } + } + return err; +} + +static int32_t SdAcmdOpCond(struct MmcCntlr *cntlr, uint32_t arg, uint32_t *ocr) +{ + struct MmcCmd cmd = {0}; + int32_t err; + uint32_t i; + + cmd.cmdCode = SD_ACMD_OP_COND; + /* [31]reserved bit, [30]HCS, [29]reserved for eSD, [28]XPC, [27:25]reserved bits, [24]S18R, [23:0]VDD voltage. */ + cmd.argument = arg; + /* Broadcast Commands with Response(bcr). */ + cmd.respType = MMC_RESP_R3 | MMC_CMD_TYPE_BCR; + /* + * The host must poll the card (by repeatedly sending CMD1 or ACMD41) until the 'in-idle-state' bit in + * the card response indicates (by being set to 0) that the card completed its initialization processes + * and is ready for the next command. + */ + for (i = 0; i < INIT_CMD_RETRY_TIMES; i++) { + err = SdSendAppCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); + if (err != HDF_SUCCESS) { + break; + } + /* if probing, just single pass */ + if (arg == 0) { + break; + } + /* + * wait until init complete. + * 0--On Initialization; 1--Initialization Complete. + */ + if ((cmd.resp[0] & MMC_CARD_BUSY_STATUS) > 0) { + break; + } + err = HDF_ERR_TIMEOUT; + OsalMDelay(20); + } + if (ocr != NULL) { + *ocr = cmd.resp[0]; + } + + return err; +} + +static int32_t SdAcmdSdStatus(struct MmcCntlr *cntlr, uint32_t *ssr, uint32_t len) +{ + struct MmcCmd cmd = {0}; + struct MmcData data = {0}; + int32_t error; + uint32_t i; + + if (cntlr == NULL || ssr == NULL || len == 0) { + return HDF_ERR_INVALID_PARAM; + } + + cmd.cmdCode = SD_ACMD_SD_STATUS; + /* [31:0] stuff bits. */ + cmd.argument = 0; + /* Addressed (point-to-point) Data Transfer Commands (adtc), data transfer on DAT. */ + cmd.respType = MMC_RESP_SPI_R2 | MMC_RESP_R1 | MMC_CMD_TYPE_ADTC; + data.blockSize = SSR_BYTES_LEN; + data.blockNum = 1; + data.dataFlags = DATA_READ; + data.dataBuffer = (uint8_t *)ssr; + error = SdSendAppCmd(cntlr, &cmd, &data, 1); + if (error != HDF_SUCCESS) { + return error; + } + for (i = 0; i < len; i++) { + ssr[i] = MmcEndianConversion(ssr[i]); + } + return error; +} + +static int32_t SdAppSendScr(struct MmcCntlr *cntlr, uint32_t *scr, uint32_t len) +{ + struct MmcCmd cmd = {0}; + struct MmcData data = {0}; + int32_t error; + uint32_t i; + + if (cntlr == NULL || scr == NULL || len == 0) { + return HDF_ERR_INVALID_PARAM; + } + + cmd.cmdCode = SD_ACMD_SEND_SCR; + /* [31:0] stuff bits. */ + cmd.argument = 0; + /* Addressed (point-to-point) Data Transfer Commands (adtc), data transfer on DAT. */ + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_ADTC; + data.blockSize = SCR_BYTES_LEN; + data.blockNum = 1; + data.dataFlags = DATA_READ; + data.dataBuffer = (uint8_t *)scr; + error = SdSendAppCmd(cntlr, &cmd, &data, 1); + if (error != HDF_SUCCESS) { + return error; + } + for (i = 0; i < len; i++) { + scr[i] = MmcEndianConversion(scr[i]); + } + return error; +} + +static int32_t SdAcmdSetBusWidth(struct MmcCntlr *cntlr, uint32_t width) +{ + struct MmcCmd cmd = {0}; + + cmd.cmdCode = SD_ACMD_SET_BUS_WIDTH; + /* [31:2] stuff bits; [1:0]bus width: '00' = 1bit and '10' = 4bits. */ + cmd.argument = width; + /* Addressed (point-to-point) Commands(ac), no data transfer on DAT. */ + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_AC; + + return SdSendAppCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); +} + +static int32_t SdCmdSendIfCond(struct MmcCntlr *cntlr, uint32_t ocr) +{ + struct MmcCmd cmd = {0}; + int32_t err; + uint32_t vhs; + + cmd.cmdCode = SD_CMD_SEND_IF_COND; + /* + * [31:12]reserved bits, [11:8]supply voltage(VHS), [7:0]check pattern. + * VHS = 0001, 2.7-3.6V. + */ + vhs = ((ocr & 0xFF8000U) > 0) ? 1 : 0; + cmd.argument = (vhs << 8) | 0xAA; + cmd.respType = MMC_RESP_SPI_R7 | MMC_RESP_R7 | MMC_CMD_TYPE_BCR; + + err = MmcSendCmd(cntlr, &cmd, NULL, 1); + if (err != HDF_SUCCESS) { + return err; + } + /* + * Check pattern is used for the host to check validity of communication between the host and the card. + * In the Response, the card echoes back the check pattern set in argument. + * If check pattern is not matched, CMD8 communication is not valid. + */ + if ((cmd.resp[0] & 0xFF) != 0xAA) { + return HDF_ERR_IO; + } + return HDF_SUCCESS; +} + +static int32_t SdCmdSendRelativeAddr(struct MmcCntlr *cntlr, uint32_t *rca) +{ + int32_t error; + struct MmcCmd cmd = {0}; + + cmd.cmdCode = SD_CMD_SEND_RELATIVE_ADDR; + /* [31:0] stuff bits. */ + cmd.argument = 0; + cmd.respType = MMC_RESP_R6 | MMC_CMD_TYPE_BCR; + error = MmcSendCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); + if (error != HDF_SUCCESS) { + return error; + } + if (rca != NULL) { + /* New published RCA [31:16] of the card. */ + *rca = cmd.resp[0] >> 16; + } + return error; +} + +static int32_t SdCmdSwitchVoltage(struct MmcCntlr *cntlr) +{ + int32_t error; + struct MmcCmd cmd = {0}; + + /* Voltage switch command to change signaling level 3.3V to 1.8V. */ + cmd.cmdCode = SD_CMD_SWITCH_VOLTAGE; + /* [31:0] stuff bits. */ + cmd.argument = 0; + cmd.respType = MMC_RESP_R1 | MMC_CMD_TYPE_AC; + error = MmcSendCmd(cntlr, &cmd, NULL, 1); + if (error != HDF_SUCCESS) { + return error; + } + if (cmd.resp[0] & GENERAL_ERROR) { + return HDF_ERR_IO; + } + return HDF_SUCCESS; +} + +static int32_t SdCmdSwitchFunc(struct MmcCntlr *cntlr, struct SdSwitchFuncParam *param, + uint8_t *status, uint32_t len) +{ + struct MmcCmd cmd = {0}; + struct MmcData data = {0}; + + cmd.cmdCode = SD_CMD_SWITCH_FUNC; + /* + * [31]Mode. The switch command can be used in two modes: + * Mode 0 (Check function) is used to query if the card supports a specific function or functions. + * Mode 1 (set function) is used to switch the functionality of the card. + */ + cmd.argument = (param->mode << 31) | 0x00FFFFFF; + /* + * [30:24] reserved(All '0'); [23:20] reserved for function group 6(All '0' or 0xF); + * [19:16] reserved for function group 5(All '0' or 0xF); [15:12] function group 4 for Power Limit; + * [11:8] function group 3 for Drive Strength; [7:4] function group 2 for command system; + * [3:0] function group 1 for access mode. + */ + cmd.argument &= ~(0xFU << (param->group * 4)); + cmd.argument |= (param->value & 0xF) << (param->group * 4); + cmd.respType = MMC_RESP_SPI_R1 | MMC_RESP_R1 | MMC_CMD_TYPE_ADTC; + /* + * As a response to the switch function command, the SD Memory Card will send R1 response on the CMD line, + * and 512 bits of status on the DAT lines. + */ + data.blockSize = len; + data.blockNum = 1; + data.dataFlags = DATA_READ; + data.dataBuffer = status; + + return MmcSendCmd(cntlr, &cmd, &data, 1); +} + +static int32_t SdDecodeScr(struct MmcCntlr *cntlr) +{ + struct SdScr *scr = NULL; + uint32_t *rawScr = NULL; + struct SdDevice *sdDev = NULL; + uint32_t scrStruct; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + sdDev = (struct SdDevice *)cntlr->curDev; + rawScr = sdDev->reg.rawScr; + scr = &(sdDev->reg.scr); + + /* + * SCR_STRUCTURE: [63:60]; + * SCR_STRUCTURE equal 0, SCR version 1.0; SCR_STRUCTURE equal 1-15, reserved. + */ + scrStruct = (uint8_t)MmcParseBits(rawScr, SCR_BITS, 60, 4); + if (scrStruct != 0) { + HDF_LOGE("SdDecodeScr: scrStruct is invalid!"); + return HDF_ERR_INVALID_PARAM; + } + /* + * SD_SPEC: [59:56]. + * Describes the SD Memory Card Physical Layer Specification version supported by this card. + * SD_SPEC = 0 , Version 1.0-1.01; SD_SPEC = 1, Version 1.1; SD_SPEC = 2, Version >= 2.0. + */ + scr->sdSpec = (uint8_t)MmcParseBits(rawScr, SCR_BITS, 56, 4); + if (scr->sdSpec == SD_SCR_SPEC_2) { + /* SD_SPEC3: [47:47] */ + scr->sdSpec3 = (uint8_t)MmcParseBits(rawScr, SCR_BITS, 47, 1); + } + /* + * SD_BUS_WIDTHS: [51:48]. + * SD_BUS_WIDTHS equal 0, 1 bit (DAT0); SD_BUS_WIDTHS equal 2, 4 bit (DAT0-3). + */ + scr->sdBusWidths = (uint8_t)MmcParseBits(rawScr, SCR_BITS, 48, 4); + /* CMD_SUPPORT: [35:32] */ + scr->cmdSupport = (uint8_t)MmcParseBits(rawScr, SCR_BITS, 32, 2); + return HDF_SUCCESS; +} + +static int32_t SdDecodeSSr(struct MmcCntlr *cntlr, uint32_t *rawSsr, uint32_t len) +{ + struct SdSsr *ssr = NULL; + struct SdScr *scr = NULL; + struct SdDevice *sdDev = NULL; + uint32_t eraseSize, eraseTimeout; + + if (cntlr == NULL || cntlr->curDev == NULL || rawSsr == NULL || len == 0) { + return HDF_ERR_INVALID_PARAM; + } + + sdDev = (struct SdDevice *)cntlr->curDev; + ssr = &(sdDev->reg.ssr); + scr = &(sdDev->reg.scr); + /* SPEED_CLASS: [447:440] */ + ssr->speedClass = MmcParseBits(rawSsr, SSR_BITS, 440, 8); + /* AU_SIZE: [431: 428]. */ + ssr->auSize = MmcParseBits(rawSsr, SSR_BITS, 428, 4); + if (ssr->auSize > 0) { + if ((ssr->auSize <= MMC_MAX_BLOCKSIZE_SHIFT) || scr->sdSpec3 > 0) { + ssr->auValue = 1 << (ssr->auSize + 4); + /* + * ERASE_SIZE: [423:408]. If this field is set to 0, the erase timeout caculation is not supported. + * ERASE_SIZE = 1, value = 1AU; ERASE_SIZE = 2, value = 2AU... + */ + eraseSize = MmcParseBits(rawSsr, SSR_BITS, 408, 16); + /* ERASE_TIMEOUT: [407:402] */ + eraseTimeout = MmcParseBits(rawSsr, SSR_BITS, 402, 6); + if (eraseSize > 0) { + ssr->eraseTimeout = (eraseTimeout * 1000) / eraseSize; + /* ERASE_OFFSET: [401:400] */ + ssr->eraseOffset = 1000 * MmcParseBits(rawSsr, SSR_BITS, 400, 2); + } + } else { + HDF_LOGD("SD Status: Invalid AU."); + } + } + /* UHS_SPEED_GRADE: [399:396] */ + ssr->uhsSpeedGrade = MmcParseBits(rawSsr, SSR_BITS, 396, 4); + return HDF_SUCCESS; +} + +static void SdDecodeCid(struct MmcCntlr *cntlr) +{ + struct MmcCid *cid = NULL; + uint32_t *rawCid = NULL; + uint32_t i; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return; + } + + rawCid = cntlr->curDev->reg.rawCid; + cid = &(cntlr->curDev->reg.cid); + /* Manufacturer ID(MID): [127:120] */ + cid->mid = MmcParseBits(rawCid, CID_BITS, 120, 8); + /* OEM/Application ID(OID): [119:104] */ + cid->oid = MmcParseBits(rawCid, CID_BITS, 104, 16); + /* Product name(PNM): [103:64] */ + for (i = 0; i < 5; i++) { + cid->pnm[i] = MmcParseBits(rawCid, CID_BITS, 96 - (i * 8), 8); + } + cid->pnm[5] = '\0'; + /* + * Product revision(PRV): [63:56]. + * The product revision is composed of two Binary Coded Decimal (BCD) digits, four bits each, + * representingan "n.m" revision number. + */ + cid->hwPrv = MmcParseBits(rawCid, CID_BITS, 60, 4); + cid->fwPrv = MmcParseBits(rawCid, CID_BITS, 56, 4); + /* Product serial number(PSN): [55:24] */ + cid->psn = MmcParseBits(rawCid, CID_BITS, 24, 32); + /* + * Manufacturing date(MDT): [19:8]. + * The manufacturing date composed of two hexadecimal digits, one is 8 bit representing the year(y) + * and the other is four bits representing the month(m). + * The "m" field [11:8] is the month code. 1 = January. + * The "y" field [19:12] is the year code. 0 = 2000. + */ + cid->year = MmcParseBits(rawCid, CID_BITS, 12, 8) + 2000; + cid->month = MmcParseBits(rawCid, CID_BITS, 8, 4); +} + +static void SdSetBlockCapacity(struct MmcCntlr *cntlr) +{ + struct SdDevice *sdDev = (struct SdDevice *)cntlr->curDev; + uint32_t gibVal, mibVal; + + sdDev->mmc.eraseSize = sdDev->mmc.reg.csd.eraseSize; + sdDev->mmc.capacity = sdDev->mmc.reg.csd.capacity << + (sdDev->mmc.reg.csd.readBlkLen - MMC_MAX_BLOCKSIZE_SHIFT); + + gibVal = sdDev->mmc.capacity >> 21; + mibVal = (sdDev->mmc.capacity & ~(gibVal << 21)) >> 11; + HDF_LOGD("SD dev capacity %d.%d Gib", gibVal, mibVal); +} + +static void SdDecodeCsdRev1Field(struct MmcCntlr *cntlr, struct MmcCsd *csd, uint32_t *rawCsd) +{ + uint32_t unit, factor; + + /* TAAC: [119:112]; TAAC bit position-->Time unit: [2:0], Multiplier factor: [6:3]. */ + factor = MmcParseBits(rawCsd, CSD_BITS, 115, 4); + unit = MmcParseBits(rawCsd, CSD_BITS, 112, 3); + csd->taccNs = (g_taccUnit[unit] * g_commFactor[factor] + 9) / 10; + /* NSAC: [111:104], the unit for NSAC is 100 clock cycles */ + csd->taccClks = MmcParseBits(rawCsd, CSD_BITS, 104, 8) * 100; + + /* TRAN_SPEED: [103:96]; TRAN_SPEED bit-->Frequency unit: [2:0], Multiplier factor: [6:3]. */ + factor = MmcParseBits(rawCsd, CSD_BITS, 99, 4); + unit = MmcParseBits(rawCsd, CSD_BITS, 96, 3); + csd->maxDtr = g_tranSpeedUnit[unit] * g_commFactor[factor]; + /* card command classes: [95:84] */ + csd->ccc = MmcParseBits(rawCsd, CSD_BITS, 84, 12); + /* C_SIZE: [73:62] */ + unit = MmcParseBits(rawCsd, CSD_BITS, 62, 12); + /* C_SIZE_MULT: [49:47] */ + factor = MmcParseBits(rawCsd, CSD_BITS, 47, 3); + csd->capacity = (1 + unit) << (factor + 2); + /* READ_BL_LEN: [83:80]. */ + csd->readBlkLen = MmcParseBits(rawCsd, CSD_BITS, 80, 4); + /* READ_BL_PARTIAL: [79:79] */ + csd->rdPartial = MmcParseBits(rawCsd, CSD_BITS, 79, 1); + /* WRITE_BLK_MISALIGN: [78:78] */ + csd->wrMisalign = MmcParseBits(rawCsd, CSD_BITS, 78, 1); + /* READ_BLK_MISALIGN: [77:77] */ + csd->rdMisalign = MmcParseBits(rawCsd, CSD_BITS, 77, 1); + /* Write speed factor(R2W_FACTOR) :[28:26] */ + csd->r2wFactor = MmcParseBits(rawCsd, CSD_BITS, 26, 3); + /* WRITE_BL_LEN: [25:22] */ + csd->writeBlkLen = MmcParseBits(rawCsd, CSD_BITS, 22, 4); + /* WRITE_BL_PARTIAL: [21:21] */ + csd->wrPartial = MmcParseBits(rawCsd, CSD_BITS, 21, 1); + /* + * erase single block enable(ERASE_BLK_EN): [46:46] + * If ERASE_BLK_EN is '0' erase area is unit of SECTOR_SIZE. + * if ERASE_BLK_EN is '1' erase area is unit of SECTOR_SIZE or unit of WRITE_BL_LEN. + */ + if (MmcParseBits(rawCsd, CSD_BITS, 46, 1) == 1) { + csd->eraseSize = 1; + } else if (csd->writeBlkLen >= MMC_MAX_BLOCKSIZE_SHIFT) { + /* + * erase sector size(SECTOR_SIZE): [45:39]. + * The actual size is computed by increasing this number by one. + * A value of zero means 1 write block, 127 means 128 write blocks. + */ + csd->eraseSize = MmcParseBits(rawCsd, CSD_BITS, 39, 7) + 1; + csd->eraseSize <<= (csd->writeBlkLen - MMC_MAX_BLOCKSIZE_SHIFT); + } +} + +static void SdDecodeCsdRev2Field(struct MmcCntlr *cntlr, struct MmcCsd *csd, uint32_t *rawCsd) +{ + uint32_t unit, factor; + + cntlr->curDev->state.bits.blockAddr = 1; + /* TRAN_SPEED: [103:96]; TRAN_SPEED bit-->Frequency unit: [2:0], Multiplier factor: [6:3]. */ + factor = MmcParseBits(rawCsd, CSD_BITS, 99, 4); + unit = MmcParseBits(rawCsd, CSD_BITS, 96, 3); + csd->maxDtr = g_tranSpeedUnit[unit] * g_commFactor[factor]; + /* card command classes: [95:84] */ + csd->ccc = MmcParseBits(rawCsd, CSD_BITS, 84, 12); + /* device size(C_SIZE): [69:48] */ + csd->cSize = MmcParseBits(rawCsd, CSD_BITS, 48, 22); + /* The Minimum value of C_SIZE for SDXC in CSD Version 2.0 is 00FFFFh(65535). */ + if (csd->cSize >= 0xFFFF) { + cntlr->curDev->state.bits.sdxc = 1; + } + /* memory capacity = (C_SIZE + 1) * 512KByte */ + csd->capacity = (1 + csd->cSize) << 10; + + csd->taccNs = 0; + csd->taccClks = 0; + csd->rdPartial = 0; + csd->readBlkLen = MMC_MAX_BLOCKSIZE_SHIFT; + csd->rdMisalign = 0; + csd->wrMisalign = 0; + csd->writeBlkLen = MMC_MAX_BLOCKSIZE_SHIFT; + csd->wrPartial = 0; + csd->r2wFactor = 2; + csd->eraseSize = 1; +} + +static int32_t SdDecodeCsd(struct MmcCntlr *cntlr) +{ + struct MmcCsd *csd = NULL; + uint32_t *rawCsd = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + rawCsd = cntlr->curDev->reg.rawCsd; + csd = &(cntlr->curDev->reg.csd); + + /* CSD_STRUCTURE: [127:126]. */ + csd->structure = MmcParseBits(rawCsd, CSD_BITS, 126, 2); + if (csd->structure == 0) { + /* CSD Version 1.0 */ + SdDecodeCsdRev1Field(cntlr, csd, rawCsd); + } else if (csd->structure == 1) { + /* CSD Version 2.0 */ + SdDecodeCsdRev2Field(cntlr, csd, rawCsd); + } else { + HDF_LOGE("SdDecodeCsd: not support, structure = %d.", csd->structure); + return HDF_ERR_NOT_SUPPORT; + } + + return HDF_SUCCESS; +} + +static uint32_t SdGetMaxClock(struct MmcCntlr *cntlr) +{ + uint32_t clock = 0xFFFFFFFF; + struct MmcDevice *mmcDev = cntlr->curDev; + struct SdDevice *sdDev = (struct SdDevice *)mmcDev; + + if (mmcDev->state.bits.highSpeed == 1) { + if (sdDev->reg.swCaps.hsMaxDtr > 0) { + clock = sdDev->reg.swCaps.hsMaxDtr; + } + } else if (mmcDev->reg.csd.maxDtr > 0) { + clock = mmcDev->reg.csd.maxDtr; + } + + if (clock > cntlr->freqMax) { + clock = cntlr->freqMax; + } + + return clock; +} + +static int32_t SdSelect(struct MmcCntlr *cntlr, uint32_t *rocr) +{ + int err; + bool try = false; + union MmcOcr ocr = cntlr->curDev->reg.ocr; + + do { + /* dev state: ready -> idle. */ + MmcGoIdleState(cntlr); + /* dev state: idle -> idle. */ + err = SdCmdSendIfCond(cntlr, cntlr->curDev->reg.ocr.ocrData); + if (err == HDF_SUCCESS) { + ocr.bits.hcs = 1; + } + if (cntlr->caps.bits.xpc330 == 1 || cntlr->caps.bits.xpc300 == 1 || cntlr->caps.bits.xpc180 == 1) { + ocr.bits.sdXpc = 1; + } + if (MmcCntlrSupportUhs(cntlr) == true && try == false) { + ocr.bits.s18r = 1; + } + /* dev state: idle -> ready. */ + err = SdAcmdOpCond(cntlr, ocr.ocrData, rocr); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdSelect: acmd41 fail, err = %d.", err); + return err; + } + + /* host not support sd procotol 3.0 */ + if (cntlr->caps.bits.sdSupportProtocol3 == 0) { + break; + } + try = false; + /* + * Send cmd 11 to switch the volt. If host or device do not support, + * we need set 3.3v again. + */ + if (rocr != NULL && (*rocr & 0x41000000) == 0x41000000) { + /* switch host and card to 1.8V + * dev state: ready -> ready. + */ + err = SdCmdSwitchVoltage(cntlr); + if (err == HDF_SUCCESS) { + err = MmcCntlrSwitchVoltage(cntlr, VOLT_1V8); + } + if (err != HDF_SUCCESS) { + ocr.bits.s18r = 0; + try = true; + } + } + }while (try == true); + + /* get cid, dev state: ready -> ident. */ + err = MmcAllSendCid(cntlr); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdSelect: cmd2 fail, err = %d.", err); + return err; + } + SdDecodeCid(cntlr); + return err; +} + +static int32_t SdReadCsd(struct MmcCntlr *cntlr) +{ + int32_t error; + + error = MmcSendCsd(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdReadCsd: cmd9 fail, error = %d.", error); + return error; + } + return SdDecodeCsd(cntlr); +} + +static int32_t SdReadScr(struct MmcCntlr *cntlr) +{ + int32_t error; + struct SdDevice *dev = (struct SdDevice *)cntlr->curDev; + + error = SdAppSendScr(cntlr, dev->reg.rawScr, SCR_LEN); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdReadScr: acmd51 fail, error = %d.", error); + return error; + } + return SdDecodeScr(cntlr); +} + +static int32_t SdReadSsr(struct MmcCntlr *cntlr) +{ + int32_t err; + uint32_t rawSsr[SSR_LEN] = {0}; + + /* don't support ACMD. */ + if ((cntlr->curDev->reg.csd.ccc & MMC_CSD_CCC_APP_SPEC) == 0) { + return HDF_SUCCESS; + } + + err = SdAcmdSdStatus(cntlr, rawSsr, SSR_LEN); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdReadSsr: acmd13 fail, err = %d.", err); + return err; + } + return SdDecodeSSr(cntlr, rawSsr, SSR_LEN); +} + +static int32_t SdReadSwitchCapbility(struct MmcCntlr *cntlr) +{ + int32_t err; + uint8_t status[SD_SWITCH_FUNCTION_STATUS_BYTES_LEN] = {0}; + struct SdDevice *dev = (struct SdDevice *)cntlr->curDev; + struct SdSwitchFuncParam param = {0}; + + if (dev->reg.scr.sdSpec < SD_SCR_SPEC_1) { + return HDF_SUCCESS; + } + if ((cntlr->curDev->reg.csd.ccc & MMC_CSD_CCC_SWITCH) == 0) { + return HDF_SUCCESS; + } + + param.mode = SD_SWITCH_FUNC_MODE_CHECK; + param.group = SD_SWITCH_FUNC_GROUP_1; + param.value = 1; + /* The data(status) is in reverse order relative to the protocol. */ + err = SdCmdSwitchFunc(cntlr, ¶m, status, SD_SWITCH_FUNCTION_STATUS_BYTES_LEN); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdReadSwitchCapbility: swutch func group 1 fail, err = %d.", err); + return err; + } + /* [415:400]Function Group 1 information, [407:400]-->status[13]. */ + if ((status[13] & SD_BUS_SPEED_MODE_HS) > 0) { + dev->reg.swCaps.hsMaxDtr = SD_HIGH_SPEED_MAX_DTR; + } + + if (dev->reg.scr.sdSpec3 == 1) { + dev->reg.swCaps.sdSpec3BusMode.data = status[13]; + param.group = SD_SWITCH_FUNC_GROUP_3; + err = SdCmdSwitchFunc(cntlr, ¶m, status, SD_SWITCH_FUNCTION_STATUS_BYTES_LEN); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdReadSwitchCapbility: swutch func group 3 fail!"); + return err; + } + /* [447:432]Function Group 3 information, [447:440]-->status[9]. */ + dev->reg.swCaps.sdSpec3DrvType = (enum SdDrvType)status[9]; + param.group = SD_SWITCH_FUNC_GROUP_4; + err = SdCmdSwitchFunc(cntlr, ¶m, status, SD_SWITCH_FUNCTION_STATUS_BYTES_LEN); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdReadSwitchCapbility: swutch func group 4 fail!"); + return err; + } + /* [463:448]Function Group 4 information, [463:456]-->status[7]. */ + dev->reg.swCaps.sdSpec3CurrLimit = (enum SdMaxCurrentLimitBit)status[7]; + } + return HDF_SUCCESS; +} + +static void SdFillBusSpeedMode(struct MmcCntlr *cntlr) +{ + struct SdDevice *dev = (struct SdDevice *)cntlr->curDev; + + if (MmcCntlrSupportUhs(cntlr) == false || dev->reg.scr.sdSpec3 == 0) { + dev->busSpeedMode = SD_BUS_SPEED_MODE_DS; + return; + } + + /* select max speed mode supported by host and device. */ + if ((cntlr->caps.bits.uhsSdr104 == 1) && (dev->reg.swCaps.sdSpec3BusMode.bits.uhsSdr104 == 1)) { + dev->busSpeedMode = SD_BUS_SPEED_MODE_UHS_SDR104; + } else if ((cntlr->caps.bits.uhsDdr50 == 1) && (dev->reg.swCaps.sdSpec3BusMode.bits.uhsDdr50 == 1)) { + dev->busSpeedMode = SD_BUS_SPEED_MODE_UHS_DDR50; + } else if ((cntlr->caps.bits.uhsSdr104 == 1 || cntlr->caps.bits.uhsSdr50 == 1) && + (dev->reg.swCaps.sdSpec3BusMode.bits.uhsSdr50 == 1)) { + dev->busSpeedMode = SD_BUS_SPEED_MODE_UHS_SDR50; + } else if ((cntlr->caps.bits.uhsSdr104 == 1 || cntlr->caps.bits.uhsSdr50 == 1 || + cntlr->caps.bits.uhsSdr25 == 1) && (dev->reg.swCaps.sdSpec3BusMode.bits.uhsSdr25 == 1)) { + dev->busSpeedMode = SD_BUS_SPEED_MODE_UHS_SDR25; + } else if ((cntlr->caps.bits.uhsSdr104 == 1 || cntlr->caps.bits.uhsSdr50 == 1 || + cntlr->caps.bits.uhsSdr25 == 1 || cntlr->caps.bits.uhsSdr12 == 1) && + (dev->reg.swCaps.sdSpec3BusMode.bits.uhsSdr12 == 1)) { + dev->busSpeedMode = SD_BUS_SPEED_MODE_UHS_SDR12; + } +} + +static int32_t SdReadRegisters(struct MmcCntlr *cntlr) +{ + int32_t error; + + /* get SCR */ + error = SdReadScr(cntlr); + if (error != HDF_SUCCESS) { + return error; + } + + /* get SSR */ + error = SdReadSsr(cntlr); + if (error != HDF_SUCCESS) { + return error; + } + + /* get sw cap */ + error = SdReadSwitchCapbility(cntlr); + if (error != HDF_SUCCESS) { + return error; + } + + if (MmcCntlrDevReadOnly(cntlr) == true) { + cntlr->curDev->state.bits.readonly = 1; + } + SdFillBusSpeedMode(cntlr); + return HDF_SUCCESS; +} + +static int32_t SdExecuteTuning(struct MmcCntlr *cntlr) +{ + struct SdDevice *dev = (struct SdDevice *)cntlr->curDev; + + if (dev->busSpeedMode == SD_BUS_SPEED_MODE_UHS_DDR50) { + return MmcCntlrTune(cntlr, SD_CMD_SWITCH_FUNC); + } + if ((dev->busSpeedMode == SD_BUS_SPEED_MODE_UHS_SDR104) || + (dev->busSpeedMode == SD_BUS_SPEED_MODE_UHS_SDR50)) { + return MmcCntlrTune(cntlr, SD_CMD_SEND_TUNING_BLOCK); + } + return HDF_SUCCESS; +} + +static uint32_t SdGetDevMaxCurrentLimitValue(enum SdMaxCurrentLimitBit devCap) +{ + uint32_t currentLimit = 0; + + if (devCap == SD_MAX_CURRENT_LIMIT_800) { + currentLimit = SD_MAX_CURRENT_LIMIT_800_VALUE; + } else if (devCap == SD_MAX_CURRENT_LIMIT_600) { + currentLimit = SD_MAX_CURRENT_LIMIT_600_VALUE; + } else if (devCap == SD_MAX_CURRENT_LIMIT_400) { + currentLimit = SD_MAX_CURRENT_LIMIT_400_VALUE; + } else if (devCap == SD_MAX_CURRENT_LIMIT_200) { + currentLimit = SD_MAX_CURRENT_LIMIT_200_VALUE; + } + return currentLimit; +} + +static uint32_t SdGetMaxCurrentLimitValue(union MmcCaps *hostCap, enum SdMaxCurrentLimitBit devCap) +{ + uint32_t currentLimit; + + /* get max support by dev. */ + currentLimit = SdGetDevMaxCurrentLimitValue(devCap); + if (hostCap->bits.maxCurrentLimit800 == 1) { + currentLimit = ((currentLimit < SD_MAX_CURRENT_LIMIT_800_VALUE) ? + currentLimit : SD_MAX_CURRENT_LIMIT_800_VALUE); + } else if (hostCap->bits.maxCurrentLimit600 == 1) { + currentLimit = ((currentLimit < SD_MAX_CURRENT_LIMIT_600_VALUE) ? + currentLimit : SD_MAX_CURRENT_LIMIT_600_VALUE); + } else if (hostCap->bits.maxCurrentLimit400 == 1) { + currentLimit = ((currentLimit < SD_MAX_CURRENT_LIMIT_400_VALUE) ? + currentLimit : SD_MAX_CURRENT_LIMIT_400_VALUE); + } else if (hostCap->bits.maxCurrentLimit200 == 1) { + currentLimit = ((currentLimit < SD_MAX_CURRENT_LIMIT_200_VALUE) ? + currentLimit : SD_MAX_CURRENT_LIMIT_200_VALUE); + } + return currentLimit; +} + +static int32_t SdSetMaxCurrentLimit(struct MmcCntlr *cntlr) +{ + struct SdDevice *dev = (struct SdDevice *)cntlr->curDev; + uint8_t status[SD_SWITCH_FUNCTION_STATUS_BYTES_LEN] = {0}; + struct SdSwitchFuncParam param = {0}; + uint32_t currentLimit; + int32_t err; + + if ((dev->busSpeedMode == SD_BUS_SPEED_MODE_UHS_SDR104) || + (dev->busSpeedMode == SD_BUS_SPEED_MODE_UHS_DDR50) || + (dev->busSpeedMode == SD_BUS_SPEED_MODE_UHS_SDR50)) { + currentLimit = SdGetMaxCurrentLimitValue(&(cntlr->caps), dev->reg.swCaps.sdSpec3CurrLimit); + } else { + currentLimit = SD_MAX_CURRENT_LIMIT_200_VALUE; + } + + param.mode = SD_SWITCH_FUNC_MODE_SET; + param.group = SD_SWITCH_FUNC_GROUP_4; + param.value = currentLimit; + /* Current Limit is selected by CMD6 Function Group 4. */ + err = SdCmdSwitchFunc(cntlr, ¶m, status, SD_SWITCH_FUNCTION_STATUS_BYTES_LEN); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdSetMaxCurrentLimit: swutch func group 3 fail!"); + return err; + } + if (((status[15] >> 4) & 0x0F) != currentLimit) { + HDF_LOGD("SdSetMaxCurrentLimit: status not match!"); + } + + return HDF_SUCCESS; +} + +static int32_t SdSetBusSpeedMode(struct MmcCntlr *cntlr) +{ + struct SdDevice *dev = (struct SdDevice *)cntlr->curDev; + uint8_t status[SD_SWITCH_FUNCTION_STATUS_BYTES_LEN] = {0}; + struct SdSwitchFuncParam param = {0}; + int32_t err; + enum MmcBusTiming timing; + + switch (dev->busSpeedMode) { + case SD_BUS_SPEED_MODE_UHS_SDR104: + timing = BUS_TIMING_UHS_SDR104; + dev->reg.swCaps.uhsMaxDtr = SD_UHS_SDR104_MAX_DTR; + break; + case SD_BUS_SPEED_MODE_UHS_DDR50: + timing = BUS_TIMING_UHS_DDR50; + dev->reg.swCaps.uhsMaxDtr = SD_UHS_DDR50_MAX_DTR; + break; + case SD_BUS_SPEED_MODE_UHS_SDR50: + timing = BUS_TIMING_UHS_SDR50; + dev->reg.swCaps.uhsMaxDtr = SD_UHS_SDR50_MAX_DTR; + break; + case SD_BUS_SPEED_MODE_UHS_SDR25: + timing = BUS_TIMING_UHS_SDR25; + dev->reg.swCaps.uhsMaxDtr = SD_UHS_SDR25_MAX_DTR; + break; + case SD_BUS_SPEED_MODE_UHS_SDR12: + timing = BUS_TIMING_UHS_SDR12; + dev->reg.swCaps.uhsMaxDtr = SD_UHS_SDR12_MAX_DTR; + break; + default: + return HDF_SUCCESS; + } + /* Bus Speed Mode is selected by CMD6 Function Group 1. */ + param.mode = SD_SWITCH_FUNC_MODE_SET; + param.group = SD_SWITCH_FUNC_GROUP_1; + param.value = dev->busSpeedMode; + err = SdCmdSwitchFunc(cntlr, ¶m, status, SD_SWITCH_FUNCTION_STATUS_BYTES_LEN); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdSetBusSpeedMode: swutch func group 1 fail!"); + return err; + } + if ((status[16] & 0xF) != dev->busSpeedMode) { + HDF_LOGD("SdSetBusSpeedMode: status not match!"); + } else { + MmcCntlrSetBusTiming(cntlr, timing); + MmcCntlrSetClock(cntlr, dev->reg.swCaps.uhsMaxDtr); + } + + return HDF_SUCCESS; +} + +static int32_t SdSwitch4BitBusWidth(struct MmcCntlr *cntlr, struct SdDevice *dev) +{ + int32_t err = HDF_SUCCESS; + + if ((dev->reg.scr.sdBusWidths & SD_SCR_BUS_WIDTHS_4) > 0 && + (cntlr->caps.bits.cap4Bit > 0)) { + err = SdAcmdSetBusWidth(cntlr, BUS_WIDTH4); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdSwitch4BitBusWidth: set 4-bits bus width fail!"); + return err; + } + MmcCntlrSetBusWidth(cntlr, BUS_WIDTH4); + } + return err; +} + +static int32_t SdUltraHighSpeedDevInit(struct MmcCntlr *cntlr) +{ + int32_t err; + struct SdDevice *dev = (struct SdDevice *)cntlr->curDev; + + if (dev->reg.scr.sdSpec3 == 0) { + return HDF_SUCCESS; + } + if ((cntlr->curDev->reg.csd.ccc & MMC_CSD_CCC_SWITCH) == 0) { + return HDF_SUCCESS; + } + + err = SdSwitch4BitBusWidth(cntlr, dev); + if (err != HDF_SUCCESS) { + return err; + } + err = SdSetMaxCurrentLimit(cntlr); + if (err != HDF_SUCCESS) { + return err; + } + err = SdSetBusSpeedMode(cntlr); + if (err != HDF_SUCCESS) { + return err; + } + return SdExecuteTuning(cntlr); +} + +static int32_t SdSwitchHighSpeed(struct MmcCntlr *cntlr) +{ + int32_t err; + struct SdDevice *dev = (struct SdDevice *)cntlr->curDev; + uint8_t status[SD_SWITCH_FUNCTION_STATUS_BYTES_LEN] = {0}; + struct SdSwitchFuncParam param = {0}; + + if (dev->reg.swCaps.hsMaxDtr == 0) { + return HDF_ERR_NOT_SUPPORT; + } + if (dev->reg.scr.sdSpec < SD_SCR_SPEC_1) { + return HDF_ERR_NOT_SUPPORT; + } + if (cntlr->caps.bits.highSpeed == 0) { + return HDF_ERR_NOT_SUPPORT; + } + if ((cntlr->curDev->reg.csd.ccc & MMC_CSD_CCC_SWITCH) == 0) { + return HDF_ERR_NOT_SUPPORT; + } + /* Bus Speed Mode is selected by CMD6 Function Group 1. */ + param.mode = SD_SWITCH_FUNC_MODE_SET; + param.group = SD_SWITCH_FUNC_GROUP_1; + param.value = SD_BUS_SPEED_MODE_HS; + err = SdCmdSwitchFunc(cntlr, ¶m, status, SD_SWITCH_FUNCTION_STATUS_BYTES_LEN); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdSwitchHighSpeed: switch func group 1 fail!"); + return err; + } + if ((status[16] & 0xF) != SD_BUS_SPEED_MODE_HS) { + return HDF_ERR_NOT_SUPPORT; + } + return HDF_SUCCESS; +} + +static int32_t SdHighSpeedDevInit(struct MmcCntlr *cntlr) +{ + int err; + struct SdDevice *sdDev = (struct SdDevice *)cntlr->curDev; + + err = SdSwitchHighSpeed(cntlr); + if (err == HDF_SUCCESS) { + cntlr->curDev->state.bits.highSpeed = 1; + MmcCntlrSetBusTiming(cntlr, BUS_TIMING_SD_HS); + } else if (err != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("SdHighSpeedDevInit: switch high speed fail!"); + return err; + } + MmcCntlrSetClock(cntlr, SdGetMaxClock(cntlr)); + return SdSwitch4BitBusWidth(cntlr, sdDev); +} + +static int32_t SdDeviceAdd(struct MmcCntlr *cntlr) +{ + /* The SD dev must be removable. */ + cntlr->curDev->state.bits.removeable = 1; + SdSetBlockCapacity(cntlr); + /* add dev. */ + if (MmcDeviceAdd(cntlr->curDev) != HDF_SUCCESS) { + HDF_LOGE("SdDeviceAdd: add device fail!"); + return HDF_FAILURE; + } + cntlr->curDev->state.bits.present = 1; + return HDF_SUCCESS; +} + +static int32_t SdInit(struct MmcCntlr *cntlr) +{ + int32_t error; + union MmcOcr ocr = {0}; + + /* acmd41, detect sd dev and get the voltage range. dev state: idle -> ready. */ + error = SdAcmdOpCond(cntlr, 0, &(ocr.ocrData)); + if (error != HDF_SUCCESS) { + HDF_LOGE("acmd41(detect sd) fail, err = %d!", error); + return error; + } + + MmcCntlrSelectWorkVoltage(cntlr, &ocr); + if (cntlr->curDev->reg.ocr.ocrData == 0) { + HDF_LOGE("SD work voltage is invalid!"); + return HDF_ERR_INVALID_PARAM; + } + error = SdSelect(cntlr, &(ocr.ocrData)); + if (error != HDF_SUCCESS) { + return error; + } + /* get RCA. dev state: ident -> stby. */ + error = SdCmdSendRelativeAddr(cntlr, &(cntlr->curDev->reg.rca)); + if (error != HDF_SUCCESS) { + HDF_LOGE("cmd3(get RCA) fail!, error = %d.", error); + return error; + } + /* get CSD, CMD9 should send in stby. dev state: stby -> stby. */ + error = SdReadCsd(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("sd read csd fail!, error = %d.", error); + return error; + } + /* select card. dev state: stby -> tran. */ + error = MmcSelectCard(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("cmd7(select card) fail!, error = %d.", error); + return error; + } + /* dev state: tran -> tran. */ + error = SdReadRegisters(cntlr); + if (error != HDF_SUCCESS) { + return error; + } + + if (ocr.bits.s18r == 1) { + /* uhs dev set */ + error = SdUltraHighSpeedDevInit(cntlr); + if (error != HDF_SUCCESS) { + return error; + } + cntlr->curDev->state.bits.uhs = 1; + } else { + /* highspeed dev set */ + error = SdHighSpeedDevInit(cntlr); + if (error != HDF_SUCCESS) { + return error; + } + } + + return SdDeviceAdd(cntlr); +} + +static int32_t SdDetect(struct MmcCntlr *cntlr) +{ + int32_t ret; + + HDF_LOGD("Detect sd dev start..."); + /* dev state: idle. */ + MmcGoIdleState(cntlr); + /* dev state: idle -> idle. */ + (void)SdCmdSendIfCond(cntlr, cntlr->ocrDef.ocrData); + /* Initialize SD. */ + ret = SdInit(cntlr); + if (ret == HDF_SUCCESS) { + HDF_LOGD("Detect sd dev success! %s dev at address 0x%x!", + cntlr->curDev->state.bits.uhs ? "Ultra high speed" : + (cntlr->curDev->state.bits.highSpeed ? "High speed" : ""), + cntlr->curDev->reg.rca); + } + return ret; +} + +static int32_t SdioSendOpCond(struct MmcCntlr *cntlr, uint32_t arg, uint32_t *ocr) +{ + struct MmcCmd cmd = {0}; + int32_t err; + uint32_t i; + + /* + * An SDIO aware host will send CMD5 prior to the CMD55/ACMD41 pair, and thus would receive a valid OCR in + * the R4 response to CMD5 and continue to initialize the card. + */ + cmd.cmdCode = SDIO_SEND_OP_COND; + /* [23:0] OCR; [24] S18R(Switching to 1.8V Request); [31:25] Stuff Bits. */ + cmd.argument = arg; + cmd.respType = MMC_RESP_R4 | MMC_CMD_TYPE_BCR; + for (i = 0; i < INIT_CMD_RETRY_TIMES; i++) { + err = MmcSendCmd(cntlr, &cmd, NULL, MMC_CMD_DEFAULT_RETRY_TIMES); + if (err != HDF_SUCCESS) { + break; + } + if (arg == 0) { + break; + } + if ((cmd.resp[0] & MMC_CARD_BUSY_STATUS) > 0) { + break; + } + + err = HDF_ERR_TIMEOUT; + OsalMDelay(10); + } + if (ocr != NULL) { + /* + * Response R4 in Sd mode: [23:0] OCR; [24] S18A; [26:25] Stuff Bits; + * [27] Memory Present; [30: 28] Number of I/O functions. + */ + *ocr = cmd.resp[0]; + } + + return err; +} + +static int32_t SdioRespR5Check(struct MmcCmd *cmd) +{ + if (cmd->resp[0] & SDIO_R5_ERROR) { + HDF_LOGE("R5: error!"); + return HDF_ERR_IO; + } + if (cmd->resp[0] & SDIO_R5_OUT_OF_RANGE) { + HDF_LOGE("R5: out of range error!"); + return HDF_ERR_INVALID_PARAM; + } + if (cmd->resp[0] & SDIO_R5_FUNCTION_NUMBER) { + HDF_LOGE("R5: func num error!"); + return HDF_ERR_INVALID_PARAM; + } + return HDF_SUCCESS; +} + +int32_t SdioRwDirect(struct MmcCntlr *cntlr, struct SdioCmdParam *param, uint8_t *out) +{ + struct MmcCmd cmd = {0}; + int32_t err; + + if (cntlr == NULL || param == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + /* + * The IO_RW_DIRECT is the simplest means to access a single register within the total 128K of register space + * in any I/O function, including the common I/O area (CIA). This command reads or writes 1 byte using only 1 + * command/response pair. A common use is to initialize registers or monitor status values for I/O functions. + */ + cmd.cmdCode = SDIO_RW_DIRECT; + /* [31] R/W flag. */ + cmd.argument = ((param->writeflag == true) ? 0x80000000 : 0x00000000); + /* [30:28] Function Number. */ + cmd.argument |= (param->funcNum << 28); + /* [25:9] Register Address. */ + cmd.argument |= (param->regAddr << 9); + /* [7:0] Write Data or Stuff Bits. */ + cmd.argument |= param->writeData; + cmd.respType = MMC_RESP_SPI_R5 | MMC_RESP_R5 | MMC_CMD_TYPE_AC; + err = MmcSendCmd(cntlr, &cmd, NULL, 1); + if (err != HDF_SUCCESS) { + return err; + } + + /* resp error check. */ + err = SdioRespR5Check(&cmd); + if (err != HDF_SUCCESS) { + return err; + } + if (out != NULL) { + *out = cmd.resp[0] & 0xFF; + } + return HDF_SUCCESS; +} + +int32_t SdioRwExtended(struct MmcCntlr *cntlr, struct SdioCmdParam *param, + uint8_t *buf, uint32_t blockNum, uint32_t blockSize) +{ + struct MmcCmd cmd = {0}; + struct MmcData data = {0}; + int32_t err; + + if (cntlr == NULL || param == NULL) { + return HDF_ERR_INVALID_PARAM; + } + /* Register Address: Start Address of I/O register to read or write. Range is [1FFFFh:0]. */ + if (param->regAddr != ((param->regAddr) & 0x1FFFF)) { + return HDF_ERR_INVALID_PARAM; + } + + cmd.cmdCode = SDIO_RW_EXTENDED; + /* [31] R/W flag. */ + cmd.argument = ((param->writeflag == true) ? 0x80000000 : 0x00000000); + /* [30:28] Function Number. */ + cmd.argument |= (param->funcNum << 28); + /* [26] Op Code. */ + cmd.argument |= ((param->incrAddrFlag == true) ? 0x04000000 : 0x00000000); + /* [25:9] Register Address. */ + cmd.argument |= (param->regAddr << 9); + /* + * [8:0] Byte/Block Count. + * If the command is operating on bytes(Block Mode = 0), this field contains the number of bytes + * to read or write. A value of 0 shall cause 512 bytes to be read or written. + * If the command is in block mode(Block Mode = 1), the Block Count field specifies the number + * of Data Blocks to be transferred following this command. + */ + if (blockNum == 1 && blockSize <= 512) { + cmd.argument |= ((blockSize == 512) ? 0 : blockSize); /* byte mode */ + } else { + /* [27] Block Mode. */ + cmd.argument |= (0x08000000 | blockNum); + } + cmd.respType = MMC_RESP_SPI_R5 | MMC_RESP_R5 | MMC_CMD_TYPE_ADTC; + + data.blockSize = blockSize; + data.blockNum = blockNum; + data.dataFlags = ((param->writeflag == true) ? DATA_WRITE : DATA_READ); + if (param->scatterFlag == false) { + data.dataBuffer = buf; + } else { + data.scatter = (void *)buf; + data.scatterLen = param->scatterLen; + } + + err = MmcSendCmd(cntlr, &cmd, &data, 1); + if (err != HDF_SUCCESS) { + return err; + } + + /* resp error check. */ + return SdioRespR5Check(&cmd); +} + +static void SdioIoReset(struct MmcCntlr *cntlr) +{ + struct SdioCmdParam param = {0}; + uint8_t out = 0; + int32_t error; + + /* + * In order to reset an I/O only card or the I/O portion of a combo card, + * use CMD52 to write a 1 to the RES bit in the CCCR(bit3 of register 6), + * because it can't issue CMD52 after CMD0. + */ + param.regAddr = IO_ABORT; + /* read register 6 of CCCR. */ + error = SdioRwDirect(cntlr, ¶m, &out); + if (error < 0) { + out = SDIO_CCCR_RES; + } else { + out |= SDIO_CCCR_RES; + } + /* write the RES bit to 1. */ + param.writeflag = true; + param.writeData = out; + (void)SdioRwDirect(cntlr, ¶m, NULL); +} + +int32_t SdioReadWriteByte(struct MmcCntlr *cntlr, bool writeFlag, + uint32_t funcNum, uint32_t addr, uint8_t *data) +{ + struct SdioCmdParam param = {0}; + + if (cntlr == NULL || data == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + param.regAddr = addr; + param.funcNum = funcNum; + if (writeFlag == true) { + param.writeflag = true; + param.writeData = *data; + return SdioRwDirect(cntlr, ¶m, NULL); + } + return SdioRwDirect(cntlr, ¶m, data); +} + +static int32_t SdioReadWriteRemainBytes(struct MmcCntlr *cntlr, struct SdioCmdParam *param, + uint8_t *data, uint32_t size, uint32_t addr) +{ + uint32_t maxBlkSize, curSize; + struct SdioDevice *dev = (struct SdioDevice *)cntlr->curDev; + uint32_t remLen = size; + uint32_t curAddr = addr; + uint8_t *buffer = data; + int32_t err; + + maxBlkSize = MMC_MIN(cntlr->maxBlkSize, dev->curFunction->maxBlkSize); + maxBlkSize = MMC_MIN(maxBlkSize, BYTES_PER_BLOCK); + if (maxBlkSize == 0) { + HDF_LOGE("max block size is invalid!"); + return HDF_ERR_INVALID_PARAM; + } + + while (remLen > 0) { + curSize = MMC_MIN(remLen, maxBlkSize); + param->regAddr = curAddr; + err = SdioRwExtended(cntlr, param, buffer, 1, curSize); + if (err != HDF_SUCCESS) { + HDF_LOGD("SdioReadWriteBlock: bytes mode, err = %d, addr = %d, curSize = %d!", err, addr, curSize); + return err; + } + buffer += curSize; + if (param->incrAddrFlag == true) { + curAddr += curSize; + } + remLen -= curSize; + } + return HDF_SUCCESS; +} + +static void SdioFillRwExtendedCmdParam(struct SdioCmdParam *param, + struct SdioDevice *dev, struct SdioRwBlockInfo *info) +{ + param->funcNum = dev->curFunction->funcNum; + param->incrAddrFlag = info->incrAddrFlag; + param->writeflag = info->writeFlag; + if (info->scatterFlag == true) { + param->scatterFlag = true; + param->scatterLen = info->scatterLen; + param->regAddr = info->addr; + } +} + +int32_t SdioReadWriteBlock(struct MmcCntlr *cntlr, struct SdioRwBlockInfo *info) +{ + uint32_t maxBlkNum, maxBlkSize, curblkNum, curSize, curAddr, remLen; + int32_t err; + struct SdioCmdParam param = {0}; + struct SdioDevice *dev = NULL; + uint8_t *buffer = NULL; + + if (cntlr == NULL || info == NULL) { + return HDF_ERR_INVALID_PARAM; + } + dev = (struct SdioDevice *)cntlr->curDev; + if (dev == NULL || dev->curFunction == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + maxBlkSize = MMC_MIN(cntlr->maxBlkSize, dev->curFunction->maxBlkSize); + maxBlkSize = MMC_MIN(maxBlkSize, BYTES_PER_BLOCK); + if (maxBlkSize == 0) { + return HDF_ERR_INVALID_PARAM; + } + if (dev->curFunction->curBlkSize == 0 || cntlr->maxBlkNum == 0) { + return HDF_ERR_INVALID_PARAM; + } + + remLen = info->size; + curAddr = info->addr; + buffer = info->buf; + SdioFillRwExtendedCmdParam(¶m, dev, info); + if (info->scatterFlag == true) { + return SdioRwExtended(cntlr, ¶m, buffer, MMC_MAX(1, remLen / maxBlkSize), MMC_MIN(remLen, maxBlkSize)); + } + /* send block. */ + if (dev->sdioReg.cccr.multiBlock > 0 && (remLen > maxBlkSize)) { + maxBlkNum = MMC_MIN((cntlr->maxReqSize / dev->curFunction->curBlkSize), cntlr->maxBlkNum); + maxBlkNum = MMC_MIN(maxBlkNum, SDIO_BLOCK_TRANSFER_MAX_BLKNUM); + while (remLen > dev->curFunction->curBlkSize) { + curblkNum = remLen / dev->curFunction->curBlkSize; + curblkNum = MMC_MIN(curblkNum, maxBlkNum); + curSize = curblkNum * dev->curFunction->curBlkSize; + param.regAddr = curAddr; + err = SdioRwExtended(cntlr, ¶m, buffer, curblkNum, dev->curFunction->curBlkSize); + if (err != HDF_SUCCESS) { + return err; + } + buffer += curSize; + if (info->incrAddrFlag == true) { + curAddr += curSize; + } + remLen -= curSize; + } + } + + /* send remaind bytes. */ + return SdioReadWriteRemainBytes(cntlr, ¶m, buffer, remLen, curAddr); +} + +static int32_t SdioCdDisable(struct MmcCntlr *cntlr) +{ + struct SdioCmdParam param = {0}; + int32_t error; + uint8_t ctrl; + + param.regAddr = BUS_INTERFACE_CONTROL; + /* read register 7 of CCCR. */ + error = SdioRwDirect(cntlr, ¶m, &ctrl); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdioCdDisable: read BIC fail!"); + return error; + } + /* + * write the CD Disable bit to 1. + * This bit shall be set to 1 before issuing CMD53. + */ + ctrl |= SDIO_CCCR_CD_DISABLE; + param.writeflag = true; + param.writeData = ctrl; + return SdioRwDirect(cntlr, ¶m, NULL); +} + +static int32_t SdioReadCccrSdioRev(struct MmcCntlr *cntlr, struct SdioCccr *cccr, uint8_t *cccrRev) +{ + struct SdioCmdParam param = {0}; + int32_t err; + uint8_t data; + + /* read register 0 of CCCR. */ + param.regAddr = CCCR_SDIO_REVISION; + err = SdioRwDirect(cntlr, ¶m, &data); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdioReadCccrSdioRev: read sdio rev fail!"); + return err; + } + /* bit3-bit0: CCCR_REVISION */ + *cccrRev = data & 0x0f; + if ((*cccrRev) > SDIO_CCCR_VERSION_3_00) { + HDF_LOGE("SdioReadCccrSdioRev: cccr rev error!"); + return HDF_FAILURE; + } + /* bit7-bit4: SDIO_REVISION */ + cccr->sdioRev = (data & 0xf0) >> 4; + return HDF_SUCCESS; +} + +static int32_t SdioReadCccrCapbility(struct MmcCntlr *cntlr, struct SdioCccr *cccr) +{ + struct SdioCmdParam param = {0}; + int32_t error; + uint8_t cap; + + /* read register 8 of CCCR. */ + param.regAddr = CARD_CAPBILITY; + error = SdioRwDirect(cntlr, ¶m, &cap); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdioReadCccrCapbility: read card cap fail!"); + return error; + } + + if ((cap & SDIO_CCCR_CAP_4BLS) > 0) { + cccr->lowSpeed4Bit = 1; + } + if ((cap & SDIO_CCCR_CAP_LSC) > 0) { + cccr->lowSpeed = 1; + } + if ((cap & SDIO_CCCR_CAP_SMB) > 0) { + cccr->multiBlock = 1; + } + return HDF_SUCCESS; +} + +static int32_t SdioReadCccrPowerControl(struct MmcCntlr *cntlr, struct SdioCccr *cccr) +{ + struct SdioCmdParam param = {0}; + int32_t error; + uint8_t ctrl; + + /* read register 18 of CCCR. */ + param.regAddr = POWER_CONTROL; + error = SdioRwDirect(cntlr, ¶m, &ctrl); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdioReadCccrPowerControl: read power control fail!"); + return error; + } + + if ((ctrl & SDIO_CCCR_POWER_SMPC) > 0) { + cccr->highPower = 1; + } + return HDF_SUCCESS; +} + +static int32_t SdioReadCccrBusSpeed(struct MmcCntlr *cntlr, struct SdioCccr *cccr) +{ + struct SdioCmdParam param = {0}; + int32_t error; + uint8_t speed; + + /* read register 19 of CCCR. */ + param.regAddr = BUS_SPEED_SELECT; + error = SdioRwDirect(cntlr, ¶m, &speed); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdioReadCccrBusSpeed: read bus speed select fail!"); + return error; + } + + if ((speed & SDIO_CCCR_BUS_SPEED_SHS) > 0) { + cccr->highSpeed = 1; + } + return HDF_SUCCESS; +} + +int32_t SdioReadCccrIoEnable(struct MmcCntlr *cntlr, uint8_t *val) +{ + struct SdioCmdParam param = {0}; + + if (cntlr == NULL || val == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + /* read register 2(IOEx) of CCCR. */ + param.regAddr = IO_ENABLE; + return SdioRwDirect(cntlr, ¶m, val); +} + +int32_t SdioCccrIoEnable(struct MmcCntlr *cntlr) +{ + struct SdioCmdParam param = {0}; + struct SdioDevice *dev = NULL; + int32_t err; + uint8_t data; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + dev = (struct SdioDevice *)cntlr->curDev; + if (dev == NULL || dev->curFunction == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + /* read register 2(IOEx) of CCCR. */ + param.regAddr = IO_ENABLE; + err = SdioRwDirect(cntlr, ¶m, &data); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdioCccrIoEnable: read io enable fail! err = %d.", err); + return err; + } + /* + * IOEx:Enable Function x. + * If this bit is reset to 0, the function is disable. If this bit is set to 1, the function is enabled to start + * its initialization. The complation of initialization is indicated in IORx. On power up or after a reset, the + * card shall reset this bit to 0. The host can also use IOEx as a per function reset for error recovery. The host + * sequence for a per function reset is to reset IOEx to 0, wait until IORx becomes 0 and then set IOEx to 1 again. + */ + data |= (1 << dev->curFunction->funcNum); + param.writeflag = true; + param.writeData = data; + /* write register 2(IOEx) of CCCR. */ + return SdioRwDirect(cntlr, ¶m, NULL); +} + +int32_t SdioCccrIoDisable(struct MmcCntlr *cntlr) +{ + struct SdioCmdParam param = {0}; + struct SdioDevice *dev = NULL; + int32_t err; + uint8_t data; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + dev = (struct SdioDevice *)cntlr->curDev; + if (dev == NULL || dev->curFunction == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + /* read register 2(IOEx) of CCCR. */ + param.regAddr = IO_ENABLE; + err = SdioRwDirect(cntlr, ¶m, &data); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdioCccrIoDisable: read io enable fail! err = %d.", err); + return err; + } + + data &= (~(1 << dev->curFunction->funcNum)); + param.writeflag = true; + param.writeData = data; + /* write register 2(IOEx) of CCCR. */ + return SdioRwDirect(cntlr, ¶m, NULL); +} + +int32_t SdioReadCccrIoReady(struct MmcCntlr *cntlr, uint8_t *val) +{ + struct SdioCmdParam param = {0}; + + if (cntlr == NULL || val == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + /* read register 3(IORx) of CCCR. */ + param.regAddr = IO_READY; + /* + * IORx: I/O Function x Ready. + * If this bit is set to 0, the function is not ready to operate. This may be caused by the function being + * disabled or not ready due to internal causes such as a built-in-self-test in progress. If this bit is set to 1, + * the function is ready to operate. On power up or after a reset, this bit shall be set to 0. + */ + return SdioRwDirect(cntlr, ¶m, val); +} + +int32_t SdioReadCccrIntPending(struct MmcCntlr *cntlr, uint8_t *val) +{ + struct SdioCmdParam param = {0}; + + if (cntlr == NULL || val == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + /* read register 5(INTx) of CCCR. */ + param.regAddr = INT_PENDING; + /* + * INTx: Interrupt Pending for Function x. + * If this bit is cleared to 0, this indicates that no Interrupts are pending from this function. + * If this bit is set to 1, then this function has Interrupt pending. + * Note that if the IENx or IENM bits are not set, the host cannot receive this pending Interrupt. + */ + return SdioRwDirect(cntlr, ¶m, val); +} + +int32_t SdioCccrIntEnable(struct MmcCntlr *cntlr) +{ + int32_t err; + uint8_t val; + struct SdioCmdParam param = {0}; + struct SdioDevice *dev = NULL; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + dev = (struct SdioDevice *)cntlr->curDev; + if (dev == NULL || dev->curFunction == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + /* read register 4 of CCCR. */ + param.regAddr = INT_ENABLE; + err = SdioRwDirect(cntlr, ¶m, &val); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdioCccrIntEnable: read int enable fail! err = %d.", err); + return err; + } + /* + * [0]IENM: Interrupt Enable Master. + * If this bit is cleared to 0, no interrupts from this card shall be sent to the host. + * If this bit is set to 1, then any function's interrupt shall be sent to the host. + */ + val |= 1; + /* + * [1-7]IENx:Interrupt Enable for Function x. + * If this bit is cleared to 0, any interrupt form this function shall not be sent to the host. + * If this bit is set to 1, function's interrupt shall be sent to the host if the IENM is also set to 1. + */ + val |= (1 << dev->curFunction->funcNum); + param.writeflag = true; + param.writeData = val; + /* write register 4 of CCCR. */ + return SdioRwDirect(cntlr, ¶m, NULL); +} + +int32_t SdioCccrIntDisable(struct MmcCntlr *cntlr) +{ + struct SdioDevice *dev = (struct SdioDevice *)cntlr->curDev; + struct SdioCmdParam param = {0}; + int32_t err; + uint8_t val; + + if (dev == NULL || dev->curFunction == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + /* read register 4 of CCCR. */ + param.regAddr = INT_ENABLE; + err = SdioRwDirect(cntlr, ¶m, &val); + if (err != HDF_SUCCESS) { + HDF_LOGE("SdioCccrIntDisable: read int enable fail! err = %d.", err); + return err; + } + /* clear the function's interrupt. */ + val &= (~(1U << dev->curFunction->funcNum)); + if ((val & 0xFE) == 0) { + val = 0; + } + param.writeflag = true; + param.writeData = val; + /* write register 4 of CCCR. */ + return SdioRwDirect(cntlr, ¶m, NULL); +} + +static int32_t SdioReadCccr(struct MmcCntlr *cntlr) +{ + int32_t ret; + uint8_t cccrRev; + struct SdioDevice *sdioDev = NULL; + struct SdioCccr *cccr = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + sdioDev = (struct SdioDevice *)cntlr->curDev; + cccr = &(sdioDev->sdioReg.cccr); + ret = SdioReadCccrSdioRev(cntlr, cccr, &cccrRev); + if (ret != HDF_SUCCESS) { + return ret; + } + ret = SdioReadCccrCapbility(cntlr, cccr); + if (ret != HDF_SUCCESS) { + return ret; + } + + if (cccrRev >= SDIO_CCCR_VERSION_1_10) { + ret = SdioReadCccrPowerControl(cntlr, cccr); + if (ret != HDF_SUCCESS) { + return ret; + } + } + /* SDIO version 1.20 cards indicate their support for High-Speed mode with the SHS bit in the CCCR. */ + if (cccrRev >= SDIO_CCCR_VERSION_1_20) { + ret = SdioReadCccrBusSpeed(cntlr, cccr); + } + return ret; +} + +static int32_t SdioBusSpeedSelect(struct MmcCntlr *cntlr, bool enableHighSpeed) +{ + struct SdioCmdParam param = {0}; + int32_t error; + uint8_t speed; + + /* read register 19 of CCCR. */ + param.regAddr = BUS_SPEED_SELECT; + error = SdioRwDirect(cntlr, ¶m, &speed); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdioBusSpeedSelect: read bus speed select fail! err = %d.", error); + return error; + } + /* fill BSS0. */ + if (enableHighSpeed == true) { + speed |= SDIO_CCCR_BUS_SPEED_EHS; + } else { + speed &= (~SDIO_CCCR_BUS_SPEED_EHS); + } + /* write BSS0. */ + param.writeflag = true; + param.writeData = speed; + return SdioRwDirect(cntlr, ¶m, NULL); +} + +static int32_t SdioSwitchHighSpeed(struct MmcCntlr *cntlr) +{ + struct SdioDevice *sdioDev = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + sdioDev = (struct SdioDevice *)cntlr->curDev; + if (cntlr->caps.bits.highSpeed == 0 || sdioDev->sdioReg.cccr.highSpeed == 0) { + return HDF_ERR_NOT_SUPPORT; + } + return SdioBusSpeedSelect(cntlr, true); +} + +static int32_t SdioEnableHighSpeed(struct MmcCntlr *cntlr) +{ + int32_t err; + + err = SdioSwitchHighSpeed(cntlr); + if (cntlr->curDev->type == MMC_DEV_SDIO || err != HDF_SUCCESS) { + return err; + } + + /* COMBO card, need switch SD memory. */ + err = SdSwitchHighSpeed(cntlr); + if (err == HDF_SUCCESS) { + return err; + } + /* sd switch fail, need switch sdio to default speed. */ + if (SdioBusSpeedSelect(cntlr, false) != HDF_SUCCESS) { + HDF_LOGD("SdioEnableHighSpeed: switch sdio to default speed fail."); + } + return err; +} + +static int32_t SdioSetBusWidth(struct MmcCntlr *cntlr, uint8_t width) +{ + struct SdioCmdParam param = {0}; + int32_t error; + uint8_t data; + + /* read register 7 of CCCR. */ + param.regAddr = BUS_INTERFACE_CONTROL; + error = SdioRwDirect(cntlr, ¶m, &data); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdioSetBusWidth: read BIC fail, error = %d!", error); + return error; + } + + data |= width; + /* write bit1-bit0: Bus Width. */ + param.writeflag = true; + param.writeData = data; + return SdioRwDirect(cntlr, ¶m, NULL); +} + +static int32_t SdioSwitch4BitBusWidth(struct MmcCntlr *cntlr) +{ + struct SdioDevice *sdioDev = NULL; + + if (cntlr == NULL || cntlr->curDev == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + sdioDev = (struct SdioDevice *)cntlr->curDev; + if (cntlr->caps.bits.cap4Bit == 0) { + return HDF_ERR_NOT_SUPPORT; + } + /* + * Note that Low-Speed SDIO cards support 4-bit transfer as an optipn. When communicating with a + * Low-Speed SDIO card, the host shall fist determine if the card supports 4-bit transfer prior to + * attempting to select that mode. + */ + if (sdioDev->sdioReg.cccr.lowSpeed > 0 && sdioDev->sdioReg.cccr.lowSpeed4Bit == 0) { + return HDF_ERR_NOT_SUPPORT; + } + return SdioSetBusWidth(cntlr, SDIO_CCCR_WIDTH_4BIT); +} + +static int32_t SdioEnable4BitBusWidth(struct MmcCntlr *cntlr) +{ + int32_t error; + struct SdDevice *dev = (struct SdDevice *)cntlr->curDev; + + /* + * For an SD memory card, the bus width for SD mode is set using ACMD6. + * For an SDIO card a write to the CCCR usingCMD52 is used to select bus width. + * In the case of a combo card, both selection methods exist. + */ + error = SdioSwitch4BitBusWidth(cntlr); + if (cntlr->curDev->type == MMC_DEV_SDIO || error != HDF_SUCCESS) { + return error; + } + + /* COMBO card, need switch SD memory. */ + if ((dev->reg.scr.sdBusWidths & SD_SCR_BUS_WIDTHS_4) > 0 && + (cntlr->caps.bits.cap4Bit > 0)) { + error = SdAcmdSetBusWidth(cntlr, BUS_WIDTH4); + } + + return error; +} + +static int32_t SdioReadCisTplField(struct MmcCntlr *cntlr, uint32_t addr, uint8_t *data) +{ + struct SdioCmdParam param = {0}; + + param.regAddr = addr; + return SdioRwDirect(cntlr, ¶m, data); +} + +static void SdioDecodeCisTplManfId(struct MmcCntlr *cntlr, struct SdioFunction *function, + struct SdioCisTuple *tuple) +{ + uint16_t vendorId, deviceId; + struct SdioDevice *dev = NULL; + + if (tuple->tplLink < SDIO_CIS_TPL_MANFID_MIN_SIZE) { + return; + } + + /* + * CISTPL_MANFID Field: bit00[TPL_CODE(20)], bit01[TPL_LINK(at least 4)], bit02-03[SDIO Card manufacturer code], + * bit04-05[manufacturer information(Part Number and /or Revision)]. + * bit02-03-->tplBody[0:1], bit04-05-->tplBody[2:3]. + */ + vendorId = (tuple->tplBody[1] << BITS_PER_BYTE) | tuple->tplBody[0]; + deviceId = (tuple->tplBody[3] << BITS_PER_BYTE) | tuple->tplBody[2]; + HDF_LOGD("Sdio vendorId = 0x%x, deviceId = 0x%x.", vendorId, deviceId); + /* function CISTPL_MANFID. */ + if (function != NULL) { + function->deviceId = deviceId; + function->vendorId = vendorId; + return; + } + + /* common CISTPL_MANFID. */ + dev = (struct SdioDevice *)cntlr->curDev; + dev->sdioReg.cis.deviceId = deviceId; + dev->sdioReg.cis.vendorId = vendorId; +} + +static void SdioDecodeCisTplFunceCommon(struct MmcCntlr *cntlr, struct SdioCisTuple *tuple) +{ + struct SdioDevice *dev = (struct SdioDevice *)cntlr->curDev; + const uint8_t value[16] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; + const uint32_t unit[8] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; + + if (tuple->tplLink < SDIO_CIS_TPL_FUNCE_COMMON_MIN_SIZE) { + return; + } + + /* + * CISTPL_FUNCE(common): bit00[TPL_CODE(22h)], bit01[TPL_LINK], bit02[TPLFE_TYPE(00h)], + * bit03-04[TPLFE_FN0_BLK_SIZE], bit05[TPLFE_MAX_TRAN_SPEED]. + * bit2-->tplBody[0], bit03-04-->tplBody[1:2], bit05-->tplBody[3]. + */ + dev->sdioReg.cis.blkSize = (tuple->tplBody[2] << BITS_PER_BYTE) | tuple->tplBody[1]; + + /* + * This byte indicates the maximum transfer rate per one data line during data transfer. This value applies to + * all functions in the SDIO card. This value shall be 25Mb/Sec(32h) for all Full-Speed SDIO card. The minimum + * value for Low-Speed SDIO cards shall be 400 Kb/Sec(48h). The format is identical to the TRAN_SPEED value stored + * in the CSD of SD memory cards. The maximum data transfer rate is coded according to the following method: + * Bits 2:0 contain the transfer rate unit coded as follows: + * 0=100kbit/s, 1=1Mbit/s, 2=10Mbit/s, 3=100Mbit/s, ... 7=reserved. + * Bits 6:3 contain the time value coded as follows: + * 0=reserved, 1=1.0, 2=1.2, 3=1.3, 4=1.5, 5=2.0, 6=2.5, 7=3.0, 8= 3.5, 9=4.0, A=4.5, B=5.0, C=5.5, D=6.0, E=7.0, + * F= 8.0. + * Bit 7 is reserved and shall be 0. + */ + dev->sdioReg.cis.maxDtr = unit[tuple->tplBody[3] & 7] * value[(tuple->tplBody[3] >> 3) & 15]; +} + +static void SdioDecodeCisTplFunceFunction(struct SdioFunction *function, struct SdioCisTuple *tuple) +{ + uint32_t minSize; + + /* Rev 1.0, TPL_LINK 28bytes; >Rev 1.0, TPL_LINK 42bytes. */ + minSize = (function->dev->sdioReg.cccr.sdioRev == SDIO_CCCR_SDIO_REV_1_00) ? + SDIO_CIS_TPL_FUNCE_FUNCTION_PC_MIN_SIZE : SDIO_CIS_TPL_FUNCE_FUNCTION_PC_MAX_SIZE; + if (tuple->tplLink < minSize) { + return; + } + + /* + * CISTPL_FUNCE(function): bit00[TPL_CODE(22h)], bit01[TPL_LINK], bit02[TPLFE_TYPE(01h)], + * bit0E-0F[TPLFE_MAX_BLK_SIZE], bit1E-1F[TPLFE_ENABLE_TIMEOUT_VAL]. + * bit2-->tplBody[0], bit0E-0F-->tplBody[12:13], bit1E-1F-->tplBody[28:29]. + */ + function->maxBlkSize = (tuple->tplBody[13] << BITS_PER_BYTE) | tuple->tplBody[12]; + + /* + * TPLFE_ENABLE_TIMEOUT_VAL is added in SDIO Rev 1.1. This 16-bit value indicates the function's required time-out + * value for coming ready after being enabled. This per-function value indicated the time a host should wait from + * asserting IOEx until expecting the card to indicate ready by asserting IORx. Different SDIO functions take + * different amounts of time to become raedy after being enabled due to different internal initialization + * requirements. The required time-out limit is in 10mS steps. If the card required no time-out, this field shall + * be set to 0000h. + */ + if (function->dev->sdioReg.cccr.sdioRev > SDIO_CCCR_SDIO_REV_1_00) { + function->timeOut = (tuple->tplBody[28] | (tuple->tplBody[29] << BITS_PER_BYTE)) * 10; + } else { + function->timeOut = SDIO_CIS_TPLFE_ENABLE_TIMEOUT_VAL_DEF; + } +} + +static void SdioDecodeCisTplFunce(struct MmcCntlr *cntlr, struct SdioFunction *function, + struct SdioCisTuple *tuple) +{ + /* + * The Function Extension Tuple provides standard information about the card(common) and each individual function. + * There shall be one CISTPL_FUNCE in each function's CIS. There are two versions of the CISTPL_FUNCE tuple, one + * for the common CIS(function 0) and a version uesd by the individual function's CIS(1-7). + */ + if (tuple->tplBody[0] == SDIO_CIS_TPL_FUNCE_COMMON) { + if (function != NULL) { + return; + } + SdioDecodeCisTplFunceCommon(cntlr, tuple); + return; + } + + if (tuple->tplBody[0] == SDIO_CIS_TPL_FUNCE_FUNCTION_PC) { + if (function == NULL) { + return; + } + SdioDecodeCisTplFunceFunction(function, tuple); + } +} + +static void SdioDecodeCisTplField(struct MmcCntlr *cntlr, struct SdioFunction *function, + struct SdioCisTuple *tuple) +{ + /* decode MANFID. */ + if (tuple->tplCode == SDIO_CIS_TPL_MANFID) { + SdioDecodeCisTplManfId(cntlr, function, tuple); + return; + } + /* decode FUNCE. */ + if (tuple->tplCode == SDIO_CIS_TPL_FUNCE) { + SdioDecodeCisTplFunce(cntlr, function, tuple); + } +} + +static int32_t SdioFillTplInfo(struct MmcCntlr *cntlr, struct SdioCisTuple *tuple, + uint32_t *addr, uint8_t tplCode, uint8_t tplLink) +{ + int32_t ret; + uint32_t i; + + tuple->tplCode = tplCode; + tuple->tplLink = tplLink; + /* read tuple body. */ + for (i = 0; i < tplLink; i++) { + (*addr)++; + ret = SdioReadCisTplField(cntlr, *addr, &(tuple->tplBody[i])); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioFillTplInfo: read tuple body fail, err = %d.", ret); + return ret; + } + } + return HDF_SUCCESS; +} + +static int32_t SdioDecodeCis(struct MmcCntlr *cntlr, struct SdioFunction *function, uint32_t cisStartAddr) +{ + int32_t ret = HDF_SUCCESS; + uint8_t tplCode, tplLink; + struct SdioCisTuple *tuple = NULL; + uint32_t addr = cisStartAddr; + + while (ret == HDF_SUCCESS) { + /* read TPL_CODE. */ + ret = SdioReadCisTplField(cntlr, addr, &tplCode); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioDecodeCis: read TPL_CODE fail, err = %d.", ret); + return ret; + } + if (tplCode == SDIO_CIS_TPL_END || tplCode == SDIO_CIS_TPL_NULL) { + return HDF_SUCCESS; + } + /* read TPL_LINK. */ + addr++; + ret = SdioReadCisTplField(cntlr, addr, &tplLink); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioDecodeCis: read TPL_LINK fail, err = %d.", ret); + return ret; + } + if (tplLink == SDIO_CIS_TPL_END) { + return HDF_SUCCESS; + } + /* If the link field is 0, the the tuple body is empty. */ + if (tplLink == SDIO_CIS_TPL_NULL) { + continue; + } + + tuple = (struct SdioCisTuple *)OsalMemCalloc(sizeof(*tuple) + tplLink); + if (tuple == NULL) { + HDF_LOGE("SdioDecodeCis: mem alloc fail!"); + return HDF_ERR_MALLOC_FAIL; + } + + ret = SdioFillTplInfo(cntlr, tuple, &addr, tplCode, tplLink); + if (ret != HDF_SUCCESS) { + OsalMemFree(tuple); + return ret; + } + /* decode. */ + SdioDecodeCisTplField(cntlr, function, tuple); + OsalMemFree(tuple); + tuple = NULL; + addr++; + } + return ret; +} + +static int32_t SdioReadCis(struct MmcCntlr *cntlr, struct SdioFunction *function) +{ + uint32_t funcNum, i, cisStartAddr; + uint8_t data; + int32_t ret; + struct SdioCmdParam param = {0}; + + if (function == NULL) { + funcNum = 0; + } else { + funcNum = function->funcNum; + } + + /* read CIS pointer. */ + cisStartAddr = 0; + for (i = 0; i < SDIO_CCCR_CIS_START_ADDR_BYTES; i++) { + /* read register 0xn09-0xn0B of FBR. */ + param.regAddr = SDIO_FBR_BASE_ADDR(funcNum) + SDIO_FBR_POINTER_CIS + i; + ret = SdioRwDirect(cntlr, ¶m, &data); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReadCis: read CIS pointer fail, err = %d.", ret); + return ret; + } + cisStartAddr |= data << (i * BITS_PER_BYTE); + } + return SdioDecodeCis(cntlr, function, cisStartAddr); +} + +static int32_t SdioReadFbr(struct MmcCntlr *cntlr, struct SdioFunction *func) +{ + struct SdioCmdParam param = {0}; + int32_t error; + uint8_t data; + + /* read register 0xn00 of FBR. */ + param.regAddr = SDIO_FBR_BASE_ADDR(func->funcNum) + SDIO_FBR_STD_FUNCTION_INTERFACE_CODE; + error = SdioRwDirect(cntlr, ¶m, &data); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdioReadFbr: read SFIC fail, err = %d.", error); + return error; + } + + /* bit3-bit0: Standard SDIO Function Interface Code. */ + data &= 0x0f; + if (data == SDIO_FBR_STD_SDIO_IF) { + /* read register 0xn01 of FBR. */ + param.regAddr = SDIO_FBR_BASE_ADDR(func->funcNum) + SDIO_FBR_EXT_STD_FUNCTION_INTERFACE_CODE; + error = SdioRwDirect(cntlr, ¶m, &data); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdioReadFbr: read ESFIC fail, err = %d.", error); + return error; + } + } + + func->funcClass = data; + return HDF_SUCCESS; +} + +int32_t SdioSetFbrIoBlockSize(struct MmcCntlr *cntlr, uint32_t blkSize) +{ + struct SdioCmdParam param = {0}; + struct SdioDevice *dev = NULL; + int32_t ret; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + dev = (struct SdioDevice *)cntlr->curDev; + if (dev == NULL || dev->curFunction == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + /* write register 0xn10 of FBR. */ + param.regAddr = SDIO_FBR_BASE_ADDR(dev->curFunction->funcNum) + SDIO_FBR_IO_BLOCK_SIZE; + param.writeflag = true; + /* + * Function 1-7 I/O Block Size. + * This 16-bit register sets the block size for I/O block operations for each function(1-7). + * The maximum block size is 2048 and the minimum is 1. At power-up or reset, this register + * shall be initially loaded with a value of 0. The host is responsible for setting the appropriate + * value for the block size supported by each function. This pointer is stored in little-endian format. + */ + param.writeData = (blkSize & 0xff); + ret = SdioRwDirect(cntlr, ¶m, NULL); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioSetFbrIoBlockSize: write I/O Block Size fail, err = %d.", ret); + return ret; + } + + /* write register 0xn11 of FBR. */ + param.regAddr++; + param.writeData = ((blkSize >> BITS_PER_BYTE) & 0xff); + return SdioRwDirect(cntlr, ¶m, NULL); +} + +static uint32_t SdioGetMaxClock(struct MmcCntlr *cntlr) +{ + uint32_t clock; + struct MmcDevice *mmcDev = cntlr->curDev; + struct SdioDevice *sdioDev = (struct SdioDevice *)mmcDev; + + if (mmcDev->state.bits.highSpeed == 1) { + /* The card operates in High-Speed timing mode with a clock rate up to 50MHz. */ + clock = 50000000; + } else { + clock = sdioDev->sdioReg.cis.maxDtr; + } + + if (mmcDev->type == MMC_DEV_COMBO) { + clock = (clock < SdGetMaxClock(cntlr)) ? clock : SdGetMaxClock(cntlr); + } + + if (clock > cntlr->freqMax) { + clock = cntlr->freqMax; + } + return clock; +} + +static struct SdioFunction *SdioAllocFunction(struct SdioDevice *sdioDev, uint32_t funcNum) +{ + struct SdioFunction *function = NULL; + + function = (struct SdioFunction *)OsalMemCalloc(sizeof(struct SdioFunction)); + if (function == NULL) { + HDF_LOGE("SdioAllocFunction: alloc fail!"); + return NULL; + } + function->dev = sdioDev; + function->funcNum = funcNum; + return function; +} + +static void SdioDeleteFunction(struct SdioFunction *function) +{ + if (function == NULL) { + return; + } + OsalMemFree(function); +} + +static int32_t SdioAddFunctions(struct MmcCntlr *cntlr, uint32_t funcs) +{ + struct SdioDevice *sdioDev = (struct SdioDevice *)cntlr->curDev; + struct SdioFunction *function = NULL; + uint32_t i; + int32_t ret; + + sdioDev->functions = 0; + for (i = 0; i < funcs; i++) { + function = SdioAllocFunction(sdioDev, i + 1); + if (function == NULL) { + return HDF_ERR_MALLOC_FAIL; + } + ret = SdioReadFbr(cntlr, function); + if (ret != HDF_SUCCESS) { + return ret; + } + ret = SdioReadCis(cntlr, function); + if (ret != HDF_SUCCESS) { + SdioDeleteFunction(function); + return ret; + } + + if (function->vendorId == 0) { + function->vendorId = sdioDev->sdioReg.cis.vendorId; + function->deviceId = sdioDev->sdioReg.cis.deviceId; + } + /* A value of zero is not valid and shall not be used. */ + if (function->maxBlkSize == 0) { + function->maxBlkSize = sdioDev->sdioReg.cis.blkSize; + } + + sdioDev->sdioFunc[i] = function; + (sdioDev->functions)++; + } + return HDF_SUCCESS; +} + +static void SdioDelete(struct SdioDevice *sdioDev) +{ + uint32_t i; + + for (i = 0; i < sdioDev->functions; i++) { + SdioDeleteFunction(sdioDev->sdioFunc[i]); + sdioDev->sdioFunc[i] = NULL; + } +} + +static int32_t SdioDeviceAdd(struct MmcCntlr *cntlr) +{ + /* add dev. */ + if (MmcDeviceAdd(cntlr->curDev) != HDF_SUCCESS) { + HDF_LOGE("SdioDeviceAdd: Add device fail!"); + return HDF_FAILURE; + } + cntlr->curDev->state.bits.present = 1; + return HDF_SUCCESS; +} + +static int32_t SdioSelect(struct MmcCntlr *cntlr, uint32_t *rocr) +{ + int32_t error; + + /* cmd5, set the support voltage. */ + error = SdioSendOpCond(cntlr, cntlr->curDev->reg.ocr.ocrData, rocr); + if (error != HDF_SUCCESS) { + HDF_LOGE("cmd5(set voltage) fail, err = %d.", error); + return error; + } + + /* Judge Combo Card. */ + if (((*rocr) & SDIO_R4_MEMORY_PRESENT) > 0) { + if (SdSelect(cntlr, rocr) == 0) { + cntlr->curDev->type = MMC_DEV_COMBO; + HDF_LOGD("combo dev!!"); + } + } + /* get RCA. */ + error = SdCmdSendRelativeAddr(cntlr, &(cntlr->curDev->reg.rca)); + if (error != HDF_SUCCESS) { + HDF_LOGE("cmd3(get RCA) fail, err = %d.", error); + return error; + } + if (cntlr->curDev->type == MMC_DEV_COMBO) { + /* get CSD, CMD9 should send in stby. */ + error = SdReadCsd(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("combo dev, read csd fail, err = %d.", error); + return error; + } + } + /* select card. */ + error = MmcSelectCard(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("cmd7(select card) fail, err = %d.", error); + return error; + } + + error = SdioReadCccr(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("read cccr fail, err = %d.", error); + return error; + } + /* read common CIS. */ + error = SdioReadCis(cntlr, NULL); + if (error != HDF_SUCCESS) { + HDF_LOGE("SdioInit: read cis fail, err = %d.", error); + return error; + } + if (cntlr->curDev->type == MMC_DEV_COMBO) { + error = SdReadRegisters(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("combo dev, read registers fail, err = %d.", error); + MmcGoIdleState(cntlr); + cntlr->curDev->type = MMC_DEV_SDIO; + } + } + return HDF_SUCCESS; +} + +static int32_t SdioInit(struct MmcCntlr *cntlr) +{ + int32_t error; + union MmcOcr ocr = {0}; + + /* cmd5, detect sdio dev and get the voltage range. */ + error = SdioSendOpCond(cntlr, 0, &(ocr.ocrData)); + if (error != HDF_SUCCESS) { + HDF_LOGE("cmd5(detect sdio) fail, err = %d", error); + return error; + } + + MmcCntlrSelectWorkVoltage(cntlr, &ocr); + if (cntlr->curDev->reg.ocr.ocrData == 0) { + HDF_LOGE("SdioInit: ocr is invalid!"); + return HDF_ERR_INVALID_PARAM; + } + + error = SdioSelect(cntlr, &(ocr.ocrData)); + if (error != HDF_SUCCESS) { + return error; + } + + error = SdioCdDisable(cntlr); + if (error != HDF_SUCCESS) { + HDF_LOGE("Cd disable fail, err = %d.", error); + return error; + } + error = SdioEnableHighSpeed(cntlr); + if (error == HDF_SUCCESS) { + cntlr->curDev->state.bits.highSpeed = 1; + MmcCntlrSetBusTiming(cntlr, BUS_TIMING_SD_HS); + } else if (error != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("Enable HS fail, err = %d.", error); + return error; + } + MmcCntlrSetClock(cntlr, SdioGetMaxClock(cntlr)); + + error = SdioEnable4BitBusWidth(cntlr); + if (error == HDF_SUCCESS) { + MmcCntlrSetBusWidth(cntlr, BUS_WIDTH4); + } else if (error != HDF_ERR_NOT_SUPPORT) { + HDF_LOGE("Enable 4-bits bus width fail, err = %d.", error); + return error; + } + /* R4, [30: 28] Number of I/O functions. */ + error = SdioAddFunctions(cntlr, ((ocr.ocrData >> 28) & SDIO_MAX_FUNCTION_NUMBER)); + if (error != HDF_SUCCESS) { + HDF_LOGE("Add functions fail, err = %d.", error); + return error; + } + + return SdioDeviceAdd(cntlr); +} + +int32_t SdioReinit(struct MmcCntlr *cntlr) +{ + union MmcOcr ocr = {0}; + int error; + + MmcGoIdleState(cntlr); + MmcCntlrSetClock(cntlr, cntlr->freqMin); + + /* cmd5, detect sdio dev and get the voltage range. */ + error = SdioSendOpCond(cntlr, 0, &(ocr.ocrData)); + if (error != HDF_SUCCESS) { + return error; + } + /* cmd5, set the support voltage. */ + error = SdioSendOpCond(cntlr, cntlr->curDev->reg.ocr.ocrData, &(ocr.ocrData)); + if (error != HDF_SUCCESS) { + return error; + } + /* get RCA. */ + error = SdCmdSendRelativeAddr(cntlr, &(cntlr->curDev->reg.rca)); + if (error != HDF_SUCCESS) { + return error; + } + /* select card. */ + error = MmcSelectCard(cntlr); + if (error != HDF_SUCCESS) { + return error; + } + error = SdioEnableHighSpeed(cntlr); + if (error != HDF_SUCCESS) { + return error; + } + MmcCntlrSetClock(cntlr, SdioGetMaxClock(cntlr)); + + error = SdioEnable4BitBusWidth(cntlr); + if (error == HDF_SUCCESS) { + MmcCntlrSetBusWidth(cntlr, BUS_WIDTH4); + } + return error; +} + +static int32_t SdioDetect(struct MmcCntlr *cntlr) +{ + int32_t err; + + HDF_LOGD("Detect sdio dev start..."); + /* + * After reset or power-up, all I/O functions on the card are disabled and the I/O portion of the card shall + * not execute any operation except CMD5 or CMD0. If there is SD memory on the card, that memory shall + * respond normally to all mandatory memory commands. + */ + SdioIoReset(cntlr); + MmcGoIdleState(cntlr); + /* Initialize SDIO. */ + err = SdioInit(cntlr); + if (err == HDF_SUCCESS) { + HDF_LOGD("Detect sdio dev success! %s dev at address 0x%x!", + cntlr->curDev->state.bits.highSpeed ? "High speed" : "", cntlr->curDev->reg.rca); + return HDF_SUCCESS; + } + SdioDelete((struct SdioDevice *)cntlr->curDev); + return err; +} + +void MmcDeleteDev(struct MmcCntlr *cntlr) +{ + if (cntlr == NULL || cntlr->curDev == NULL) { + return; + } + + if (cntlr->curDev->state.bits.present == 0) { + return; + } + cntlr->curDev->state.bits.present = 0; + + if (cntlr->curDev->type == MMC_DEV_SDIO || cntlr->curDev->type == MMC_DEV_COMBO) { + SdioDelete((struct SdioDevice *)cntlr->curDev); + } + + MmcDeviceRemove(cntlr->curDev); + MmcCntlrFreeDev(cntlr); +} + +int32_t MmcDoDetect(struct MmcCntlr *cntlr) +{ + int32_t error; + enum MmcDevType devType; + + if (cntlr == NULL) { + return HDF_ERR_INVALID_PARAM; + } + devType = (enum MmcDevType)cntlr->devType; + if (devType >= MMC_DEV_COMBO) { + return HDF_ERR_INVALID_PARAM; + } + + if (MmcCntlrAllocDev(cntlr, devType) != HDF_SUCCESS) { + return HDF_ERR_INVALID_PARAM; + } + + MmcCntlrPowerUp(cntlr); + if (devType == MMC_DEV_SDIO) { + error = SdioDetect(cntlr); + } else if (devType == MMC_DEV_SD) { + error = SdDetect(cntlr); + } else { + error = EmmcDetect(cntlr); + } + + if (error == HDF_SUCCESS) { + HDF_LOGD("MmcDoDetect: host%d detect succ!", cntlr->index); + return error; + } + + HDF_LOGD("MmcDoDetect: host%d detect fail!", cntlr->index); + MmcCntlrFreeDev(cntlr); + MmcCntlrPowerOff(cntlr); + return error; +} diff --git a/support/platform/src/mmc/mmc_sdio.c b/support/platform/src/mmc/mmc_sdio.c new file mode 100644 index 0000000000000000000000000000000000000000..9c39e7f5cd51b9410a92d5449d28e4ebef78423f --- /dev/null +++ b/support/platform/src/mmc/mmc_sdio.c @@ -0,0 +1,757 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "mmc_sdio.h" + +#define HDF_LOG_TAG mmc_sdio_c +#define SDIO_READ_IO_READY_RETRY_TIMES 10000 + +static int32_t SdioDeviceDefaultIncrAddrReadBytes(struct SdioDevice *dev, + uint8_t *data, uint32_t addr, uint32_t size) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + struct SdioRwBlockInfo info = {0}; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultIncrAddrReadBytes fail, func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (data == NULL) { + HDF_LOGE("SdioDeviceDefaultIncrAddrReadBytes fail, data is null."); + return HDF_ERR_INVALID_PARAM; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultIncrAddrReadBytes fail, cntlr is null."); + return HDF_ERR_INVALID_OBJECT; + } + + if (size == 1) { + return SdioReadWriteByte(cntlr, false, func->funcNum, addr, data); + } else if (size > 1) { + info.addr = addr; + info.buf = data; + info.incrAddrFlag = true; + info.size = size; + info.writeFlag = false; + return SdioReadWriteBlock(cntlr, &info); + } + HDF_LOGE("SdioDeviceDefaultIncrAddrReadBytes fail, data size is 0."); + return HDF_ERR_INVALID_PARAM; +} + +static int32_t SdioDeviceDefaultIncrAddrWriteBytes(struct SdioDevice *dev, + uint8_t *data, uint32_t addr, uint32_t size) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + struct SdioRwBlockInfo info = {0}; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultIncrAddrWriteBytes fail, func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (data == NULL) { + HDF_LOGE("SdioDeviceDefaultIncrAddrWriteBytes fail, data is null."); + return HDF_ERR_INVALID_PARAM; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultIncrAddrWriteBytes fail, cntlr is null."); + return HDF_ERR_INVALID_OBJECT; + } + + if (size == 1) { + return SdioReadWriteByte(cntlr, true, func->funcNum, addr, data); + } else if (size > 1) { + info.addr = addr; + info.buf = data; + info.incrAddrFlag = true; + info.size = size; + info.writeFlag = true; + return SdioReadWriteBlock(cntlr, &info); + } + HDF_LOGE("SdioDeviceDefaultIncrAddrWriteBytes fail, data size is 0."); + return HDF_ERR_INVALID_PARAM; +} + +static int32_t SdioDeviceDefaultFixedAddrReadBytes(struct SdioDevice *dev, + uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + struct SdioRwBlockInfo info = {0}; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultFixedAddrReadBytes fail, func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (data == NULL || size == 0) { + HDF_LOGE("SdioDeviceDefaultFixedAddrReadBytes fail, param is invalid."); + return HDF_ERR_INVALID_PARAM; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultFixedAddrReadBytes fail, cntlr is null."); + return HDF_ERR_INVALID_OBJECT; + } + + info.addr = addr; + info.buf = data; + info.incrAddrFlag = false; + info.size = size; + info.writeFlag = false; + if (scatterLen > 0) { + info.scatterFlag = true; + info.scatterLen = scatterLen; + } + return SdioReadWriteBlock(cntlr, &info); +} + +static int32_t SdioDeviceDefaultFixedAddrWriteBytes(struct SdioDevice *dev, + uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + struct SdioRwBlockInfo info = {0}; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultFixedAddrWriteBytes fail, func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (data == NULL || size == 0) { + HDF_LOGE("SdioDeviceDefaultFixedAddrWriteBytes fail, param is invalid."); + return HDF_ERR_INVALID_PARAM; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultFixedAddrWriteBytes fail, cntlr is null."); + return HDF_ERR_INVALID_OBJECT; + } + + info.addr = addr; + info.buf = data; + info.incrAddrFlag = false; + info.size = size; + info.writeFlag = true; + if (scatterLen > 0) { + info.scatterFlag = true; + info.scatterLen = scatterLen; + } + return SdioReadWriteBlock(cntlr, &info); +} + +static int32_t SdioDeviceDefaultFunc0ReadBytes(struct SdioDevice *dev, + uint8_t *data, uint32_t addr, uint32_t size) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + uint32_t i; + int32_t ret; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultFunc0ReadBytes fail, func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (data == NULL) { + HDF_LOGE("SdioDeviceDefaultFunc0ReadBytes fail, data is null."); + return HDF_ERR_INVALID_PARAM; + } + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultFunc0ReadBytes fail, cntlr is null."); + return HDF_ERR_INVALID_OBJECT; + } + + for (i = 0; i < size; i++) { + ret = SdioReadWriteByte(cntlr, false, 0, (addr + i), &data[i]); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioDeviceDefaultFunc0ReadBytes fail, i = %d.", i); + return ret; + } + } + return HDF_SUCCESS; +} + +static int32_t SdioDeviceDefaultFunc0WriteBytes(struct SdioDevice *dev, + uint8_t *data, uint32_t addr, uint32_t size) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + int32_t ret; + uint32_t i; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultFunc0WriteBytes fail, func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (data == NULL) { + HDF_LOGE("SdioDeviceDefaultFunc0WriteBytes fail, data is null."); + return HDF_ERR_INVALID_PARAM; + } + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultFunc0WriteBytes fail, cntlr is null."); + return HDF_ERR_INVALID_OBJECT; + } + + for (i = 0; i < size; i++) { + ret = SdioReadWriteByte(cntlr, true, 0, (addr + i), &data[i]); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioDeviceDefaultFunc0WriteBytes fail, i = %d.", i); + return ret; + } + } + return HDF_SUCCESS; +} + +static int32_t SdioDeviceDefaultSetBlockSize(struct SdioDevice *dev, uint32_t blkSize) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + uint32_t blockSize = blkSize; + int32_t ret; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultSetBlockSize: func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + cntlr = dev->sd.mmc.cntlr; + if (blockSize == 0) { + blockSize = ((func->maxBlkSize < cntlr->maxBlkSize) ? func->maxBlkSize : cntlr->maxBlkSize); + } + blockSize = MMC_MIN(blockSize, MMC_SEC_SIZE); + + ret = SdioSetFbrIoBlockSize(cntlr, blockSize); + if (ret == HDF_SUCCESS) { + func->curBlkSize = blockSize; + } + return ret; +} + +static int32_t SdioDeviceDefaultGetCommonInfo(struct SdioDevice *dev, + SdioCommonInfo *info, SdioCommonInfoType infoType) +{ + struct MmcCntlr *cntlr = NULL; + struct SdioFunction *func = dev->curFunction; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultGetCommonInfo: func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (info == NULL) { + HDF_LOGE("SdioDeviceDefaultGetCommonInfo: info is null."); + return HDF_ERR_INVALID_PARAM; + } + if (infoType != SDIO_FUNC_INFO) { + HDF_LOGE("SdioDeviceDefaultGetCommonInfo: cur type %d is not support.", infoType); + return HDF_ERR_NOT_SUPPORT; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultGetCommonInfo fail, cntlr is null."); + return HDF_ERR_INVALID_PARAM; + } + info->funcInfo.enTimeout = func->timeOut; + info->funcInfo.maxBlockNum = cntlr->maxBlkNum; + info->funcInfo.maxBlockSize = cntlr->maxBlkSize; + info->funcInfo.maxRequestSize = cntlr->maxReqSize; + info->funcInfo.funcNum = func->funcNum; + info->funcInfo.irqCap = cntlr->caps.bits.sdioIrq; + info->funcInfo.data = (void *)func; + return HDF_SUCCESS; +} + +static int32_t SdioDeviceDefaultSetCommonInfo(struct SdioDevice *dev, + SdioCommonInfo *info, SdioCommonInfoType infoType) +{ + struct MmcCntlr *cntlr = NULL; + struct SdioFunction *func = dev->curFunction; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultSetCommonInfo: func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (info == NULL) { + HDF_LOGE("SdioDeviceDefaultSetCommonInfo: info is null."); + return HDF_ERR_INVALID_PARAM; + } + if (infoType != SDIO_FUNC_INFO) { + HDF_LOGE("SdioDeviceDefaultSetCommonInfo: cur type %d is not support.", infoType); + return HDF_ERR_NOT_SUPPORT; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultSetCommonInfo fail, cntlr is null."); + return HDF_ERR_INVALID_PARAM; + } + + func->timeOut = info->funcInfo.enTimeout; + cntlr->maxBlkNum = info->funcInfo.maxBlockNum; + cntlr->maxBlkSize = info->funcInfo.maxBlockSize; + cntlr->maxReqSize = info->funcInfo.maxRequestSize; + func->funcNum = info->funcInfo.funcNum; + return HDF_SUCCESS; +} + +static int32_t SdioDeviceDefaultFlushData(struct SdioDevice *dev) +{ + struct MmcCntlr *cntlr = NULL; + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultFlushData fail, cntlr is null."); + return HDF_ERR_INVALID_OBJECT; + } + return SdioReinit(cntlr); +} + +static int32_t SdioDeviceDefaultEnableFunc(struct SdioDevice *dev) +{ + struct SdioFunction *func = dev->curFunction; + int32_t ret; + uint32_t i; + uint8_t val; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultEnableFunc: func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + ret = SdioCccrIoEnable(dev->sd.mmc.cntlr); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioDeviceDefaultEnableFunc: Io Enable fail."); + return ret; + } + + for (i = 0; i < SDIO_READ_IO_READY_RETRY_TIMES; i++) { + ret = SdioReadCccrIoReady(dev->sd.mmc.cntlr, &val); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioDeviceDefaultEnableFunc: read Io Ready fail."); + return ret; + } + if ((val & (1 << func->funcNum)) > 0) { + return HDF_SUCCESS; + } + } + + HDF_LOGE("SdioDeviceDefaultEnableFunc: Io not Ready."); + return HDF_ERR_TIMEOUT; +} + +static int32_t SdioDeviceDefaultDisableFunc(struct SdioDevice *dev) +{ + struct SdioFunction *func = dev->curFunction; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultDisableFunc: func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + return SdioCccrIoDisable(dev->sd.mmc.cntlr); +} + +static int32_t SdioDeviceDefaultClaimIrq(struct SdioDevice *dev, SdioIrqHandler *irqHandler) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + int32_t ret; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultClaimIrq: func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (func->irqHandler != NULL) { + HDF_LOGE("SdioDeviceDefaultClaimIrq: irq has been registered."); + return HDF_ERR_DEVICE_BUSY; + } + if (irqHandler == NULL) { + HDF_LOGE("SdioDeviceDefaultClaimIrq: irqHandler is NULL."); + return HDF_ERR_INVALID_PARAM; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultClaimIrq: cntlr is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + ret = SdioCccrIntEnable(cntlr); + if (ret != HDF_SUCCESS) { + return ret; + } + + func->irqHandler = irqHandler; + if (cntlr->caps.bits.sdioIrq > 0) { + ret = MmcCntlrCreatSdioIrqThread(cntlr); + } + return ret; +} + +static int32_t SdioDeviceDefaultReleaseIrq(struct SdioDevice *dev) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultReleaseIrq: func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultReleaseIrq: cntlr is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + if (func->irqHandler != NULL) { + func->irqHandler = NULL; + MmcCntlrDestroySdioIrqThread(cntlr); + } + if (cntlr->caps.bits.sdioIrq > 0 && cntlr->ops != NULL && cntlr->ops->setSdioIrq != NULL) { + (void)cntlr->ops->setSdioIrq(cntlr, false); + } + return SdioCccrIntDisable(cntlr); +} + +static int32_t SdioDeviceDefaultFindFunc(struct SdioDevice *dev, struct SdioFunctionConfig *cfg) +{ + uint32_t i; + struct SdioFunction *func = NULL; + + if (dev->functions > SDIO_MAX_FUNCTION_NUMBER) { + HDF_LOGE("SdioDeviceDefaultFindFunc: functions = %d, error!", dev->functions); + return HDF_ERR_INVALID_PARAM; + } + + for (i = 0; i < dev->functions; i++) { + func = dev->sdioFunc[i]; + if (func == NULL) { + continue; + } + if (cfg->deviceId == func->deviceId && + cfg->vendorId == func->vendorId && + cfg->funcNr == func->funcNum) { + dev->curFunction = func; + return HDF_SUCCESS; + } + } + + HDF_LOGE("SdioDeviceDefaultFindFunc: find func fail!"); + return HDF_FAILURE; +} + +static int32_t SdioDeviceDefaultClaimHost(struct SdioDevice *dev) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultClaimHost: func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultClaimHost: cntlr is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + MmcCntlrLock(cntlr); + return HDF_SUCCESS; +} + +static int32_t SdioDeviceDefaultReleaseHost(struct SdioDevice *dev) +{ + struct SdioFunction *func = dev->curFunction; + struct MmcCntlr *cntlr = NULL; + + if (func == NULL) { + HDF_LOGE("SdioDeviceDefaultReleaseHost: func is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + cntlr = dev->sd.mmc.cntlr; + if (cntlr == NULL) { + HDF_LOGE("SdioDeviceDefaultReleaseHost: cntlr is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + MmcCntlrUnlock(cntlr); + return HDF_SUCCESS; +} + +static struct SdioDeviceOps g_sdioDefaultOps = { + .incrAddrReadBytes = SdioDeviceDefaultIncrAddrReadBytes, + .incrAddrWriteBytes = SdioDeviceDefaultIncrAddrWriteBytes, + .fixedAddrReadBytes = SdioDeviceDefaultFixedAddrReadBytes, + .fixedAddrWriteBytes = SdioDeviceDefaultFixedAddrWriteBytes, + .func0ReadBytes = SdioDeviceDefaultFunc0ReadBytes, + .func0WriteBytes = SdioDeviceDefaultFunc0WriteBytes, + .setBlockSize = SdioDeviceDefaultSetBlockSize, + .getCommonInfo = SdioDeviceDefaultGetCommonInfo, + .setCommonInfo = SdioDeviceDefaultSetCommonInfo, + .flushData = SdioDeviceDefaultFlushData, + .enableFunc = SdioDeviceDefaultEnableFunc, + .disableFunc = SdioDeviceDefaultDisableFunc, + .claimIrq = SdioDeviceDefaultClaimIrq, + .releaseIrq = SdioDeviceDefaultReleaseIrq, + .findFunc = SdioDeviceDefaultFindFunc, + .claimHost = SdioDeviceDefaultClaimHost, + .releaseHost = SdioDeviceDefaultReleaseHost, +}; + +int32_t SdioDeviceFindFunction(struct SdioDevice *sdio, struct SdioFunctionConfig *config) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceFindFunction: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->findFunc == NULL) { + HDF_LOGE("SdioDeviceFindFunction: func is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->findFunc(sdio, config); +} + +int32_t SdioDeviceIncrAddrReadBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceIncrAddrReadBytes: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->incrAddrReadBytes == NULL) { + HDF_LOGE("SdioDeviceIncrAddrReadBytes: incrAddrReadBytes is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->incrAddrReadBytes(sdio, data, addr, size); +} + +int32_t SdioDeviceIncrAddrWriteBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceIncrAddrWriteBytes: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->incrAddrWriteBytes == NULL) { + HDF_LOGE("SdioDeviceIncrAddrWriteBytes: incrAddrWriteBytes is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->incrAddrWriteBytes(sdio, data, addr, size); +} + +int32_t SdioDeviceFixedAddrReadBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceFixedAddrReadBytes: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->fixedAddrReadBytes == NULL) { + HDF_LOGE("SdioDeviceFixedAddrReadBytes: fixedAddrReadBytes is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->fixedAddrReadBytes(sdio, data, addr, size, scatterLen); +} + +int32_t SdioDeviceFixedAddrWriteBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size, uint32_t scatterLen) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceFixedAddrWriteBytes: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->fixedAddrWriteBytes == NULL) { + HDF_LOGE("SdioDeviceFixedAddrWriteBytes: fixedAddrWriteBytes is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->fixedAddrWriteBytes(sdio, data, addr, size, scatterLen); +} + +int32_t SdioDeviceFunc0ReadBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceFunc0ReadBytes: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->func0ReadBytes == NULL) { + HDF_LOGE("SdioDeviceFunc0ReadBytes: func0ReadBytes is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->func0ReadBytes(sdio, data, addr, size); +} + +int32_t SdioDeviceFunc0WriteBytes(struct SdioDevice *sdio, + uint8_t *data, uint32_t addr, uint32_t size) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceFunc0WriteBytes: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->func0WriteBytes == NULL) { + HDF_LOGE("SdioDeviceFunc0WriteBytes: func0WriteBytes is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->func0WriteBytes(sdio, data, addr, size); +} + +int32_t SdioDeviceSetBlockSize(struct SdioDevice *sdio, uint32_t blockSize) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceSetBlockSize: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->setBlockSize == NULL) { + HDF_LOGE("SdioDeviceSetBlockSize: setBlockSize is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->setBlockSize(sdio, blockSize); +} + +int32_t SdioDeviceGetCommonInfo(struct SdioDevice *sdio, + SdioCommonInfo *info, SdioCommonInfoType infoType) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceGetCommonInfo: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->getCommonInfo == NULL) { + HDF_LOGE("SdioDeviceGetCommonInfo: getCommonInfo is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->getCommonInfo(sdio, info, infoType); +} + +int32_t SdioDeviceSetCommonInfo(struct SdioDevice *sdio, + SdioCommonInfo *info, SdioCommonInfoType infoType) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceSetCommonInfo: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->setCommonInfo == NULL) { + HDF_LOGE("SdioDeviceSetCommonInfo: setCommonInfo is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->setCommonInfo(sdio, info, infoType); +} + +int32_t SdioDeviceFlushData(struct SdioDevice *sdio) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceFlushData: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->flushData == NULL) { + HDF_LOGE("SdioDeviceFlushData: flushData is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->flushData(sdio); +} + +int32_t SdioDeviceClaimHost(struct SdioDevice *sdio) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceClaimHost: cntlr is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + if (sdio->sdioOps->claimHost != NULL) { + return sdio->sdioOps->claimHost(sdio); + } + return HDF_SUCCESS; +} + +int32_t SdioDeviceReleaseHost(struct SdioDevice *sdio) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceReleaseHost: cntlr is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + + if (sdio->sdioOps->releaseHost != NULL) { + return sdio->sdioOps->releaseHost(sdio); + } + return HDF_SUCCESS; +} + +int32_t SdioDeviceEnableFunc(struct SdioDevice *sdio) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceEnableFunc: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->enableFunc == NULL) { + HDF_LOGE("SdioDeviceEnableFunc: enableFunc is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->enableFunc(sdio); +} + +int32_t SdioDeviceDisableFunc(struct SdioDevice *sdio) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceDisableFunc: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->disableFunc == NULL) { + HDF_LOGE("SdioDeviceDisableFunc: disableFunc is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->disableFunc(sdio); +} + +int32_t SdioDeviceClaimIrq(struct SdioDevice *sdio, SdioIrqHandler *irqHandler) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceClaimIrq: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->claimIrq == NULL) { + HDF_LOGE("SdioDeviceClaimIrq: claimIrq is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->claimIrq(sdio, irqHandler); +} + +int32_t SdioDeviceReleaseIrq(struct SdioDevice *sdio) +{ + if (sdio->sdioOps == NULL) { + HDF_LOGE("SdioDeviceReleaseIrq: ops is NULL."); + return HDF_ERR_INVALID_OBJECT; + } + if (sdio->sdioOps->releaseIrq == NULL) { + HDF_LOGE("SdioDeviceReleaseIrq: releaseIrq is NULL."); + return HDF_FAILURE; + } + return sdio->sdioOps->releaseIrq(sdio); +} + +void SdioDeviceAddOps(struct SdioDevice *sdio, void *ops) +{ + if (sdio == NULL) { + HDF_LOGE("SdioDeviceAddOps: sdio is NULL."); + return; + } + if (ops == NULL) { + sdio->sdioOps = &g_sdioDefaultOps; + HDF_LOGD("SdioDeviceAddOps: use default ops."); + } else { + sdio->sdioOps = (struct SdioDeviceOps *)ops; + } +} diff --git a/support/platform/src/mmc/sdio_if.c b/support/platform/src/mmc/sdio_if.c new file mode 100644 index 0000000000000000000000000000000000000000..cd05ef458d5aa6238f7afe5320b57aa4f962db67 --- /dev/null +++ b/support/platform/src/mmc/sdio_if.c @@ -0,0 +1,336 @@ +/* + * Copyright (c) 2020-2021 Huawei Device Co., Ltd. + * + * HDF is dual licensed: you can use it either under the terms of + * the GPL, or the BSD license, at your option. + * See the LICENSE file in the root of this repository for complete details. + */ + +#include "sdio_if.h" +#include "hdf_base.h" +#include "hdf_log.h" +#include "mmc_sdio.h" +#include "osal_mem.h" +#include "securec.h" + +#define HDF_LOG_TAG sdio_if_c + +static int32_t SdioDeviceGetFromHandle(DevHandle handle, struct SdioDevice **sdio) +{ + struct MmcDevice *mmc = NULL; + + if (handle == NULL) { + return HDF_ERR_INVALID_OBJECT; + } + + if (sdio == NULL) { + return HDF_ERR_INVALID_PARAM; + } + + mmc = MmcCntlrGetDevice((struct MmcCntlr *)handle); + if (mmc == NULL) { + return HDF_PLT_ERR_NO_DEV; + } + if (mmc->type != MMC_DEV_SDIO && mmc->type != MMC_DEV_COMBO) { + MmcDevicePut(mmc); + return HDF_PLT_ERR_DEV_TYPE; + } + + *sdio = (struct SdioDevice *)mmc; + return HDF_SUCCESS; +} + +DevHandle SdioOpen(int16_t mmcBusNum, struct SdioFunctionConfig *config) +{ + int32_t ret; + struct MmcCntlr *cntlr = NULL; + struct SdioDevice *sdio = NULL; + DevHandle handle = NULL; + + if (config == NULL) { + HDF_LOGE("SdioOpen: config can't be null!"); + return NULL; + } + + handle = MmcOpen(mmcBusNum); + if (handle == NULL) { + HDF_LOGE("SdioOpen: SdioGetCntlrByBusNum fail!"); + return NULL; + } + cntlr = (struct MmcCntlr *)handle; + if (cntlr != NULL && cntlr->ops != NULL && cntlr->ops->rescanSdioDev != NULL) { + ret = cntlr->ops->rescanSdioDev(cntlr); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioOpen: sdio rescan fail!"); + MmcClose(handle); + return NULL; + } + } + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioOpen: get sdio dev fail!"); + MmcClose(handle); + return NULL; + } + ret = SdioDeviceFindFunction(sdio, config); + MmcDevicePut((struct MmcDevice *)sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioOpen: set function fail!"); + MmcClose(handle); + return NULL; + } + + return (DevHandle)cntlr; +} + +void SdioClose(DevHandle handle) +{ + (void)handle; +} + +int32_t SdioReadBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReadBytes: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceIncrAddrReadBytes(sdio, data, addr, size); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioWriteBytes(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioWriteBytes: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceIncrAddrWriteBytes(sdio, data, addr, size); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioReadBytesFromFixedAddr(DevHandle handle, uint8_t *data, + uint32_t addr, uint32_t size, uint32_t scatterLen) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReadBytesFromFixedAddr: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceFixedAddrReadBytes(sdio, data, addr, size, scatterLen); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioWriteBytesToFixedAddr(DevHandle handle, uint8_t *data, + uint32_t addr, uint32_t size, uint32_t scatterLen) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioWriteBytesToFixedAddr: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceFixedAddrWriteBytes(sdio, data, addr, size, scatterLen); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioReadBytesFromFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReadBytesFromFunc0: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceFunc0ReadBytes(sdio, data, addr, size); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioWriteBytesToFunc0(DevHandle handle, uint8_t *data, uint32_t addr, uint32_t size) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioWriteBytesToFunc0: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceFunc0WriteBytes(sdio, data, addr, size); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioSetBlockSize(DevHandle handle, uint32_t blockSize) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioSetBlockSize: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceSetBlockSize(sdio, blockSize); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioGetCommonInfo(DevHandle handle, SdioCommonInfo *info, SdioCommonInfoType infoType) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioGetCommonInfo: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceGetCommonInfo(sdio, info, infoType); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioSetCommonInfo(DevHandle handle, SdioCommonInfo *info, SdioCommonInfoType infoType) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioSetCommonInfo: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceSetCommonInfo(sdio, info, infoType); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioFlushData(DevHandle handle) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioFlushData: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceFlushData(sdio); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +void SdioClaimHost(DevHandle handle) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioClaimHost: get sdio dev fail!"); + return; + } + ret = SdioDeviceClaimHost(sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioClaimHost: claim host fail!"); + } + MmcDevicePut((struct MmcDevice *)sdio); +} + +void SdioReleaseHost(DevHandle handle) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReleaseHost: get sdio dev fail!"); + return; + } + ret = SdioDeviceReleaseHost(sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReleaseHost: claim host fail!"); + } + MmcDevicePut((struct MmcDevice *)sdio); +} + +int32_t SdioEnableFunc(DevHandle handle) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioEnableFunc: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceEnableFunc(sdio); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioDisableFunc(DevHandle handle) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioDisableFunc: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceDisableFunc(sdio); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioClaimIrq(DevHandle handle, SdioIrqHandler *irqHandler) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioClaimIrq: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceClaimIrq(sdio, irqHandler); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +} + +int32_t SdioReleaseIrq(DevHandle handle) +{ + int32_t ret; + struct SdioDevice *sdio = NULL; + + ret = SdioDeviceGetFromHandle(handle, &sdio); + if (ret != HDF_SUCCESS) { + HDF_LOGE("SdioReleaseIrq: get sdio dev fail!"); + return ret; + } + ret = SdioDeviceReleaseIrq(sdio); + MmcDevicePut((struct MmcDevice *)sdio); + return ret; +}