1565 lines
42 KiB
C
Executable File
1565 lines
42 KiB
C
Executable File
#include <FreeRTOS.h>
|
|
#include <task.h>
|
|
#include <timers.h>
|
|
#include <stdio.h>
|
|
#include "kwrap/semaphore.h"
|
|
#include "semphr.h"
|
|
#include "sys.h"
|
|
#include <kwrap/flag.h>
|
|
#include <kwrap/task.h>
|
|
#include <sdio.h>
|
|
|
|
#define u32 unsigned int
|
|
#define u16 unsigned short
|
|
#define u8 unsigned char
|
|
|
|
#define min(a,b) (a > b ? b : a)
|
|
|
|
#define SDIO_HOST_ID_2 (1) //< SDIO2 host
|
|
|
|
#define SDIO_IO_R 0x0 ///< IO Read flag
|
|
#define SDIO_IO_W 0x1 ///< IO Write flag
|
|
|
|
#define SDIO_IO_BYTEMODE 0x0 ///< IO byte access mode
|
|
#define SDIO_IO_BLOCKMODE 0x1 ///< IO block access mode
|
|
|
|
#define SDIO_IO_DIS_RAW 0x0 ///< Disable IO RAW function
|
|
|
|
#define SDIO_CCCR_IENx 0x04 /* Function/Master Interrupt Enable */
|
|
|
|
#define SDIO_FBR_BASE(f) ((f) * 0x100) /* base of function f's FBRs */
|
|
#define SDIO_FBR_CIS 0x09 /* CIS pointer (3 bytes) */
|
|
#define SDIO_FBR_BLKSIZE 0x10 /* block size (2 bytes) */
|
|
#define __must_be_array(x) 0
|
|
#define ARRAY_SIZE(arr) (sizeof(arr) / sizeof((arr)[0]) + __must_be_array(arr))
|
|
|
|
struct mmc_ios {
|
|
unsigned int clock; /* clock rate */
|
|
unsigned short vdd;
|
|
|
|
/* vdd stores the bit number of the selected voltage range from below. */
|
|
|
|
unsigned char bus_mode; /* command output mode */
|
|
|
|
#define MMC_BUSMODE_OPENDRAIN 1
|
|
#define MMC_BUSMODE_PUSHPULL 2
|
|
|
|
unsigned char chip_select; /* SPI chip select */
|
|
|
|
#define MMC_CS_DONTCARE 0
|
|
#define MMC_CS_HIGH 1
|
|
#define MMC_CS_LOW 2
|
|
|
|
unsigned char power_mode; /* power supply mode */
|
|
|
|
#define MMC_POWER_OFF 0
|
|
#define MMC_POWER_UP 1
|
|
#define MMC_POWER_ON 2
|
|
|
|
unsigned char bus_width; /* data bus width */
|
|
|
|
#define MMC_BUS_WIDTH_1 0
|
|
#define MMC_BUS_WIDTH_4 2
|
|
#define MMC_BUS_WIDTH_8 3
|
|
|
|
unsigned char timing; /* timing specification used */
|
|
|
|
#define MMC_TIMING_LEGACY 0
|
|
#define MMC_TIMING_MMC_HS 1
|
|
#define MMC_TIMING_SD_HS 2
|
|
};
|
|
|
|
struct mmc_host_ops {
|
|
#if 0
|
|
void (*request)(struct mmc_host *host, struct mmc_request *req);
|
|
/*
|
|
* Avoid calling these three functions too often or in a "fast path",
|
|
* since underlaying controller might implement them in an expensive
|
|
* and/or slow way.
|
|
*
|
|
* Also note that these functions might sleep, so don't call them
|
|
* in the atomic contexts!
|
|
*
|
|
* Return values for the get_ro callback should be:
|
|
* 0 for a read/write card
|
|
* 1 for a read-only card
|
|
* -ENOSYS when not supported (equal to NULL callback)
|
|
* or a negative errno value when something bad happened
|
|
*
|
|
* Return values for the get_cd callback should be:
|
|
* 0 for a absent card
|
|
* 1 for a present card
|
|
* -ENOSYS when not supported (equal to NULL callback)
|
|
* or a negative errno value when something bad happened
|
|
*/
|
|
void (*set_ios)(struct mmc_host *host, struct mmc_ios *ios);
|
|
int (*get_ro)(struct mmc_host *host);
|
|
int (*get_cd)(struct mmc_host *host);
|
|
|
|
void (*enable_sdio_irq)(struct mmc_host *host, int enable);
|
|
#endif
|
|
};
|
|
|
|
struct mmc_host {
|
|
//struct device *parent;
|
|
//struct device class_dev;
|
|
int index;
|
|
const struct mmc_host_ops *ops;
|
|
unsigned int f_min;
|
|
unsigned int f_max;
|
|
u32 ocr_avail;
|
|
|
|
#define MMC_VDD_165_195 0x00000080 /* VDD voltage 1.65 - 1.95 */
|
|
#define MMC_VDD_20_21 0x00000100 /* VDD voltage 2.0 ~ 2.1 */
|
|
#define MMC_VDD_21_22 0x00000200 /* VDD voltage 2.1 ~ 2.2 */
|
|
#define MMC_VDD_22_23 0x00000400 /* VDD voltage 2.2 ~ 2.3 */
|
|
#define MMC_VDD_23_24 0x00000800 /* VDD voltage 2.3 ~ 2.4 */
|
|
#define MMC_VDD_24_25 0x00001000 /* VDD voltage 2.4 ~ 2.5 */
|
|
#define MMC_VDD_25_26 0x00002000 /* VDD voltage 2.5 ~ 2.6 */
|
|
#define MMC_VDD_26_27 0x00004000 /* VDD voltage 2.6 ~ 2.7 */
|
|
#define MMC_VDD_27_28 0x00008000 /* VDD voltage 2.7 ~ 2.8 */
|
|
#define MMC_VDD_28_29 0x00010000 /* VDD voltage 2.8 ~ 2.9 */
|
|
#define MMC_VDD_29_30 0x00020000 /* VDD voltage 2.9 ~ 3.0 */
|
|
#define MMC_VDD_30_31 0x00040000 /* VDD voltage 3.0 ~ 3.1 */
|
|
#define MMC_VDD_31_32 0x00080000 /* VDD voltage 3.1 ~ 3.2 */
|
|
#define MMC_VDD_32_33 0x00100000 /* VDD voltage 3.2 ~ 3.3 */
|
|
#define MMC_VDD_33_34 0x00200000 /* VDD voltage 3.3 ~ 3.4 */
|
|
#define MMC_VDD_34_35 0x00400000 /* VDD voltage 3.4 ~ 3.5 */
|
|
#define MMC_VDD_35_36 0x00800000 /* VDD voltage 3.5 ~ 3.6 */
|
|
|
|
unsigned long caps; /* Host capabilities */
|
|
|
|
#define MMC_CAP_4_BIT_DATA (1 << 0) /* Can the host do 4 bit transfers */
|
|
#define MMC_CAP_MMC_HIGHSPEED (1 << 1) /* Can do MMC high-speed timing */
|
|
#define MMC_CAP_SD_HIGHSPEED (1 << 2) /* Can do SD high-speed timing */
|
|
#define MMC_CAP_SDIO_IRQ (1 << 3) /* Can signal pending SDIO IRQs */
|
|
#define MMC_CAP_SPI (1 << 4) /* Talks only SPI protocols */
|
|
#define MMC_CAP_NEEDS_POLL (1 << 5) /* Needs polling for card-detection */
|
|
#define MMC_CAP_8_BIT_DATA (1 << 6) /* Can the host do 8 bit transfers */
|
|
|
|
/* host specific block data */
|
|
unsigned int max_seg_size; /* see blk_queue_max_segment_size */
|
|
unsigned short max_hw_segs; /* see blk_queue_max_hw_segments */
|
|
unsigned short max_phys_segs; /* see blk_queue_max_phys_segments */
|
|
unsigned short unused;
|
|
unsigned int max_req_size; /* maximum number of bytes in one req */
|
|
unsigned int max_blk_size; /* maximum size of one mmc block */
|
|
unsigned int max_blk_count; /* maximum number of blocks in one req */
|
|
|
|
/* private data */
|
|
//spinlock_t lock; /* lock for claim and bus ops */
|
|
|
|
struct mmc_ios ios; /* current io bus settings */
|
|
u32 ocr; /* the current OCR setting */
|
|
|
|
/* group bitfields together to minimize padding */
|
|
unsigned int use_spi_crc:1;
|
|
unsigned int claimed:1; /* host exclusively claimed */
|
|
unsigned int bus_dead:1; /* bus has been released */
|
|
#ifdef CONFIG_MMC_DEBUG
|
|
unsigned int removed:1; /* host is being removed */
|
|
#endif
|
|
|
|
struct mmc_card *card; /* device attached to this host */
|
|
|
|
//wait_queue_head_t wq;
|
|
|
|
//struct delayed_work detect;
|
|
|
|
//const struct mmc_bus_ops *bus_ops; /* current bus driver */
|
|
unsigned int bus_refs; /* reference counter */
|
|
|
|
//unsigned int sdio_irqs;
|
|
//struct task_struct *sdio_irq_thread;
|
|
//atomic_t sdio_irq_thread_abort;
|
|
|
|
#ifdef CONFIG_LEDS_TRIGGERS
|
|
struct led_trigger *led; /* activity led */
|
|
#endif
|
|
|
|
//struct dentry *debugfs_root;
|
|
|
|
//unsigned long private[0] ____cacheline_aligned;
|
|
};
|
|
|
|
struct sdio_func_tuple {
|
|
struct sdio_func_tuple *next;
|
|
unsigned char code;
|
|
unsigned char size;
|
|
unsigned char data[0];
|
|
};
|
|
|
|
struct sdio_func {
|
|
struct mmc_card *card;
|
|
void (*irq_handler)(struct sdio_func *);
|
|
unsigned int max_blksize;
|
|
unsigned int cur_blksize;
|
|
unsigned int enable_timeout;
|
|
unsigned int num;
|
|
unsigned short vendor;
|
|
unsigned short device;
|
|
unsigned num_info;
|
|
const char **info;
|
|
unsigned char class;
|
|
unsigned int tmpbuf_reserved;
|
|
unsigned char tmpbuf[4];
|
|
void *drv_priv;
|
|
};
|
|
|
|
struct sdio_cccr {
|
|
unsigned int sdio_vsn;
|
|
unsigned int sd_vsn;
|
|
unsigned int multi_block:1;
|
|
unsigned int low_speed:1;
|
|
unsigned int wide_bus:1;
|
|
unsigned int high_power:1;
|
|
unsigned int high_speed:1;
|
|
unsigned int disable_cd:1;
|
|
};
|
|
|
|
struct sdio_cis {
|
|
unsigned short vendor;
|
|
unsigned short device;
|
|
unsigned short blksize;
|
|
unsigned int max_dtr;
|
|
};
|
|
|
|
struct mmc_card {
|
|
struct mmc_host *host;
|
|
struct sdio_cccr cccr;
|
|
struct sdio_cis cis;
|
|
struct sdio_func *sdio_func[7];
|
|
unsigned int sdio_funcs;
|
|
unsigned int rca;
|
|
unsigned int type;
|
|
unsigned num_info;
|
|
const char **info;
|
|
struct sdio_func_tuple *tuples;
|
|
};
|
|
|
|
typedef int (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;
|
|
};
|
|
|
|
typedef enum
|
|
{
|
|
SDIO_HOST_IOINT_DIS = 0, ///< sdio host io interrupt enable
|
|
SDIO_HOST_IOINT_EN = 1 ///< sdio host io interrupt disable
|
|
}SDIO_HOST_IOINT_ENDIS;
|
|
|
|
static struct mmc_card sdiocard;
|
|
static struct mmc_host sdiohost;
|
|
static struct sdio_func func_sdio = { 0 };
|
|
struct sdio_func *wifi_sdio_func = &func_sdio;
|
|
static struct sdio_func_tuple *func_tuples = NULL;
|
|
static struct sdio_func_tuple *card_tuples = NULL;
|
|
static struct sdio_func *save_sdio_func;
|
|
static void(*vpSdioLinuxIoHdl)(struct sdio_func *) = NULL;
|
|
|
|
static SEM_HANDLE wifi_sdio_mutex;
|
|
static TaskHandle_t sdio_irq_task_handler;
|
|
static ID FLG_ID_WIFI_SDIO;
|
|
#define FLGPTN_WIFI_SDIO 0x4
|
|
|
|
#define uimempoolsize 512
|
|
static unsigned char uimempool[uimempoolsize];
|
|
|
|
static unsigned int func_max_byte_size = 512u;
|
|
|
|
static unsigned int sdio_max_byte_size(struct sdio_func *func)
|
|
{
|
|
//?? todo
|
|
#if 0
|
|
unsigned mval = min(func->card->host->max_seg_size,
|
|
func->card->host->max_blk_size);
|
|
mval = min(mval, func->max_blksize);
|
|
return min(mval, 512u); /* maximum size for byte mode */
|
|
#else
|
|
return func_max_byte_size;
|
|
#endif
|
|
//?? todo end
|
|
}
|
|
|
|
void sdio_set_max_byte_size(struct sdio_func *func, unsigned int size)
|
|
{
|
|
func_max_byte_size = size;
|
|
//?? todo end
|
|
}
|
|
|
|
static int mmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn, unsigned addr, u8 in, u8* out)
|
|
{
|
|
SDIO_CMD52_STRUCT cmd52;
|
|
|
|
unsigned char rw;
|
|
//cyg_io_handle_t handle;
|
|
//unsigned char data;
|
|
|
|
if (write)
|
|
rw = SDIO_IO_W;
|
|
else
|
|
rw = SDIO_IO_R;
|
|
|
|
cmd52.Function_Num = (unsigned char)fn;
|
|
cmd52.Reg_Addr = (unsigned long)addr;
|
|
if (write)
|
|
cmd52.pData = (unsigned char *)∈
|
|
else
|
|
cmd52.pData = (unsigned char *)out;
|
|
cmd52.ByteCount = 1;
|
|
cmd52.RW_flag = rw;
|
|
cmd52.RAW = SDIO_IO_DIS_RAW;
|
|
cmd52.Stuff = 0;
|
|
|
|
extern int sdio2_sendIOCMD52(PSDIO_CMD52_STRUCT pSDIO_CMD52);
|
|
if(sdio2_sendIOCMD52(&cmd52))
|
|
{
|
|
printf("mmc_io_rw_direct error\r\n");
|
|
|
|
//if (vpSdioCmdErrHdl)
|
|
// vpSdioCmdErrHdl(SDIO_HOST_ERR_CMD52);
|
|
//return -EIO;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int mmc_io_rw_extended(struct mmc_card *card, int write, unsigned fn,
|
|
unsigned addr, int incr_addr, u8 *buf, unsigned blocks, unsigned blksz)
|
|
{
|
|
|
|
SDIO_CMD53_STRUCT cmd53;
|
|
|
|
unsigned char rw;
|
|
//cyg_io_handle_t handle;
|
|
unsigned char uiblock;
|
|
unsigned int uibytecnt;
|
|
|
|
if (write)
|
|
rw = SDIO_IO_W;
|
|
else
|
|
rw = SDIO_IO_R;
|
|
|
|
cmd53.Function_Num = (unsigned char)fn;
|
|
cmd53.Reg_Addr = (unsigned int)addr;
|
|
cmd53.pData = (unsigned char *)buf;
|
|
cmd53.OpCode = incr_addr;
|
|
cmd53.RW_flag = rw;
|
|
cmd53.Stuff = 0;
|
|
|
|
if (blocks == 1 && blksz <= 512)
|
|
{
|
|
//uibytecnt = (blksz == 512) ? 0 : blksz; /* byte mode */
|
|
uibytecnt = blksz;
|
|
uiblock = SDIO_IO_BYTEMODE;
|
|
}
|
|
else
|
|
{
|
|
uiblock = SDIO_IO_BLOCKMODE;
|
|
uibytecnt = blocks; /* block mode */
|
|
}
|
|
|
|
cmd53.Block = uiblock;
|
|
cmd53.ByteCount = uibytecnt;
|
|
|
|
extern int sdio2_sendIOCMD53(PSDIO_CMD53_STRUCT pSDIO_CMD53);
|
|
if(sdio2_sendIOCMD53((void *)&cmd53))
|
|
{
|
|
printf("mmc_io_rw_extended error\r\n");
|
|
|
|
//if (vpSdioCmdErrHdl)
|
|
// vpSdioCmdErrHdl(SDIO_HOST_ERR_CMD53);
|
|
|
|
//return -EIO;
|
|
return -1;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
static int mmc_io_rw_ext_helper(struct sdio_func *func, int write,
|
|
unsigned addr, int incr_addr, u8 *buf, unsigned size)
|
|
{
|
|
unsigned remainder = size;
|
|
unsigned max_blocks;
|
|
int ret;
|
|
|
|
/* Do the bulk of the transfer using block mode (if supported). */
|
|
if (func->card->cccr.multi_block && (size > sdio_max_byte_size(func))) {
|
|
/* Blocks per command is limited by host count, host transfer
|
|
* size (we only use a single sg entry) and the maximum for
|
|
* IO_RW_EXTENDED of 511 blocks. */
|
|
//??
|
|
//?? todo
|
|
/*max_blocks = min(func->card->host->max_blk_count,
|
|
func->card->host->max_seg_size / func->cur_blksize);
|
|
max_blocks = min(max_blocks, 511u);*/
|
|
max_blocks = 511u;
|
|
//?? todo end
|
|
|
|
while (remainder > func->cur_blksize) {
|
|
unsigned blocks;
|
|
|
|
blocks = remainder / func->cur_blksize;
|
|
if (blocks > max_blocks)
|
|
blocks = max_blocks;
|
|
size = blocks * func->cur_blksize;
|
|
|
|
ret = mmc_io_rw_extended(func->card, write,
|
|
func->num, addr, incr_addr, buf,
|
|
blocks, func->cur_blksize);
|
|
if (ret)
|
|
return ret;
|
|
|
|
remainder -= size;
|
|
buf += size;
|
|
if (incr_addr)
|
|
addr += size;
|
|
}
|
|
}
|
|
|
|
/* Write the remainder using byte mode. */
|
|
while (remainder > 0) {
|
|
size = min(remainder, sdio_max_byte_size(func));
|
|
|
|
ret = mmc_io_rw_extended(func->card, write, func->num, addr,
|
|
incr_addr, buf, 1, size);
|
|
if (ret)
|
|
return ret;
|
|
|
|
remainder -= size;
|
|
buf += size;
|
|
if (incr_addr)
|
|
addr += size;
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
static int sdio_cardDetectCB(void)
|
|
{
|
|
return 1;
|
|
}
|
|
|
|
static int sdio_writeProtCB(void)
|
|
{
|
|
return 0;
|
|
}
|
|
|
|
static int sdio_read_cccr(struct mmc_card *card)
|
|
{
|
|
int ret;
|
|
int cccr_vsn;
|
|
unsigned char data;
|
|
|
|
memset(&card->cccr, 0, sizeof(struct sdio_cccr));
|
|
|
|
#define SDIO_CCCR_CCCR 0x00
|
|
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CCCR, 0, &data);
|
|
if (ret)
|
|
goto out;
|
|
|
|
cccr_vsn = data & 0x0f;
|
|
|
|
#define SDIO_CCCR_REV_3_00 3 /* CCCR/FBR Version 3.00 */
|
|
if (cccr_vsn > SDIO_CCCR_REV_3_00) {
|
|
//??printk(KERN_ERR "%s: unrecognised CCCR structure version %d\n",
|
|
//?? mmc_hostname(card->host), cccr_vsn);
|
|
printf("unrecognised CCCR structure version %d\n", cccr_vsn);
|
|
return -1;
|
|
}
|
|
|
|
card->cccr.sdio_vsn = (data & 0xf0) >> 4;
|
|
|
|
#define SDIO_CCCR_CAPS 0x08
|
|
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_CAPS, 0, &data);
|
|
if (ret)
|
|
goto out;
|
|
|
|
#define SDIO_CCCR_CAP_SMB 0x02 /* can do multi-block xfers (CMD53) */
|
|
if (data & SDIO_CCCR_CAP_SMB)
|
|
card->cccr.multi_block = 1;
|
|
#define SDIO_CCCR_CAP_LSC 0x40 /* low speed card */
|
|
if (data & SDIO_CCCR_CAP_LSC)
|
|
card->cccr.low_speed = 1;
|
|
#define SDIO_CCCR_CAP_4BLS 0x80 /* 4 bit low speed card */
|
|
if (data & SDIO_CCCR_CAP_4BLS)
|
|
card->cccr.wide_bus = 1;
|
|
|
|
#define SDIO_CCCR_REV_1_10 1 /* CCCR/FBR Version 1.10 */
|
|
if (cccr_vsn >= SDIO_CCCR_REV_1_10) {
|
|
#define SDIO_CCCR_POWER 0x12
|
|
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_POWER, 0, &data);
|
|
if (ret)
|
|
goto out;
|
|
|
|
#define SDIO_POWER_SMPC 0x01 /* Supports Master Power Control */
|
|
if (data & SDIO_POWER_SMPC)
|
|
card->cccr.high_power = 1;
|
|
}
|
|
|
|
#define SDIO_CCCR_REV_1_20 2 /* CCCR/FBR Version 1.20 */
|
|
if (cccr_vsn >= SDIO_CCCR_REV_1_20) {
|
|
#define SDIO_CCCR_SPEED 0x13
|
|
ret = mmc_io_rw_direct(card, 0, 0, SDIO_CCCR_SPEED, 0, &data);
|
|
if (ret)
|
|
goto out;
|
|
|
|
#define SDIO_SPEED_SHS 0x01 /* Supports High-Speed mode */
|
|
if (data & SDIO_SPEED_SHS)
|
|
card->cccr.high_speed = 1;
|
|
}
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static int cistpl_vers_1(struct mmc_card *card, struct sdio_func *func,
|
|
const unsigned char *buf, unsigned size)
|
|
{
|
|
unsigned i, nr_strings;
|
|
char **buffer, *string;
|
|
|
|
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 (buf[i-1] != '\0') {
|
|
printf("SDIO: ignoring broken CISTPL_VERS_1\n");
|
|
return 0;
|
|
}
|
|
size = i;
|
|
|
|
//buffer = kzalloc(sizeof(char*) * nr_strings + size, GFP_KERNEL);
|
|
buffer = pvPortMalloc(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 char**)buffer;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cistpl_manfid(struct mmc_card *card, struct sdio_func *func,
|
|
const unsigned char *buf, unsigned size)
|
|
{
|
|
unsigned int 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 unsigned int speed_unit[8] =
|
|
{ 10000, 100000, 1000000, 10000000, 0, 0, 0, 0 };
|
|
|
|
static int cistpl_funce_common(struct mmc_card *card,
|
|
const unsigned char *buf, unsigned size)
|
|
{
|
|
if (size < 0x04 || buf[0] != 0)
|
|
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 int cistpl_funce_func(struct sdio_func *func,
|
|
const unsigned char *buf, unsigned size)
|
|
{
|
|
unsigned vsn;
|
|
unsigned min_size;
|
|
|
|
vsn = func->card->cccr.sdio_vsn;
|
|
#define SDIO_SDIO_REV_1_00 0 /* SDIO Spec Version 1.00 */
|
|
min_size = (vsn == SDIO_SDIO_REV_1_00) ? 28 : 42;
|
|
|
|
if (size < min_size || buf[0] != 1)
|
|
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 */
|
|
if (vsn > SDIO_SDIO_REV_1_00)
|
|
func->enable_timeout = (buf[28] | (buf[29] << 8)) * 10;
|
|
else
|
|
func->enable_timeout = 0;//??jiffies_to_msecs(HZ);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int cistpl_funce(struct mmc_card *card, struct sdio_func *func,
|
|
const unsigned char *buf, unsigned size)
|
|
{
|
|
int ret;
|
|
|
|
/*
|
|
* There should be two versions of the CISTPL_FUNCE tuple,
|
|
* one for the common CIS (function 0) and a version used by
|
|
* the individual function's CIS (1-7). Yet, the later has a
|
|
* different length depending on the SDIO spec version.
|
|
*/
|
|
if (func)
|
|
ret = cistpl_funce_func(func, buf, size);
|
|
else
|
|
ret = cistpl_funce_common(card, buf, size);
|
|
|
|
if (ret) {
|
|
printf("bad CISTPL_FUNCE size %u,type %u\n", size, buf[0]);
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
static const struct cis_tpl cis_tpl_list[] = {
|
|
{ 0x15, 3, cistpl_vers_1 },
|
|
{ 0x20, 4, cistpl_manfid },
|
|
{ 0x21, 2, /* cistpl_funcid */ },
|
|
{ 0x22, 0, cistpl_funce },
|
|
};
|
|
|
|
static int sdio_read_cis(struct mmc_card *card, struct sdio_func *func)
|
|
{
|
|
int ret;
|
|
struct sdio_func_tuple *this, **prev;
|
|
unsigned 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++) {
|
|
unsigned char x, fn;
|
|
|
|
if (func)
|
|
fn = func->num;
|
|
else
|
|
fn = 0;
|
|
|
|
ret = mmc_io_rw_direct(card, 0, 0,
|
|
SDIO_FBR_BASE(fn) + SDIO_FBR_CIS + i, 0, &x);
|
|
|
|
if (ret)
|
|
return ret;
|
|
ptr |= x << (i * 8);
|
|
printf("tpl_ptr = 0x%x\r\n", ptr);
|
|
}
|
|
if (func)
|
|
//prev = &func->tuples;
|
|
prev = &func_tuples;
|
|
else
|
|
//prev = &card->tuples;
|
|
prev = &card_tuples;
|
|
|
|
//BUG_ON(*prev);
|
|
|
|
do {
|
|
unsigned char tpl_code, tpl_link;
|
|
|
|
ret = mmc_io_rw_direct(card, 0, 0, ptr++, 0, &tpl_code);
|
|
printf("tpl_code = 0x%02x\r\n", 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, 0, ptr++, 0, &tpl_link);
|
|
printf("tpl_link = 0x%02x\r\n", tpl_link);
|
|
if (ret)
|
|
break;
|
|
|
|
/* a size of 0xff also means we're done */
|
|
if (tpl_link == 0xff)
|
|
{
|
|
printf("tpl_link done, because equals to 0xff\r\n");
|
|
break;
|
|
}
|
|
|
|
this = pvPortMalloc(sizeof(*this) + tpl_link);
|
|
if (!this)
|
|
return -1;
|
|
|
|
printf("tpl_data =\r\n");
|
|
for (i = 0; i < tpl_link; i++) {
|
|
ret = mmc_io_rw_direct(card, 0, 0,
|
|
ptr + i, 0, &this->data[i]);
|
|
|
|
printf("0x%02x ", this->data[i]);
|
|
if ((i % 8) == 7)
|
|
{
|
|
printf("\r\n");
|
|
}
|
|
if (ret)
|
|
break;
|
|
}
|
|
if (ret) {
|
|
vPortFree(this);
|
|
break;
|
|
}
|
|
|
|
printf("\r\ni = %d, ARRAY_SIZE = %d\r\n", i, ARRAY_SIZE(cis_tpl_list));
|
|
for (i = 0; i < ARRAY_SIZE(cis_tpl_list); i++)
|
|
if (cis_tpl_list[i].code == tpl_code)
|
|
break;
|
|
|
|
if (i >= ARRAY_SIZE(cis_tpl_list))
|
|
{
|
|
/* this tuple is unknown to the core */
|
|
this->next = NULL;
|
|
this->code = tpl_code;
|
|
this->size = tpl_link;
|
|
*prev = this;
|
|
prev = &this->next;
|
|
printf("queuing CIS tuple 0x%02x length %u\n", tpl_code, tpl_link);
|
|
} else {
|
|
const struct cis_tpl *tpl = cis_tpl_list + i;
|
|
if (tpl_link < tpl->min_size)
|
|
{
|
|
printf("bad CIS tuple 0x%02x (length = %u, expected >= %u)\n",tpl_code, tpl_link, tpl->min_size);
|
|
ret = -1;
|
|
} else if (tpl->parse) {
|
|
ret = tpl->parse(card, func,
|
|
this->data, tpl_link);
|
|
}
|
|
vPortFree(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;
|
|
*prev = card_tuples;
|
|
|
|
return ret;
|
|
}
|
|
|
|
static int sdio_read_common_cis(struct mmc_card *card)
|
|
{
|
|
return sdio_read_cis(card, NULL);
|
|
}
|
|
|
|
static struct sdio_func *sdio_alloc_func(struct mmc_card *card)
|
|
{
|
|
struct sdio_func *func;
|
|
|
|
func = pvPortMalloc(sizeof(struct sdio_func));
|
|
if (!func)
|
|
return NULL;
|
|
|
|
memset(func, 0, sizeof(struct sdio_func));
|
|
func->card = card;
|
|
|
|
//device_initialize(&func->dev);
|
|
|
|
//??func->dev.parent = &card->dev;
|
|
//??func->dev.bus = &sdio_bus_type;
|
|
//??func->dev.release = sdio_release_func;
|
|
|
|
return func;
|
|
}
|
|
|
|
static int sdio_read_fbr(struct sdio_func *func)
|
|
{
|
|
int ret;
|
|
unsigned char data;
|
|
|
|
#define SDIO_FBR_STD_IF 0x00
|
|
ret = mmc_io_rw_direct(func->card, 0, 0,
|
|
SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF, 0, &data);
|
|
if (ret)
|
|
goto out;
|
|
|
|
data &= 0x0f;
|
|
|
|
if (data == 0x0f) {
|
|
#define SDIO_FBR_STD_IF_EXT 0x01
|
|
ret = mmc_io_rw_direct(func->card, 0, 0,
|
|
SDIO_FBR_BASE(func->num) + SDIO_FBR_STD_IF_EXT, 0, &data);
|
|
if (ret)
|
|
goto out;
|
|
}
|
|
|
|
func->class = data;
|
|
|
|
out:
|
|
return ret;
|
|
}
|
|
|
|
static 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 void sdio_remove_func(struct sdio_func *func)
|
|
{
|
|
//??
|
|
/*if (sdio_func_present(func))
|
|
device_del(&func->dev);
|
|
|
|
put_device(&func->dev);*/
|
|
}
|
|
|
|
static int sdio_init_func(struct mmc_card *card, unsigned int fn)
|
|
{
|
|
int ret;
|
|
struct sdio_func *func;
|
|
|
|
//BUG_ON(fn > SDIO_MAX_FUNCS);
|
|
|
|
func = sdio_alloc_func(card);
|
|
if (func == NULL)
|
|
return -1;
|
|
|
|
func->num = fn;
|
|
|
|
ret = sdio_read_fbr(func);
|
|
if (ret)
|
|
goto fail;
|
|
|
|
ret = sdio_read_func_cis(func);
|
|
if (ret)
|
|
goto fail;
|
|
|
|
card->sdio_func[fn - 1] = 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;
|
|
}
|
|
|
|
int sdio_set_block_size(struct sdio_func *func, unsigned blksz)
|
|
{
|
|
int ret;
|
|
|
|
if (blksz > func->card->host->max_blk_size){
|
|
printf("sdio_set_block_size:blksz(%d) > max(%d)\n", blksz, func->card->host->max_blk_size);
|
|
return -1;
|
|
}
|
|
|
|
if (blksz == 0) {
|
|
blksz = min(func->max_blksize, func->card->host->max_blk_size);
|
|
blksz = min(blksz, 512u);
|
|
}
|
|
|
|
ret = mmc_io_rw_direct(func->card, 1, 0,
|
|
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE,
|
|
blksz & 0xff, NULL);
|
|
if (ret){
|
|
printf("sdio_set_block_size:set SDIO_FBR_BLKSIZE fail\n");
|
|
return ret;
|
|
}
|
|
ret = mmc_io_rw_direct(func->card, 1, 0,
|
|
SDIO_FBR_BASE(func->num) + SDIO_FBR_BLKSIZE + 1,
|
|
(blksz >> 8) & 0xff, NULL);
|
|
if (ret){
|
|
printf("sdio_set_block_size:set SDIO_FBR_BLKSIZE+1 fail\n");
|
|
return ret;
|
|
}
|
|
func->cur_blksize = blksz;
|
|
|
|
// set sdio host block size
|
|
extern void sdioHost_setBlkSize(UINT32 id, UINT32 size);
|
|
sdioHost_setBlkSize(SDIO_HOST_ID_2, blksz);
|
|
|
|
return 0;
|
|
}
|
|
|
|
void sdio_claim_host(struct sdio_func*func)
|
|
{
|
|
SEM_WAIT(wifi_sdio_mutex);
|
|
}
|
|
|
|
void sdio_release_host(struct sdio_func *func)
|
|
{
|
|
SEM_SIGNAL(wifi_sdio_mutex);
|
|
}
|
|
|
|
static void sdio_irq_task(void *pvParameters)
|
|
{
|
|
FLGPTN uiFlag;
|
|
|
|
if(!wifi_sdio_func){
|
|
printf("sdio_irq_task : wifi_sdio_func is NULL\n");
|
|
return;
|
|
}
|
|
|
|
//coverity[no_escape]
|
|
while(1)
|
|
{
|
|
wai_flg(&uiFlag, FLG_ID_WIFI_SDIO, FLGPTN_WIFI_SDIO, TWF_ORW | TWF_CLR);
|
|
//extern volatile int sdio_io_int_count;
|
|
//printf("task in(%d)\n", sdio_io_int_count);sdio_io_int_count++;
|
|
//if(sdio_card_intr)
|
|
{
|
|
//sdio_card_intr = 0;
|
|
sdio_claim_host(wifi_sdio_func);
|
|
if (wifi_sdio_func->irq_handler)
|
|
{
|
|
wifi_sdio_func->irq_handler(wifi_sdio_func);
|
|
//wifi_sdio_func->irq_handler(wifi_sdio_func);
|
|
//wifi_sdio_func->irq_handler(wifi_sdio_func);
|
|
}
|
|
sdio_release_host(wifi_sdio_func);
|
|
}
|
|
//printf("task out(%d)\n", sdio_io_int_count);sdio_io_int_count++;
|
|
|
|
//extern UINT32 sdioHost_setIoIntEn(unsigned int id, int bEn);
|
|
//sdioHost_setIoIntEn(SDIO_HOST_ID_2, 1);
|
|
//vTaskDelay(5);
|
|
}
|
|
vTaskDelete(NULL);
|
|
}
|
|
|
|
int wifi_sdio_init(void)
|
|
{
|
|
int funcs;
|
|
int i;
|
|
|
|
SEM_CREATE(wifi_sdio_mutex, 1);
|
|
cre_flg(&FLG_ID_WIFI_SDIO, NULL, "wifi_sdio_flag");
|
|
|
|
extern void sdioPrtcl_setDetectCardExistHdl(UINT32 id, void *pHdl);
|
|
sdioPrtcl_setDetectCardExistHdl(SDIO_HOST_ID_2, sdio_cardDetectCB);
|
|
extern void sdioPrtcl_setDetectCardProtectHdl(UINT32 id, void *pHdl);
|
|
sdioPrtcl_setDetectCardProtectHdl(SDIO_HOST_ID_2, sdio_writeProtCB);
|
|
//sdioHost_creatInt(SDIO_HOST);
|
|
|
|
extern void sdioPrtcl_setMemPool(unsigned int id, unsigned int uiAddr, unsigned int uiSize);
|
|
sdioPrtcl_setMemPool(SDIO_HOST_ID_2, (unsigned int)uimempool, uimempoolsize);
|
|
sdio2_setIOEnable(1);
|
|
|
|
extern ER sdioPrtcl_setConfig(UINT32 id, SDIO_CONFIG_ID configID, UINT32 configContext);
|
|
if(sdioPrtcl_setConfig(SDIO_HOST_ID_2, SDIO_CONFIG_ID_DS_MAX_CLK, 48000000)){
|
|
printf("fail to set sdio2 to 48Mhz\n");
|
|
return -1;
|
|
}
|
|
|
|
extern int sdio2strg_open(int id);
|
|
if(sdio2strg_open(0)){
|
|
printf("sdio2strg_open(0) fail\n");
|
|
return -1;
|
|
}
|
|
|
|
extern ER sdioHost_setPadDriving(UINT32 id, UINT32 driving);
|
|
if(sdioHost_setPadDriving(SDIO_HOST_ID_2, 150)){
|
|
printf("fail to set sdio driving padding\n");
|
|
return -1;
|
|
}
|
|
|
|
memset(&func_sdio, 0, sizeof(struct sdio_func));
|
|
memset(&sdiocard, 0, sizeof(struct mmc_card));
|
|
memset(&sdiohost, 0, sizeof(struct mmc_host));
|
|
|
|
//get ocr value
|
|
funcs = 1;
|
|
|
|
//function number
|
|
printf("funcs = 0x%x\r\n", funcs);
|
|
|
|
//save sdio functions
|
|
//??sdiocard.sdio_funcs = funcs;
|
|
func_sdio.card = (struct mmc_card *)&sdiocard;
|
|
|
|
//read cccr
|
|
if(sdio_read_cccr((struct mmc_card *)&sdiocard)){
|
|
printf("sdio_read_cccr() fail\n");
|
|
return -1;
|
|
}
|
|
|
|
//read cis
|
|
if(sdio_read_common_cis((struct mmc_card *)&sdiocard)){
|
|
printf("sdio_read_common_cis() fail\n");
|
|
return -1;
|
|
}
|
|
|
|
//set to 4 bit
|
|
//sdio_enable_wide((struct mmc_card *)&sdiocard);
|
|
|
|
/*
|
|
* Initialize (but don't add) all present functions.
|
|
*/
|
|
for (i = 0;i < funcs;i++) {
|
|
if(sdio_init_func((struct mmc_card *)&sdiocard, i + 1)){
|
|
printf("sdio_init_func(%d) fail\n", i+1);
|
|
return -1;
|
|
}
|
|
//??if (err)
|
|
//?? goto remove;
|
|
}
|
|
|
|
//func_sdio.max_blksize = param.SEL.SET_BLOCK_SIZE.uiblocksize;
|
|
//func_sdio.cur_blksize = param.SEL.SET_BLOCK_SIZE.uiblocksize;
|
|
//func_sdio.num =funcs; //??temp solution
|
|
sdiocard.host = (struct mmc_host *)&sdiohost;
|
|
//func_sdio.card->host->max_blk_size = param.SEL.SET_BLOCK_SIZE.uiblocksize;
|
|
//func_sdio.card->host->max_blk_count = 511u; //?? todo
|
|
|
|
for (i = 0; i < funcs; i++)
|
|
sdiocard.sdio_func[i]->card->host->max_blk_size = 512u;
|
|
|
|
//func_sdio.num = 0;//funcs;//func.num;
|
|
func_sdio.num = sdiocard.sdio_func[funcs-1]->num;//funcs;//func.num;
|
|
func_sdio.irq_handler = NULL;
|
|
//func_sdio.max_blksize = 512u;
|
|
func_sdio.max_blksize = sdiocard.sdio_func[funcs-1]->max_blksize;
|
|
//func_sdio.cur_blksize = sdioHost_getBlksize(SDIO_HOST_ID_2);
|
|
func_sdio.enable_timeout = sdiocard.sdio_func[funcs-1]->enable_timeout;
|
|
func_sdio.vendor = sdiocard.sdio_func[funcs-1]->vendor;
|
|
func_sdio.device = sdiocard.sdio_func[funcs-1]->device;
|
|
func_sdio.num_info = sdiocard.sdio_func[funcs-1]->num_info;
|
|
func_sdio.class = sdiocard.sdio_func[funcs-1]->class;
|
|
wifi_sdio_func = &func_sdio;
|
|
|
|
sdio_set_block_size(sdiocard.sdio_func[funcs-1], 0);
|
|
func_sdio.cur_blksize = sdiocard.sdio_func[funcs-1]->cur_blksize;
|
|
|
|
//xTaskCreate(sdio_irq_task, "sdio_irq_thread", 8192, NULL, 6, &sdio_irq_task_handler );
|
|
//xTaskCreate(sdio_irq_task, "sdio_irq_thread", 8192, NULL, 27, &sdio_irq_task_handler );
|
|
if(0)
|
|
xTaskCreate(sdio_irq_task, "sdio_irq_thread", 8192, NULL, 30, &sdio_irq_task_handler );
|
|
else{
|
|
THREAD_HANDLE wifi_sdio_irq_task = 0;
|
|
wifi_sdio_irq_task = vos_task_create(sdio_irq_task, 0, "sdio_irq_thread", 1, 8192*4);
|
|
if (wifi_sdio_irq_task == 0) {
|
|
printf("create sdio_irq_thread fail\n");
|
|
} else {
|
|
vos_task_resume(wifi_sdio_irq_task);
|
|
}
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sdio_bus_probe(void)
|
|
{
|
|
printf("sdio_bus_probe()\n");
|
|
|
|
return 0;
|
|
}
|
|
|
|
int sdio_bus_remove(void)
|
|
{
|
|
printf("%s \n",__func__);
|
|
|
|
return 0;
|
|
}
|
|
|
|
struct sdio_func *sdio_get_func(void)
|
|
{
|
|
return &func_sdio;
|
|
}
|
|
|
|
#define SDIO_CCCR_IOEx 0x02
|
|
#define SDIO_CCCR_IORx 0x03
|
|
|
|
int sdio_enable_func(struct sdio_func *func)
|
|
{
|
|
int ret;
|
|
unsigned char reg;
|
|
//unsigned long timeout;
|
|
|
|
//BUG_ON(!func);
|
|
//BUG_ON(!func->card);
|
|
|
|
//sdiolinux_printf("SDIO: Enabling device %s...\n", sdio_func_id(func));
|
|
|
|
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®);
|
|
if (ret){
|
|
printf("sdio_enable_func:set SDIO_CCCR_IOEx fail\n");
|
|
goto err;
|
|
}
|
|
|
|
reg |= 1 << func->num;
|
|
|
|
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
|
|
if (ret){
|
|
printf("sdio_enable_func:set SDIO_CCCR_IOEx fail\n");
|
|
goto err;
|
|
}
|
|
|
|
//?? todo
|
|
//??timeout = jiffies + msecs_to_jiffies(func->enable_timeout);
|
|
|
|
while (1) {
|
|
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IORx, 0, ®);
|
|
if (ret)
|
|
goto err;
|
|
if (reg & (1 << func->num))
|
|
break;
|
|
//??ret = -ETIME;
|
|
//??if (time_after(jiffies, timeout))
|
|
//?? goto err;
|
|
}
|
|
|
|
//?? todo end
|
|
|
|
//sdiolinux_printf("SDIO: Enabled device %s\n", sdio_func_id(func));
|
|
|
|
return 0;
|
|
|
|
err:
|
|
printf("SDIO: Failed to enable device %p\n", func);
|
|
return ret;
|
|
}
|
|
|
|
int sdio_disable_func(struct sdio_func *func)
|
|
{
|
|
int ret;
|
|
unsigned char reg;
|
|
|
|
//BUG_ON(!func);
|
|
//BUG_ON(!func->card);
|
|
|
|
//sdiolinux_printf("SDIO: Disabling device %s...\n", sdio_func_id(func));
|
|
|
|
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IOEx, 0, ®);
|
|
if (ret){
|
|
printf("sdio_disable_func:set SDIO_CCCR_IOEx fail\n");
|
|
goto err;
|
|
}
|
|
|
|
reg &= ~(1 << func->num);
|
|
|
|
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IOEx, reg, NULL);
|
|
if (ret){
|
|
printf("sdio_disable_func:set SDIO_CCCR_IOEx fail\n");
|
|
goto err;
|
|
}
|
|
|
|
//sdiolinux_printf("SDIO: Disabled device %s\n", sdio_func_id(func));
|
|
|
|
return 0;
|
|
|
|
err:
|
|
printf("SDIO: Failed to disable device %p\n", func);
|
|
return -1;
|
|
|
|
}
|
|
|
|
static BOOL sdio_default_callback(BOOL b)
|
|
{
|
|
iset_flg(FLG_ID_WIFI_SDIO, FLGPTN_WIFI_SDIO);
|
|
|
|
return TRUE;
|
|
}
|
|
|
|
void sdio_host_ioint_enable(unsigned int enable)
|
|
{
|
|
extern UINT32 sdioHost_setIoIntEn(unsigned int id, int bEn);
|
|
sdioHost_setIoIntEn(SDIO_HOST_ID_2, enable);
|
|
}
|
|
|
|
int sdio_claim_irq(struct sdio_func *func, void(*handler)(struct sdio_func *))
|
|
{
|
|
int ret;
|
|
unsigned char reg;
|
|
|
|
func->irq_handler = NULL;
|
|
|
|
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);
|
|
if (ret){
|
|
printf("sdio_claim_irq:set SDIO_CCCR_IENx fail\n");
|
|
return ret;
|
|
}
|
|
|
|
reg |= 1 << func->num;
|
|
|
|
reg |= 1; /* Master interrupt enable */
|
|
|
|
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
|
|
if (ret){
|
|
printf("sdio_claim_irq:set SDIO_CCCR_IENx fail\n");
|
|
return ret;
|
|
}
|
|
|
|
|
|
//sdio_reg_iocallback((sdio_iocallback_hdl)handler);
|
|
save_sdio_func = func;
|
|
vpSdioLinuxIoHdl = handler;
|
|
//sdio_reg_iocallback((sdio_iocallback_hdl)sdio_default_callback);
|
|
#define SDIO_CALLBACK_IO_INT 4
|
|
sdio2_setCallBack(SDIO_CALLBACK_IO_INT, sdio_default_callback);
|
|
extern UINT32 sdioHost_setIoIntEn(unsigned int id, int bEn);
|
|
sdioHost_setIoIntEn(SDIO_HOST_ID_2, 1);
|
|
|
|
func->irq_handler = handler;
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int sdio_release_irq(struct sdio_func *func)
|
|
{
|
|
int ret;
|
|
unsigned char reg;
|
|
|
|
if (func->irq_handler)
|
|
{
|
|
func->irq_handler = NULL;
|
|
|
|
}
|
|
//sdio_reg_iocallback((sdio_iocallback_hdl)func->irq_handler);
|
|
#define SDIO_CALLBACK_IO_INT 4
|
|
sdio2_setCallBack(SDIO_CALLBACK_IO_INT, NULL);
|
|
sdio2_setIOEnable(SDIO_HOST_IOINT_DIS);
|
|
|
|
ret = mmc_io_rw_direct(func->card, 0, 0, SDIO_CCCR_IENx, 0, ®);
|
|
if (ret){
|
|
printf("sdio_release_irq:set SDIO_CCCR_IENx fail\n");
|
|
return ret;
|
|
}
|
|
|
|
reg &= ~(1 << func->num);
|
|
|
|
/* Disable master interrupt with the last function interrupt */
|
|
if (!(reg & 0xFE))
|
|
reg = 0;
|
|
|
|
ret = mmc_io_rw_direct(func->card, 1, 0, SDIO_CCCR_IENx, reg, NULL);
|
|
if (ret){
|
|
printf("sdio_release_irq:set SDIO_CCCR_IENx fail\n");
|
|
return ret;
|
|
}
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
int sdio_memcpy_fromio(struct sdio_func *func, void *dst,unsigned int addr, int count)
|
|
{
|
|
return mmc_io_rw_ext_helper(func, 0, addr, 1, dst, count);
|
|
}
|
|
|
|
int sdio_memcpy_toio(struct sdio_func *func, unsigned int addr,void *src, int count)
|
|
{
|
|
return mmc_io_rw_ext_helper(func, 1, addr, 1, src, count);
|
|
}
|
|
|
|
u8 sdio_readb(struct sdio_func *func, unsigned int addr, int *err_ret)
|
|
{
|
|
int ret;
|
|
u8 val;
|
|
//u8 in;
|
|
|
|
//BUG_ON(!func);
|
|
|
|
if (err_ret)
|
|
*err_ret = 0;
|
|
|
|
//in = 0;
|
|
|
|
ret = mmc_io_rw_direct(func->card, 0, func->num, addr, 0, &val);
|
|
if (ret) {
|
|
if (err_ret)
|
|
*err_ret = ret;
|
|
return 0xFF;
|
|
}
|
|
|
|
return val;
|
|
|
|
}
|
|
|
|
u16 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, func->tmpbuf, addr, 2);
|
|
if (ret) {
|
|
if (err_ret)
|
|
*err_ret = ret;
|
|
return 0xFFFF;
|
|
}
|
|
|
|
return *(u16 *)func->tmpbuf;
|
|
}
|
|
|
|
static u32 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, func->tmpbuf, addr, 4);
|
|
if (ret) {
|
|
if (err_ret)
|
|
*err_ret = ret;
|
|
return 0xFFFFFFFF;
|
|
}
|
|
|
|
return *(u32 *)(func->tmpbuf);
|
|
}
|
|
|
|
void sdio_writeb(struct sdio_func *func, u8 b,unsigned int addr, int *err_ret)
|
|
{
|
|
int ret;
|
|
|
|
//BUG_ON(!func);
|
|
|
|
ret = mmc_io_rw_direct(func->card, 1, func->num, addr, b, NULL);
|
|
if (err_ret)
|
|
*err_ret = ret;
|
|
}
|
|
|
|
static void sdio_writew(struct sdio_func *func, u16 b,unsigned int addr, int *err_ret)
|
|
{
|
|
int ret;
|
|
|
|
*(u16 *)func->tmpbuf = b;
|
|
|
|
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 2);
|
|
if (err_ret)
|
|
*err_ret = ret;
|
|
}
|
|
|
|
static void sdio_writel(struct sdio_func *func, u32 b,unsigned int addr, int *err_ret)
|
|
{
|
|
int ret;
|
|
|
|
*(u32 *)func->tmpbuf = b;
|
|
|
|
ret = sdio_memcpy_toio(func, addr, func->tmpbuf, 4);
|
|
if (err_ret)
|
|
*err_ret = ret;
|
|
}
|
|
|
|
/* test wifi driver */
|
|
#define ADDR_MASK 0x10000
|
|
#define LOCAL_ADDR_MASK 0x00000
|
|
|
|
int wifi_read(struct sdio_func *func, u32 addr, u32 cnt, void *pdata)
|
|
{
|
|
int err;
|
|
|
|
sdio_claim_host(func);
|
|
|
|
err = sdio_memcpy_fromio(func, pdata, addr, cnt);
|
|
if (err) {
|
|
printf("%s: FAIL(%d)! ADDR=%#x Size=%d\n", __func__, err, addr, cnt);
|
|
}
|
|
|
|
sdio_release_host(func);
|
|
|
|
return err;
|
|
}
|
|
|
|
int wifi_write(struct sdio_func *func, u32 addr, u32 cnt, void *pdata)
|
|
{
|
|
int err;
|
|
u32 size;
|
|
|
|
sdio_claim_host(func);
|
|
|
|
size = cnt;
|
|
err = sdio_memcpy_toio(func, addr, pdata, size);
|
|
if (err) {
|
|
printf("%s: FAIL(%d)! ADDR=%#x Size=%d(%d)\n", __func__, err, addr, cnt, size);
|
|
}
|
|
|
|
sdio_release_host(func);
|
|
|
|
return err;
|
|
}
|
|
|
|
u8 wifi_readb(struct sdio_func *func, u32 addr)
|
|
{
|
|
int err;
|
|
u8 ret = 0;
|
|
|
|
sdio_claim_host(func);
|
|
ret = sdio_readb(func, ADDR_MASK | addr, &err);
|
|
sdio_release_host(func);
|
|
|
|
if (err)
|
|
printf("%s: FAIL!(%d) addr=0x%05x\n", __func__, err, addr);
|
|
|
|
return ret;
|
|
}
|
|
|
|
u16 wifi_readw(struct sdio_func *func, u32 addr)
|
|
{
|
|
int err;
|
|
u16 v;
|
|
|
|
sdio_claim_host(func);
|
|
v = sdio_readw(func, ADDR_MASK | addr, &err);
|
|
sdio_release_host(func);
|
|
if (err)
|
|
printf("%s: FAIL!(%d) addr=0x%05x\n", __func__, err, addr);
|
|
|
|
return v;
|
|
}
|
|
|
|
u32 wifi_readl(struct sdio_func *func, u32 addr)
|
|
{
|
|
int err;
|
|
u32 v;
|
|
|
|
sdio_claim_host(func);
|
|
v = sdio_readl(func, ADDR_MASK | addr, &err);
|
|
sdio_release_host(func);
|
|
|
|
return v;
|
|
}
|
|
|
|
void wifi_writeb(struct sdio_func *func, u32 addr, u8 val)
|
|
{
|
|
int err;
|
|
|
|
sdio_claim_host(func);
|
|
sdio_writeb(func, val, ADDR_MASK | addr, &err);
|
|
sdio_release_host(func);
|
|
if (err)
|
|
printf("%s: FAIL!(%d) addr=0x%05x val=0x%02x\n", __func__, err, addr, val);
|
|
}
|
|
|
|
void wifi_writew(struct sdio_func *func, u32 addr, u16 v)
|
|
{
|
|
int err;
|
|
|
|
sdio_claim_host(func);
|
|
sdio_writew(func, v, ADDR_MASK | addr, &err);
|
|
sdio_release_host(func);
|
|
if (err)
|
|
printf("%s: FAIL!(%d) addr=0x%05x val=0x%04x\n", __func__, err, addr, v);
|
|
}
|
|
|
|
void wifi_writel(struct sdio_func *func, u32 addr, u32 v)
|
|
{
|
|
int err;
|
|
|
|
sdio_claim_host(func);
|
|
sdio_writel(func, v, ADDR_MASK | addr, &err);
|
|
sdio_release_host(func);
|
|
}
|
|
|
|
u8 wifi_readb_local(struct sdio_func *func, u32 addr)
|
|
{
|
|
int err;
|
|
u8 ret = 0;
|
|
|
|
ret = sdio_readb(func, LOCAL_ADDR_MASK | addr, &err);
|
|
|
|
return ret;
|
|
}
|
|
|
|
void wifi_writeb_local(struct sdio_func *func, u32 addr, u8 val)
|
|
{
|
|
int err;
|
|
|
|
sdio_writeb(func, val, LOCAL_ADDR_MASK | addr, &err);
|
|
}
|
|
|
|
/*below functions are exported
|
|
sdio_bus_probe,
|
|
sdio_bus_remove,
|
|
sdio_enable_func,
|
|
sdio_disable_func,
|
|
sdio_reg_driver,
|
|
sdio_unreg_driver,
|
|
sdio_claim_irq,
|
|
sdio_release_irq,
|
|
sdio_claim_host,
|
|
sdio_release_host,
|
|
sdio_readb,
|
|
sdio_readw,
|
|
sdio_readl,
|
|
sdio_writeb,
|
|
sdio_writew,
|
|
sdio_writel,
|
|
sdio_memcpy_fromio,
|
|
sdio_memcpy_toio
|
|
*/
|