rt-thread-official/bsp/allwinner/libraries/sunxi-hal/hal/source/sdmmc/sdio.c

2293 lines
62 KiB
C

/*
* 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 */