diff options
| author | Linux Build Service Account <lnxbuild@localhost> | 2019-09-03 14:02:19 -0700 |
|---|---|---|
| committer | Gerrit - the friendly Code Review server <code-review@localhost> | 2019-09-03 14:02:19 -0700 |
| commit | 81a9e55e0863fb3e14be5e6b996e5c0bb795bdfb (patch) | |
| tree | 876fb15a9c254462f71c9dbde3803ad4f4b9bf5e /drivers/platform/msm | |
| parent | a7499d6acea5c581b1f56fc663010d3b90a3c9d8 (diff) | |
| parent | f9d70693889be5024d0e423e5924018300b902af (diff) | |
Merge "diag: Add diag over sdio support"
Diffstat (limited to 'drivers/platform/msm')
| -rw-r--r-- | drivers/platform/msm/qcn/Kconfig | 8 | ||||
| -rw-r--r-- | drivers/platform/msm/qcn/Makefile | 1 | ||||
| -rw-r--r-- | drivers/platform/msm/qcn/qcn_sdio.c | 1122 | ||||
| -rw-r--r-- | drivers/platform/msm/qcn/qcn_sdio.h | 175 | ||||
| -rw-r--r-- | drivers/platform/msm/qcn/qcn_sdio_hwio.h | 296 |
5 files changed, 1602 insertions, 0 deletions
diff --git a/drivers/platform/msm/qcn/Kconfig b/drivers/platform/msm/qcn/Kconfig new file mode 100644 index 000000000000..50f798fa1645 --- /dev/null +++ b/drivers/platform/msm/qcn/Kconfig @@ -0,0 +1,8 @@ +config SDIO_QCN + tristate "Qualcomm Technologies, Inc SDIO Function 1 Driver" + depends on MMC + default n + help + This module adds the support for SDIO based Wi-Fi devices with QCN7605 + chipsets. This module interfaces the diag, IPC router, Qsahara and + WLAN driver with the external sdio based soc. diff --git a/drivers/platform/msm/qcn/Makefile b/drivers/platform/msm/qcn/Makefile new file mode 100644 index 000000000000..0e57bd91e799 --- /dev/null +++ b/drivers/platform/msm/qcn/Makefile @@ -0,0 +1 @@ +obj-$(CONFIG_SDIO_QCN) += qcn_sdio.o diff --git a/drivers/platform/msm/qcn/qcn_sdio.c b/drivers/platform/msm/qcn/qcn_sdio.c new file mode 100644 index 000000000000..71787acd6940 --- /dev/null +++ b/drivers/platform/msm/qcn/qcn_sdio.c @@ -0,0 +1,1122 @@ +/* Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#include <linux/module.h> +#include <linux/kernel.h> +#include <linux/slab.h> +#include <linux/mmc/card.h> +#include <linux/mmc/mmc.h> +#include <linux/mmc/host.h> +#include <linux/mmc/sdio_func.h> +#include <linux/mmc/sdio_ids.h> +#include <linux/mmc/sdio.h> +#include <linux/mmc/sd.h> +#include <linux/delay.h> +#include <linux/platform_device.h> +#include "qcn_sdio.h" + +static bool tx_dump; +module_param(tx_dump, bool, S_IRUGO | S_IWUSR | S_IWGRP); + +static bool rx_dump; +module_param(rx_dump, bool, S_IRUGO | S_IWUSR | S_IWGRP); + +static int dump_len = 32; +module_param(dump_len, int, S_IRUGO | S_IWUSR | S_IWGRP); + +#define HEX_DUMP(mode, buf, len) \ + print_hex_dump(KERN_ERR, mode, 2, 32, 4, buf, \ + dump_len > len ? len : dump_len, 0) + +struct qcn_sdio { + enum qcn_sdio_sw_mode curr_sw_mode; + struct sdio_func *func; + const struct sdio_device_id *id; + struct qcn_sdio_ch_info *ch[QCN_SDIO_CH_MAX]; + atomic_t ch_status[QCN_SDIO_CH_MAX]; + spinlock_t lock_free_q; + spinlock_t lock_wait_q; + u32 rx_addr_base; + u32 tx_addr_base; + u8 rx_cnum_base; + u8 tx_cnum_base; + struct qcn_sdio_rw_info rw_req_info[QCN_SDIO_RW_REQ_MAX]; + struct list_head rw_free_q; + struct list_head rw_wait_q; + atomic_t free_list_count; + atomic_t wait_list_count; + struct workqueue_struct *qcn_sdio_wq; + struct work_struct sdio_rw_w; +}; + +static struct qcn_sdio *sdio_ctxt; +struct completion client_probe_complete; +static struct mutex lock; +static struct list_head cinfo_head; +static atomic_t status; +static spinlock_t async_lock; + +#if (QCN_SDIO_META_VER_0) +#define META_INFO(event, data) \ + ((u32)((u32)data << QCN_SDIO_HMETA_DATA_SHFT) | \ + (u32)(((u32)event << QCN_SDIO_HMETA_EVENT_SHFT) & \ + QCN_SDIO_HMETA_EVENT_BMSK) | (u32)(((u32)(sdio_ctxt->curr_sw_mode)\ + << QCN_SDIO_HMETA_SW_SHFT) & QCN_SDIO_HMETA_SW_BMSK) | \ + (u32)(QCN_SDIO_HMETA_FMT_VER & QCN_SDIO_HMETA_VER_BMSK)) +#elif (QCN_SDIO_META_VER_1) +#define META_INFO(even, data) \ + ((u32)(((u32)event << QCN_SDIO_HMETA_EVENT_SHFT) & \ + QCN_SDIO_HMETA_EVENT_BMSK) | (u32)(((u32)data << \ + QCN_SDIO_HMETA_DATA_SHFT) & QCN_SDIO_HMETA_DATA_BMSK)) +#endif + +#define SDIO_RW_OFFSET 31 +#define SDIO_RW_MASK 1 +#define SDIO_FUNCTION_OFFSET 28 +#define SDIO_FUNCTION_MASK 7 +#define SDIO_MODE_OFFSET 27 +#define SDIO_MODE_MASK 1 +#define SDIO_OPCODE_OFFSET 26 +#define SDIO_OPCODE_MASK 1 +#define SDIO_ADDRESS_OFFSET 9 +#define SDIO_ADDRESS_MASK 0x1FFFF +#define SDIO_RAW_OFFSET 27 +#define SDIO_RAW_MASK 1 +#define SDIO_STUFF_OFFSET1 26 +#define SDIO_STUFF_OFFSET2 8 +#define SDIO_STUFF_MASK 1 +#define SDIO_BLOCKSZ_MASK 0x1FF +#define SDIO_DATA_MASK 0xFF + +static inline +void qcn_sdio_set_cmd53_arg(u32 *arg, u8 rw, u8 func, u8 mode, u8 opcode, + u32 addr, u16 blksz) +{ + *arg = (((rw & SDIO_RW_MASK) << SDIO_RW_OFFSET) | + ((func & SDIO_FUNCTION_MASK) << SDIO_FUNCTION_OFFSET) | + ((mode & SDIO_MODE_MASK) << SDIO_MODE_OFFSET) | + ((opcode & SDIO_OPCODE_MASK) << SDIO_OPCODE_OFFSET) | + ((addr & SDIO_ADDRESS_MASK) << SDIO_ADDRESS_OFFSET) | + (blksz & SDIO_BLOCKSZ_MASK)); +} + +static inline +void qcn_sdio_set_cmd52_arg(u32 *arg, u8 rw, u8 func, u8 raw, u32 addr, u8 val) +{ + *arg = ((rw & SDIO_RW_MASK) << SDIO_RW_OFFSET) | + ((func & SDIO_FUNCTION_MASK) << SDIO_FUNCTION_OFFSET) | + ((raw & SDIO_RAW_MASK) << SDIO_RAW_OFFSET) | + (SDIO_STUFF_MASK << SDIO_STUFF_OFFSET1) | + ((addr & SDIO_ADDRESS_MASK) << SDIO_ADDRESS_OFFSET) | + (SDIO_STUFF_MASK << SDIO_STUFF_OFFSET2) | + (val & SDIO_DATA_MASK); +} + +static void qcn_sdio_free_rw_req(struct qcn_sdio_rw_info *rw_req) +{ + spin_lock(&sdio_ctxt->lock_free_q); + list_add_tail(&rw_req->list, &sdio_ctxt->rw_free_q); + atomic_inc(&sdio_ctxt->free_list_count); + spin_unlock(&sdio_ctxt->lock_free_q); +} + +void qcn_sdio_client_probe_complete(int id) +{ + complete(&client_probe_complete); +} +EXPORT_SYMBOL(qcn_sdio_client_probe_complete); + +static struct qcn_sdio_rw_info *qcn_sdio_alloc_rw_req(void) +{ + struct qcn_sdio_rw_info *rw_req = NULL; + + spin_lock(&sdio_ctxt->lock_free_q); + if (list_empty(&sdio_ctxt->rw_free_q)) { + spin_unlock(&sdio_ctxt->lock_free_q); + return rw_req; + } + + rw_req = list_first_entry(&sdio_ctxt->rw_free_q, + struct qcn_sdio_rw_info, list); + list_del(&rw_req->list); + atomic_dec(&sdio_ctxt->free_list_count); + spin_unlock(&sdio_ctxt->lock_free_q); + + return rw_req; +} + +static void qcn_sdio_add_rw_req(struct qcn_sdio_rw_info *rw_req) +{ + spin_lock(&sdio_ctxt->lock_wait_q); + list_add_tail(&rw_req->list, &sdio_ctxt->rw_wait_q); + atomic_inc(&sdio_ctxt->wait_list_count); + spin_unlock(&sdio_ctxt->lock_wait_q); +} + +static int qcn_enable_async_irq(bool enable) +{ + unsigned int num = 0; + int ret = 0; + u32 data = 0; + + num = sdio_ctxt->func->num; + sdio_claim_host(sdio_ctxt->func); + sdio_ctxt->func->num = 0; + data = sdio_readb(sdio_ctxt->func, SDIO_CCCR_INTERRUPT_EXTENSION, NULL); + if (enable) + data |= SDIO_ENABLE_ASYNC_INTR; + else + data &= ~SDIO_ENABLE_ASYNC_INTR; + sdio_writeb(sdio_ctxt->func, data, SDIO_CCCR_INTERRUPT_EXTENSION, &ret); + sdio_ctxt->func->num = num; + sdio_release_host(sdio_ctxt->func); + + return ret; +} + +static int qcn_send_io_abort(void) +{ + unsigned int num = 0; + int ret = 0; + + num = sdio_ctxt->func->num; + sdio_claim_host(sdio_ctxt->func); + sdio_ctxt->func->num = 0; + sdio_writeb(sdio_ctxt->func, 0x1, SDIO_CCCR_ABORT, &ret); + sdio_ctxt->func->num = num; + sdio_release_host(sdio_ctxt->func); + + return ret; +} + +static int qcn_send_meta_info(u8 event, u32 data) +{ + int ret = 0; + u32 i = 0; + u32 value = 0; + u8 temp = 0; + + value = META_INFO(event, data); + + sdio_claim_host(sdio_ctxt->func); + if (sdio_ctxt->curr_sw_mode < QCN_SDIO_SW_SBL) { + for (i = 0; i < 4; i++) { + temp = (u8)((value >> (i * 8)) & 0x000000FF); + sdio_writeb(sdio_ctxt->func, temp, + (SDIO_QCN_HRQ_PUSH + i), &ret); + } + } else { + sdio_writel(sdio_ctxt->func, value, SDIO_QCN_HRQ_PUSH, &ret); + } + + sdio_release_host(sdio_ctxt->func); + + return ret; +} + +static int qcn_read_crq_info(void) +{ + int ret = 0; + u32 i = 0; + u32 temp = 0; + u32 data = 0; + u32 len = 0; + u8 cid = 0; + + struct sdio_al_channel_handle *ch_handle = NULL; + + sdio_claim_host(sdio_ctxt->func); + if (sdio_ctxt->curr_sw_mode < QCN_SDIO_SW_SBL) { + for (i = 0; i < 4; i++) { + temp = sdio_readb(sdio_ctxt->func, + (SDIO_QCN_CRQ_PULL + i), &ret); + temp = temp << (i * 8); + data |= temp; + } + } else { + data = sdio_readl(sdio_ctxt->func, SDIO_QCN_CRQ_PULL, &ret); + } + + sdio_release_host(sdio_ctxt->func); + if (ret) + return ret; + + if (data & SDIO_QCN_CRQ_PULL_TRANS_MASK) { + cid = (u8)(data & SDIO_QCN_CRQ_PULL_CH_NUM_MASK); + cid -= sdio_ctxt->rx_cnum_base; + len = (data & SDIO_QCN_CRQ_PULL_BLK_CNT_MASK) >> + SDIO_QCN_CRQ_PULL_BLK_CNT_SHIFT; + + if (data & SDIO_QCN_CRQ_PULL_BLK_MASK) + len *= sdio_ctxt->func->cur_blksize; + temp = (data & SDIO_QCN_CRQ_PULL_UD_MASK) >> + SDIO_QCN_CRQ_PULL_UD_SHIFT; + + if (!sdio_ctxt->ch[cid]) { + pr_err("Client Id not initialized\n"); + return -EINVAL; + } + switch (temp) { + case QCN_SDIO_CRQ_START: + sdio_ctxt->ch[cid]->crq_len = len; + return ret; + case QCN_SDIO_CRQ_END: + sdio_ctxt->ch[cid]->crq_len += len; + break; + default: + sdio_ctxt->ch[cid]->crq_len = len; + } + + ch_handle = &(sdio_ctxt->ch[cid]->ch_handle); + if (sdio_ctxt->ch[cid]->ch_data.dl_data_avail_cb) + sdio_ctxt->ch[cid]->ch_data.dl_data_avail_cb(ch_handle, + sdio_ctxt->ch[cid]->crq_len); + } + + return ret; +} + +static int qcn_sdio_config(struct qcn_sdio_client_info *cinfo) +{ + int ret = 0; + u32 data = 0; + + sdio_claim_host(sdio_ctxt->func); + ret = sdio_set_block_size(sdio_ctxt->func, + cinfo->cli_handle.block_size); + + if (ret) { + sdio_release_host(sdio_ctxt->func); + goto err; + } + + data = SDIO_QCN_CONFIG_QE_MASK; + + sdio_writeb(sdio_ctxt->func, (u8)data, SDIO_QCN_CONFIG, &ret); + if (ret) { + sdio_release_host(sdio_ctxt->func); + goto err; + } + + data = (SDIO_QCN_IRQ_EN_LOCAL_MASK | + SDIO_QCN_IRQ_EN_SYS_ERR_MASK | + SDIO_QCN_IRQ_UNDERFLOW_MASK | + SDIO_QCN_IRQ_OVERFLOW_MASK | + SDIO_QCN_IRQ_CH_MISMATCH_MASK | + SDIO_QCN_IRQ_CRQ_READY_MASK); + + sdio_writeb(sdio_ctxt->func, (u8)data, SDIO_QCN_IRQ_EN, &ret); + sdio_release_host(sdio_ctxt->func); + if (ret) { + pr_err("%s: failed write config\n", __func__); + goto err; + } + + sdio_ctxt->rx_addr_base = SDIO_QCN_MC_DMA0_RX_CH0; + sdio_ctxt->rx_cnum_base = QCN_SDIO_DMA0_RX_CNUM; + sdio_ctxt->tx_addr_base = SDIO_QCN_MC_DMA1_TX_CH0; + sdio_ctxt->tx_cnum_base = QCN_SDIO_DMA1_TX_CNUM; + +#if (QCN_SDIO_META_VER_0) + data = ((cinfo->cli_handle.block_size / 8) - 1); +#elif (QCN_SDIO_META_VER_1) + data = cinfo->cli_handle.block_size; +#endif + ret = qcn_send_meta_info(QCN_SDIO_BLK_SZ_HEVENT, data); +err: + return ret; +} + + +int qcn_sw_mode_change(enum qcn_sdio_sw_mode mode) +{ + struct qcn_sdio_client_info *cinfo = NULL; + struct qcn_sdio_ch_info *chinfo = NULL; + int ret = 0; + + if (!(mode) && !(mode < QCN_SDIO_SW_MAX)) + return -EINVAL; + + if (sdio_ctxt->curr_sw_mode == mode) + return 0; + + if ((sdio_ctxt->curr_sw_mode == QCN_SDIO_SW_PBL) && + (mode == QCN_SDIO_SW_SBL)) { + sdio_ctxt->curr_sw_mode = QCN_SDIO_SW_SBL; + qcn_send_meta_info(QCN_SDIO_BLK_SZ_HEVENT, + sdio_ctxt->func->cur_blksize); + qcn_send_meta_info(QCN_SDIO_DOORBELL_HEVENT, (u32)0); + return 0; + } + + switch (sdio_ctxt->curr_sw_mode) { + case QCN_SDIO_SW_PBL: + case QCN_SDIO_SW_SBL: + case QCN_SDIO_SW_RDDM: + mutex_lock(&lock); + list_for_each_entry(cinfo, &cinfo_head, cli_list) { + while (!list_empty(&cinfo->ch_head)) { + chinfo = list_first_entry(&cinfo->ch_head, + struct qcn_sdio_ch_info, ch_list); + sdio_al_deregister_channel(&chinfo->ch_handle); + } + cinfo->cli_handle.func = NULL; + + if (cinfo->is_probed) { + cinfo->cli_data.remove(&cinfo->cli_handle); + cinfo->is_probed = 0; + } + if (((cinfo->cli_handle.id == QCN_SDIO_CLI_ID_WLAN) || + (cinfo->cli_handle.id == QCN_SDIO_CLI_ID_QMI) || + (cinfo->cli_handle.id == QCN_SDIO_CLI_ID_DIAG)) && + (mode == QCN_SDIO_SW_MROM)) { + qcn_send_meta_info((u8)QCN_SDIO_SW_MODE_HEVENT, + (u32)(mode | QCN_SDIO_MAJOR_VER + | QCN_SDIO_MINOR_VER)); + cinfo->cli_handle.block_size = + QCN_SDIO_MROM_BLK_SZ; + cinfo->cli_handle.func = sdio_ctxt->func; + qcn_sdio_config(cinfo); + cinfo->is_probed = !cinfo->cli_data.probe( + &cinfo->cli_handle); + qcn_send_meta_info(QCN_SDIO_DOORBELL_HEVENT, + (u32)0); + } + } + mutex_unlock(&lock); + break; + case QCN_SDIO_SW_RESET: + case QCN_SDIO_SW_MROM: + ret = wait_for_completion_timeout(&client_probe_complete, + msecs_to_jiffies(3000)); + if (!ret) + pr_err("Timeout waiting for clients\n"); + + mutex_lock(&lock); + list_for_each_entry(cinfo, &cinfo_head, cli_list) { + while (!list_empty(&cinfo->ch_head)) { + chinfo = list_first_entry(&cinfo->ch_head, + struct qcn_sdio_ch_info, ch_list); + sdio_al_deregister_channel(&chinfo->ch_handle); + } + cinfo->cli_handle.func = NULL; + + + if (cinfo->is_probed) { + cinfo->cli_data.remove(&cinfo->cli_handle); + cinfo->is_probed = 0; + } + + if ((cinfo->cli_handle.id == QCN_SDIO_CLI_ID_TTY) && + (mode <= QCN_SDIO_SW_MROM)) { + qcn_send_meta_info((u8)QCN_SDIO_SW_MODE_HEVENT, + (u32)(mode | QCN_SDIO_MAJOR_VER + | QCN_SDIO_MINOR_VER)); + cinfo->cli_handle.block_size = + QCN_SDIO_TTY_BLK_SZ; + cinfo->cli_handle.func = sdio_ctxt->func; + qcn_sdio_config(cinfo); + cinfo->is_probed = !cinfo->cli_data.probe( + &cinfo->cli_handle); + qcn_send_meta_info(QCN_SDIO_DOORBELL_HEVENT, + (u32)0); + } + } + mutex_unlock(&lock); + break; + default: + pr_err("Invalid mode\n"); + } + + sdio_ctxt->curr_sw_mode = mode; + return 0; +} + +static int qcn_read_meta_info(void) +{ + int ret = 0; + u32 i = 0; + u32 data = 0; + u32 temp = 0; + + sdio_claim_host(sdio_ctxt->func); + + if (sdio_ctxt->curr_sw_mode < QCN_SDIO_SW_SBL) { + for (i = 0; i < 4; i++) { + temp = sdio_readb(sdio_ctxt->func, + (SDIO_QCN_LOCAL_INFO + i), &ret); + temp = temp << (i * 8); + data |= temp; + } + } else { + data = sdio_readl(sdio_ctxt->func, SDIO_QCN_LOCAL_INFO, &ret); + } + + sdio_writeb(sdio_ctxt->func, (u8)SDIO_QCN_IRQ_CLR_LOCAL_MASK, + SDIO_QCN_IRQ_CLR, NULL); + + sdio_release_host(sdio_ctxt->func); + + if (ret) + return ret; + + temp = (data & QCN_SDIO_LMETA_EVENT_BMSK) >> QCN_SDIO_LMETA_EVENT_SHFT; + switch (temp) { + case QCN_SDIO_SW_MODE_LEVENT: + temp = (data & QCN_SDIO_LMETA_SW_BMSK) >> + QCN_SDIO_LMETA_SW_SHFT; + qcn_sw_mode_change((enum qcn_sdio_sw_mode)temp); + break; + default: + if ((temp >= QCN_SDIO_META_START_CH0) && + (temp < QCN_SDIO_META_START_CH1)) { + if (sdio_ctxt->ch[0] && + sdio_ctxt->ch[0]->ch_data.dl_meta_data_cb) + sdio_ctxt->ch[0]->ch_data.dl_meta_data_cb( + &(sdio_ctxt->ch[0]->ch_handle), data); + } else if ((temp >= QCN_SDIO_META_START_CH1) && + (temp < QCN_SDIO_META_START_CH2)) { + if (sdio_ctxt->ch[1] && + sdio_ctxt->ch[1]->ch_data.dl_meta_data_cb) + sdio_ctxt->ch[1]->ch_data.dl_meta_data_cb( + &(sdio_ctxt->ch[1]->ch_handle), data); + } else if ((temp >= QCN_SDIO_META_START_CH2) && + (temp < QCN_SDIO_META_START_CH3)) { + if (sdio_ctxt->ch[2] && + sdio_ctxt->ch[2]->ch_data.dl_meta_data_cb) + sdio_ctxt->ch[2]->ch_data.dl_meta_data_cb( + &(sdio_ctxt->ch[2]->ch_handle), data); + } else if ((temp >= QCN_SDIO_META_START_CH3) && + (temp < QCN_SDIO_META_END)) { + if (sdio_ctxt->ch[3] && + sdio_ctxt->ch[3]->ch_data.dl_meta_data_cb) + sdio_ctxt->ch[3]->ch_data.dl_meta_data_cb( + &(sdio_ctxt->ch[3]->ch_handle), data); + } else { + ret = -EINVAL; + } + } + + return ret; +} + +static void qcn_sdio_irq_handler(struct sdio_func *func) +{ + u8 data = 0; + + + sdio_claim_host(sdio_ctxt->func); + data = sdio_readb(sdio_ctxt->func, SDIO_QCN_IRQ_STATUS, NULL); + sdio_release_host(sdio_ctxt->func); + + if (data & SDIO_QCN_IRQ_CRQ_READY_MASK) { + qcn_read_crq_info(); + } else if (data & SDIO_QCN_IRQ_LOCAL_MASK) { + qcn_read_meta_info(); + } else if (data & SDIO_QCN_IRQ_EN_SYS_ERR_MASK) { + sdio_claim_host(sdio_ctxt->func); + sdio_writeb(sdio_ctxt->func, (u8)SDIO_QCN_IRQ_CLR_SYS_ERR_MASK, + SDIO_QCN_IRQ_CLR, NULL); + sdio_release_host(sdio_ctxt->func); + pr_err("%s: sys_err interrupt triggered\n", __func__); + } else if (data & SDIO_QCN_IRQ_EN_UNDERFLOW_MASK) { + sdio_claim_host(sdio_ctxt->func); + sdio_writeb(sdio_ctxt->func, + (u8)SDIO_QCN_IRQ_CLR_UNDERFLOW_MASK, + SDIO_QCN_IRQ_CLR, NULL); + sdio_release_host(sdio_ctxt->func); + pr_err("%s: underflow interrupt triggered\n", __func__); + } else if (data & SDIO_QCN_IRQ_EN_OVERFLOW_MASK) { + sdio_claim_host(sdio_ctxt->func); + sdio_writeb(sdio_ctxt->func, (u8)SDIO_QCN_IRQ_CLR_OVERFLOW_MASK, + SDIO_QCN_IRQ_CLR, NULL); + sdio_release_host(sdio_ctxt->func); + pr_err("%s: overflow interrupt triggered\n", __func__); + } else if (data & SDIO_QCN_IRQ_EN_CH_MISMATCH_MASK) { + sdio_claim_host(sdio_ctxt->func); + sdio_writeb(sdio_ctxt->func, + (u8)SDIO_QCN_IRQ_CLR_CH_MISMATCH_MASK, + SDIO_QCN_IRQ_CLR, NULL); + sdio_release_host(sdio_ctxt->func); + pr_err("%s: channel mismatch interrupt triggered\n", __func__); + } else { + sdio_claim_host(sdio_ctxt->func); + sdio_writeb(sdio_ctxt->func, (u8)data, SDIO_QCN_IRQ_CLR, NULL); + sdio_release_host(sdio_ctxt->func); + } +} + +static int qcn_sdio_send_buff(u32 cid, void *buff, size_t len) +{ + int ret = 0; + + sdio_claim_host(sdio_ctxt->func); + ret = sdio_writesb(sdio_ctxt->func, + (sdio_ctxt->tx_addr_base + (cid * (u32)4)), buff, len); + + if (ret) + qcn_send_io_abort(); + + sdio_release_host(sdio_ctxt->func); + + return ret; +} + +static int qcn_sdio_recv_buff(u32 cid, void *buff, size_t len) +{ + int ret = 0; + + sdio_claim_host(sdio_ctxt->func); + ret = sdio_readsb(sdio_ctxt->func, buff, + (sdio_ctxt->rx_addr_base + (cid * (u32)4)), len); + + if (ret) + qcn_send_io_abort(); + + sdio_release_host(sdio_ctxt->func); + + return ret; +} + +static void qcn_sdio_rw_work(struct work_struct *work) +{ + int ret = 0; + struct qcn_sdio_rw_info *rw_req = NULL; + struct sdio_al_xfer_result *result = NULL; + struct sdio_al_channel_handle *ch_handle = NULL; + + while (1) { + spin_lock(&sdio_ctxt->lock_wait_q); + if (list_empty(&sdio_ctxt->rw_wait_q)) { + spin_unlock(&sdio_ctxt->lock_wait_q); + break; + } + rw_req = list_first_entry(&sdio_ctxt->rw_wait_q, + struct qcn_sdio_rw_info, list); + list_del(&rw_req->list); + spin_unlock(&sdio_ctxt->lock_wait_q); + + if (rw_req->dir) { + ret = qcn_sdio_recv_buff(rw_req->cid, rw_req->buf, + rw_req->len); + if (rx_dump) + HEX_DUMP("ASYNC_RECV: ", rw_req->buf, + rw_req->len); + } else { + ret = qcn_sdio_send_buff(rw_req->cid, rw_req->buf, + rw_req->len); + if (tx_dump) + HEX_DUMP("ASYNC_SEND: ", rw_req->buf, + rw_req->len); + } + + ch_handle = &sdio_ctxt->ch[rw_req->cid]->ch_handle; + result = &sdio_ctxt->ch[rw_req->cid]->result; + result->xfer_status = ret; + result->buf_addr = rw_req->buf; + result->xfer_len = rw_req->len; + if (rw_req->dir) + sdio_ctxt->ch[rw_req->cid]->ch_data.dl_xfer_cb( + ch_handle, result, rw_req->ctxt); + else + sdio_ctxt->ch[rw_req->cid]->ch_data.ul_xfer_cb( + ch_handle, result, rw_req->ctxt); + atomic_set(&sdio_ctxt->ch_status[rw_req->cid], 0); + qcn_sdio_free_rw_req(rw_req); + atomic_dec(&sdio_ctxt->wait_list_count); + } +} + +static +int qcn_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id) +{ + int ret = 0; + + sdio_ctxt = kzalloc(sizeof(struct qcn_sdio), GFP_KERNEL); + if (!sdio_ctxt) + return -ENOMEM; + + sdio_ctxt->func = func; + sdio_ctxt->id = id; + sdio_set_drvdata(func, sdio_ctxt); + sdio_ctxt->qcn_sdio_wq = create_singlethread_workqueue("qcn_sdio"); + if (!sdio_ctxt->qcn_sdio_wq) { + pr_err("%s: Error: SDIO create wq\n", __func__); + goto err; + } + + for (ret = 0; ret < QCN_SDIO_CH_MAX; ret++) { + sdio_ctxt->ch[ret] = NULL; + atomic_set(&sdio_ctxt->ch_status[ret], -1); + } + + spin_lock_init(&sdio_ctxt->lock_free_q); + spin_lock_init(&sdio_ctxt->lock_wait_q); + spin_lock_init(&async_lock); + INIT_WORK(&sdio_ctxt->sdio_rw_w, qcn_sdio_rw_work); + INIT_LIST_HEAD(&sdio_ctxt->rw_free_q); + INIT_LIST_HEAD(&sdio_ctxt->rw_wait_q); + + for (ret = 0; ret < QCN_SDIO_RW_REQ_MAX; ret++) + qcn_sdio_free_rw_req(&sdio_ctxt->rw_req_info[ret]); + + sdio_claim_host(sdio_ctxt->func); + ret = sdio_enable_func(sdio_ctxt->func); + if (ret) { + pr_err("%s: Error:%d SDIO enable func\n", __func__, ret); + sdio_release_host(sdio_ctxt->func); + goto err; + } + ret = sdio_claim_irq(sdio_ctxt->func, qcn_sdio_irq_handler); + if (ret) { + pr_err("%s: Error:%d SDIO claim irq\n", __func__, ret); + sdio_release_host(sdio_ctxt->func); + goto err; + } + + qcn_enable_async_irq(true); + sdio_release_host(sdio_ctxt->func); + if (qcn_read_meta_info()) { + pr_err("%s: Error: SDIO Config\n", __func__); + qcn_send_meta_info((u8)QCN_SDIO_SW_MODE_HEVENT, (u32)0); + } + + return 0; +err: + kfree(sdio_ctxt); + sdio_ctxt = NULL; + return ret; +} + +static void qcn_sdio_remove(struct sdio_func *func) +{ + struct qcn_sdio_client_info *cinfo = NULL; + struct qcn_sdio_ch_info *ch_info = NULL; + + qcn_enable_async_irq(false); + mutex_lock(&lock); + list_for_each_entry(cinfo, &cinfo_head, cli_list) { + while (!list_empty(&cinfo->ch_head)) { + ch_info = list_first_entry(&cinfo->ch_head, + struct qcn_sdio_ch_info, ch_list); + sdio_al_deregister_channel(&ch_info->ch_handle); + } + mutex_unlock(&lock); + cinfo->cli_data.remove(&cinfo->cli_handle); + mutex_lock(&lock); + } + mutex_unlock(&lock); + destroy_workqueue(sdio_ctxt->qcn_sdio_wq); + kfree(sdio_ctxt); + sdio_ctxt = NULL; +} + +static const struct sdio_device_id qcn_sdio_devices[] = { + {SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_QCN_BASE | 0x0))}, + {}, +}; + +MODULE_DEVICE_TABLE(sdio, qcn_sdio_devices); + +static struct sdio_driver qcn_sdio_driver = { + .name = "qcn_sdio", + .id_table = qcn_sdio_devices, + .probe = qcn_sdio_probe, + .remove = qcn_sdio_remove, +}; + +static int qcn_sdio_plat_probe(struct platform_device *pdev) +{ + int ret = 0; + + mutex_init(&lock); + INIT_LIST_HEAD(&cinfo_head); + atomic_set(&status, 1); + + ret = sdio_register_driver(&qcn_sdio_driver); + if (ret) { + pr_err("%s: SDIO driver registration failed: %d\n", __func__, + ret); + mutex_destroy(&lock); + atomic_set(&status, 0); + } + + init_completion(&client_probe_complete); + + return ret; +} + +static int qcn_sdio_plat_remove(struct platform_device *pdev) +{ + struct qcn_sdio_client_info *cinfo = NULL; + + mutex_lock(&lock); + while (!list_empty(&cinfo_head)) { + cinfo = list_first_entry(&cinfo_head, struct + qcn_sdio_client_info, cli_list); + mutex_unlock(&lock); + sdio_al_deregister_client(&cinfo->cli_handle); + mutex_lock(&lock); + list_del(&cinfo->cli_list); + } + mutex_unlock(&lock); + mutex_destroy(&lock); + if (sdio_ctxt) { + destroy_workqueue(sdio_ctxt->qcn_sdio_wq); + sdio_release_irq(sdio_ctxt->func); + kfree(sdio_ctxt); + sdio_ctxt = NULL; + } + sdio_unregister_driver(&qcn_sdio_driver); + atomic_set(&status, 0); + + return 0; +} + +static const struct of_device_id qcn_sdio_dt_match[] = { + {.compatible = "qcom,qcn-sdio"}, + {} +}; +MODULE_DEVICE_TABLE(of, qcn_sdio_dt_match); + +static struct platform_driver qcn_sdio_plat_driver = { + .probe = qcn_sdio_plat_probe, + .remove = qcn_sdio_plat_remove, + .driver = { + .name = "qcn-sdio", + .owner = THIS_MODULE, + .of_match_table = qcn_sdio_dt_match, + }, +}; + +static int __init qcn_sdio_init(void) +{ + return platform_driver_register(&qcn_sdio_plat_driver); +} + +static void __exit qcn_sdio_exit(void) +{ + platform_driver_unregister(&qcn_sdio_plat_driver); +} + +module_init(qcn_sdio_init); +module_exit(qcn_sdio_exit); + +int sdio_al_is_ready(void) +{ + if (atomic_read(&status)) + return 0; + else + return -EBUSY; +} +EXPORT_SYMBOL(sdio_al_is_ready); + +struct sdio_al_client_handle *sdio_al_register_client( + struct sdio_al_client_data *client_data) +{ + struct qcn_sdio_client_info *client_info = NULL; + + if (!((client_data) && (client_data->name) && + (client_data->probe) && (client_data->remove))) { + pr_err("%s: SDIO: Invalid param\n", __func__); + return ERR_PTR(-EINVAL); + } + + client_info = (struct qcn_sdio_client_info *) + kzalloc(sizeof(struct qcn_sdio_client_info), GFP_KERNEL); + if (!client_info) + return ERR_PTR(-ENOMEM); + + memcpy(&client_info->cli_data, client_data, + sizeof(struct sdio_al_client_data)); + + if (!strcmp(client_data->name, "SDIO_AL_CLIENT_TTY")) { + client_info->cli_handle.id = QCN_SDIO_CLI_ID_TTY; + client_info->cli_handle.block_size = QCN_SDIO_TTY_BLK_SZ; + } else if (!strcmp(client_data->name, "SDIO_AL_CLIENT_WLAN")) { + client_info->cli_handle.id = QCN_SDIO_CLI_ID_WLAN; + client_info->cli_handle.block_size = QCN_SDIO_MROM_BLK_SZ; + } else if (!strcmp(client_data->name, "SDIO_AL_CLIENT_QMI")) { + client_info->cli_handle.id = QCN_SDIO_CLI_ID_QMI; + client_info->cli_handle.block_size = QCN_SDIO_MROM_BLK_SZ; + } else if (!strcmp(client_data->name, "SDIO_AL_CLIENT_DIAG")) { + client_info->cli_handle.id = QCN_SDIO_CLI_ID_DIAG; + client_info->cli_handle.block_size = QCN_SDIO_MROM_BLK_SZ; + } else { + pr_err("%s: SDIO: Invalid name\n", __func__); + kfree(client_info); + return ERR_PTR(-EINVAL); + } + client_info->cli_handle.client_data = &client_info->cli_data; + + INIT_LIST_HEAD(&client_info->ch_head); + mutex_lock(&lock); + list_add_tail(&client_info->cli_list, &cinfo_head); + mutex_unlock(&lock); + + client_info->is_probed = 0; + if ((sdio_ctxt) && (sdio_ctxt->curr_sw_mode)) { + if ((sdio_ctxt->curr_sw_mode == QCN_SDIO_SW_MROM) && + (client_info->cli_handle.id > QCN_SDIO_CLI_ID_TTY)) { + qcn_sdio_config(client_info); + client_info->is_probed = !client_data->probe( + &client_info->cli_handle); + qcn_send_meta_info(QCN_SDIO_DOORBELL_HEVENT, (u32)0); + } + } + + return &client_info->cli_handle; +} +EXPORT_SYMBOL(sdio_al_register_client); + +void sdio_al_deregister_client(struct sdio_al_client_handle *handle) +{ + struct qcn_sdio_ch_info *ch_info = NULL; + struct qcn_sdio_client_info *client_info = NULL; + + if (!handle) { + pr_err("%s: SDIO: Invalid param\n", __func__); + return; + } + + client_info = container_of(handle, struct qcn_sdio_client_info, + cli_handle); + + while (!list_empty(&client_info->ch_head)) { + ch_info = list_first_entry(&client_info->ch_head, + struct qcn_sdio_ch_info, ch_list); + sdio_al_deregister_channel(&ch_info->ch_handle); + } + mutex_lock(&lock); + list_del(&client_info->cli_list); + kfree(client_info); + mutex_unlock(&lock); +} +EXPORT_SYMBOL(sdio_al_deregister_client); + +struct sdio_al_channel_handle *sdio_al_register_channel( + struct sdio_al_client_handle *client_handle, + struct sdio_al_channel_data *channel_data) +{ + struct qcn_sdio_ch_info *ch_info = NULL; + struct qcn_sdio_client_info *client_info = NULL; + + if (!((channel_data) && (channel_data->name) && (client_handle) && + (channel_data->client_data))) { + pr_err("%s: SDIO: Invalid param\n", __func__); + return ERR_PTR(-EINVAL); + } + + ch_info = kzalloc(sizeof(struct qcn_sdio_client_info), GFP_KERNEL); + if (!ch_info) + return ERR_PTR(-ENOMEM); + + memcpy(&ch_info->ch_data, channel_data, + sizeof(struct sdio_al_channel_data)); + + if ((!strcmp(channel_data->name, "SDIO_AL_WLAN_CH0")) || + (!strcmp(channel_data->name, "SDIO_AL_TTY_CH0"))) { + if (atomic_read(&sdio_ctxt->ch_status[QCN_SDIO_CH_0]) < 0) + ch_info->ch_handle.channel_id = QCN_SDIO_CH_0; + } else if (!strcmp(channel_data->name, "SDIO_AL_WLAN_CH1")) { + if (atomic_read(&sdio_ctxt->ch_status[QCN_SDIO_CH_1]) < 0) + ch_info->ch_handle.channel_id = QCN_SDIO_CH_1; + } else if (!strcmp(channel_data->name, "SDIO_AL_QMI_CH0")) { + if (atomic_read(&sdio_ctxt->ch_status[QCN_SDIO_CH_2]) < 0) + ch_info->ch_handle.channel_id = QCN_SDIO_CH_2; + } else if (!strcmp(channel_data->name, "SDIO_AL_DIAG_CH0")) { + if (atomic_read(&sdio_ctxt->ch_status[QCN_SDIO_CH_3]) < 0) + ch_info->ch_handle.channel_id = QCN_SDIO_CH_3; + } else { + pr_err("%s: SDIO: Invalid CH name: %s\n", __func__, + channel_data->name); + kfree(ch_info); + return ERR_PTR(-EINVAL); + } + + client_info = container_of(client_handle, struct qcn_sdio_client_info, + cli_handle); + ch_info->ch_handle.channel_data = &ch_info->ch_data; + ch_info->chandle = &client_info->cli_handle; + list_add_tail(&ch_info->ch_list, &client_info->ch_head); + sdio_ctxt->ch[ch_info->ch_handle.channel_id] = ch_info; + atomic_set(&sdio_ctxt->ch_status[ch_info->ch_handle.channel_id], 0); + + return &ch_info->ch_handle; +} +EXPORT_SYMBOL(sdio_al_register_channel); + +void sdio_al_deregister_channel(struct sdio_al_channel_handle *ch_handle) +{ + int ret = 0; + struct qcn_sdio_ch_info *ch_info = NULL; + + if (!ch_handle) { + pr_err("%s: Error: Invalid Param\n", __func__); + return; + } + + do { + ret = atomic_cmpxchg( + &sdio_ctxt->ch_status[ch_handle->channel_id], 0, 1); + if (ret) { + if (ret == -1) + return; + + usleep_range(1000, 1500); + } + } while (ret); + + ch_info = sdio_ctxt->ch[ch_handle->channel_id]; + if (ch_info) { + list_del(&ch_info->ch_list); + sdio_ctxt->ch[ch_handle->channel_id] = NULL; + atomic_set(&sdio_ctxt->ch_status[ch_handle->channel_id], -1); + kfree(ch_info); + } +} +EXPORT_SYMBOL(sdio_al_deregister_channel); + +int sdio_al_queue_transfer_async(struct sdio_al_channel_handle *handle, + enum sdio_al_dma_direction dir, + void *buf, size_t len, int priority, void *ctxt) +{ + struct qcn_sdio_rw_info *rw_req = NULL; + u32 cid = QCN_SDIO_CH_MAX; + + if (!handle) { + pr_err("%s: Error: Invalid Param\n", __func__); + return -EINVAL; + } + + cid = handle->channel_id; + + if (!(cid < QCN_SDIO_CH_MAX) && + (atomic_read(&sdio_ctxt->ch_status[cid]) < 0)) + return -EINVAL; + + if (dir == SDIO_AL_TX && atomic_read(&sdio_ctxt->free_list_count) <= 8) + return -ENOMEM; + + rw_req = qcn_sdio_alloc_rw_req(); + if (!rw_req) + return -ENOMEM; + + rw_req->cid = cid; + rw_req->dir = dir; + rw_req->buf = buf; + rw_req->len = len; + rw_req->ctxt = ctxt; + + if (dir == SDIO_AL_RX) + spin_lock(&async_lock); + + qcn_sdio_add_rw_req(rw_req); + queue_work(sdio_ctxt->qcn_sdio_wq, &sdio_ctxt->sdio_rw_w); + + if (dir == SDIO_AL_RX) + spin_unlock(&async_lock); + + return 0; +} +EXPORT_SYMBOL(sdio_al_queue_transfer_async); + +int sdio_al_queue_transfer(struct sdio_al_channel_handle *ch_handle, + enum sdio_al_dma_direction dir, + void *buf, size_t len, int priority) +{ + int ret = 0; + u32 cid = QCN_SDIO_CH_MAX; + + if (!ch_handle) { + pr_err("%s: SDIO: Invalid Param\n", __func__); + return -EINVAL; + } + + if (dir == SDIO_AL_RX && !list_empty(&sdio_ctxt->rw_wait_q) && + !atomic_read(&sdio_ctxt->wait_list_count)) { + sdio_al_queue_transfer_async(ch_handle, dir, buf, len, true, + (void *)(uintptr_t)len); + pr_info("%s: switching to async\n", __func__); + ret = 1; + } else { + cid = ch_handle->channel_id; + + if (!(cid < QCN_SDIO_CH_MAX)) + return -EINVAL; + + if (dir == SDIO_AL_RX) { + if (!atomic_read(&sdio_ctxt->wait_list_count)) + ret = qcn_sdio_recv_buff(cid, buf, len); + else { + sdio_al_queue_transfer_async(ch_handle, dir, + buf, len, true, (void *)(uintptr_t)len); + pr_info("%s switching to async\n", __func__); + ret = 1; + } + + if (rx_dump) + HEX_DUMP("SYNC_RECV: ", buf, len); + } else if (dir == SDIO_AL_TX) { + ret = qcn_sdio_send_buff(cid, buf, len); + if (tx_dump) + HEX_DUMP("SYNC_SEND: ", buf, len); + } else + ret = -EINVAL; + } + + return ret; +} +EXPORT_SYMBOL(sdio_al_queue_transfer); + +int sdio_al_meta_transfer(struct sdio_al_channel_handle *handle, + unsigned int data, unsigned int trans) +{ + u32 cid = QCN_SDIO_CH_MAX; + u8 event = 0; + + if (!handle) + return -EINVAL; + + cid = handle->channel_id; + + if (!(cid < QCN_SDIO_CH_MAX)) + return -EINVAL; + + event = (u8)((data & QCN_SDIO_HMETA_EVENT_BMSK) >> + QCN_SDIO_HMETA_EVENT_SHFT); + + if (cid == QCN_SDIO_CH_0) { + if ((event < QCN_SDIO_META_START_CH0) && + (event >= QCN_SDIO_META_START_CH1)) { + return -EINVAL; + } + } else if (cid == QCN_SDIO_CH_1) { + if ((event < QCN_SDIO_META_START_CH1) && + (event >= QCN_SDIO_META_START_CH2)) { + return -EINVAL; + } + } else if (cid == QCN_SDIO_CH_2) { + if ((event < QCN_SDIO_META_START_CH2) && + (event >= QCN_SDIO_META_START_CH3)) { + return -EINVAL; + } + } else if (cid == QCN_SDIO_CH_3) { + if ((event < QCN_SDIO_META_START_CH3) && + (event >= QCN_SDIO_META_END)) { + return -EINVAL; + } + } + + return qcn_send_meta_info(event, data); +} +EXPORT_SYMBOL(sdio_al_meta_transfer); diff --git a/drivers/platform/msm/qcn/qcn_sdio.h b/drivers/platform/msm/qcn/qcn_sdio.h new file mode 100644 index 000000000000..1a63e8e08e0c --- /dev/null +++ b/drivers/platform/msm/qcn/qcn_sdio.h @@ -0,0 +1,175 @@ +/* Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _QCN_SDIO_H_ +#define _QCN_SDIO_H_ + +#include <linux/qcn_sdio_al.h> +#include "qcn_sdio_hwio.h" + + +#define MANUFACTURER_CODE (0x70) +#define MANUFACTURER_ID_QCN_BASE (0x400B) +#define QCN_SDIO_TTY_BLK_SZ (512) +#define QCN_SDIO_MROM_BLK_SZ (512) +#define QCN_SDIO_RW_REQ_MAX (128) + +#define QCN_SDIO_DMA0_RX_CNUM (0x4) +#define QCN_SDIO_DMA0_TX_CNUM (0xC) +#define QCN_SDIO_DMA1_RX_CNUM (0x14) +#define QCN_SDIO_DMA1_TX_CNUM (0x1C) + +#define QCN_SDIO_CRQ_START (0x1) +#define QCN_SDIO_CRQ_END (0x2) + +#define QCN_SDIO_META_VER_0 (0) +#define QCN_SDIO_META_VER_1 (1) + +#if (QCN_SDIO_META_VER_0) +#define QCN_SDIO_LMETA_FMT_VER (0) +#define QCN_SDIO_LMETA_VER_BMSK (0x0000000F) +#define QCN_SDIO_LMETA_VER_SHFT (0) +#define QCN_SDIO_LMETA_SW_BMSK (0x000000F0) +#define QCN_SDIO_LMETA_SW_SHFT (4) +#define QCN_SDIO_LMETA_EVENT_BMSK (0x0000FF00) +#define QCN_SDIO_LMETA_EVENT_SHFT (8) +#define QCN_SDIO_LMETA_DATA_BMSK (0xFFFF0000) +#define QCN_SDIO_LMETA_DATA_SHFT (16) +#define QCN_SDIO_LMETA_TREG_BMSK (0x00F00000) +#define QCN_SDIO_LMETA_TREG_SHFT (20) +#define QCN_SDIO_LMETA_TLEN_BMSK (0x000F0000) +#define QCN_SDIO_LMETA_TLEN_SHFT (16) + +#define QCN_SDIO_HMETA_FMT_VER (0) +#define QCN_SDIO_HMETA_VER_BMSK (0x0000000F) +#define QCN_SDIO_HMETA_VER_SHFT (0) +#define QCN_SDIO_HMETA_SW_BMSK (0x000000F0) +#define QCN_SDIO_HMETA_SW_SHFT (4) +#define QCN_SDIO_HMETA_EVENT_BMSK (0x00003F00) +#define QCN_SDIO_HMETA_EVENT_SHFT (8) +#define QCN_SDIO_HMETA_TRANS_BMSK (0x00400000) +#define QCN_SDIO_HMETA_TRANS_SHFT (22) +#define QCN_SDIO_HMETA_DATA_BMSK (0xFF000000) +#define QCN_SDIO_HMETA_DATA_SHFT (24) +#define QCN_SDIO_HMETA_TREG_BMSK (0xF0000000) +#define QCN_SDIO_HMETA_TREG_SHFT (28) +#define QCN_SDIO_HMETA_TLEN_BMSK (0x0F000000) +#define QCN_SDIO_HMETA_TLEN_SHFT (24) + +#elif (QCN_SDIO_META_VER_1) + +#define QCN_SDIO_MAJOR_VER (0x00000000) +#define QCN_SDIO_MINOR_VER (0x00000010) + +#define QCN_SDIO_LMETA_FMT_VER (1) +#define QCN_SDIO_LMETA_EVENT_BMSK (0xFF000000) +#define QCN_SDIO_LMETA_EVENT_SHFT (24) +#define QCN_SDIO_LMETA_DATA_BMSK (0x00003FFF) +#define QCN_SDIO_LMETA_DATA_SHFT (0) +#define QCN_SDIO_LMETA_SW_BMSK (0x0000000F) +#define QCN_SDIO_LMETA_SW_SHFT (0) +#define QCN_SDIO_LMETA_VER_MAJ_BMSK (0x000000F0) +#define QCN_SDIO_LMETA_VER_MAJ_SHFT (4) +#define QCN_SDIO_LMETA_VER_MIN_BMSK (0x00000F00) +#define QCN_SDIO_LMETA_VER_MIN_SHFT (8) + +#define QCN_SDIO_HMETA_FMT_VER (1) +#define QCN_SDIO_HMETA_EVENT_BMSK (0xFF000000) +#define QCN_SDIO_HMETA_EVENT_SHFT (24) +#define QCN_SDIO_HMETA_DATA_BMSK (0x00003FFF) +#define QCN_SDIO_HMETA_DATA_SHFT (0) +#define QCN_SDIO_HMETA_TRANS_BMSK (0x00400000) +#define QCN_SDIO_HMETA_TRANS_SHFT (22) +#define QCN_SDIO_HMETA_SW_BMSK (0x0000000F) +#define QCN_SDIO_HMETA_SW_SHFT (0) +#define QCN_SDIO_HMETA_VER_MAJ_BMSK (0x000000F0) +#define QCN_SDIO_HMETA_VER_MAJ_SHFT (4) +#define QCN_SDIO_HMETA_VER_MIN_BMSK (0x00000F00) +#define QCN_SDIO_HMETA_VER_MIN_SHFT (8) + +#define QCN_SDIO_META_START_CH0 (0x20) +#define QCN_SDIO_META_START_CH1 (0x40) +#define QCN_SDIO_META_START_CH2 (0x60) +#define QCN_SDIO_META_START_CH3 (0x80) +#define QCN_SDIO_META_END (0xA0) + +#endif /* QCN_SDIO_META_VER */ + +enum qcn_sdio_cli_id { + QCN_SDIO_CLI_ID_INVALID = 0, + QCN_SDIO_CLI_ID_TTY, + QCN_SDIO_CLI_ID_WLAN, + QCN_SDIO_CLI_ID_QMI, + QCN_SDIO_CLI_ID_DIAG, + QCN_SDIO_CLI_ID_MAX +}; + +enum qcn_sdio_ch_id { + QCN_SDIO_CH_0 = 0, + QCN_SDIO_CH_1, + QCN_SDIO_CH_2, + QCN_SDIO_CH_3, + QCN_SDIO_CH_MAX, +}; + +enum qcn_sdio_sw_mode { + QCN_SDIO_SW_RESET = 0, + QCN_SDIO_SW_PBL, + QCN_SDIO_SW_SBL, + QCN_SDIO_SW_RDDM, + QCN_SDIO_SW_MROM, + QCN_SDIO_SW_MAX, +}; + +enum qcn_sdio_host_event { + QCN_SDIO_INVALID_HEVENT = 0, + QCN_SDIO_SW_MODE_HEVENT, + QCN_SDIO_BLK_SZ_HEVENT, + QCN_SDIO_DOORBELL_HEVENT, + QCN_SDIO_MAX_HEVENT = 63, +}; + +enum qcn_sdio_local_event { + QCN_SDIO_INVALID_LEVENT = 0, + QCN_SDIO_SW_MODE_LEVENT, + QCN_SDIO_MAX_LEVENT = 255, +}; + + +struct qcn_sdio_client_info { + int is_probed; + struct sdio_al_client_data cli_data; + struct sdio_al_client_handle cli_handle; + struct list_head cli_list; + struct list_head ch_head; +}; + +struct qcn_sdio_ch_info { + struct sdio_al_xfer_result result; + struct sdio_al_client_handle *chandle; + struct sdio_al_channel_data ch_data; + struct sdio_al_channel_handle ch_handle; + struct list_head ch_list; + u32 crq_len; +}; + +struct qcn_sdio_rw_info { + struct list_head list; + u32 cid; + enum sdio_al_dma_direction dir; + void *buf; + size_t len; + void *ctxt; +}; + +#endif /* _QCN_SDIO_H_ */ + diff --git a/drivers/platform/msm/qcn/qcn_sdio_hwio.h b/drivers/platform/msm/qcn/qcn_sdio_hwio.h new file mode 100644 index 000000000000..9ea9b6a666bf --- /dev/null +++ b/drivers/platform/msm/qcn/qcn_sdio_hwio.h @@ -0,0 +1,296 @@ +/* Copyright (c) 2019 The Linux Foundation. All rights reserved. + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 and + * only version 2 as published by the Free Software Foundation. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + */ + +#ifndef _QCN_SDIO_HWIO_ +#define _QCN_SDIO_HWIO_ + +#define SDIO_QCN_DMA0_RX (0x0) +#define SDIO_QCN_DMA0_RX_MASK (0xffffffff) +#define SDIO_QCN_DMA0_RX_SHIFT (0) + +#define SDIO_QCN_DMA1_RX (0x4) +#define SDIO_QCN_DMA1_RX_MASK (0xffffffff) +#define SDIO_QCN_DMA1_RX_SHIFT (0) + +#define SDIO_QCN_DMA0_TX (0x8) +#define SDIO_QCN_DMA0_TX_MASK (0xffffffff) +#define SDIO_QCN_DMA0_TX_SHIFT (0) + +#define SDIO_QCN_DMA1_TX (0xC) +#define SDIO_QCN_DMA1_TX_MASK (0xffffffff) +#define SDIO_QCN_DMA1_TX_SHIFT (0) + + +#define SDIO_QCN_MC_DMA0_RX_CH0 (0x10) +#define SDIO_QCN_MC_DMA0_RX_CH0_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA0_RX_CH0_SHIFT (0) + +#define SDIO_QCN_MC_DMA0_RX_CH1 (0x14) +#define SDIO_QCN_MC_DMA0_RX_CH1_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA0_RX_CH1_SHIFT (0) + +#define SDIO_QCN_MC_DMA0_RX_CH2 (0x18) +#define SDIO_QCN_MC_DMA0_RX_CH2_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA0_RX_CH2_SHIFT (0) + +#define SDIO_QCN_MC_DMA0_RX_CH3 (0x1C) +#define SDIO_QCN_MC_DMA0_RX_CH3_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA0_RX_CH3_SHIFT (0) + +#define SDIO_QCN_MC_DMA0_TX_CH0 (0x30) +#define SDIO_QCN_MC_DMA0_TX_CH0_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA0_TX_CH0_SHIFT (0) + +#define SDIO_QCN_MC_DMA0_TX_CH1 (0x34) +#define SDIO_QCN_MC_DMA0_TX_CH1_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA0_TX_CH1_SHIFT (0) + +#define SDIO_QCN_MC_DMA0_TX_CH2 (0x38) +#define SDIO_QCN_MC_DMA0_TX_CH2_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA0_TX_CH2_SHIFT (0) + +#define SDIO_QCN_MC_DMA0_TX_CH3 (0x3C) +#define SDIO_QCN_MC_DMA0_TX_CH3_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA0_TX_CH3_SHIFT (0) + + +#define SDIO_QCN_MC_DMA1_RX_CH0 (0x50) +#define SDIO_QCN_MC_DMA1_RX_CH0_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA1_RX_CH0_SHIFT (0) + +#define SDIO_QCN_MC_DMA1_RX_CH1 (0x54) +#define SDIO_QCN_MC_DMA1_RX_CH1_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA1_RX_CH1_SHIFT (0) + +#define SDIO_QCN_MC_DMA1_RX_CH2 (0x58) +#define SDIO_QCN_MC_DMA1_RX_CH2_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA1_RX_CH2_SHIFT (0) + +#define SDIO_QCN_MC_DMA1_RX_CH3 (0x5C) +#define SDIO_QCN_MC_DMA1_RX_CH3_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA1_RX_CH3_SHIFT (0) + +#define SDIO_QCN_MC_DMA1_TX_CH0 (0x70) +#define SDIO_QCN_MC_DMA1_TX_CH0_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA1_TX_CH0_SHIFT (0) + +#define SDIO_QCN_MC_DMA1_TX_CH1 (0x74) +#define SDIO_QCN_MC_DMA1_TX_CH1_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA1_TX_CH1_SHIFT (0) + +#define SDIO_QCN_MC_DMA1_TX_CH2 (0x78) +#define SDIO_QCN_MC_DMA1_TX_CH2_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA1_TX_CH2_SHIFT (0) + +#define SDIO_QCN_MC_DMA1_TX_CH3 (0x7C) +#define SDIO_QCN_MC_DMA1_TX_CH3_MASK (0xffffffff) +#define SDIO_QCN_MC_DMA1_TX_CH3_SHIFT (0) + + +#define SDIO_QCN_CONFIG (0x100) +#define SDIO_QCN_CONFIG_OOB_MASK (0x00000002) +#define SDIO_QCN_CONFIG_OOB_SHIFT (1) +#define SDIO_QCN_CONFIG_QE_MASK (0x00000001) +#define SDIO_QCN_CONFIG_QE_SHIFT (0) + + +#define SDIO_QCN_HRQ_PUSH (0x104) +#define SDIO_QCN_HRQ_PUSH_UD_MASK (0xff000000) +#define SDIO_QCN_HRQ_PUSH_UD_SHIFT (24) +#define SDIO_QCN_HRQ_PUSH_BLK_MASK (0x00800000) +#define SDIO_QCN_HRQ_PUSH_BLK_SHIFT (23) +#define SDIO_QCN_HRQ_PUSH_TRANS_MASK (0x00400000) +#define SDIO_QCN_HRQ_PUSH_TRANS_SHIFT (22) +#define SDIO_QCN_HRQ_PUSH_BLK_CNT_MASK (0x00003ffe) +#define SDIO_QCN_HRQ_PUSH_BLK_CNT_SHIFT (1) +#define SDIO_QCN_HRQ_PUSH_DIR_MASK (0x00000001) +#define SDIO_QCN_HRQ_PUSH_DIR_SHIFT (0) +#define SDIO_QCN_HRQ_PUSH_UD1_MASK (0xffc00000) +#define SDIO_QCN_HRQ_PUSH_UD1_SHIFT (22) +#define SDIO_QCN_HRQ_PUSH_UD0_MASK (0x00003fff) +#define SDIO_QCN_HRQ_PUSH_UD0_SHIFT (0) + + +#define SDIO_QCN_CRQ_TX_PULL (0x108) +#define SDIO_QCN_CRQ_TX_PULL_REQ_ID_MASK (0x3c) +#define SDIO_QCN_CRQ_TX_PULL_REQ_ID_SHIFT (2) +#define SDIO_QCN_CRQ_TX_PULL_CH_NUM_MASK (0x3) +#define SDIO_QCN_CRQ_TX_PULL_CH_NUM_SHIFT (0) + +#define SDIO_QCN_CRQ_RX_PULL (0x108) +#define SDIO_QCN_CRQ_RX_PULL_REQ_ID_MASK (0x7ffc) +#define SDIO_QCN_CRQ_RX_PULL_REQ_ID_SHIFT (2) +#define SDIO_QCN_CRQ_RX_PULL_CH_NUM_MASK (0x3) +#define SDIO_QCN_CRQ_RX_PULL_CH_NUM_SHIFT (0) + + +#define SDIO_QCN_IRQ_STATUS (0x10C) +#define SDIO_QCN_IRQ_LOCAL_MASK (0x80) +#define SDIO_QCN_IRQ_LOCAL_SHIFT (7) +#define SDIO_QCN_IRQ_DAM_WACCESS_MASK (0x40) +#define SDIO_QCN_IRQ_DAM_WACCESS_SHIFT (6) +#define SDIO_QCN_IRQ_DAM_MISMATCH_MASK (0x20) +#define SDIO_QCN_IRQ_DAM_MISMATCH_SHIFT (5) +#define SDIO_QCN_IRQ_SYS_ERR_MASK (0x10) +#define SDIO_QCN_IRQ_SYS_ERR_SHIFT (4) +#define SDIO_QCN_IRQ_UNDERFLOW_MASK (0x8) +#define SDIO_QCN_IRQ_UNDERFLOW_SHIFT (3) +#define SDIO_QCN_IRQ_OVERFLOW_MASK (0x4) +#define SDIO_QCN_IRQ_OVERFLOW_SHIFT (2) +#define SDIO_QCN_IRQ_CH_MISMATCH_MASK (0x2) +#define SDIO_QCN_IRQ_CH_MISMATCH_SHIFT (1) +#define SDIO_QCN_IRQ_CRQ_READY_MASK (0x1) +#define SDIO_QCN_IRQ_CRQ_READY_SHIFT (0) + + +#define SDIO_QCN_IRQ_EN (0x110) +#define SDIO_QCN_IRQ_EN_LOCAL_MASK (0x80) +#define SDIO_QCN_IRQ_EN_LOCAL_SHIFT (7) +#define SDIO_QCN_IRQ_EN_DAM_WACCESS_MASK (0x40) +#define SDIO_QCN_IRQ_EN_DAM_WACCESS_SHIFT (6) +#define SDIO_QCN_IRQ_EN_DAM_MISMATCH_MASK (0x20) +#define SDIO_QCN_IRQ_EN_DAM_MISMATCH_SHIFT (5) +#define SDIO_QCN_IRQ_EN_SYS_ERR_MASK (0x10) +#define SDIO_QCN_IRQ_EN_SYS_ERR_SHIFT (4) +#define SDIO_QCN_IRQ_EN_UNDERFLOW_MASK (0x8) +#define SDIO_QCN_IRQ_EN_UNDERFLOW_SHIFT (3) +#define SDIO_QCN_IRQ_EN_OVERFLOW_MASK (0x4) +#define SDIO_QCN_IRQ_EN_OVERFLOW_SHIFT (2) +#define SDIO_QCN_IRQ_EN_CH_MISMATCH_MASK (0x2) +#define SDIO_QCN_IRQ_EN_CH_MISMATCH_SHIFT (1) +#define SDIO_QCN_IRQ_EN_CRQ_READY_MASK (0x1) +#define SDIO_QCN_IRQ_EN_CRQ_READY_SHIFT (0) + + +#define SDIO_QCN_IRQ_CLR (0x114) +#define SDIO_QCN_IRQ_CLR_LOCAL_MASK (0x80) +#define SDIO_QCN_IRQ_CLR_LOCAL_SHIFT (7) +#define SDIO_QCN_IRQ_CLR_DAM_WACCESS_MASK (0x40) +#define SDIO_QCN_IRQ_CLR_DAM_WACCESS_SHIFT (6) +#define SDIO_QCN_IRQ_CLR_DAM_MISMATCH_MASK (0x20) +#define SDIO_QCN_IRQ_CLR_DAM_MISMATCH_SHIFT (5) +#define SDIO_QCN_IRQ_CLR_SYS_ERR_MASK (0x10) +#define SDIO_QCN_IRQ_CLR_SYS_ERR_SHIFT (4) +#define SDIO_QCN_IRQ_CLR_UNDERFLOW_MASK (0x8) +#define SDIO_QCN_IRQ_CLR_UNDERFLOW_SHIFT (3) +#define SDIO_QCN_IRQ_CLR_OVERFLOW_MASK (0x4) +#define SDIO_QCN_IRQ_CLR_OVERFLOW_SHIFT (2) +#define SDIO_QCN_IRQ_CLR_CH_MISMATCH_MASK (0x2) +#define SDIO_QCN_IRQ_CLR_CH_MISMATCH_SHIFT (1) +#define SDIO_QCN_IRQ_CLR_CRQ_READY_MASK (0x1) +#define SDIO_QCN_IRQ_CLR_CRQ_READY_SHIFT (0) + + +#define SDIO_QCN_IRQ_FRC (0x11c) +#define SDIO_QCN_IRQ_FRC_LOCAL_MASK (0x80) +#define SDIO_QCN_IRQ_FRC_LOCAL_SHIFT (7) +#define SDIO_QCN_IRQ_FRC_DAM_WACCESS_MASK (0x40) +#define SDIO_QCN_IRQ_FRC_DAM_WACCESS_SHIFT (6) +#define SDIO_QCN_IRQ_FRC_DAM_MISMATCH_MASK (0x20) +#define SDIO_QCN_IRQ_FRC_DAM_MISMATCH_SHIFT (5) +#define SDIO_QCN_IRQ_FRC_SYS_ERR_MASK (0x10) +#define SDIO_QCN_IRQ_FRC_SYS_ERR_SHIFT (4) +#define SDIO_QCN_IRQ_FRC_UNDERFLOW_MASK (0x8) +#define SDIO_QCN_IRQ_FRC_UNDERFLOW_SHIFT (3) +#define SDIO_QCN_IRQ_FRC_OVERFLOW_MASK (0x4) +#define SDIO_QCN_IRQ_FRC_OVERFLOW_SHIFT (2) +#define SDIO_QCN_IRQ_FRC_CH_MISMATCH_MASK (0x2) +#define SDIO_QCN_IRQ_FRC_CH_MISMATCH_SHIFT (1) +#define SDIO_QCN_IRQ_FRC_CRQ_READY_MASK (0x1) +#define SDIO_QCN_IRQ_FRC_CRQ_READY_SHIFT (0) + + +#define SDIO_QCN_HRQ_ACK_PULL (0x118) +#define SDIO_QCN_HRQ_ACK_PULL_REQ_ID_MASK (0x3c0) +#define SDIO_QCN_HRQ_ACK_PULL_REQ_ID_SHIFT (6) +#define SDIO_QCN_HRQ_ACK_PULL_CH_NUM_MASK (0x3f) +#define SDIO_QCN_HRQ_ACK_PULL_CH_NUM_SHIFT (0) + + +#define SDIO_QCN_CRQ_PULL (0x118) +#define SDIO_QCN_CRQ_PULL_UD_MASK (0xff000000) +#define SDIO_QCN_CRQ_PULL_UD_SHIFT (24) +#define SDIO_QCN_CRQ_PULL_BLK_MASK (0x00800000) +#define SDIO_QCN_CRQ_PULL_BLK_SHIFT (23) +#define SDIO_QCN_CRQ_PULL_TRANS_MASK (0x00400000) +#define SDIO_QCN_CRQ_PULL_TRANS_SHIFT (22) +#define SDIO_QCN_CRQ_PULL_BLK_CNT_MASK (0x0007ffc0) +#define SDIO_QCN_CRQ_PULL_BLK_CNT_SHIFT (6) +#define SDIO_QCN_CRQ_PULL_CH_NUM_MASK (0x3f) +#define SDIO_QCN_CRQ_PULL_CH_NUM_SHIFT (0) + + +#define SDIO_QCN_LOW_PWR (0x120) +#define SDIO_QCN_LOW_PWR_GO_MASK (0x1) +#define SDIO_QCN_LOW_PWR_GO_SHIFT (0) + + +#define SDIO_QCN_HOST_TRANS_REG0 (0x124) +#define SDIO_QCN_HOST_TRANS_REG0_MASK (0xffffffff) +#define SDIO_QCN_HOST_TRANS_REG0_SHIFT (0) +#define SDIO_QCN_HOST_TRANS_REG1 (0x128) +#define SDIO_QCN_HOST_TRANS_REG1_MASK (0xffffffff) +#define SDIO_QCN_HOST_TRANS_REG1_SHIFT (0) +#define SDIO_QCN_HOST_TRANS_REG2 (0x12C) +#define SDIO_QCN_HOST_TRANS_REG2_MASK (0xffffffff) +#define SDIO_QCN_HOST_TRANS_REG2_SHIFT (0) +#define SDIO_QCN_HOST_TRANS_REG3 (0x130) +#define SDIO_QCN_HOST_TRANS_REG3_MASK (0xffffffff) +#define SDIO_QCN_HOST_TRANS_REG3_SHIFT (0) + + +#define SDIO_QCN_CLIENT_TRANS_REG0 (0x144) +#define SDIO_QCN_CLIENT_TRANS_REG0_MASK (0xffffffff) +#define SDIO_QCN_CLIENT_TRANS_REG0_SHIFT (0) +#define SDIO_QCN_CLIENT_TRANS_REG1 (0x148) +#define SDIO_QCN_CLIENT_TRANS_REG1_MASK (0xffffffff) +#define SDIO_QCN_CLIENT_TRANS_REG1_SHIFT (0) +#define SDIO_QCN_CLIENT_TRANS_REG2 (0x14C) +#define SDIO_QCN_CLIENT_TRANS_REG2_MASK (0xffffffff) +#define SDIO_QCN_CLIENT_TRANS_REG2_SHIFT (0) +#define SDIO_QCN_CLIENT_TRANS_REG3 (0x150) +#define SDIO_QCN_CLIENT_TRANS_REG3_MASK (0xffffffff) +#define SDIO_QCN_CLIENT_TRANS_REG3_SHIFT (0) + + +#define SDIO_QCN_LOCAL_INFO (0x00000164) +#define SDIO_QCN_LOCAL_INFO_MASK (0xffffffff) +#define SDIO_QCN_LOCAL_INFO_SHIFT (0) + +#define SDIO_QCN_SDIOC_CONFIG (0x200) +#define SDIO_QCN_SDIOC_CONFIG_MC_DMA1_DIR_BMSK (0x40000) +#define SDIO_QCN_SDIOC_CONFIG_MC_DMA1_DIR_SHFT (0x12) +#define SDIO_QCN_SDIOC_CONFIG_MC_DMA0_DIR_BMSK (0x20000) +#define SDIO_QCN_SDIOC_CONFIG_MC_DMA0_DIR_SHFT (0x11) +#define SDIO_QCN_SDIOC_CONFIG_MC_DMA_EN_BMSK (0x10000) +#define SDIO_QCN_SDIOC_CONFIG_MC_DMA_EN_SHFT (0x10) +#define SDIO_QCN_SDIOC_CONFIG_SW_CLK_EN_BMSK (0x80) +#define SDIO_QCN_SDIOC_CONFIG_SW_CLK_EN_SHFT (0x7) +#define SDIO_QCN_SDIOC_CONFIG_WR_HRQ_MEM_BMSK (0x40) +#define SDIO_QCN_SDIOC_CONFIG_WR_HRQ_MEM_SHFT (0x6) +#define SDIO_QCN_SDIOC_CONFIG_CLR_CRQ_PUSH_BMSK (0x20) +#define SDIO_QCN_SDIOC_CONFIG_CLR_CRQ_PUSH_SHFT (0x5) +#define SDIO_QCN_SDIOC_CONFIG_DIS_LEN_CHK_BMSK (0x10) +#define SDIO_QCN_SDIOC_CONFIG_DIS_LEN_CHK_SHFT (0x4) +#define SDIO_QCN_SDIOC_CONFIG_WRAP_ERROR_BMSK (0x8) +#define SDIO_QCN_SDIOC_CONFIG_WRAP_ERROR_SHFT (0x3) +#define SDIO_QCN_SDIOC_CONFIG_ADMA_64BIT_BMSK (0x4) +#define SDIO_QCN_SDIOC_CONFIG_ADMA_64BIT_SHFT (0x2) +#define SDIO_QCN_SDIOC_CONFIG_ADMA_INT_BMSK (0x2) +#define SDIO_QCN_SDIOC_CONFIG_ADMA_INT_SHFT (0x1) +#define SDIO_QCN_SDIOC_CONFIG_DMA_ENABLE_BMSK (0x1) +#define SDIO_QCN_SDIOC_CONFIG_DMA_ENABLE_SHFT (0x0) + + + +#endif /* _QCN_SDIO_HWIO_ */ + |
