2023-12-25 21:53:30 +03:00

1390 lines
36 KiB
C

/*
* Copyright (c) 2010 -2013 Espressif System.
*
* sdio serial i/f driver
* - sdio device control routines
* - sync/async DMA/PIO read/write
*
*/
#include <linux/mmc/card.h>
#include <linux/mmc/mmc.h>
#include <linux/mmc/core.h>
#include <linux/mmc/host.h>
#include <linux/mmc/sdio_func.h>
#include <linux/mmc/sdio_ids.h>
#include <linux/mmc/sdio.h>
#include <linux/mmc/sd.h>
#include <linux/module.h>
#include <net/mac80211.h>
#include <linux/time.h>
#include <linux/pm.h>
#include "esp_pub.h"
#include "esp_sif.h"
#include "esp_sip.h"
#include "esp_debug.h"
#include "slc_host_register.h"
#include "esp_version.h"
#include "esp_ctrl.h"
#ifdef ANDROID
#include "esp_android.h"
#endif /* ANDROID */
#define CCCR_SDIO_IRQ_MODE_REG 0xF0
#define SDIO_IRQ_MODE_ASYNC_4BIT_IRQ (1 << 0)
#define CMD53_FIXED_ADDRESS 1
#define CMD53_INCR_ADDRESS 2
#define ESP_DMA_IBUFSZ 2048
//unsigned int esp_msg_level = 0;
unsigned int esp_msg_level = ESP_DBG_ERROR | ESP_SHOW ;
static struct semaphore esp_powerup_sem;
static enum esp_sdio_state sif_sdio_state;
struct esp_sdio_ctrl *sif_sctrl = NULL;
#ifdef ESP_ANDROID_LOGGER
bool log_off = false;
#endif /* ESP_ANDROID_LOGGER */
typedef struct esp_sdio_ctrl {
struct sdio_func *func;
struct esp_pub *epub;
struct list_head free_req;
struct sif_req reqs[SIF_REQ_MAX_NUM];
u8 *dma_buffer;
spinlock_t scat_lock;
struct list_head scat_req;
bool off;
atomic_t irq_handling;
const struct sdio_device_id *id;
u32 slc_blk_sz;
u32 target_id;
u32 slc_window_end_addr;
struct slc_host_regs slc_regs;
atomic_t irq_installed;
} esp_sdio_ctrl_t;
static int esdio_power_off(struct esp_sdio_ctrl *sctrl);
static int esdio_power_on(struct esp_sdio_ctrl *sctrl);
void sif_set_clock(struct sdio_func *func, int clk);
#ifdef SIF_DEBUG_DSR_DUMP_REG
static void dump_slc_regs(struct slc_host_regs *regs);
#endif /* SIF_DEBUG_DSR_DUMP_REG */
struct sif_req * sif_alloc_req(struct esp_sdio_ctrl *sctrl);
#if 1
static inline void set_cmd53_arg(u32 *arg, u8 rw, u8 func,
u8 mode, u8 opcode, u32 addr,
u16 blksz)
{
*arg = (((rw & 1) << 31) |
((func & 0x7) << 28) |
((mode & 1) << 27) |
((opcode & 1) << 26) |
((addr & 0x1FFFF) << 9) |
(blksz & 0x1FF));
}
static inline void set_cmd52_arg(u32 *arg, u8 write, u8 raw,
unsigned int address,
unsigned char val)
{
const u8 func = 0;
*arg = ((write & 1) << 31) |
((func & 0x7) << 28) |
((raw & 1) << 27) |
(1 << 26) |
((address & 0x1FFFF) << 9) |
(1 << 8) |
(val & 0xFF);
}
#if 0
static int func0_cmd52_wr_byte(struct mmc_card *card,
unsigned int address,
unsigned char byte)
{
struct mmc_command io_cmd;
memset(&io_cmd, 0, sizeof(io_cmd));
set_cmd52_arg(&io_cmd.arg, 1, 0, address, byte);
io_cmd.opcode = SD_IO_RW_DIRECT;
io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
return mmc_wait_for_cmd(card->host, &io_cmd, 0);
}
static int func0_cmd52_rd_byte(struct mmc_card *card,
unsigned int address,
unsigned char *byte)
{
struct mmc_command io_cmd;
int err = 0;
memset(&io_cmd, 0, sizeof(io_cmd));
set_cmd52_arg(&io_cmd.arg, 0, 0, address, 0);
io_cmd.opcode = SD_IO_RW_DIRECT;
io_cmd.flags = MMC_RSP_R5 | MMC_CMD_AC;
err = mmc_wait_for_cmd(card->host, &io_cmd, 0);
if (!err && byte)
*byte = io_cmd.resp[0] & 0xff;
return err;
}
#endif /*0000*/
/*
* Bypass linux sdio layer to do raw sdio cmds
*/
#if 0
static int emmc_io_rw_direct(struct mmc_card *card, int write, unsigned fn,
unsigned addr, u8 in, u8* out)
{
struct mmc_command cmd;
int err;
BUG_ON(!card);
BUG_ON(fn > 7);
/* sanity check */
if (addr & ~0x1FFFF)
return -EINVAL;
memset(&cmd, 0, sizeof(struct mmc_command));
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(card->host, &cmd, 0);
if (err)
return err;
if (cmd.resp[0] & R5_ERROR)
return -EIO;
if (cmd.resp[0] & R5_FUNCTION_NUMBER)
return -EINVAL;
if (cmd.resp[0] & R5_OUT_OF_RANGE)
return -ERANGE;
if (out) {
if (mmc_host_is_spi(card->host))
*out = (cmd.resp[0] >> 8) & 0xFF;
else
*out = cmd.resp[0] & 0xFF;
}
return 0;
}
#endif /* 0000 */
#endif /* 1111 */
#ifdef USE_OOB_INTR
typedef enum _SDIO_INTR_MODE {
SDIO_INTR_IB = 0,
SDIO_INTR_OOB_TOGGLE,
SDIO_INTR_OOB_HIGH_LEVEL,
SDIO_INTR_OOB_LOW_LEVEL,
} SDIO_INTR_MODE;
#define GEN_GPIO_SEL(_gpio_num, _sel_func, _intr_mode, _offset) (((_offset)<< 9 ) |((_intr_mode) << 7)|((_sel_func) << 4)|(_gpio_num))
//bit[3:0] = gpio num, 2
//bit[6:4] = gpio sel func, 0
//bit[8:7] = gpio intr mode, SDIO_INTR_OOB_TOGGLE
//bit[15:9] = register offset, 0x38
u16 gpio_sel_sets[17] = {
GEN_GPIO_SEL(0, 0, SDIO_INTR_OOB_TOGGLE, 0x34),//GPIO0
GEN_GPIO_SEL(1, 3, SDIO_INTR_OOB_TOGGLE, 0x18),//U0TXD
GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38),//GPIO2
GEN_GPIO_SEL(3, 3, SDIO_INTR_OOB_TOGGLE, 0x14),//U0RXD
GEN_GPIO_SEL(4, 0, SDIO_INTR_OOB_TOGGLE, 0x3C),//GPIO4
GEN_GPIO_SEL(5, 0, SDIO_INTR_OOB_TOGGLE, 0x40),//GPIO5
GEN_GPIO_SEL(6, 3, SDIO_INTR_OOB_TOGGLE, 0x1C),//SD_CLK
GEN_GPIO_SEL(7, 3, SDIO_INTR_OOB_TOGGLE, 0x20),//SD_DATA0
GEN_GPIO_SEL(8, 3, SDIO_INTR_OOB_TOGGLE, 0x24),//SD_DATA1
GEN_GPIO_SEL(9, 3, SDIO_INTR_OOB_TOGGLE, 0x28),//SD_DATA2
GEN_GPIO_SEL(10, 3, SDIO_INTR_OOB_TOGGLE, 0x2C),//SD_DATA3
GEN_GPIO_SEL(11, 3, SDIO_INTR_OOB_TOGGLE, 0x30),//SD_CMD
GEN_GPIO_SEL(12, 3, SDIO_INTR_OOB_TOGGLE, 0x04),//MTDI
GEN_GPIO_SEL(13, 3, SDIO_INTR_OOB_TOGGLE, 0x08),//MTCK
GEN_GPIO_SEL(14, 3, SDIO_INTR_OOB_TOGGLE, 0x0C),//MTMS
GEN_GPIO_SEL(15, 3, SDIO_INTR_OOB_TOGGLE, 0x10),//MTDO
//pls do not change sel before, if you want to change intr mode,change the one blow
//GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_TOGGLE, 0x38)
GEN_GPIO_SEL(2, 0, SDIO_INTR_OOB_LOW_LEVEL, 0x38)
};
#endif
void sif_raw_dummy_read(struct esp_pub *epub)
{
struct esp_sdio_ctrl *sctrl = NULL;
struct sdio_func *func = NULL;
// int err = 0;
int retry = 0;
// int i;
// u32 v = 0;
u32 *p_target_id = NULL;
static u32 read_err_cnt = 0;
static u32 write_err_cnt = 0;
static u32 unknow_err_cnt = 0;
static u32 check_cnt = 0;
ASSERT(epub != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
func = sctrl->func;
p_target_id = kzalloc(sizeof(u32), GFP_KERNEL);
ASSERT(p_target_id != NULL);
*p_target_id = 0;
*p_target_id = 0x010001ff;
sdio_memcpy_toio(func, SLC_HOST_CONF_W4, p_target_id, sizeof(u32));
do {
#if 0
for (i = 0; i < 4; i++) {
err = func0_cmd52_rd_byte(func->card, SLC_HOST_ID+i, ((u8 *)&v + i));
}
if (v == sctrl->target_id) {
//printk("%s err %d v %x counter %d\n", __func__, err, v, 6 - retry);
break;
}
#endif
*p_target_id = 0xffffffff;
udelay(20);
sdio_memcpy_fromio(func, p_target_id, SLC_HOST_CONF_W4, sizeof(u32));
if(*p_target_id == 0x020001ff)
break;
if(*p_target_id == 0x010001ff){
if(retry < 5)
continue;
}else if(*p_target_id == 0x000001ff){
write_err_cnt++;
}else if(*p_target_id == 0xffffffff){
read_err_cnt++;
write_err_cnt++;
}else
unknow_err_cnt++;
*p_target_id = 0x010001ff;
udelay(20);
sdio_memcpy_toio(func, SLC_HOST_CONF_W4, p_target_id, sizeof(u32));
//sdio_memcpy_fromio(func, p_target_id, SLC_HOST_ID, sizeof(u32));
//if (*p_target_id == sctrl->target_id)
//break;
} while (retry++ < 100);
kfree(p_target_id);
if(read_err_cnt || write_err_cnt || unknow_err_cnt){
if((check_cnt & 0xf) == 0)
printk("==============sdio err===============\n,read:%u, write:%u, unknow:%u\n", read_err_cnt,write_err_cnt,unknow_err_cnt);
check_cnt++;
}
if (retry > 1) {
printk("=========%s tried %d times===========\n", __func__, retry - 1);
//if (retry>=100)
// ASSERT(0);
}
#if 0
if (err)
printk("=========%s error %d after %d times read!!===============\n", __func__, err, retry);
#endif
}
static void check_target_id(struct sdio_func *func)
{
struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
u32 date;
int err = 0;
int i;
sdio_claim_host(func);
//err = sdio_memcpy_fromio(func, &date, SLC_HOST_DATE, sizeof(u32));
//err = sdio_memcpy_fromio(func, &(sctrl->target_id), SLC_HOST_ID, sizeof(u32));
for(i = 0; i < 4; i++) {
*((u8 *)&date + i) = sdio_readb(func, SLC_HOST_DATE + i, &err);
*((u8 *)&sctrl->target_id + i) = sdio_readb(func, SLC_HOST_ID + i, &err);
}
sdio_release_host(func);
esp_dbg(ESP_DBG_LOG, "\n\n \t\t SLC data 0x%08x, ID 0x%08x\n\n", date, sctrl->target_id);
switch(sctrl->target_id) {
case 0x100:
sctrl->slc_window_end_addr = 0x20000;
break;
case 0x600:
sctrl->slc_window_end_addr = 0x20000 - 0x800;
#ifdef USE_OOB_INTR
do{
u16 gpio_sel = gpio_sel_sets[16];
u8 low_byte = gpio_sel;
u8 high_byte = gpio_sel >> 8;
sdio_claim_host(func);
sdio_writeb(func, low_byte, SLC_HOST_CONF_W5, &err);
sdio_writeb(func, high_byte, SLC_HOST_CONF_W5 + 1, &err);
sdio_release_host(func);
}while(0);
#endif
break;
default:
sctrl->slc_window_end_addr = 0x20000;
break;
}
}
static inline bool bad_buf(u8 * buf)
{
return ((unsigned long) buf & 0x3) || !virt_addr_valid(buf);
}
void sif_lock_bus(struct esp_pub *epub)
{
struct esp_sdio_ctrl *sctrl = NULL;
struct sdio_func *func = NULL;
ASSERT(epub != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
func = sctrl->func;
ASSERT(func != NULL);
sdio_claim_host(func);
}
void sif_unlock_bus(struct esp_pub *epub)
{
struct esp_sdio_ctrl *sctrl = NULL;
struct sdio_func *func = NULL;
ASSERT(epub != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
func = sctrl->func;
ASSERT(func != NULL);
sdio_release_host(func);
}
int sif_io_raw(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag)
{
int err = 0;
u8 *ibuf = NULL;
bool need_ibuf = false;
struct esp_sdio_ctrl *sctrl = NULL;
struct sdio_func *func = NULL;
ASSERT(epub != NULL);
ASSERT(buf != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
func = sctrl->func;
ASSERT(func != NULL);
if (bad_buf(buf)) {
esp_dbg(ESP_DBG_TRACE, "%s dst 0x%08x, len %d badbuf\n", __func__, addr, len);
need_ibuf = true;
ibuf = sctrl->dma_buffer;
} else {
ibuf = buf;
}
if (flag & SIF_BLOCK_BASIS) {
/* round up for block data transcation */
}
if (flag & SIF_TO_DEVICE) {
if (need_ibuf)
memcpy(ibuf, buf, len);
if (flag & SIF_FIXED_ADDR)
err = sdio_writesb(func, addr, ibuf, len);
else if (flag & SIF_INC_ADDR) {
err = sdio_memcpy_toio(func, addr, ibuf, len);
}
} else if (flag & SIF_FROM_DEVICE) {
if (flag & SIF_FIXED_ADDR)
err = sdio_readsb(func, ibuf, addr, len);
else if (flag & SIF_INC_ADDR) {
err = sdio_memcpy_fromio(func, ibuf, addr, len);
}
if (!err && need_ibuf)
memcpy(buf, ibuf, len);
}
return err;
}
#ifdef SDIO_TEST
static void sif_test_tx(struct esp_sdio_ctrl *sctrl)
{
int i, err = 0;
for (i = 0; i < 500; i++) {
sctrl->dma_buffer[i] = i;
}
sdio_claim_host(sctrl->func);
err = sdio_memcpy_toio(sctrl->func, 0x10001 - 500, sctrl->dma_buffer, 500);
sdio_release_host(sctrl->func);
esp_dbg(ESP_DBG, "%s toio err %d\n", __func__, err);
}
static void sif_test_dsr(struct sdio_func *func)
{
struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
sdio_release_host(sctrl->func);
/* no need to read out registers in normal operation any more */
//sif_io_sync(sctrl->epub, SIF_SLC_WINDOW_END_ADDR - 64, sctrl->dma_buffer, 64, SIF_FROM_DEVICE | SIF_INC_ADDR | SIF_SYNC | SIF_BYTE_BASIS);
//
esp_dsr(sctrl->epub);
sdio_claim_host(func);
//show_buf(sctrl->dma_buffer, 64);
}
void sif_test_rx(struct esp_sdio_ctrl *sctrl)
{
int err = 0;
sdio_claim_host(sctrl->func);
err = sdio_claim_irq(sctrl->func, sif_test_dsr);
if (err)
esp_dbg(ESP_DBG_ERROR, "sif %s failed\n", __func__);
sdio_release_host(sctrl->func);
}
#endif //SDIO_TEST
void sdio_io_writeb(struct esp_pub *epub, u8 value, int addr, int *res)
{
struct esp_sdio_ctrl *sctrl = NULL;
struct sdio_func *func = NULL;
sctrl = (struct esp_sdio_ctrl *)epub->sif;
func = sctrl->func;
sdio_f0_writeb(func, value, addr, res);
}
u8 sdio_io_readb(struct esp_pub *epub, int addr, int *res)
{
struct esp_sdio_ctrl *sctrl = NULL;
struct sdio_func *func = NULL;
sctrl = (struct esp_sdio_ctrl *)epub->sif;
func = sctrl->func;
return sdio_f0_readb(func, addr, res);
}
int sif_io_sync(struct esp_pub *epub, u32 addr, u8 *buf, u32 len, u32 flag)
{
int err = 0;
u8 * ibuf = NULL;
bool need_ibuf = false;
struct esp_sdio_ctrl *sctrl = NULL;
struct sdio_func *func = NULL;
ASSERT(epub != NULL);
ASSERT(buf != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
func = sctrl->func;
ASSERT(func != NULL);
if (bad_buf(buf)) {
esp_dbg(ESP_DBG_TRACE, "%s dst 0x%08x, len %d badbuf\n", __func__, addr, len);
need_ibuf = true;
ibuf = sctrl->dma_buffer;
} else {
ibuf = buf;
}
if (flag & SIF_BLOCK_BASIS) {
/* round up for block data transcation */
}
if (flag & SIF_TO_DEVICE) {
esp_dbg(ESP_DBG_TRACE, "%s to addr 0x%08x, len %d \n", __func__, addr, len);
if (need_ibuf)
memcpy(ibuf, buf, len);
sdio_claim_host(func);
if (flag & SIF_FIXED_ADDR)
err = sdio_writesb(func, addr, ibuf, len);
else if (flag & SIF_INC_ADDR) {
err = sdio_memcpy_toio(func, addr, ibuf, len);
}
sdio_release_host(func);
} else if (flag & SIF_FROM_DEVICE) {
esp_dbg(ESP_DBG_TRACE, "%s from addr 0x%08x, len %d \n", __func__, addr, len);
sdio_claim_host(func);
if (flag & SIF_FIXED_ADDR)
err = sdio_readsb(func, ibuf, addr, len);
else if (flag & SIF_INC_ADDR) {
err = sdio_memcpy_fromio(func, ibuf, addr, len);
}
sdio_release_host(func);
if (!err && need_ibuf)
memcpy(buf, ibuf, len);
}
return err;
}
u32 sif_get_blksz(struct esp_pub *epub)
{
struct esp_sdio_ctrl *sctrl = NULL;
ASSERT(epub != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
return sctrl->slc_blk_sz;
}
u32 sif_get_target_id(struct esp_pub *epub)
{
struct esp_sdio_ctrl *sctrl = NULL;
ASSERT(epub != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
return sctrl->target_id;
}
int sif_lldesc_read_sync(struct esp_pub *epub, u8 *buf, u32 len)
{
struct esp_sdio_ctrl *sctrl = NULL;
u32 read_len;
ASSERT(epub != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
switch(sctrl->target_id) {
case 0x100:
read_len = len;
break;
case 0x600:
read_len = roundup(len, sctrl->slc_blk_sz);
break;
default:
read_len = len;
break;
}
return sif_io_sync((epub), (sctrl->slc_window_end_addr - 2 - (len)), (buf), (read_len), SIF_FROM_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR);
}
int sif_lldesc_write_sync(struct esp_pub *epub, u8 *buf, u32 len)
{
struct esp_sdio_ctrl *sctrl = NULL;
u32 write_len;
ASSERT(epub != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
switch(sctrl->target_id) {
case 0x100:
write_len = len;
break;
case 0x600:
write_len = roundup(len, sctrl->slc_blk_sz);
break;
default:
write_len = len;
break;
}
return sif_io_sync((epub), (sctrl->slc_window_end_addr - (len)), (buf), (write_len), SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR);
}
int sif_lldesc_read_raw(struct esp_pub *epub, u8 *buf, u32 len, bool noround)
{
struct esp_sdio_ctrl *sctrl = NULL;
u32 read_len;
ASSERT(epub != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
switch(sctrl->target_id) {
case 0x100:
read_len = len;
break;
case 0x600:
if(!noround)
read_len = roundup(len, sctrl->slc_blk_sz);
else
read_len = len;
break;
default:
read_len = len;
break;
}
return sif_io_raw((epub), (sctrl->slc_window_end_addr - 2 - (len)), (buf), (read_len), SIF_FROM_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR);
}
int sif_lldesc_write_raw(struct esp_pub *epub, u8 *buf, u32 len)
{
struct esp_sdio_ctrl *sctrl = NULL;
u32 write_len;
ASSERT(epub != NULL);
sctrl = (struct esp_sdio_ctrl *)epub->sif;
switch(sctrl->target_id) {
case 0x100:
write_len = len;
break;
case 0x600:
write_len = roundup(len, sctrl->slc_blk_sz);
break;
default:
write_len = len;
break;
}
return sif_io_raw((epub), (sctrl->slc_window_end_addr - (len)), (buf), (write_len), SIF_TO_DEVICE | SIF_BYTE_BASIS | SIF_INC_ADDR);
}
#define MANUFACTURER_ID_EAGLE_BASE 0x1110
#define MANUFACTURER_ID_EAGLE_BASE_MASK 0xFF00
#define MANUFACTURER_CODE 0x6666
static const struct sdio_device_id esp_sdio_devices[] = {
{SDIO_DEVICE(MANUFACTURER_CODE, (MANUFACTURER_ID_EAGLE_BASE | 0x1))},
{},
};
static int esdio_power_on(struct esp_sdio_ctrl *sctrl)
{
int err = 0;
assert(sctrl != NULL);
if (sctrl->off == false)
return err;
sdio_claim_host(sctrl->func);
err = sdio_enable_func(sctrl->func);
if (err) {
esp_dbg(ESP_DBG_ERROR, "Unable to enable sdio func: %d\n", err);
sdio_release_host(sctrl->func);
return err;
}
sdio_release_host(sctrl->func);
/* ensure device is up */
msleep(5);
sctrl->off = false;
return err;
}
static int esdio_power_off(struct esp_sdio_ctrl *sctrl)
{
int err;
if (sctrl->off)
return 0;
sdio_claim_host(sctrl->func);
err = sdio_disable_func(sctrl->func);
sdio_release_host(sctrl->func);
if (err)
return err;
sctrl->off = true;
return err;
}
#ifdef SIF_CHECK_FIRST_INTR
static bool first_intr_checked = false;
#endif //SIF_CHECK_FIRST_INTR
static void sif_dsr(struct sdio_func *func)
{
struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
#ifdef SIF_DSR_WAR
static int dsr_cnt = 0, real_intr_cnt = 0, bogus_intr_cnt = 0;
struct slc_host_regs *regs = &(sctrl->slc_regs);
esp_dbg(ESP_DBG_TRACE, " %s enter %d \n", __func__, dsr_cnt++);
#endif /* SIF_DSR_WAR */
atomic_set(&sctrl->irq_handling, 1);
sdio_release_host(sctrl->func);
#ifdef SIF_DSR_WAR
do {
#ifdef SIF_CHECK_FIRST_INTR
if (likely(first_intr_checked)) {
esp_dsr(sctrl->epub);
break;
}
#endif //SIF_CHECK_FIRST_INTR
memset(regs, 0xff, sizeof(struct slc_host_regs));
sif_io_sync(sctrl->epub, REG_SLC_HOST_BASE + 8, (u8 *)regs, sizeof(struct slc_host_regs), SIF_FROM_DEVICE | SIF_INC_ADDR | SIF_SYNC | SIF_BYTE_BASIS);
if (regs->intr_status & SLC_HOST_RX_ST) {
#ifdef SIF_CHECK_FIRST_INTR
first_intr_checked = true;
#endif //SIF_CHECK_FIRST_INTR
esp_dbg(ESP_DBG_TRACE, "%s eal intr cnt: %d", __func__, ++real_intr_cnt);
esp_dsr(sctrl->epub);
} else {
#ifdef ESP_ACK_INTERRUPT
sif_lock_bus(sctrl->epub);
sif_platform_ack_interrupt(sctrl->epub);
sif_unlock_bus(sctrl->epub);
#endif //ESP_ACK_INTERRUPT
esp_dbg(ESP_DBG_TRACE, "%s bogus_intr_cnt %d\n", __func__, ++bogus_intr_cnt);
}
#ifdef SIF_DEBUG_DSR_DUMP_REG
dump_slc_regs(regs);
#endif /* SIF_DEBUG_DUMP_DSR */
} while (0);
#else
esp_dsr(sctrl->epub);
#endif /* SIF_DSR_WAR */
sdio_claim_host(func);
atomic_set(&sctrl->irq_handling, 0);
}
struct slc_host_regs * sif_get_regs(struct esp_pub *epub)
{
struct slc_host_regs * regs;
struct esp_sdio_ctrl *sctrl = (struct esp_sdio_ctrl *)epub->sif;
regs = &(sctrl->slc_regs);
return regs;
}
void sif_enable_irq(struct esp_pub *epub)
{
int err;
struct esp_sdio_ctrl *sctrl = NULL;
sctrl = (struct esp_sdio_ctrl *)epub->sif;
sdio_claim_host(sctrl->func);
err = sdio_claim_irq(sctrl->func, sif_dsr);
if (err)
esp_dbg(ESP_DBG_ERROR, "sif %s failed\n", __func__);
atomic_set(&epub->sip->state, SIP_BOOT);
atomic_set(&sctrl->irq_installed, 1);
sdio_release_host(sctrl->func);
}
void sif_disable_target_interrupt(struct esp_pub *epub)
{
struct esp_sdio_ctrl *sctrl = (struct esp_sdio_ctrl *)epub->sif;
sdio_claim_host(sctrl->func);
#ifdef HOST_RESET_BUG
mdelay(10);
#endif
memset(sctrl->dma_buffer, 0x00, sizeof(u32));
sdio_memcpy_toio(sctrl->func, SLC_HOST_INT_ENA, sctrl->dma_buffer, sizeof(u32));
#ifdef HOST_RESET_BUG
mdelay(10);
#endif
sdio_release_host(sctrl->func);
}
void sif_disable_irq(struct esp_pub *epub)
{
int err;
struct esp_sdio_ctrl *sctrl = (struct esp_sdio_ctrl *)epub->sif;
int i = 0;
if (atomic_read(&sctrl->irq_installed) == 0)
return;
sdio_claim_host(sctrl->func);
while (atomic_read(&sctrl->irq_handling)) {
sdio_release_host(sctrl->func);
schedule_timeout(HZ / 100);
sdio_claim_host(sctrl->func);
if (i++ >= 400) {
esp_dbg(ESP_DBG_ERROR, "%s force to stop irq\n", __func__);
break;
}
}
err = sdio_release_irq(sctrl->func);
if (err) {
esp_dbg(ESP_DBG_ERROR, "%s release irq failed\n", __func__);
}
atomic_set(&sctrl->irq_installed, 0);
sdio_release_host(sctrl->func);
}
void sif_set_clock(struct sdio_func *func, int clk)
{
struct mmc_host *host = NULL;
struct mmc_card *card = NULL;
card = func->card;
host = card->host;
sdio_claim_host(func);
//currently only set clock
host->ios.clock = clk * 1000000;
esp_dbg(ESP_SHOW, "%s clock is %u\n", __func__, host->ios.clock);
if (host->ios.clock > host->f_max) {
host->ios.clock = host->f_max;
}
host->ops->set_ios(host, &host->ios);
mdelay(2);
sdio_release_host(func);
}
static int esp_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id);
static void esp_sdio_remove(struct sdio_func *func);
static int esp_sdio_probe(struct sdio_func *func, const struct sdio_device_id *id)
{
int err;
struct esp_pub *epub;
struct esp_sdio_ctrl *sctrl;
esp_dbg(ESP_DBG_TRACE,
"sdio_func_num: 0x%X, vendor id: 0x%X, dev id: 0x%X, block size: 0x%X/0x%X\n",
func->num, func->vendor, func->device, func->max_blksize,
func->cur_blksize);
if(sif_sdio_state == ESP_SDIO_STATE_FIRST_INIT){
sctrl = kzalloc(sizeof(struct esp_sdio_ctrl), GFP_KERNEL);
if (sctrl == NULL) {
assert(0);
return -ENOMEM;
}
/* temp buffer reserved for un-dma-able request */
sctrl->dma_buffer = kzalloc(ESP_DMA_IBUFSZ, GFP_KERNEL);
if (sctrl->dma_buffer == NULL) {
assert(0);
goto _err_last;
}
sif_sctrl = sctrl;
sctrl->slc_blk_sz = SIF_SLC_BLOCK_SIZE;
epub = esp_pub_alloc_mac80211(&func->dev);
if (epub == NULL) {
esp_dbg(ESP_DBG_ERROR, "no mem for epub \n");
err = -ENOMEM;
goto _err_dma;
}
epub->sif = (void *)sctrl;
sctrl->epub = epub;
} else {
ASSERT(sif_sctrl != NULL);
sctrl = sif_sctrl;
sif_sctrl = NULL;
epub = sctrl->epub;
SET_IEEE80211_DEV(epub->hw, &func->dev);
epub->dev = &func->dev;
}
epub->sdio_state = sif_sdio_state;
sctrl->func = func;
sdio_set_drvdata(func, sctrl);
sctrl->id = id;
sctrl->off = true;
/* give us some time to enable, in ms */
func->enable_timeout = 100;
err = esdio_power_on(sctrl);
esp_dbg(ESP_DBG_TRACE, " %s >> power_on err %d \n", __func__, err);
if (err){
if(sif_sdio_state == ESP_SDIO_STATE_FIRST_INIT)
goto _err_epub;
else
goto _err_second_init;
}
check_target_id(func);
sdio_claim_host(func);
err = sdio_set_block_size(func, sctrl->slc_blk_sz);
if (err) {
esp_dbg(ESP_DBG_ERROR, "Set sdio block size %d failed: %d)\n",
sctrl->slc_blk_sz, err);
sdio_release_host(func);
goto _err_off;
}
sdio_release_host(func);
#ifdef SDIO_TEST
sif_test_tx(sctrl);
#else
#ifdef LOWER_CLK
/* fix clock for dongle */
sif_set_clock(func, 23);
#endif //LOWER_CLK
err = esp_pub_init_all(epub);
if (err) {
esp_dbg(ESP_DBG_ERROR, "esp_init_all failed: %d\n", err);
if(sif_sdio_state == ESP_SDIO_STATE_SECOND_INIT)
goto _err_second_init;
}
#endif //SDIO_TEST
esp_dbg(ESP_DBG_TRACE, " %s return %d\n", __func__, err);
if(sif_sdio_state == ESP_SDIO_STATE_FIRST_INIT){
printk("first normal exit\n");
sif_sdio_state = ESP_SDIO_STATE_FIRST_NORMAL_EXIT;
up(&esp_powerup_sem);
}
return err;
_err_off:
esdio_power_off(sctrl);
_err_epub:
esp_pub_dealloc_mac80211(epub);
_err_dma:
kfree(sctrl->dma_buffer);
_err_last:
kfree(sctrl);
if(sif_sdio_state == ESP_SDIO_STATE_FIRST_INIT){
sif_sdio_state = ESP_SDIO_STATE_FIRST_ERROR_EXIT;
up(&esp_powerup_sem);
}
return err;
_err_second_init:
sif_sdio_state = ESP_SDIO_STATE_SECOND_ERROR_EXIT;
esp_sdio_remove(func);
return err;
}
#ifdef SIF_DEBUG_DSR_DUMP_REG
static void dump_slc_regs(struct slc_host_regs *regs)
{
esp_dbg(ESP_DBG_TRACE, "\n\n ------- %s --------------\n", __func__);
esp_dbg(ESP_DBG_TRACE, " \
intr_raw 0x%08X \t \n \
state_w0 0x%08X \t state_w1 0x%08X \n \
config_w0 0x%08X \t config_w1 0x%08X \n \
intr_status 0x%08X \t config_w2 0x%08X \n \
config_w3 0x%08X \t config_w4 0x%08X \n \
token_wdata 0x%08X \t intr_clear 0x%08X \n \
intr_enable 0x%08X \n\n", regs->intr_raw, \
regs->state_w0, regs->state_w1, regs->config_w0, regs->config_w1, \
regs->intr_status, \
regs->config_w2, regs->config_w3, regs->config_w4, regs->token_wdata, \
regs->intr_clear, regs->intr_enable);
}
#endif /* SIF_DEBUG_DSR_DUMP_REG */
static void esp_sdio_remove(struct sdio_func *func)
{
struct esp_sdio_ctrl *sctrl = NULL;
sctrl = sdio_get_drvdata(func);
if (sctrl == NULL) {
esp_dbg(ESP_DBG_ERROR, "%s no sctrl\n", __func__);
return;
}
do {
if (sctrl->epub == NULL) {
esp_dbg(ESP_DBG_ERROR, "%s epub null\n", __func__);
break;
}
sctrl->epub->sdio_state = sif_sdio_state;
if(sif_sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT){
do{
u8 low_byte = 0x80;
int err;
sdio_claim_host(func);
sdio_writeb(func, low_byte, SLC_HOST_CONF_W4 + 2, &err);
sdio_release_host(func);
}while(0);
if (sctrl->epub->sip) {
sip_detach(sctrl->epub->sip);
sctrl->epub->sip = NULL;
esp_dbg(ESP_DBG_TRACE, "%s sip detached \n", __func__);
}
} else {
//sif_disable_target_interrupt(sctrl->epub);
atomic_set(&sctrl->epub->sip->state, SIP_STOP);
sif_disable_irq(sctrl->epub);
}
#if (LINUX_VERSION_CODE < KERNEL_VERSION(3, 3, 0))
esdio_power_off(sctrl);
esp_dbg(ESP_DBG_TRACE, "%s power off \n", __func__);
#endif /* kernel < 3.3.0 */
#ifdef TEST_MODE
test_exit_netlink();
#endif /* TEST_MODE */
if(sif_sdio_state != ESP_SDIO_STATE_FIRST_NORMAL_EXIT){
esp_pub_dealloc_mac80211(sctrl->epub);
esp_dbg(ESP_DBG_TRACE, "%s dealloc mac80211 \n", __func__);
if (sctrl->dma_buffer) {
kfree(sctrl->dma_buffer);
sctrl->dma_buffer = NULL;
esp_dbg(ESP_DBG_TRACE, "%s free dma_buffer \n", __func__);
}
kfree(sctrl);
}
} while (0);
sdio_set_drvdata(func,NULL);
esp_dbg(ESP_DBG_TRACE, "eagle sdio remove complete\n");
}
MODULE_DEVICE_TABLE(sdio, esp_sdio_devices);
static int esp_sdio_suspend(struct device *dev)
{
struct sdio_func *func = dev_to_sdio_func(dev);
struct esp_sdio_ctrl *sctrl = sdio_get_drvdata(func);
struct esp_pub *epub = sctrl->epub;
printk("%s", __func__);
#if 0
sip_send_suspend_config(epub, 1);
#endif
atomic_set(&epub->ps.state, ESP_PM_ON);
#if (LINUX_VERSION_CODE >= KERNEL_VERSION(2, 6, 34))
do{
u32 sdio_flags = 0;
int ret = 0;
sdio_flags = sdio_get_host_pm_caps(func);
if (!(sdio_flags & MMC_PM_KEEP_POWER)) {
printk("%s can't keep power while host is suspended\n", __func__);
}
/* keep power while host suspended */
ret = sdio_set_host_pm_flags(func, MMC_PM_KEEP_POWER);
if (ret) {
printk("%s error while trying to keep power\n", __func__);
}
}while(0);
#endif
return 0;
}
static int esp_sdio_resume(struct device *dev)
{
printk("%s", __func__);
return 0;
}
static const struct dev_pm_ops esp_sdio_pm_ops = {
.suspend= esp_sdio_suspend,
.resume= esp_sdio_resume,
};
static struct sdio_driver esp_sdio_driver = {
.name = "eagle_sdio",
.id_table = esp_sdio_devices,
.probe = esp_sdio_probe,
.remove = esp_sdio_remove,
.drv = { .pm = &esp_sdio_pm_ops, },
};
static int esp_sdio_dummy_probe(struct sdio_func *func, const struct sdio_device_id *id)
{
printk("%s enter\n", __func__);
up(&esp_powerup_sem);
return 0;
}
static void esp_sdio_dummy_remove(struct sdio_func *func)
{
return;
}
static struct sdio_driver esp_sdio_dummy_driver = {
.name = "eagle_sdio_dummy",
.id_table = esp_sdio_devices,
.probe = esp_sdio_dummy_probe,
.remove = esp_sdio_dummy_remove,
};
static int __init esp_sdio_init(void)
{
#define ESP_WAIT_UP_TIME_MS 11000
int err;
u64 ver;
int retry = 3;
bool powerup = false;
int edf_ret = 0;
esp_dbg(ESP_DBG_TRACE, "%s \n", __func__);
#ifdef DRIVER_VER
ver = DRIVER_VER;
esp_dbg(ESP_SHOW, "\n*****%s %s EAGLE DRIVER VER:%llx*****\n\n", __DATE__, __TIME__, ver);
#endif
edf_ret = esp_debugfs_init();
#ifdef ANDROID
android_request_init_conf();
#endif /* defined(ANDROID)*/
esp_wakelock_init();
esp_wake_lock();
do {
sema_init(&esp_powerup_sem, 0);
sif_platform_target_poweron();
sif_platform_rescan_card(1);
err = sdio_register_driver(&esp_sdio_dummy_driver);
if (err) {
esp_dbg(ESP_DBG_ERROR, "eagle sdio driver registration failed, error code: %d\n", err);
goto _fail;
}
if (down_timeout(&esp_powerup_sem,
msecs_to_jiffies(ESP_WAIT_UP_TIME_MS)) == 0)
{
powerup = true;
break;
}
esp_dbg(ESP_SHOW, "%s ------ RETRY ------ \n", __func__);
sif_record_retry_config();
sdio_unregister_driver(&esp_sdio_dummy_driver);
sif_platform_rescan_card(0);
sif_platform_target_poweroff();
} while (retry--);
if (!powerup) {
esp_dbg(ESP_DBG_ERROR, "eagle sdio can not power up!\n");
err = -ENODEV;
goto _fail;
}
esp_dbg(ESP_SHOW, "%s power up OK\n", __func__);
sdio_unregister_driver(&esp_sdio_dummy_driver);
sif_sdio_state = ESP_SDIO_STATE_FIRST_INIT;
sema_init(&esp_powerup_sem, 0);
sdio_register_driver(&esp_sdio_driver);
if (down_timeout(&esp_powerup_sem,
msecs_to_jiffies(ESP_WAIT_UP_TIME_MS)) == 0)
{
if(sif_sdio_state == ESP_SDIO_STATE_FIRST_NORMAL_EXIT){
sdio_unregister_driver(&esp_sdio_driver);
sif_platform_rescan_card(0);
msleep(80);
sif_platform_rescan_card(1);
sif_sdio_state = ESP_SDIO_STATE_SECOND_INIT;
sdio_register_driver(&esp_sdio_driver);
}
}
esp_register_early_suspend();
esp_wake_unlock();
return err;
_fail:
esp_wake_unlock();
esp_wakelock_destroy();
return err;
}
static void __exit esp_sdio_exit(void)
{
esp_dbg(ESP_DBG_TRACE, "%s \n", __func__);
esp_debugfs_exit();
esp_unregister_early_suspend();
sdio_unregister_driver(&esp_sdio_driver);
sif_platform_rescan_card(0);
#ifndef FPGA_DEBUG
sif_platform_target_poweroff();
#endif /* !FPGA_DEBUG */
//esp_wake_unlock();
esp_wakelock_destroy();
}
static int bt_config = 0;
void sif_record_bt_config(int value)
{
bt_config = value;
}
int sif_get_bt_config(void)
{
return bt_config;
}
static int rst_config = 0;
void sif_record_rst_config(int value)
{
rst_config = value;
}
int sif_get_rst_config(void)
{
return rst_config;
}
static int retry_reset = 0;
void sif_record_retry_config(void)
{
retry_reset = 1;
}
int sif_get_retry_config(void)
{
return retry_reset;
}
#include "sdio_stub.c"
MODULE_AUTHOR("Espressif System");
MODULE_DESCRIPTION("Driver for SDIO interconnected eagle low-power WLAN devices");
MODULE_LICENSE("GPL");