/* * Copyright (C) 2017 ALLWINNERTECH TECHNOLOGY CO., LTD. All rights reserved. * * Redistribution and use in source and binary forms, with or without * modification, are permitted provided that the following conditions * are met: * 1. Redistributions of source code must retain the above copyright * notice, this list of conditions and the following disclaimer. * 2. Redistributions in binary form must reproduce the above copyright * notice, this list of conditions and the following disclaimer in the * documentation and/or other materials provided with the * distribution. * 3. Neither the name of ALLWINNERTECH TECHNOLOGY CO., LTD. nor the names of * its contributors may be used to endorse or promote products derived * from this software without specific prior written permission. * * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS * "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT * LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR * A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT * OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, * SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT * LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, * DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY * THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT * (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE * OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. */ #include "stdint.h" #include "sys/param.h" #include "sys/endian.h" #include "hal_sdhost.h" #include "sdmmc.h" #include "sdio.h" #include "_sd_define.h" #include "_core.h" #include "_sdio.h" #ifdef CONFIG_USE_SDIO //inline int mmc_io_rw_direct(struct mmc_card *card, int32_t write, uint32_t fn, uint32_t addr, uint8_t in, uint8_t *out); #define SDIO_MIN(a, b) MIN(a, b) /* * Calculate the maximum byte mode transfer size */ static inline unsigned int sdio_max_byte_size(struct sdio_func *func) { unsigned mval = func->card->host->max_blk_size; //if (mmc_blksz_for_byte_mode(func->card)) // mval = min(mval, func->cur_blksize); //else mval = SDIO_MIN(mval, func->max_blksize); //if (mmc_card_broken_byte_mode_512(func->card)) return SDIO_MIN(mval, 511u); return SDIO_MIN(mval, 512u); /* maximum size for byte mode */ } #ifdef CONFIG_SDIO_USE_FUNS static int32_t cistpl_vers_1(struct mmc_card *card, struct sdio_func *func, const unsigned char *buf, uint32_t size) { uint32_t i, nr_strings; char **buffer, *string; /* Find all null-terminated (including zero length) strings in the TPLLV1_INFO field. Trailing garbage is ignored. */ buf += 2; size -= 2; nr_strings = 0; for (i = 0; i < size; i++) { if (buf[i] == 0xff) break; if (buf[i] == 0) nr_strings++; } if (nr_strings == 0) return 0; size = i; buffer = HAL_Malloc(sizeof(char *) * nr_strings + size); SDC_Memset(buffer, 0, sizeof(char *) * nr_strings + size); if (!buffer) return -1; string = (char*)(buffer + nr_strings); for (i = 0; i < nr_strings; i++) { buffer[i] = string; strcpy(string, (char *)buf); string += strlen(string) + 1; buf += strlen((char *)buf) + 1; } if (func) { func->num_info = nr_strings; func->info = (const char**)buffer; } else { card->num_info = nr_strings; card->info = (const int8_t **)buffer; } return 0; } static int32_t cistpl_manfid(struct mmc_card *card, struct sdio_func *func, const unsigned char *buf, unsigned size) { uint32_t vendor, device; /* TPLMID_MANF */ vendor = buf[0] | (buf[1] << 8); /* TPLMID_CARD */ device = buf[2] | (buf[3] << 8); if (func) { func->vendor = vendor; func->device = device; } else { card->cis.vendor = vendor; card->cis.device = device; } return 0; } static const unsigned char speed_val[16] = { 0, 10, 12, 13, 15, 20, 25, 30, 35, 40, 45, 50, 55, 60, 70, 80 }; static const uint32_t speed_unit[8] = { 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 }; typedef int32_t (tpl_parse_t)(struct mmc_card *, struct sdio_func *, const unsigned char *, unsigned); struct cis_tpl { unsigned char code; unsigned char min_size; tpl_parse_t *parse; }; static int32_t cis_tpl_parse(struct mmc_card *card, struct sdio_func *func, const char *tpl_descr, const struct cis_tpl *tpl, int32_t tpl_count, uint8_t code, const uint8_t *buf, uint32_t size) { int32_t i, ret = 0; /* look for a matching code in the table */ for (i = 0; i < tpl_count; i++, tpl++) { if (tpl->code == code) break; } if (i < tpl_count) { if (size >= tpl->min_size) { if (tpl->parse) ret = tpl->parse(card, func, buf, size); else ret = 0; /* known tuple, not parsed */ } else { /* invalid tuple */ ret = -1; } if (ret) { SD_LOGW("%s: bad %s tuple 0x%02x (%u bytes)\n", __func__, tpl_descr, (unsigned int)code, (unsigned int)size); } } else { /* warn about unknown tuples */ SD_LOGW("%s: queuing unknown CIS tuple 0x%02x (%u bytes)\n", __func__, (unsigned int)code, (unsigned int)size); } return ret; } static int32_t cistpl_funce_common(struct mmc_card *card, struct sdio_func *func, const unsigned char *buf, unsigned size) { /* Only valid for the common CIS (function 0) */ if (func) return -1; /* TPLFE_FN0_BLK_SIZE */ card->cis.blksize = buf[1] | (buf[2] << 8); /* TPLFE_MAX_TRAN_SPEED */ card->cis.max_dtr = speed_val[(buf[3] >> 3) & 15] * speed_unit[buf[3] & 7]; return 0; } static int32_t cistpl_funce_func(struct mmc_card *card, struct sdio_func *func, const unsigned char *buf, uint32_t size) { uint32_t vsn; uint32_t min_size; /* Only valid for the individual function's CIS (1-7) */ if (!func) return -1; /* * This tuple has a different length depending on the SDIO spec * version. */ vsn = func->card->cccr.sdio_vsn; min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42; if (size < min_size) return -1; /* TPLFE_MAX_BLK_SIZE */ func->max_blksize = buf[12] | (buf[13] << 8); /* TPLFE_ENABLE_TIMEOUT_VAL, present in ver 1.1 and above */ #ifdef CONFIG_MMC_ERASE if (vsn > SDIO_SDIO_REV_1_00) func->enable_timeout = (buf[28] | (buf[29] << 8)) * 10; else func->enable_timeout = jiffies_to_msecs(HZ); #endif return 0; } /* * Known TPLFE_TYPEs table for CISTPL_FUNCE tuples. * * Note that, unlike PCMCIA, CISTPL_FUNCE tuples are not parsed depending * on the TPLFID_FUNCTION value of the previous CISTPL_FUNCID as on SDIO * TPLFID_FUNCTION is always hardcoded to 0x0C. */ static const struct cis_tpl cis_tpl_funce_list[] = { { (unsigned char)0x00, (unsigned char)4, (tpl_parse_t *)cistpl_funce_common }, { (unsigned char)0x01, (unsigned char)0, (tpl_parse_t *)cistpl_funce_func }, { (unsigned char)0x04, (unsigned char)(1+1+6), NULL/* CISTPL_FUNCE_LAN_NODE_ID */ }, }; static int32_t cistpl_funce(struct mmc_card *card, struct sdio_func *func, const unsigned char *buf, unsigned size) { if (size < 1) return -1; return cis_tpl_parse(card, func, "CISTPL_FUNCE", cis_tpl_funce_list, HAL_ARRAY_SIZE(cis_tpl_funce_list), buf[0], buf, size); } /* Known TPL_CODEs table for CIS tuples */ static const struct cis_tpl cis_tpl_list[] = { { 0x15, 3, (tpl_parse_t *)cistpl_vers_1 }, { 0x20, 4, (tpl_parse_t *)cistpl_manfid }, { 0x21, 2, NULL/* cistpl_funcid */ }, { 0x22, 0, (tpl_parse_t *)cistpl_funce }, }; static int32_t sdio_read_cis(struct mmc_card *card, struct sdio_func *func) { int32_t ret; struct sdio_func_tuple *this, **prev; uint32_t i, ptr = 0; /* * Note that this works for the common CIS (function number 0) as * well as a function's CIS * since SDIO_CCCR_CIS and SDIO_FBR_CIS * have the same offset. */ for (i = 0; i < 3; i++) { uint8_t x, fn; if (func) fn = func->num; else fn = 0; ret = mmc_io_rw_direct(card, 0, FN0, SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x); if (ret) return ret; ptr |= x << (i * 8); } if (func) prev = &func->tuples; else prev = &card->tuples; if (*prev) { SD_LOGE("%s,%d no prev found\n", __func__, __LINE__); return -1; } do { uint8_t tpl_code, tpl_link; ret = mmc_io_rw_direct(card, 0, FN0, ptr++, 0, &tpl_code); if (ret) break; /* 0xff means we're done */ if (tpl_code == 0xff) break; /* null entries have no link field or data */ if (tpl_code == 0x00) continue; ret = mmc_io_rw_direct(card, 0, FN0, ptr++, 0, &tpl_link); if (ret) break; /* a size of 0xff also means we're done */ if (tpl_link == 0xff) break; this = HAL_Malloc(sizeof(*this) + tpl_link); if (!this) return -1; for (i = 0; i < tpl_link; i++) { ret = mmc_io_rw_direct(card, 0, FN0, ptr + i, 0, &this->data[i]); if (ret) break; } if (ret) { HAL_Free(this); break; } /* Try to parse the CIS tuple */ ret = cis_tpl_parse(card, func, "CIS", cis_tpl_list, HAL_ARRAY_SIZE(cis_tpl_list), tpl_code, this->data, tpl_link); if (ret == -1) { /* * The tuple is unknown or known but not parsed. * Queue the tuple for the function driver. */ this->next = NULL; this->code = tpl_code; this->size = tpl_link; *prev = this; prev = &this->next; /* keep on analyzing tuples */ ret = 0; } else { /* * We don't need the tuple anymore if it was * successfully parsed by the SDIO core or if it is * not going to be queued for a driver. */ HAL_Free(this); } ptr += tpl_link; } while (!ret); /* * Link in all unknown tuples found in the common CIS so that * drivers don't have to go digging in two places. */ if (func) *prev = card->tuples; return ret; } int32_t sdio_read_common_cis(struct mmc_card *card) { return sdio_read_cis(card, NULL); } void sdio_free_common_cis(struct mmc_card *card) { struct sdio_func_tuple *tuple, *victim; tuple = card->tuples; while (tuple) { victim = tuple; tuple = tuple->next; HAL_Free(victim); } card->tuples = NULL; } void sdio_free_func_cis(struct sdio_func *func) { struct sdio_func_tuple *tuple, *victim; tuple = func->tuples; while (tuple && tuple != func->card->tuples) { victim = tuple; tuple = tuple->next; HAL_Free(victim); } func->tuples = NULL; } int sdio_read_func_cis(struct sdio_func *func) { int ret; ret = sdio_read_cis(func->card, func); if (ret) return ret; /* * Since we've linked to tuples in the card structure, * we must make sure we have a reference to it. */ // get_device(&func->card->dev); /* * Vendor/device id is optional for function CIS, so * copy it from the card structure as needed. */ if (func->vendor == 0) { func->vendor = func->card->cis.vendor; func->device = func->card->cis.device; } return 0; } static int32_t sdio_read_fbr(struct sdio_func *func) { int32_t ret; unsigned char data; //if (mmc_card_nonstd_func_interface(func->card)) { // func->class = SDIO_CLASS_NONE; // return 0; //} ret = mmc_io_rw_direct(func->card, 0, FN0, SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data); if (ret) goto out; data &= 0x0f; if (data == 0x0f) { ret = mmc_io_rw_direct(func->card, 0, FN0, SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data); if (ret) goto out; } func->class = data; out: return ret; } #endif static int32_t mmc_send_io_op_cond(struct mmc_card *card, uint32_t ocr, uint32_t *rocr) { struct mmc_command cmd = {0}; uint32_t i, err = 0; cmd.opcode = SD_IO_SEND_OP_COND; cmd.arg = ocr; cmd.flags = MMC_RSP_SPI_R4 | MMC_RSP_R4 | MMC_CMD_BCR; for (i = 1000; i; i--) { err = mmc_wait_for_cmd(card->host, &cmd); if (err) break; /* if we're just probing, do a single pass */ if (ocr == 0) break; /* otherwise wait until reset completes */ if (cmd.resp[0] & MMC_CARD_BUSY) break; err = -1; mmc_mdelay(1); } if (rocr) *rocr = cmd.resp[0]; return err; } int32_t mmc_io_rw_direct_host(struct mmc_host *host, int32_t write, uint32_t fn, uint32_t addr, uint8_t in, uint8_t *out) { struct mmc_command cmd = {0}; int32_t err; uint32_t temp; if (!host) { SD_LOGE_RAW(ROM_ERR_MASK, "%s,%d no host exist!\n", __func__, __LINE__); return -1; } if (fn > 7) { SDC_LOGE("%s,%d wrong fn:%ld!\n", __func__, __LINE__, HAL_PR_SZ_L(fn)); return -1; } SDC_LOGD("%s,fun %ld, raw %d ,addr %lx,wrte data %x\n", write?"Write":"Read", HAL_PR_SZ_L(fn), (write && out)?1:0, HAL_PR_SZ_L(addr), in); /* sanity check */ if (addr & ~0x1FFFF) return -1; cmd.opcode = SD_IO_RW_DIRECT; cmd.arg = write ? 0x80000000 : 0x00000000; cmd.arg |= fn << 28; cmd.arg |= (write && out) ? 0x08000000 : 0x00000000; cmd.arg |= addr << 9; cmd.arg |= in; cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_AC; err = mmc_wait_for_cmd(host, &cmd); if (err) return err; temp = cmd.resp[0]; if (temp & 0xcf00) { SDC_LOGE("CMD52 %s Operation Error:%s%s%s%s%s !\n", write ? "wirte" : "read", temp&0x8000 ? " CMDCRCErr" : "", temp&0x4000 ? " ILLEGALCmd" : "", temp&0x800 ? " GenERR" : "", temp&0x200 ? " FucNumErr" : "", temp&0x100 ? " ArgOutRange" : ""); } if (cmd.resp[0] & R5_ERROR) return -1; if (cmd.resp[0] & R5_FUNCTION_NUMBER) return -1; if (cmd.resp[0] & R5_OUT_OF_RANGE) return -1; if (out) { *out = cmd.resp[0] & 0xFF; } return 0; } int mmc_io_rw_direct(struct mmc_card *card, int32_t write, uint32_t fn, uint32_t addr, uint8_t in, uint8_t *out) { if (!card) { SD_LOGE_RAW(ROM_ERR_MASK, "%s,%d no card exist!\n", __func__, __LINE__); return -1; } return mmc_io_rw_direct_host(card->host, write, fn, addr, in, out); } /** * SDIO Spec: CMD53 is 48 bit long. read or write multiple I/O registers with a * single command * ---------------------------------------------------------------------------- * S|D|CMDIND|R|FUN|BLOCK MODE|OP CODE|REGISTER ADDRESS|BYTE/BLOCK COUNT|CRC7|E * ---------------------------------------------------------------------------- * @ret : RET_OK, RET_FAIL */ int32_t mmc_io_rw_extended(struct mmc_card *card, uint8_t write, uint8_t fn, uint32_t addr, uint32_t incr_addr, const void *buf, uint32_t blocks, uint32_t blksz) { struct mmc_request mrq; struct mmc_command cmd = {0}; struct mmc_data data = {0}; struct scatterlist sg = {0}; int32_t ret; if (fn > 7) { SD_LOGE("%s,%d err fn:%d\n", __func__, __LINE__, fn); return -1; } SDC_WARN_ON(blksz == 0); /* sanity check */ if (addr & ~0x1FFFF) return -1; cmd.opcode = SD_IO_RW_EXTENDED; /* cmd53 --index */ cmd.arg = write ? 0x80000000 : 0x00000000; /* write/read --flag */ cmd.arg |= fn << 28; /* function number */ cmd.arg |= incr_addr ? 0x04000000 : 0x00000000; /* incrementing address */ cmd.arg |= addr << 9; if (blocks == 0 ) cmd.arg |= (blksz == 512) ? 0 : blksz; /* byte mode */ else cmd.arg |= 0x08000000| blocks; /* block mode */ cmd.flags = MMC_RSP_SPI_R5 | MMC_RSP_R5 | MMC_CMD_ADTC; data.blksz = blksz; data.blocks = blocks ? blocks : 1; data.flags = write ? MMC_DATA_WRITE : MMC_DATA_READ; data.sg = &sg; data.sg_len = 1; sg.buffer = (void *)buf; sg.len = data.blksz * data.blocks; mrq.cmd = &cmd; mrq.data = &data; SD_LOGD("starting %s CMD%ld add %lx,arg 0x%08lx flags %lx\n", write?"Write":"Read", HAL_PR_SZ_L(cmd.opcode), HAL_PR_SZ_L(addr), HAL_PR_SZ_L(cmd.arg), HAL_PR_SZ_L(cmd.flags)); SD_LOGD("blksz %ld blocks %ld flags %lx\n", HAL_PR_SZ_L(data.blksz), HAL_PR_SZ_L(data.blocks), HAL_PR_SZ_L(data.flags)); ret = mmc_wait_for_req(card->host, &mrq); if (ret) { SD_LOGE("SDC write operation failed\n"); } if (cmd.resp[0] & R5_ERROR) return -1; if (cmd.resp[0] & R5_FUNCTION_NUMBER) return -1; if (cmd.resp[0] & R5_OUT_OF_RANGE) return -1; // printf("%s,%d,%x\n", __FUNCTION__,__LINE__, sg.buffer); return ret; } /** * read/write IO memory in Block/Stream mode with fixed/increaming address * This will split an arbitrarily sized data transfer into several IO_RW_EXTENDED commands. * @func_num: function number * @write: IOMEM_WR:write, IOMEM_RD:read * @addr: address of register read from or write to * @incr_addr: 1: in, 0: not, used for fifio * @buf: pointer to data buffer * @size: block counter(block mode)/byte counter(byte mode) * @ret: RET_OK, RET_FAIL */ static int32_t sdio_io_rw_ext_helper(struct mmc_card *card, uint8_t func_num, int32_t write, uint32_t addr, uint32_t incr_addr, uint8_t *buf, uint32_t size) { uint32_t remainder = size; int32_t ret = -1; uint32_t blocks; int32_t fn_bsize; uint8_t *buf_tmp = buf; /* Do the bulk of the transfer using block mode (if supported). */ //if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) { fn_bsize = card->fn_bsize[func_num]; /* Do the bulk of the transfer using block mode (if supported). */ while (remainder >= fn_bsize) { blocks = remainder / fn_bsize; size = blocks * fn_bsize; ret = mmc_io_rw_extended(card, write, func_num, addr, incr_addr, buf, blocks, fn_bsize); if (ret) { SD_LOGE("%s,%d %s IO%x [%lx] SZ:%ld Err:%ld !!\n", __func__, __LINE__, write?"W":"R", func_num, HAL_PR_SZ_L(addr), HAL_PR_SZ_L(size), HAL_PR_SZ_L(ret)); return ret; } remainder -= size; buf += size; if (incr_addr) addr += size; } /* Write the remainder using byte mode. */ while (remainder > 0) { size = SDIO_MIN(remainder, 512); ret = mmc_io_rw_extended(card, write, func_num, addr, incr_addr, buf, 0, size); if (ret) { SD_LOGE("%s,%d %s IO%x [%lx] SZ:%ld Err:%ld !!\n", __func__, __LINE__, write?"W":"R", func_num, HAL_PR_SZ_L(addr), HAL_PR_SZ_L(size), HAL_PR_SZ_L(ret)); return ret; } remainder -= size; buf += size; if (incr_addr) addr += size; } // printf("%s,%d,%x %x,%x\n", __FUNCTION__,__LINE__, buf_tmp,(uint32_t)buf_tmp, (uint32_t)buf); return ret; } /** * sdio_readb - read a single byte from a SDIO function * @func_num: SDIO function to access * @addr: address to read * @err_ret: optional status value from transfer * * Reads a single byte from the address space of a given SDIO * function. If there is a problem reading the address, 0xff * is returned and @err_ret will contain the error code. */ uint8_t sdio_readb(struct mmc_card *card, uint32_t func_num, uint32_t addr, int32_t *err_ret) { int32_t ret; uint8_t val; if (err_ret) *err_ret = 0; ret = mmc_io_rw_direct_host(card->host, 0, func_num, addr, 0, &val); if (ret) { if (err_ret) *err_ret = ret; return 0xFF; } return val; } /** * sdio_writeb - write a single byte to a SDIO function * @func_num: SDIO function to access * @b: byte to write * @addr: address to write to * @err_ret: optional status value from transfer * * Writes a single byte to the address space of a given SDIO * function. @err_ret will contain the status of the actual * transfer. */ void sdio_writeb(struct mmc_card *card, uint32_t func_num, const uint8_t b, uint32_t addr, int32_t *err_ret) { int32_t ret; ret = mmc_io_rw_direct_host(card->host, 1, func_num, addr, b, NULL); if (err_ret) *err_ret = ret; } /** * sdio_readw - read a 16 bit integer from a SDIO function * @func: SDIO function to access * @addr: address to read * @err_ret: optional status value from transfer * * Reads a 16 bit integer from the address space of a given SDIO * function. If there is a problem reading the address, 0xffff * is returned and @err_ret will contain the error code. */ uint16_t sdio_readw(struct sdio_func *func, unsigned int addr, int *err_ret) { int ret; if (err_ret) *err_ret = 0; ret = sdio_memcpy_fromio(func->card, func->num,func->tmpbuf, addr, 2); if (ret) { if (err_ret) *err_ret = ret; return 0xFFFF; } //return le16_to_cpup((__le16 *)func->tmpbuf); return le16toh( *((uint16_t *)func->tmpbuf)); } //EXPORT_SYMBOL_GPL(sdio_readw); /** * sdio_writew - write a 16 bit integer to a SDIO function * @func: SDIO function to access * @b: integer to write * @addr: address to write to * @err_ret: optional status value from transfer * * Writes a 16 bit integer to the address space of a given SDIO * function. @err_ret will contain the status of the actual * transfer. */ void sdio_writew(struct sdio_func *func, uint16_t b, unsigned int addr, int *err_ret) { int ret; //*(__le16 *)func->tmpbuf = cpu_to_le16(b); *(int16_t *)func->tmpbuf = cpu_to_le16(b); ret = sdio_memcpy_toio(func->card, func->num, addr, func->tmpbuf, 2); if (err_ret) *err_ret = ret; } //EXPORT_SYMBOL_GPL(sdio_writew); /** * sdio_readl - read a 32 bit integer from a SDIO function * @func: SDIO function to access * @addr: address to read * @err_ret: optional status value from transfer * * Reads a 32 bit integer from the address space of a given SDIO * function. If there is a problem reading the address, * 0xffffffff is returned and @err_ret will contain the error * code. */ uint32_t sdio_readl(struct sdio_func *func, unsigned int addr, int *err_ret) { int ret; if (err_ret) *err_ret = 0; ret = sdio_memcpy_fromio(func->card, func->num,func->tmpbuf, addr, 4); if (ret) { if (err_ret) *err_ret = ret; return 0xFFFFFFFF; } //return le32_to_cpup((__le32 *)func->tmpbuf); return le32toh(*((uint32_t *)func->tmpbuf)); } //EXPORT_SYMBOL_GPL(sdio_readl); /** * sdio_writel - write a 32 bit integer to a SDIO function * @func: SDIO function to access * @b: integer to write * @addr: address to write to * @err_ret: optional status value from transfer * * Writes a 32 bit integer to the address space of a given SDIO * function. @err_ret will contain the status of the actual * transfer. */ void sdio_writel(struct sdio_func *func, uint32_t b, unsigned int addr, int *err_ret) { int ret; //*(__le32 *)func->tmpbuf = cpu_to_le32(b); *(int32_t *)func->tmpbuf = cpu_to_le32(b); ret = sdio_memcpy_toio(func->card, func->num, addr, func->tmpbuf, 4); if (err_ret) *err_ret = ret; } //EXPORT_SYMBOL_GPL(sdio_writel); /** * sdio_enable_func - enables a SDIO function for usage * @func: SDIO function to enable * * Powers up and activates a SDIO function so that register * access is possible. */ int32_t sdio_enable_func(struct mmc_card *card, uint32_t func_num) { uint8_t dat; mmc_io_rw_direct_host(card->host, 0, FN0, SDIO_CCCR_IOEx, 0, &dat); dat |= (1 << func_num); /* keep the value of other FN's IO enb state */ mmc_io_rw_direct_host(card->host, 1, FN0, SDIO_CCCR_IOEx, dat, &dat); if (!(dat & (1 << func_num))) { SD_LOGE("%s,%d IO%ld Enable failed !!\n", __func__, __LINE__, HAL_PR_SZ_L(func_num)); return -1; } SD_LOGD("IO%ld is enabled !!\n", HAL_PR_SZ_L(func_num)); mmc_io_rw_direct_host(card->host, 0, FN0, SDIO_CCCR_IORx, 0, &dat); if (!(dat & (1 << func_num))) { SD_LOGD("%s,%d IO%ld is not ready !!\n", __func__, __LINE__, HAL_PR_SZ_L(func_num)); return -1; } SD_LOGD("IO%ld is ready !!\n", HAL_PR_SZ_L(func_num)); return 0; } /** * Disable IOx's Function. * @io_num: 1~7 */ int32_t sdio_disable_func(struct mmc_card *card, uint32_t func_num) { uint8_t dat; mmc_io_rw_direct_host(card->host, 0, FN0, SDIO_CCCR_IOEx, 0, &dat); dat &= ~(1 << func_num); /* keep the value of other FN's IO enb state */ mmc_io_rw_direct_host(card->host, 1, FN0, SDIO_CCCR_IOEx, dat, &dat); if (dat & (1 << func_num)) { SD_LOGE("%s,%d IO%ld Disable failed !!\n", __func__, __LINE__, HAL_PR_SZ_L(func_num)); return -1; } SD_LOGD("IO%ld is Disabled !!\n", HAL_PR_SZ_L(func_num)); return 0; } int32_t sdio_set_block_size(struct mmc_card *card, uint32_t fn_num, uint32_t blksz) { int32_t ret; #if 0 if ((blksz == 0) || card->fn_bsize[fn_num] == blksz) { SD_LOGW("block size has beed set to %d !!\n", blksz); return -1; } #endif if ((blksz == 0)) { SD_LOGW("block size has beed set to %ld !!\n", HAL_PR_SZ_L(blksz)); return -1; } /* sdio block size set in FN0 CCIA */ sdio_writeb(card, FN0, blksz & 0xff, SDIO_FBR_BASE(fn_num) + SDIO_FBR_BLKSIZE, &ret); if (ret) return ret; sdio_writeb(card, FN0, (blksz >> 8) & 0xff, SDIO_FBR_BASE(fn_num) + SDIO_FBR_BLKSIZE + 1, &ret); if (ret) return ret; card->fn_bsize[fn_num] = blksz; SD_LOGD("sdio FN%ld set block size to %ld\n", HAL_PR_SZ_L(fn_num), HAL_PR_SZ_L(blksz)); return 0; } /* * Test if the card supports high-speed mode and, if so, switch to it. */ static int32_t mmc_sdio_switch_hs(struct mmc_card *card, uint32_t enable) { int32_t ret; uint8_t speed; if (!(card->host->caps & MMC_CAP_SD_HIGHSPEED)) return 0; if (!card->cccr.high_speed) return 0; ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_SPEED, 0, &speed); if (ret) return ret; if (!(speed & SDIO_SPEED_SHS)) { SD_LOGE("%s not support high speed mode!\n", __func__); return -1; } if (enable) { speed |= SDIO_SPEED_EHS; } else { speed &= ~SDIO_SPEED_EHS; } ret = mmc_io_rw_direct(card, 1, FN0, SDIO_CCCR_SPEED, speed, NULL); if(ret) return ret; return 1; } uint32_t mmc_sdio_get_max_clock(struct mmc_card *card) { unsigned max_dtr; if (mmc_card_highspeed(card)) { /* * The SDIO specification doesn't mention how * the CIS transfer speed register relates to * high-speed, but it seems that 50 MHz is * mandatory. */ max_dtr = 50000000; } else { max_dtr = card->cis.max_dtr; } #ifdef CONFIG_USE_SDIO_COMBO if (card->type == MMC_TYPE_SD_COMBO) max_dtr = min(max_dtr, mmc_sd_get_max_clock(card)); #endif return max_dtr; } #ifdef CONFIG_SDIO_USE_FUNS /* * Allocate and initialise a new SDIO function structure. */ struct sdio_func *sdio_alloc_func(struct mmc_card *card) { struct sdio_func *func; func = HAL_Malloc(sizeof(struct sdio_func)); if (!func) return NULL; SDC_Memset(func, 0, sizeof(struct sdio_func)); func->card = card; return func; } /* * Register a new SDIO function with the driver model. */ int32_t sdio_add_func(struct sdio_func *func, uint32_t id) { // fprintf(func->name, "%s:%d", id, func->num); sdio_func_set_present(func); return 0; } /* * Unregister a SDIO function with the driver model, and * (eventually) free it. * This function can be called through error paths where sdio_add_func() was * never executed (because a failure occurred at an earlier point). */ void sdio_remove_func(struct sdio_func *func) { if (!sdio_func_present(func)) return; } static int32_t sdio_init_func(struct mmc_card *card, uint32_t fn) { int32_t ret; struct sdio_func *func; if (fn > SDIO_MAX_FUNCS) { SD_LOGE("%s,%d wrong fn:%ld!\n", __func__, __LINE__, HAL_PR_SZ_L(fn)); return -1; } func = sdio_alloc_func(card); if (!func) return -1; func->num = fn; if (!(card->quirks & MMC_QUIRK_NONSTD_SDIO)) { ret = sdio_read_fbr(func); if (ret) goto fail; ret = sdio_read_func_cis(func); if (ret) goto fail; } else { func->vendor = func->card->cis.vendor; func->device = func->card->cis.device; func->max_blksize = func->card->cis.blksize; } card->sdio_func[fn - 1] = func; printf("func address %p\n",func); return 0; fail: /* * It is okay to remove the function here even though we hold * the host lock as we haven't registered the device yet. */ sdio_remove_func(func); return ret; } /* * Host is being removed. Free up the current card. */ int mmc_sdio_remove(struct mmc_host *host) { int i; if (!host) { SD_LOGE_RAW(ROM_ERR_MASK, "%s,%d no host exist!\n", __func__, __LINE__); return -1; } if (!host->card) { SDC_LOGE("%s,%d no card exist!\n", __func__, __LINE__); return -1; } for (i = 0;i < host->card->sdio_funcs;i++) { if (host->card->sdio_func[i]) { sdio_remove_func(host->card->sdio_func[i]); host->card->sdio_func[i] = NULL; } } //mmc_remove_card(host->card); /* do nothing */ host->card = NULL; return 0; } #endif static int32_t sdio_read_cccr(struct mmc_card *card, uint32_t ocr) { int32_t ret; int32_t cccr_vsn; int32_t uhs = ocr & R4_18V_PRESENT; uint8_t data; uint8_t speed; SDC_Memset(&card->cccr, 0, sizeof(struct sdio_cccr)); ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_CCCR, 0, &data); if (ret) goto out; cccr_vsn = data & 0x0f; if (cccr_vsn > SDIO_CCCR_REV_3_00) { SD_LOGW("%s: unrecognised CCCR structure version %ld\n", __func__, HAL_PR_SZ_L(cccr_vsn)); return -1; } card->cccr.sdio_vsn = (data & 0xf0) >> 4; ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_CAPS, 0, &data); if (ret) goto out; if (data & SDIO_CCCR_CAP_SMB) card->cccr.multi_block = 1; if (data & SDIO_CCCR_CAP_LSC) card->cccr.low_speed = 1; if (data & SDIO_CCCR_CAP_4BLS) card->cccr.wide_bus = 1; if (cccr_vsn >= SDIO_CCCR_REV_1_10) { ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_POWER, 0, &data); if (ret) goto out; if (data & SDIO_POWER_SMPC) card->cccr.high_power = 1; } if (cccr_vsn >= SDIO_CCCR_REV_1_20) { ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_SPEED, 0, &speed); if (ret) goto out; card->scr.sda_spec3 = 0; card->sw_caps.sd3_bus_mode = 0; card->sw_caps.sd3_drv_type = 0; if (cccr_vsn >= SDIO_CCCR_REV_3_00 && uhs) { card->scr.sda_spec3 = 1; ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_UHS, 0, &data); if (ret) goto out; if (card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)) { if (data & SDIO_UHS_DDR50) card->sw_caps.sd3_bus_mode |= SD_MODE_UHS_DDR50; if (data & SDIO_UHS_SDR50) card->sw_caps.sd3_bus_mode |= SD_MODE_UHS_SDR50; if (data & SDIO_UHS_SDR104) card->sw_caps.sd3_bus_mode |= SD_MODE_UHS_SDR104; } ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_DRIVE_STRENGTH, 0, &data); if (ret) goto out; if (data & SDIO_DRIVE_SDTA) card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_A; if (data & SDIO_DRIVE_SDTC) card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_C; if (data & SDIO_DRIVE_SDTD) card->sw_caps.sd3_drv_type |= SD_DRIVER_TYPE_D; } /* if no uhs mode ensure we check for high speed */ if (!card->sw_caps.sd3_bus_mode) { if (speed & SDIO_SPEED_SHS) { card->cccr.high_speed = 1; card->sw_caps.hs_max_dtr = 50000000; } else { card->cccr.high_speed = 0; card->sw_caps.hs_max_dtr = 25000000; } } } out: SD_LOGD("%s high_speed:%d sda_spec3:%d sd3_drv_type:%lx sd3_bus_mode:%lx\n", __func__, card->cccr.high_speed, card->scr.sda_spec3, HAL_PR_SZ_L(card->sw_caps.sd3_drv_type), HAL_PR_SZ_L(card->sw_caps.sd3_bus_mode)); return ret; } static int32_t sdio_enable_wide(struct mmc_card *card) { int32_t ret; uint8_t ctrl; if (!(card->host->caps & MMC_CAP_4_BIT_DATA)) return 0; if (card->cccr.low_speed && !card->cccr.wide_bus) return 0; ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_IF, 0, &ctrl); if (ret) return ret; ctrl |= SDIO_BUS_WIDTH_4BIT; ret = mmc_io_rw_direct(card, 1, FN0, SDIO_CCCR_IF, ctrl, NULL); if (!ret && card->type == MMC_TYPE_SDIO) card->bus_width = MMC_BUS_WIDTH_4; return ret; } /* * If desired, disconnect the pull-up resistor on CD/DAT[3] (pin 1) * of the card. This may be required on certain setups of boards, * controllers and embedded sdio device which do not need the card's * pull-up. As a result, card detection is disabled and power is saved. */ static int32_t sdio_disable_cd(struct mmc_card *card) { int32_t ret; uint8_t ctrl; if (!mmc_card_disable_cd(card)) return 0; ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_IF, 0, &ctrl); if (ret) return ret; ctrl |= SDIO_BUS_CD_DISABLE; return mmc_io_rw_direct(card, 1, FN0, SDIO_CCCR_IF, ctrl, NULL); } /* * Devices that remain active during a system suspend are * put back into 1-bit mode. called by mmc_sdio_suspend(host). */ #ifdef CONFIG_SD_PM static int32_t sdio_disable_wide(struct mmc_card *card) { int32_t ret; uint8_t ctrl; if (!(card->host->caps & MMC_CAP_4_BIT_DATA)) return 0; if (card->cccr.low_speed && !card->cccr.wide_bus) return 0; ret = mmc_io_rw_direct(card, 0, FN0, SDIO_CCCR_IF, 0, &ctrl); if (ret) return ret; if (!(ctrl & SDIO_BUS_WIDTH_4BIT)) return 0; ctrl &= ~SDIO_BUS_WIDTH_4BIT; ctrl |= SDIO_BUS_ASYNC_INT; ret = mmc_io_rw_direct(card, 1, FN0, SDIO_CCCR_IF, ctrl, NULL); if (ret) return ret; HAL_SDC_Set_BusWidth(card->host, MMC_BUS_WIDTH_1); return 0; } #endif static int32_t sdio_enable_4bit_bus(struct mmc_card *card) { int32_t err = 0; if (card->type == MMC_TYPE_SDIO) return sdio_enable_wide(card); #ifdef CONFIG_USE_SDIO_COMBO if ((card->host->caps & MMC_CAP_4_BIT_DATA) && (card->scr.bus_widths & SD_SCR_BUS_WIDTH_4)) { err = mmc_app_set_bus_width(card, MMC_BUS_WIDTH_4); if (err) return err; } else return 0; err = sdio_enable_wide(card); if (err <= 0) mmc_app_set_bus_width(card, MMC_BUS_WIDTH_1); #endif return err; } /* * Enable SDIO/combo card's high-speed mode. Return 0/1 if [not]supported. */ static int32_t sdio_enable_hs(struct mmc_card *card) { int32_t ret; ret = mmc_sdio_switch_hs(card, 1); if (ret <= 0 || card->type == MMC_TYPE_SDIO) return ret; #ifdef CONFIG_USE_SDIO_COMBO ret = mmc_sd_switch_hs(card); if (ret <= 0) mmc_sdio_switch_hs(card, 0); #endif return ret; } /*----------------------------------------------------------------------------------------------*\ Function : This function gets the CIS adress present on the SDIO card Argumennt : pointer to controller structure Return : 0, -1 \*----------------------------------------------------------------------------------------------*/ /* static uint32_t sdio_get_cisptr(struct mmc_card *card) { uint32_t fn; uint8_t dat; for (fn=0; fn<=card->io_num; fn++) { sdio_ioreg_read(card, FN0, FN_CIS_POINTER_0_REG(fn), &dat); card->cisptr[fn] = dat; sdio_ioreg_read(card, FN0, FN_CIS_POINTER_1_REG(fn), &dat); card->cisptr[fn] |= (uint32_t)dat << 8; sdio_ioreg_read(card, FN0, FN_CIS_POINTER_2_REG(fn), &dat); card->cisptr[fn] |= (uint32_t)dat << 16; SD_LOGD("Card CIS Addr = %x, fn = %d\n", card->cisptr[fn], fn); } return 0; } */ #if 0 /*--------------------------------------------------------------*\ @function : uint32_t sdio_read_cisinfo(uint8_t* buf) @brief : this function read CIS infomation of card @arguments: buf - pointer to data buffer @return : 0, -1 \*--------------------------------------------------------------*/ int32_t sdio_read_cisinfo(struct mmc_card *card, uint8_t* buf) { uint32_t i; int32_t ret=0; /* Read the Tuple Data */ for (i = 0; i < SIZE_OF_TUPLE; i++) { ret = sdio_ioreg_read(card, FN0, card->cisptr[FN0]+i, buf+i); if (ret==-1) return ret; } return ret; } /*--------------------------------------------------------------*\ @function : uint32_t sdio_read_manfid(uint32_t func_num) @brief : this function read Manufacturer identification of card @arguments: func_num - pointer to data buffer @return : 0, -1 \*--------------------------------------------------------------*/ int32_t sdio_read_manfid(struct mmc_card *card, uint32_t func_num) { uint32_t offset = 0; uint32_t manfid, card_id; int32_t ret=0; uint8_t tuple, link, datah, datal; do { ret = sdio_ioreg_read(card, func_num, card->cisptr[func_num]+offset, &tuple); if (ret == -1) return ret; if (tuple == CISTPL_MANFID) { offset += 2; ret = sdio_ioreg_read(card, func_num, card->cisptr[func_num]+offset, &datal); if (ret == -1) return ret; offset++; ret = sdio_ioreg_read(card, func_num, card->cisptr[func_num]+offset, &datah); if (ret == -1) return ret; manfid = (uint32_t)datal | (uint32_t)datah << 8; offset++; ret = sdio_ioreg_read(card, func_num, card->cisptr[func_num]+offset, &datal); if (ret == -1) return ret; offset++; ret = sdio_ioreg_read(card, func_num, card->cisptr[func_num]+offset, &datah); if (ret == -1) return ret; card_id = (uint32_t)datal | (uint32_t)datah << 8; card->manfid = (card_id<<16)|manfid; SD_LOGD("Card id = %08x manfid = %08x\n", card_id, manfid); return manfid; } ret = sdio_ioreg_read(card, func_num, card->cisptr[func_num]+offset+1, &link); if (ret == -1) return ret; offset += link + 2; } while(tuple != CISTPL_END); return 0; } int32_t sdio_get_vendor_id(struct mmc_card *card) { return (sdio_read_manfid(card, FN0)); } #endif int sdio_memcpy_fromio(struct mmc_card *card, unsigned int func_num, void *dst, unsigned int addr, int count) { return sdio_io_rw_ext_helper(card, func_num, 0, addr, 1, dst, count); } int sdio_memcpy_toio(struct mmc_card *card, unsigned int func_num, unsigned int addr, const void *src, int count) { return sdio_io_rw_ext_helper(card, func_num, 1, addr, 1, (uint8_t *)src, count); } #ifdef CONFIG_SDIO_IRQ_SUPPORT #if 0 /*-----------------------------------*\ Read IOx's Interrupt Pending Argument: io_num - 1~7 Return: 1-pending, 0-not pending \*-----------------------------------*/ int32_t sdio_intx_pend_rd(struct mmc_card *card, uint32_t io_num) { uint8_t dat; //func_num uses default value 0 mmc_io_rw_direct_host(card->host, 0, FN0, INT_PENDING_REG, 0, &dat); if (dat & INT(io_num)) { SD_LOGE("IO%x Interrupt is pending !!\n", io_num); return 0; } SD_LOGD("IO%x Interrupt is not pending !!\n", io_num); return -1; } /* IOx's Abort *\ \* io_num: 1~7 */ int32_t sdio_iox_abort(struct mmc_card *card, uint32_t io_num) { mmc_io_rw_direct_host(card->host, 0, FN0, IO_ABORT_REG, AS(io_num), NULL); return 0; } /** * sdio_claim_irq - claim the IRQ for a SDIO function * @func: SDIO function * @handler: IRQ handler callback * * Claim and activate the IRQ for the given SDIO function. The provided * handler will be called when that IRQ is asserted. The host is always * claimed already when the handler is called so the handler must not * call sdio_claim_host() nor sdio_release_host(). */ int sdio_claim_irq(struct mmc_card *card, unsigned int func_num, sdio_irq_handler_t *handler) { int32_t ret; uint8_t reg; if (!card) { SD_LOGE_RAW(ROM_ERR_MASK, "%s,%d\n", __func__, __LINE__); return -1; } SD_LOGN("SDIO: Enabling IRQ for %d...\n", func_num); if (card->irq_handler) { SD_LOGE("SDIO: IRQ for %d already in use.\n", func_num); return -EBUSY; } reg = sdio_readb(card, FN0, SDIO_CCCR_IENx, &ret); if (ret) return ret; reg |= 1 << func_num; reg |= 1; /* Master interrupt enable */ sdio_writeb(card, FN0, reg, SDIO_CCCR_IENx, &ret); if (ret) return ret; card->irq_handler = handler; return ret; } /** * sdio_release_irq - release the IRQ for a SDIO function * @func: SDIO function * * Disable and release the IRQ for the given SDIO function. */ int sdio_release_irq(struct mmc_card *card, unsigned int func_num) { int ret; uint8_t reg; if (!card) { SD_LOGE_RAW(ROM_ERR_MASK, "%s,%d\n", __func__, __LINE__); return -1; } SD_LOGN("SDIO: Disabling IRQ for %d...\n", func_num); if (card->irq_handler) card->irq_handler = NULL; reg = sdio_readb(card, FN0, SDIO_CCCR_IENx, &ret); if (ret) return ret; reg &= ~(1 << func_num); /* Disable master interrupt with the last function interrupt */ if (!(reg & 0xFE)) reg = 0; sdio_writeb(card, FN0, reg, SDIO_CCCR_IENx, &ret); if (ret) return ret; return 0; } #endif #endif /* * Fetch CID from card. */ #ifdef CONFIG_USE_SDIO_COMBO int32_t mmc_sd_get_cid(struct mmc_host *host, uint32_t ocr, uint32_t *cid, uint32_t *rocr) { int32_t err; /* * Since we're changing the OCR value, we seem to * need to tell some cards to go back to the idle * state. We wait 1ms to give cards time to * respond. */ mmc_go_idle(host); /* * If SD_SEND_IF_COND indicates an SD 2.0 * compliant card and we should set bit 30 * of the ocr to indicate that we can handle * block-addressed SDHC cards. */ err = mmc_send_if_cond(host, ocr); if (!err) ocr |= SD_OCR_CCS; /* * If the host supports one of UHS-I modes, request the card * to switch to 1.8V signaling level. */ if (host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50)) ocr |= SD_OCR_S18R; /* If the host can supply more than 150mA, XPC should be set to 1. */ if (host->caps & (MMC_CAP_SET_XPC_330 | MMC_CAP_SET_XPC_300 | MMC_CAP_SET_XPC_180)) ocr |= SD_OCR_XPC; try_again: err = mmc_send_app_op_cond(host, ocr, rocr); if (err) return err; /* * In case CCS and S18A in the response is set, start Signal Voltage * Switch procedure. SPI mode doesn't support CMD11. */ if (rocr && ((*rocr & 0x41000000) == 0x41000000)) { err = mmc_set_signal_voltage(host, MMC_SIGNAL_VOLTAGE_180, true); if (err) { ocr &= ~SD_OCR_S18R; goto try_again; } } err = mmc_all_send_cid(host, cid); return err; } #endif /*--------------------------------------------------------------*\ @function : uint32_t sdio_enumerate(uint32_t smc_no) @brief : this function enumerates and initializes SDIO card @arguments: smc_no - number of the smc channel selected @return : 0, -1 \*--------------------------------------------------------------*/ int32_t mmc_sdio_init_card(struct mmc_card *card) { int32_t err; //struct mmc_host *host = card->host; /* The initialization should be done at 3.3 V I/O voltage. */ //mmc_set_signal_voltage(); err = mmc_send_io_op_cond(card, card->ocr.ocr & 0xFF8000, &card->ocr.ocr); if (err) return err; #ifdef CONFIG_USE_SDIO_COMBO if ((card->ocr.ocr & R4_MEMORY_PRESENT) && mmc_sd_get_cid(host, host->ocr & card->ocr.ocr, card->raw_cid, NULL) == 0) { card->type = MMC_TYPE_SD_COMBO; SD_LOGW("SDIO Combo Card(With I/O & Memory) !!\n"); /* if with memory in it, allocate memory space for storing memory portion information */ //card->mem_info_p = (SDMMCInfo *)HAL_Malloc(sizeof(SDMMCInfo)); /* relative operations for memory portion */ } else #endif { card->type = MMC_TYPE_SDIO; SD_LOGD("Standard SDIO Card with IO portion only !!\n"); } /* * If the host and card support UHS-I mode request the card * to switch to 1.8V signaling level. No 1.8v signalling if * UHS mode is not enabled to maintain compatibilty and some * systems that claim 1.8v signalling in fact do not support * it. */ #ifdef SD_SUPPORT_VERSION3 if ((card->ocr.ocr & R4_18V_PRESENT) && (card->host->caps & (MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25 | MMC_CAP_UHS_SDR50 | MMC_CAP_UHS_SDR104 | MMC_CAP_UHS_DDR50))) { err = mmc_set_signal_voltage(card->host, MMC_SIGNAL_VOLTAGE_180, true); if (err) { card->ocr.ocr &= ~R4_18V_PRESENT; card->host->ocr &= ~R4_18V_PRESENT; } err = 0; } else { card->ocr.ocr &= ~R4_18V_PRESENT; card->host->ocr &= ~R4_18V_PRESENT; } #endif /* CMD3, Ask the card to publish a new Relative Card Address (RCA) */ err = mmc_send_relative_addr(card->host, &card->rca); if (err) { SD_LOGW("SD public RCA failed\n"); return err; } SD_LOGD("Card RCA %lx\n", HAL_PR_SZ_L(card->rca)); HAL_SDC_Update_Clk(card->host, 25000000); /* * Read CSD, before selecting the card */ #ifdef CONFIG_USE_SDIO_COMBO if (card->type == MMC_TYPE_SD_COMBO) { err = mmc_sd_get_csd(host, card); if (err) return err; mmc_decode_cid(card); } #endif /* CMD7, Select card, as all following commands rely on that. */ err = mmc_select_card(card, 1); if (err) { SD_LOGE("Card Select Error!!\n"); return -1; } #ifdef CONFIG_USE_MMC_QUIRK if (card->quirks & MMC_QUIRK_NONSTD_SDIO) { /* * This is non-standard SDIO device, meaning it doesn't * have any CIA (Common I/O area) registers present. * It's host's responsibility to fill cccr and cis * structures in init_card(). */ mmc_set_clock(host, card->cis.max_dtr); if (card->cccr.high_speed) { mmc_card_set_highspeed(card); mmc_set_timing(card->host, MMC_TIMING_SD_HS); } goto finish; } #endif /* * Read the common registers. */ err = sdio_read_cccr(card, card->ocr.ocr); if (err) goto remove; /* * Read the common CIS tuples. */ #ifdef CONFIG_SDIO_USE_FUNS err = sdio_read_common_cis(card); if (err) goto remove; #endif #ifdef CONFIG_USE_MMC_QUIRK mmc_fixup_device(card, NULL); #endif /* * If needed, disconnect card detection pull-up resistor. */ err = sdio_disable_cd(card); if (err) goto remove; /* Initialization sequence for UHS-I cards */ /* Only if card supports 1.8v and UHS signaling */ #ifdef SD_SUPPORT_VERSION3 if ((card->ocr.ocr & R4_18V_PRESENT) && card->sw_caps.sd3_bus_mode) { err = mmc_sdio_init_uhs_card(card); if (err) goto remove; /* Card is an ultra-high-speed card */ mmc_card_set_uhs(card); } else #endif { /* * Switch to high-speed (if supported). */ err = sdio_enable_hs(card); if (err > 0) { mmc_card_set_highspeed(card); SD_LOGN("sdio highspeed \n"); } else if (err) goto remove; /* * Change to the card's maximum speed. */ HAL_SDC_Update_Clk(card->host, mmc_sdio_get_max_clock(card)); /* * Switch to wider bus (if supported). */ err = sdio_enable_4bit_bus(card); if (!err) { /* sd Host Controller Register SDXC_REG_WIDTH */ HAL_SDC_Set_BusWidth(card->host, MMC_BUS_WIDTH_4); SD_LOGN("%s bus width type:%d\n", __func__, MMC_BUS_WIDTH_4); } else if (err) goto remove; } #ifdef CONFIG_USE_MMC_QUIRK finish: #endif card->host->card = card; return 0; remove: card->host->card = NULL; #ifdef CONFIG_SDIO_USE_FUNS sdio_free_func_cis((struct sdio_func *)card->sdio_func); sdio_free_common_cis(card); #endif return err; } uint32_t sdio_reset(struct mmc_host *host) { int32_t ret; uint8_t abort; /* SDIO Simplified Specification V2.0, 4.4 Reset for SDIO */ ret = mmc_io_rw_direct_host(host, 0, 0, SDIO_CCCR_ABORT, 0, &abort); if (ret) abort = 0x08; else abort |= 0x08; ret = mmc_io_rw_direct_host(host, 1, 0, SDIO_CCCR_ABORT, abort, NULL); return ret; } /** * sdio_set_host_pm_flags - set wanted host power management capabilities * @func: SDIO function attached to host * * Set a capability bitmask corresponding to wanted host controller * power management features for the upcoming suspend state. * This must be called, if needed, each time the suspend method of * the function driver is called, and must contain only bits that * were returned by sdio_get_host_pm_caps(). * The host doesn't need to be claimed, nor the function active, * for this information to be set. */ static int32_t sdio_set_host_pm_flags(sdio_t *card, uint32_t flags) { #ifdef CONFIG_SD_PM struct mmc_host *host = card->host; if (flags & ~host->pm_caps) return -1; /* function suspend methods are serialized, hence no lock needed */ host->pm_flags |= flags; #endif return 0; } int32_t sdio_pm(sdio_t *card, int32_t suspend) { int32_t ret = 0; if (suspend) { /* Notify SDIO that ALLWINNERTECH will remain powered during suspend */ ret = sdio_set_host_pm_flags(card, MMC_PM_KEEP_POWER); if (ret) SD_LOGE("Error setting SDIO pm flags: %ld\n", HAL_PR_SZ_L(ret)); } return ret; } #ifdef CONFIG_SD_PM /* * SDIO suspend. We need to suspend all functions separately. * Therefore all registered functions must have drivers with suspend * and resume methods. Failing that we simply remove the whole card. */ static int mmc_sdio_suspend(struct mmc_host *host) { int32_t i, err = 0; struct mmc_card *card; card = host->card; card->suspend = 1; #ifdef CONFIG_SDIO_USE_FUNS for (i = 0; i < host->card->sdio_funcs; i++) { struct sdio_func *func = host->card->sdio_func[i]; if (func && sdio_func_present(func) && func->dev.driver) { err = sdio_bus_pm_suspend(&func->dev); sdio_disable_func(host->card, FN1); if (err) break; } } while (err && --i >= 0) { struct sdio_func *func = host->card->sdio_func[i]; if (func && sdio_func_present(func) && func->dev.driver) { sdio_bus_pm_resume(&func->dev); } } #else (void)i; #endif if (mmc_card_keep_power(host) && mmc_card_wake_sdio_irq(host)) { mmc_claim_host(host); sdio_disable_wide(host->card); mmc_release_host(host); } return err; } int mmc_sdio_resume(struct mmc_host *host) { int32_t i, err = 0; struct mmc_card *card; if (!host) { SDC_LOGE_RAW(ROM_ERR_MASK, "%s,%d no host exist!\n", __func__, __LINE__); return -1; } if (!host->card) { SDC_LOGE("%s,%d no card exist!\n", __func__, __LINE__); return -1; } card = host->card; /* Basic card reinitialization. */ mmc_claim_host(host); /* No need to reinitialize powered-resumed nonremovable cards */ if (!mmc_card_keep_power(host)) { err = mmc_sdio_init_card(host->card); } else if (mmc_card_wake_sdio_irq(host)) { /* We may have switched to 1-bit mode during suspend */ err = sdio_enable_4bit_bus(host->card); if (!err) { HAL_SDC_Set_BusWidth(host->card->host, MMC_BUS_WIDTH_4); SDC_LOGN("%s bus width type:%d\n", __func__, MMC_BUS_WIDTH_4); err = 0; } } #ifdef CONFIG_SDIO_IRQ_SUPPORT if (!err && host->sdio_irqs) wake_up_process(host->sdio_irq_thread); #endif mmc_release_host(host); #ifdef CONFIG_SDIO_USE_FUNS /* * If the card looked to be the same as before suspending, then * we proceed to resume all card functions. If one of them returns * an error then we simply return that error to the core and the * card will be redetected as new. It is the responsibility of * the function driver to perform further tests with the extra * knowledge it has of the card to confirm the card is indeed the * same as before suspending (same MAC address for network cards, * etc.) and return an error otherwise. */ for (i = 0; !err && i < host->card->sdio_funcs; i++) { struct sdio_func *func = host->card->sdio_func[i]; if (func && sdio_func_present(func) && func->dev.driver) { err = sdio_bus_pm_resume(&func->dev); sdio_enable_func(host->card, FN1); } } #else (void)i; #endif card->suspend = 0; return err; } static const struct mmc_bus_ops sdio_bus_ops = { .suspend = mmc_sdio_suspend, .resume = mmc_sdio_resume, }; #endif /* * Starting point for SDIO card init. */ int mmc_attach_sdio(struct mmc_card *card, struct mmc_host *host) { int err = -1; int32_t funcs; int i; if (!host) { SD_LOGE("%s,%d no host exist!\n", __func__, __LINE__); return -1; } /* CMD5 arg=0; get card support Voltage */ err = mmc_send_io_op_cond(card, 0, &card->ocr.ocr); if (err) return err; SD_LOGD("card ocr:%lx\n", HAL_PR_SZ_L(card->ocr.ocr)); /* * Sanity check the voltages that the card claims to * support. */ if (card->ocr.ocr & 0x7F) { SD_LOGW("card claims to support voltages below the defined range." "These will be ignored.\n"); card->ocr.ocr &= ~0x7F; } /* Detect and init the card. */ err = mmc_sdio_init_card(card); if (err) return err; funcs = (card->ocr.ocr >> 28) & 0x7; SD_LOGD("Number of I/O Functions: %02lx\n", HAL_PR_SZ_L(funcs)); /* * Initialize (but don't add) all present functions. */ #ifdef CONFIG_SDIO_USE_FUNS card->sdio_funcs = 0; // for (int i = 0; i < card->io_num; i++, card->sdio_funcs++) { // for (int i = 0; i < funcs; i++, card->sdio_funcs++) { for (i = 0; i < funcs; i++, card->sdio_funcs++) { err = sdio_init_func(host->card, i + 1); if (err) goto remove; } #endif /* * First add the card to the driver model... */ mmc_release_host(host); if(host->caps&MMC_CAP_SDIO_IRQ){ int ret = OS_SemaphoreCreateBinary(&host->sdio_irq_signal); SDC_BUG_ON(ret!=OS_OK); } mmc_add_card(host->card); /* * ...then the SDIO functions. */ #ifdef CONFIG_SDIO_USE_FUNS // for (int i = 0; i < funcs; i++) { for (i = 0; i < funcs; i++) { err = sdio_add_func(host->card->sdio_func[i], i); if (err) goto remove_added; } #else (void)funcs; #endif card->fn_bsize[1] = 512; mmc_claim_host(host); #ifdef CONFIG_SD_PM if (!card->suspend) { mmc_attach_bus(host, &sdio_bus_ops); } #endif return 0; #ifdef CONFIG_SDIO_USE_FUNS remove_added: /* Remove without lock if the device has been added. */ mmc_sdio_remove(host); #endif card->host->card = NULL; mmc_claim_host(host); #ifdef CONFIG_SDIO_USE_FUNS remove: /* And with lock if it hasn't been added. */ mmc_release_host(host); if (host->card) { mmc_sdio_remove(host); card->host->card = NULL; } mmc_claim_host(host); #else card->host->card = NULL; #endif SD_LOGE("%s: error %d whilst initialising SDIO card\n", __func__, err); return err; } void mmc_deattach_sdio(struct mmc_card *card, struct mmc_host *host) { if (!card) { SD_LOGE_RAW(ROM_ERR_MASK, "%s,%d\n", __func__, __LINE__); return ; } #ifdef CONFIG_SD_PM if (!card->suspend) { mmc_detach_bus(card->host); } #endif if(host->caps&MMC_CAP_SDIO_IRQ){ int ret = OS_SemaphoreDelete(&host->sdio_irq_signal); SDC_BUG_ON(ret!=OS_OK); } } /** * sdio_align_size - pads a transfer size to a more optimal value * @card: SDIO card * @sz: original transfer size * * Pads the original data size with a number of extra bytes in * order to avoid controller bugs and/or performance hits * (e.g. some controllers revert to PIO for certain sizes). * * If possible, it will also adjust the size so that it can be * handled in just a single request. * * Returns the improved size, which might be unmodified. */ //unsigned int sdio_align_size(struct mmc_card *card, unsigned int sz) unsigned int sdio_align_size(struct sdio_func *func, unsigned int sz) { #ifdef CONFIG_SDIO_USE_FUNS unsigned int orig_sz; unsigned int blk_sz, byte_sz; unsigned chunk_sz; orig_sz = sz; #endif /* Do a first check with the controller, in case it * wants to increase the size up to a point where it * might need more than one block. */ //sz = mmc_align_data_size(card, sz); sz = mmc_align_data_size(func->card, sz); #ifdef CONFIG_SDIO_USE_FUNS /* * If we can still do this with just a byte transfer, then * we're done. */ if (sz <= sdio_max_byte_size(func)) return sz; if (func->card->cccr.multi_block) { /* * Check if the transfer is already block aligned */ if ((sz % func->cur_blksize) == 0) { return sz; } /* * Realign it so that it can be done with one request, * and recheck if the controller still likes it. */ blk_sz = ((sz + func->cur_blksize - 1) / func->cur_blksize) * func->cur_blksize; blk_sz = mmc_align_data_size(func->card, blk_sz); /* * This value is only good if it is still just * one request. */ if ((blk_sz % func->cur_blksize) == 0) return blk_sz; /* * We failed to do one request, but at least try to * pad the remainder properly. */ byte_sz = mmc_align_data_size(func->card, sz % func->cur_blksize); if (byte_sz <= sdio_max_byte_size(func)) { blk_sz = sz / func->cur_blksize; return blk_sz * func->cur_blksize + byte_sz; } } else { /* * We need multiple requests, so first check that the * controller can handle the chunk size; */ chunk_sz = mmc_align_data_size(func->card, sdio_max_byte_size(func)); if (chunk_sz == sdio_max_byte_size(func)) { /* * Fix up the size of the remainder (if any) */ byte_sz = orig_sz % chunk_sz; if (byte_sz) { byte_sz = mmc_align_data_size(func->card, byte_sz); } return (orig_sz / chunk_sz) * chunk_sz + byte_sz; } } /* The controller is simply incapable of transferring the size * we want in decent manner, so just return the original size. */ return orig_sz; #endif return sz; } #ifdef SDIO_EXCLUSIVE_HOST /** * sdio_claim_host - exclusively claim a bus for a certain SDIO function * @card: SDIO that will be accessed * * Claim a bus for a set of operations. The SDIO function given * is used to figure out which bus is relevant. */ void sdio_claim_host(struct mmc_card *card) { HAL_SDC_Claim_Host(card->host); } /** * sdio_release_host - release a bus for a certain SDIO function * @card: SDIO that was accessed * * Release a bus, allowing others to claim the bus for their * operations. */ void sdio_release_host(struct mmc_card *card) { HAL_SDC_Release_Host(card->host); } #endif #endif /* CONFIG_USE_SDIO */