cyb4_linux/drivers/mmc/host/s3c-hsmmc.c

2611 lines
72 KiB
C

/*
* linux/drivers/mmc/s3c-hsmmc.c - Samsung S3C24XX HS-MMC driver
*
* $Id: s3c-hsmmc.c,v 1.73 2008/04/13 23:56:19 jsgood Exp $
*
* Copyright (C) 2006 Samsung Electronics, All Rights Reserved.
* by Suh, Seung-Chull<sc.suh@samsung.com>
*
* This driver is made for High Speed MMC interface. This interface
* is adopted and implemented since s3c2443 was made.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
*
* Modified by Ryu,Euiyoul <steven.ryu@samsung.com>
* Modified by Suh, Seung-chull to support s3c6400
*
* Note: s3c-hsmmc.c can be used for 1 channel only at same time.
*
*/
#include <linux/module.h>
#include <linux/init.h>
#include <linux/ioport.h>
#include <linux/platform_device.h>
#include <linux/delay.h>
#include <linux/interrupt.h>
#include <linux/highmem.h>
#include <linux/dma-mapping.h>
#include <linux/mmc/host.h>
#include <linux/mmc/protocol.h>
#include <linux/mmc/card.h>
#include <linux/err.h>
#include <linux/clk.h>
#include <linux/proc_fs.h>
#include <linux/irq.h>
#include <linux/major.h> //Qisda, Asaku Chen, 2009/08/20, for wifi power
#include <asm/dma.h>
#include <asm/dma-mapping.h>
#include <asm/io.h>
#include <asm/irq.h>
#include <asm/scatterlist.h>
#include <asm/sizes.h>
#include <asm/mach/mmc.h>
#include <asm/arch/regs-gpio.h>
#include <asm/arch/regs-gpioj.h>
#include <asm/arch/regs-hsmmc.h>
#include <asm/plat-s3c24xx/clock.h>
#include <asm/arch/dma.h>
#include <asm/arch/hsmmc.h>
/*Qisda , wenny cheng , 20091224 , board id info {*/
#include <../include/asm-arm/plat-s3c24xx/common-smdk.h>
/*Qisda , wenny cheng , 20091224 , board id info }*/
/*Trevor add for debug*/
/*#define CONFIG_S3CMMC_DEBUG*/
#ifdef CONFIG_S3CMMC_DEBUG
#define DBG(x...) printk(PFX x)
#else
#define DBG(x...) do { } while (0)
#endif
#include "s3c-hsmmc.h"
#define DRIVER_NAME "s3c-hsmmc"
#define PFX DRIVER_NAME ": "
#define MULTICARD_ON_SINGLEBUS_SUPPORT
#undef MULTICARD_ON_SINGLEBUS_SUPPORT
#define RESSIZE(ressource) (((ressource)->end - (ressource)->start)+1)
#if defined(CONFIG_PM)
struct s3c_hsmmc_host *global_host[3];
#endif
static void s3c_hsmmc_tasklet_finish (unsigned long param);
/*Qisda , wenny cheng , 20091224 , board id info {*/
extern int board_id;
/*Qisda , wenny cheng , 20091224 , board id info }*/
static int inResume = 0; //howard, 2009/12/22, fix resume card-detect
/*Qisda,2009/7/28,Leo SJ Yang {*/
/*Fix: Must plug-in twice ,SD is reconginzed by system*/
/* GEORGE 20090608 update for module on board.*/
static int card_detect = 0;
static int card_detect2 = 2;
/* GEORGE 20090608 update for module on board.*/
/*}Qisda,2009/7/28,Leo SJ Yang*/
/* Qisda , howard hsu, 2010/01/16 , op_mode-delay key event {*/
//extern void op_mode_delay_message_to_ap();
/* } Qisda , howard hsu, 2010/01/16 , op_mode-delay key event */
/* Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card { */
// disable it first, wait integration test
#ifdef CONFIG_SD_SW_DEBOUNCE
static int SDCardStatus = 16;
#define SD_INSERTED 0
#define SD_REMOVED 2
#define CHECK_MSEC 50
#define INSERT_MSEC 800
#define REMOVE_MSEC 800
static struct timer_list sd_detect_timer;
static void sdcard_debounce_timer(unsigned long data);
static int ReadSDCardStatus()
{
int SDStatus= 0;
#if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
SDStatus = readl(S3C2410_GPFDAT);
SDStatus &= SD_REMOVED; /* GPF1 */
#elif defined(CONFIG_CPU_S3C6410)
SDStatus = readl(S3C_GPNDAT);
SDStatus &= 0x2000; /* GPN13 */
#endif
return SDStatus;
}
static void sdcard_debounce_timer(unsigned long data)
{
static unsigned int StayCount = 0;
static unsigned int MaxCount = 2*INSERT_MSEC/CHECK_MSEC;
static unsigned int MidCount = INSERT_MSEC/CHECK_MSEC;
static unsigned int MinCount = INSERT_MSEC/CHECK_MSEC/2;
static int LastSDCardStatus = 0xF;
int RawStatus = ReadSDCardStatus();
/* stay count in case of resume in progress */
if (inResume == 1)
{
mod_timer(&sd_detect_timer ,jiffies+msecs_to_jiffies(CHECK_MSEC));
return;
}
if ( SDCardStatus == RawStatus )
{
StayCount++;
if (StayCount >= MaxCount)
{
StayCount = 0;
LastSDCardStatus = SDCardStatus;
del_timer(&sd_detect_timer);
}
else if (StayCount == MidCount)
{
struct s3c_hsmmc_host *s3c_host = global_host[0];
struct mmc_host *host = s3c_host->mmc;
if (SDCardStatus == LastSDCardStatus )
{
//StayCount = 0;
//LastSDCardStatus = 0xF;
}
mmc_detect_change(host, 1);
mod_timer(&sd_detect_timer, jiffies+msecs_to_jiffies(CHECK_MSEC));
}
else if ( StayCount==MinCount )
{
/* call detect twice for SD_INSERTED case */
//if (SDCardStatus == LastSDCardStatus )
if (SDCardStatus == SD_INSERTED )
{
struct s3c_hsmmc_host *s3c_host = global_host[0];
struct mmc_host *host = s3c_host->mmc;
mmc_detect_change(host, 1);
}
mod_timer(&sd_detect_timer, jiffies+msecs_to_jiffies(CHECK_MSEC));
}
else
{
mod_timer(&sd_detect_timer, jiffies+msecs_to_jiffies(CHECK_MSEC));
}
}
else
{
SDCardStatus = RawStatus;
if (SDCardStatus==SD_INSERTED) {
MaxCount = 2*INSERT_MSEC/CHECK_MSEC;
MidCount = INSERT_MSEC/CHECK_MSEC;
}
else {
MaxCount = 2*REMOVE_MSEC/CHECK_MSEC;
MidCount = REMOVE_MSEC/CHECK_MSEC;
}
StayCount = 0;
mod_timer(&sd_detect_timer ,jiffies+msecs_to_jiffies(CHECK_MSEC));
}
}
#endif /* CONFIG_SD_SW_DEBOUNCE */
/* } Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card */
/*****************************************************************************\
* *
* Low level functions *
* *
\*****************************************************************************/
static struct s3c_hsmmc_cfg s3c_hsmmc_platform = {
.hwport = 0,
.enabled = 0,
.host_caps = (MMC_CAP_4_BIT_DATA | MMC_CAP_MULTIWRITE),
.base = NULL,
.highspeed = 0,
/* ctrl for mmc */
.fd_ctrl[0] = {
.ctrl2 = 0xC0004100, /* ctrl2 for mmc */
.ctrl3[0] = 0x80808080, /* ctrl3 for low speed */
.ctrl3[1] = 0x00000080, /* ctrl3 for high speed */
.ctrl4 = 0,
},
/* ctrl for sd */
.fd_ctrl[1] = {
.ctrl2 = 0xC0000100, /* ctrl2 for sd */
.ctrl3[0] = 0, /* ctrl3 for low speed */
.ctrl3[1] = 0, /* ctrl3 for high speed */
.ctrl4 = 0,
},
};
static int s3c_hsmmc_power_switch(int channel, int arg)
{
//printk("\n== s3c_hsmmc_power_switch, channel: %d, power: %d\n", channel, arg);
if(arg){
if(channel == 0){
//Power on
#ifdef CONFIG_QISDA_QD090B00
/*Qisda , wenny cheng , 20091224 , board id info {*/
//if((readl(S3C2416_GPKDAT) & (0x3<<3))){
if(board_id!=QD090B00_S02){
s3c2410_gpio_cfgpin(S3C2410_GPD14, S3C2410_GPD14_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD14, 1);
}
else{
s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_OUTP);
s3c2410_gpio_setpin(S3C2410_GPG2, 1);
}
/*Qisda , wenny cheng , 20091224 , board id info }*/
#endif
#ifdef CONFIG_QISDA_QD060B00
#ifdef CONFIG_QISDA_BK060B00
s3c2410_gpio_cfgpin(S3C2410_GPH4, S3C2410_GPH4_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH4, 1);
#else
s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_OUTP);
s3c2410_gpio_setpin(S3C2410_GPG2, 1);
#endif
#endif
//Signal
s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2450_GPE5_SD0_CLK);
s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2450_GPE6_SD0_CMD);
s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2450_GPE7_SD0_DAT0);
s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2450_GPE8_SD0_DAT1);
s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2450_GPE9_SD0_DAT2);
s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2450_GPE10_SD0_DAT3);
}
else if(channel == 1){
s3c2410_gpio_cfgpin(S3C2443_GPL8, S3C2450_GPL8_SD1CMD);
s3c2410_gpio_cfgpin(S3C2443_GPL9, S3C2450_GPL9_SD1CLK);
s3c2410_gpio_cfgpin(S3C2443_GPL0, S3C2450_GPL0_SD1DAT0);
s3c2410_gpio_cfgpin(S3C2443_GPL1, S3C2450_GPL1_SD1DAT1);
s3c2410_gpio_cfgpin(S3C2443_GPL2, S3C2450_GPL2_SD1DAT2);
s3c2410_gpio_cfgpin(S3C2443_GPL3, S3C2450_GPL3_SD1DAT3);
}
}
else{
if(channel == 0){
//Power off
#ifdef CONFIG_QISDA_QD090B00
/*Qisda , wenny cheng , 20091224 , board id info {*/
//if((readl(S3C2416_GPKDAT) & (0x3<<3))){
if(board_id!=QD090B00_S02){
s3c2410_gpio_cfgpin(S3C2410_GPD14, S3C2410_GPD14_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD14, 0);
}
else{
s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_OUTP);
s3c2410_gpio_setpin(S3C2410_GPG2, 0);
}
/*Qisda , wenny cheng , 20091224 , board id info }*/
#endif
#ifdef CONFIG_QISDA_QD060B00
#ifdef CONFIG_QISDA_BK060B00
s3c2410_gpio_cfgpin(S3C2410_GPH4, S3C2410_GPH4_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH4, 0);
#else
s3c2410_gpio_cfgpin(S3C2410_GPG2, S3C2410_GPG2_OUTP);
s3c2410_gpio_setpin(S3C2410_GPG2, 0);
#endif
#endif
//Signal
s3c2410_gpio_cfgpin(S3C2410_GPE5, S3C2410_GPE5_INP);
s3c2410_gpio_pullup(S3C2410_GPE5, 1);
s3c2410_gpio_cfgpin(S3C2410_GPE6, S3C2410_GPE6_INP);
s3c2410_gpio_pullup(S3C2410_GPE6, 1);
s3c2410_gpio_cfgpin(S3C2410_GPE7, S3C2410_GPE7_INP);
s3c2410_gpio_pullup(S3C2410_GPE7, 1);
s3c2410_gpio_cfgpin(S3C2410_GPE8, S3C2410_GPE8_INP);
s3c2410_gpio_pullup(S3C2410_GPE8, 1);
s3c2410_gpio_cfgpin(S3C2410_GPE9, S3C2410_GPE9_INP);
s3c2410_gpio_pullup(S3C2410_GPE9, 1);
s3c2410_gpio_cfgpin(S3C2410_GPE10, S3C2410_GPE10_INP);
s3c2410_gpio_pullup(S3C2410_GPE10, 1);
}
else if(channel == 1){
s3c2410_gpio_cfgpin(S3C2443_GPL8, S3C2443_GPL8_INP);
s3c2410_gpio_pullup(S3C2443_GPL8, 0);
s3c2410_gpio_cfgpin(S3C2443_GPL9, S3C2443_GPL9_INP);
s3c2410_gpio_pullup(S3C2443_GPL9, 1);
s3c2410_gpio_cfgpin(S3C2443_GPL0, S3C3443_GPL0_INP);
s3c2410_gpio_pullup(S3C2443_GPL0, 0);
s3c2410_gpio_cfgpin(S3C2443_GPL1, S3C2443_GPL1_INP);
s3c2410_gpio_pullup(S3C2443_GPL1, 0);
s3c2410_gpio_cfgpin(S3C2443_GPL2, S3C2443_GPL2_INP);
s3c2410_gpio_pullup(S3C2443_GPL2, 0);
s3c2410_gpio_cfgpin(S3C2443_GPL3, S3C2443_GPL3_INP);
s3c2410_gpio_pullup(S3C2443_GPL3, 0);
}
}
}
/* s3c_hsmmc_get_platdata
*
* get the platform data associated with the given device, or return
* the default if there is none
*/
static struct s3c_hsmmc_cfg *s3c_hsmmc_get_platdata (struct device *dev)
{
if (dev->platform_data != NULL)
return (struct s3c_hsmmc_cfg *)dev->platform_data;
return &s3c_hsmmc_platform;
}
static void s3c_hsmmc_reset (struct s3c_hsmmc_host *host, u8 mask)
{
unsigned long timeout;
s3c_hsmmc_writeb(mask, S3C_HSMMC_SWRST);
if (mask & S3C_HSMMC_RESET_ALL)
host->clock = (uint)-1;
/* Wait max 100 ms */
timeout = 100;
/* hw clears the bit when it's done */
while (s3c_hsmmc_readb(S3C_HSMMC_SWRST) & mask) {
if (timeout == 0) {
printk("%s: Reset 0x%x never completed. \n",
mmc_hostname(host->mmc), (int)mask);
return;
}
timeout--;
mdelay(1);
}
}
static void s3c_hsmmc_ios_init (struct s3c_hsmmc_host *host)
{
u32 intmask;
s3c_hsmmc_reset(host, S3C_HSMMC_RESET_ALL);
intmask = S3C_HSMMC_INT_BUS_POWER | S3C_HSMMC_INT_DATA_END_BIT |
S3C_HSMMC_INT_DATA_CRC | S3C_HSMMC_INT_DATA_TIMEOUT | S3C_HSMMC_INT_INDEX |
S3C_HSMMC_INT_END_BIT | S3C_HSMMC_INT_CRC | S3C_HSMMC_INT_TIMEOUT |
S3C_HSMMC_INT_CARD_REMOVE | S3C_HSMMC_INT_CARD_INSERT |
S3C_HSMMC_INT_DATA_AVAIL | S3C_HSMMC_INT_SPACE_AVAIL |
S3C_HSMMC_INT_DATA_END | S3C_HSMMC_INT_RESPONSE;
#ifdef CONFIG_HSMMC_SCATTERGATHER
intmask |= S3C_HSMMC_INT_DMA_END;
#endif
s3c_hsmmc_writel(intmask, S3C_HSMMC_NORINTSTSEN);
s3c_hsmmc_writel(intmask, S3C_HSMMC_NORINTSIGEN);
}
/*****************************************************************************\
* *
* Tasklets *
* *
\*****************************************************************************/
static void s3c_hsmmc_tasklet_card (ulong param)
{
struct s3c_hsmmc_host *host;
unsigned long iflags;
host = (struct s3c_hsmmc_host*)param;
spin_lock_irqsave(&host->lock, iflags);
if (!(s3c_hsmmc_readl(S3C_HSMMC_PRNSTS) & S3C_HSMMC_CARD_PRESENT)) {
if (host->mrq) {
printk(KERN_ERR "%s: Card removed during transfer!\n",
mmc_hostname(host->mmc));
printk(KERN_ERR "%s: Resetting controller.\n",
mmc_hostname(host->mmc));
s3c_hsmmc_reset(host, S3C_HSMMC_RESET_CMD);
s3c_hsmmc_reset(host, S3C_HSMMC_RESET_DATA);
host->mrq->cmd->error = MMC_ERR_FAILED;
/* tasklet_schedule(&host->finish_tasklet); */
s3c_hsmmc_tasklet_finish((unsigned long) host);
}
}
spin_unlock_irqrestore(&host->lock, iflags);
/* Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card { */
#ifdef CONFIG_SD_SW_DEBOUNCE
int exist = timer_pending(&sd_detect_timer);
if (exist == 0)
{
sd_detect_timer.expires = jiffies+msecs_to_jiffies(CHECK_MSEC);
add_timer(&sd_detect_timer);
}
#else
mmc_detect_change(host->mmc, msecs_to_jiffies(500));
#endif /* CONFIG_SD_SW_DEBOUNCE */
/* } Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card */
}
static void s3c_hsmmc_activate_led(struct s3c_hsmmc_host *host)
{
#if 0
u8 ctrl;
ctrl = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL);
ctrl |= S3C_HSMMC_CTRL_LED;
s3c_hsmmc_writeb(ctrl, S3C_HSMMC_HOSTCTL);
#endif
#if 0 // for 6400
s3c_gpio_cfgpin(S3C_GPJ13, S3C_GPJ13_SD0LED);
#endif
}
static void s3c_hsmmc_deactivate_led(struct s3c_hsmmc_host *host)
{
#if 0
u8 ctrl;
ctrl = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL);
ctrl &= ~S3C_HSMMC_CTRL_LED;
s3c_hsmmc_writeb(ctrl, S3C_HSMMC_HOSTCTL);
#endif
#if 0 // for 6400
s3c_gpio_cfgpin(S3C_GPJ13, S3C_GPJ13_INP);
#endif
}
/*****************************************************************************\
* *
* Core functions *
* *
\*****************************************************************************/
#ifdef CONFIG_HSMMC_SCATTERGATHER
static inline uint s3c_hsmmc_build_dma_table (struct s3c_hsmmc_host *host,
struct mmc_data *data)
{
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443)
uint i, j = 0, sub_num = 0;
dma_addr_t addr;
uint size, length, end;
int boundary, xor_bit;
struct scatterlist * sg = data->sg;
/* build dma table except last one */
for (i=0; i<(host->sg_len-1); i++) {
addr = sg[i].dma_address;
size = sg[i].length;
DBG("%d - addr: %08x, size: %08x\n", i, addr, size);
for (; (j<CONFIG_S3C_HSMMC_MAX_HW_SEGS*4) && size; j++) {
end = addr + size;
xor_bit = min(7+(2+8+2), fls(addr^end) -1);
DBG("%08x %08x %08x %d\n", addr, size, end, xor_bit);
host->dblk[j].dma_address = addr;
length = (end & ~((1<<xor_bit)-1)) - addr;
boundary = xor_bit - (2+8+2);
DBG("length: %x, boundary: %d\n", length, boundary);
if (length < S3C_HSMMC_MALLOC_SIZE) {
boundary = 0;
if ((addr+length) & (S3C_HSMMC_MALLOC_SIZE-1)) {
void *dest;
DBG("#########error fixing: %08x, %x\n", addr, length);
dest = host->sub_block[sub_num] + S3C_HSMMC_MALLOC_SIZE - length;
if (data->flags & MMC_DATA_WRITE) { /* write */
memcpy(dest, phys_to_virt(addr), length);
}
host->dblk[j].original = phys_to_virt(addr);
host->dblk[j].dma_address = dma_map_single(NULL, dest, length, host->dma_dir);
sub_num++;
}
}
host->dblk[j].length = length;
host->dblk[j].boundary = boundary;
DBG(" %d: %08x, %08x %x\n",
j, addr, length, boundary);
addr += length;
size -= length;
}
}
/* the last one */
host->dblk[j].dma_address = sg[i].dma_address;
host->dblk[j].length = sg[i].length;
host->dblk[j].boundary = 0x7;
return (j+1);
#elif defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
uint i;
struct scatterlist * sg = data->sg;
/* build dma table except the last one */
for (i=0; i<(host->sg_len-1); i++) {
host->sdma_descr_tbl[i].dma_address = sg[i].dma_address;
host->sdma_descr_tbl[i].length_attr = (sg[i].length << 16) | (S3C_HSMMC_ADMA_ATTR_ACT_TRAN) |
(S3C_HSMMC_ADMA_ATTR_VALID);
DBG(" ADMA2 descr table[%d] - addr: %08x, size+attr: %08x\n", i, host->sdma_descr_tbl[i].dma_address,
host->sdma_descr_tbl[i].length_attr);
}
/* the last one */
host->sdma_descr_tbl[i].dma_address = sg[i].dma_address;
host->sdma_descr_tbl[i].length_attr = (sg[i].length << 16) | (S3C_HSMMC_ADMA_ATTR_ACT_TRAN) |
(S3C_HSMMC_ADMA_ATTR_END | S3C_HSMMC_ADMA_ATTR_VALID);
DBG(" ADMA2 descr table[%d] - addr: %08x, size+attr: %08x\n", i, host->sdma_descr_tbl[i].dma_address,
host->sdma_descr_tbl[i].length_attr);
return (i);
#endif
}
#endif
static inline void s3c_hsmmc_prepare_data (struct s3c_hsmmc_host *host,
struct mmc_command *cmd)
{
#if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
u8 reg8;
#endif
u32 reg;
struct mmc_data *data = cmd->data;
if (data == NULL) {
reg = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTSEN) | S3C_HSMMC_NIS_CMDCMP;
s3c_hsmmc_writel(reg, S3C_HSMMC_NORINTSTSEN);
return;
}
reg = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTSEN) & ~S3C_HSMMC_NIS_CMDCMP;
s3c_hsmmc_writel(reg, S3C_HSMMC_NORINTSTSEN);
host->dma_dir = (data->flags & MMC_DATA_READ)
? DMA_FROM_DEVICE : DMA_TO_DEVICE;
#ifdef CONFIG_HSMMC_SCATTERGATHER
host->sg_len = dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
host->dma_dir);
reg = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTSEN);
if (host->sg_len == 1) {
reg &= ~S3C_HSMMC_NIS_DMA;
} else {
reg |= S3C_HSMMC_NIS_DMA;
}
s3c_hsmmc_writel(reg, S3C_HSMMC_NORINTSTSEN);
#if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
reg8 = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL);
reg8 |= S3C_HSMMC_CTRL_ADMA2_32;
s3c_hsmmc_writeb(reg8, S3C_HSMMC_HOSTCTL);
DBG("HOSTCTL(0x28) = 0x%02x\n", s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL));
#endif
DBG("data->flags(direction) = 0x%x\n", data->flags);
DBG("data->blksz: %d\n", data->blksz);
DBG("data->blocks: %d\n", data->blocks);
DBG("data->sg_len: %d\n", data->sg_len);
DBG("data->sg->addr: 0x%x\n", data->sg->dma_address);
DBG("data->sg->length: 0x%x\n", data->sg->length);
host->dma_blk = s3c_hsmmc_build_dma_table(host, data);
host->next_blk = 0;
#else
DBG("data->flags(direction) = 0x%x\n", data->flags);
DBG("data->blksz: %d\n", data->blksz);
DBG("data->blocks: %d\n", data->blocks);
DBG("data->sg_len: %d\n", data->sg_len);
dma_map_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
host->dma_dir);
#endif
}
static inline void s3c_hsmmc_set_transfer_mode (struct s3c_hsmmc_host *host,
struct mmc_data *data)
{
u16 mode;
mode = S3C_HSMMC_TRNS_DMA;
if (data->stop)
mode |= S3C_HSMMC_TRNS_ACMD12;
if (data->flags & MMC_DATA_MULTI)
mode |= S3C_HSMMC_TRNS_MULTI | S3C_HSMMC_TRNS_BLK_CNT_EN;
if (data->flags & MMC_DATA_READ)
mode |= S3C_HSMMC_TRNS_READ;
s3c_hsmmc_writew(mode, S3C_HSMMC_TRNMOD);
}
static inline void s3c_hsmmc_send_register (struct s3c_hsmmc_host *host)
{
struct mmc_command *cmd = host->cmd;
struct mmc_data *data = cmd->data;
u32 cmd_val;
if (data) {
#ifdef CONFIG_HSMMC_SCATTERGATHER
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443)
struct s3c_hsmmc_dma_blk *dblk;
dblk = &host->dblk[0];
s3c_hsmmc_writew(S3C_HSMMC_MAKE_BLKSZ(dblk->boundary, data->blksz),
S3C_HSMMC_BLKSIZE);
s3c_hsmmc_writel(dblk->dma_address, S3C_HSMMC_SYSAD);
#elif defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
s3c_hsmmc_writew(S3C_HSMMC_MAKE_BLKSZ(0x7, data->blksz), S3C_HSMMC_BLKSIZE);
s3c_hsmmc_writel(virt_to_phys(host->sdma_descr_tbl), S3C_HSMMC_ADMASYSADDR);
DBG("S3C_HSMMC_ADMASYSADDR(0x58) = 0x%08x\n", s3c_hsmmc_readl(S3C_HSMMC_ADMASYSADDR));
#endif
#else
s3c_hsmmc_writew(S3C_HSMMC_MAKE_BLKSZ(0x7, data->blksz), S3C_HSMMC_BLKSIZE);
s3c_hsmmc_writel(sg_dma_address(data->sg), S3C_HSMMC_SYSAD);
#endif
s3c_hsmmc_writew(data->blocks, S3C_HSMMC_BLKCNT);
s3c_hsmmc_set_transfer_mode(host, data);
}
s3c_hsmmc_writel(cmd->arg, S3C_HSMMC_ARGUMENT);
cmd_val = (cmd->opcode << 8);
if (cmd_val == (12<<8))
cmd_val |= (3 << 6);
if (cmd->flags & MMC_RSP_136) /* Long RSP */
cmd_val |= S3C_HSMMC_CMD_RESP_LONG;
else if (cmd->flags & MMC_RSP_BUSY) /* R1B */
cmd_val |= S3C_HSMMC_CMD_RESP_SHORT_BUSY;
else if (cmd->flags & MMC_RSP_PRESENT) /* Normal RSP */
cmd_val |= S3C_HSMMC_CMD_RESP_SHORT;
if (cmd->flags & MMC_RSP_OPCODE)
cmd_val |= S3C_HSMMC_CMD_INDEX;
if (cmd->flags & MMC_RSP_CRC)
cmd_val |= S3C_HSMMC_CMD_CRC;
if (data)
cmd_val |= S3C_HSMMC_CMD_DATA;
s3c_hsmmc_writew(cmd_val, S3C_HSMMC_CMDREG);
}
static inline void s3c_hsmmc_send_command (struct s3c_hsmmc_host *host,
struct mmc_command *cmd)
{
u32 mask=1;
ulong timeout;
#if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
while (s3c_hsmmc_readl(S3C_HSMMC_CONTROL4) & mask);
#endif
DBG("Sending cmd=(%d), arg=0x%x\n", cmd->opcode, cmd->arg);
/* Wait max 10 ms */
timeout = 10;
mask = S3C_HSMMC_CMD_INHIBIT;
if ((cmd->data != NULL) || (cmd->flags & MMC_RSP_BUSY))
mask |= S3C_HSMMC_DATA_INHIBIT;
while (s3c_hsmmc_readl(S3C_HSMMC_PRNSTS) & mask) {
printk("########### waiting controller wakeup\n");
if (timeout == 0) {
printk(KERN_ERR "%s: Controller never released "
"inhibit bit(s).\n", mmc_hostname(host->mmc));
cmd->error = MMC_ERR_FAILED;
/* tasklet_schedule(&host->finish_tasklet); */
s3c_hsmmc_tasklet_finish((unsigned long) host);
return;
}
timeout--;
mdelay(1);
}
mod_timer(&host->timer, jiffies + 10 * HZ);
host->cmd = cmd;
s3c_hsmmc_prepare_data(host, cmd);
s3c_hsmmc_send_register(host);
}
static void s3c_hsmmc_finish_data (struct s3c_hsmmc_host *host)
{
struct mmc_data *data;
u16 blocks;
//BUG_ON(!host->data);
if(!host->data) return;
data = host->data;
host->data = NULL;
dma_unmap_sg(mmc_dev(host->mmc), data->sg, data->sg_len,
(data->flags & MMC_DATA_READ)
? DMA_FROM_DEVICE : DMA_TO_DEVICE);
/*
* Controller doesn't count down when in single block mode.
*/
if ((data->blocks == 1) && (data->error == MMC_ERR_NONE))
blocks = 0;
else {
blocks = s3c_hsmmc_readw(S3C_HSMMC_BLKCNT);
}
data->bytes_xfered = data->blksz * (data->blocks - blocks);
if ((data->error == MMC_ERR_NONE) && blocks) {
printk(KERN_ERR "%s: Controller signalled completion even "
"though there were blocks left. : %d\n",
mmc_hostname(host->mmc), blocks);
data->error = MMC_ERR_FAILED;
}
DBG("data->flags(direction) FINISHED = %d\n", data->flags);
DBG("data->blksz FINISHED = %d\n", data->blksz);
DBG("data->blocks FINISHED = %d\n", data->blocks);
DBG("Not FINISHED blocks = %d\n", blocks);
DBG("Ending data transfer (%d bytes)\n", data->bytes_xfered);
DBG("\n");
/* tasklet_schedule(&host->finish_tasklet); */
s3c_hsmmc_tasklet_finish((unsigned long) host);
}
static void s3c_hsmmc_finish_command (struct s3c_hsmmc_host *host)
{
int i;
//BUG_ON(host->cmd == NULL);
if(!host->cmd) return;
if (host->cmd->flags & MMC_RSP_PRESENT) {
if (host->cmd->flags & MMC_RSP_136) {
/* CRC is stripped so we need to do some shifting. */
for (i=0; i<4; i++) {
host->cmd->resp[i] = s3c_hsmmc_readl(S3C_HSMMC_RSPREG0+ (3-i)*4) << 8;
if (i != 3)
host->cmd->resp[i] |= s3c_hsmmc_readb(S3C_HSMMC_RSPREG0 + (3-i)*4-1);
DBG("cmd (%d) resp[%d] = 0x%x\n", host->cmd->opcode, i, host->cmd->resp[i]);
}
} else {
host->cmd->resp[0] = s3c_hsmmc_readl(S3C_HSMMC_RSPREG0);
DBG("cmd (%d) resp[%d] = 0x%x\n", host->cmd->opcode, 0, host->cmd->resp[0]);
}
}
host->cmd->error = MMC_ERR_NONE;
DBG("Ending cmd (%d)\n", host->cmd->opcode);
DBG("\n");
if (host->cmd->data)
host->data = host->cmd->data;
else
/* tasklet_schedule(&host->finish_tasklet); */
s3c_hsmmc_tasklet_finish((unsigned long) host);
host->cmd = NULL;
}
static void s3c_hsmmc_tasklet_finish (unsigned long param)
{
struct s3c_hsmmc_host *host;
unsigned long iflags;
struct mmc_request *mrq;
host = (struct s3c_hsmmc_host*)param;
//BUG_ON(!host->mrq);
if(!host->mrq) return;
spin_lock_irqsave(&host->lock, iflags);
del_timer(&host->timer);
mrq = host->mrq;
udelay(10);
/*
* The controller needs a reset of internal state machines
* upon error conditions.
*/
if ((mrq->cmd->error != MMC_ERR_NONE) ||
(mrq->data && (mrq->data->error != MMC_ERR_NONE)) ) {
s3c_hsmmc_reset(host, S3C_HSMMC_RESET_CMD);
s3c_hsmmc_reset(host, S3C_HSMMC_RESET_DATA);
}
host->mrq = NULL;
host->cmd = NULL;
host->data = NULL;
s3c_hsmmc_deactivate_led(host);
mmiowb();
spin_unlock_irqrestore(&host->lock, iflags);
mmc_request_done(host->mmc, mrq);
}
/*****************************************************************************
* *
* Interrupt handling *
* *
*****************************************************************************/
static void s3c_hsmmc_cmd_irq (struct s3c_hsmmc_host *host, u32 intmask)
{
if (!host->cmd) {
printk(KERN_ERR "%s: Got command interrupt 0x%08x even "
"though no command operation was in progress.\n",
mmc_hostname(host->mmc), (unsigned)intmask);
return;
}
if (intmask & S3C_HSMMC_INT_TIMEOUT)
host->cmd->error = MMC_ERR_TIMEOUT;
else if (intmask & S3C_HSMMC_INT_CRC)
host->cmd->error = MMC_ERR_BADCRC;
else if (intmask & (S3C_HSMMC_INT_END_BIT | S3C_HSMMC_INT_INDEX))
host->cmd->error = MMC_ERR_FAILED;
else
host->cmd->error = MMC_ERR_INVALID;
/* tasklet_schedule(&host->finish_tasklet); */
s3c_hsmmc_tasklet_finish((unsigned long) host);
}
static void s3c_hsmmc_data_irq (struct s3c_hsmmc_host *host, u32 intmask)
{
if (!host->data) {
/*
* A data end interrupt is sent together with the response
* for the stop command.
*/
if (intmask & S3C_HSMMC_INT_DATA_END)
return;
printk(KERN_ERR "%s: Got data interrupt even though no "
"data operation was in progress.\n",
mmc_hostname(host->mmc));
return;
}
if (intmask & S3C_HSMMC_INT_DATA_TIMEOUT)
host->data->error = MMC_ERR_TIMEOUT;
else if (intmask & S3C_HSMMC_INT_DATA_CRC)
host->data->error = MMC_ERR_BADCRC;
else if (intmask & S3C_HSMMC_INT_DATA_END_BIT)
host->data->error = MMC_ERR_FAILED;
if (host->data->error != MMC_ERR_NONE)
s3c_hsmmc_finish_data(host);
}
/*****************************************************************************\
* *
* Interrupt handling *
* *
\*****************************************************************************/
/*
* ISR for SDI Interface IRQ
* Communication between driver and ISR works as follows:
* host->mrq points to current request
* host->complete_what tells the ISR when the request is considered done
*
* 1) Driver sets up host->mrq and host->complete_what
* 2) Driver prepares the transfer
* 3) Driver enables interrupts
* 4) Driver starts transfer
* 5) Driver waits for host->complete_rquest
* 6) ISR checks for request status (errors and success)
* 6) ISR sets host->mrq->cmd->error and host->mrq->data->error
* 7) ISR completes host->complete_request
* 8) ISR disables interrupts
* 9) Driver wakes up and takes care of the request
*/
static irqreturn_t s3c_hsmmc_irq (int irq, void *dev_id)
{
irqreturn_t result = 0;
struct s3c_hsmmc_host *host = dev_id;
struct mmc_request *mrq;
u32 intsts;
#ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND
uint i, org_irq_sts;
#endif
spin_lock(&host->lock);
mrq = host->mrq;
intsts = s3c_hsmmc_readw(S3C_HSMMC_NORINTSTS);
/* Sometimes, hsmmc does not update its status bit immediately
* when it generates irqs. by scsuh.
*/
#ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND
for (i=0; i<0x1000; i++) {
if ((intsts = s3c_hsmmc_readw(S3C_HSMMC_NORINTSTS)))
break;
}
#endif
if (unlikely(!intsts)) {
result = IRQ_NONE;
goto out;
}
intsts = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTS);
#ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND
org_irq_sts = intsts;
#endif
DBG("Got interrupt = 0x%08x\n", intsts);
if (unlikely(intsts & S3C_HSMMC_INT_CARD_CHANGE)) {
u32 reg16;
if (intsts & S3C_HSMMC_INT_CARD_INSERT)
printk(PFX "card inserted.\n");
else if (intsts & S3C_HSMMC_INT_CARD_REMOVE)
printk(PFX "card removed.\n");
reg16 = s3c_hsmmc_readw(S3C_HSMMC_NORINTSTSEN);
s3c_hsmmc_writew(reg16 & ~S3C_HSMMC_INT_CARD_CHANGE,
S3C_HSMMC_NORINTSTSEN);
s3c_hsmmc_writew(S3C_HSMMC_INT_CARD_CHANGE, S3C_HSMMC_NORINTSTS);
s3c_hsmmc_writew(reg16, S3C_HSMMC_NORINTSTSEN);
intsts &= ~S3C_HSMMC_INT_CARD_CHANGE;
tasklet_schedule(&host->card_tasklet);
goto insert;
}
if (likely(!(intsts & S3C_HSMMC_NIS_ERR))) {
s3c_hsmmc_writel(intsts, S3C_HSMMC_NORINTSTS);
if (intsts & S3C_HSMMC_NIS_CMDCMP) {
DBG("command done\n");
s3c_hsmmc_finish_command(host);
}
if (intsts & S3C_HSMMC_NIS_TRSCMP) {
DBG("transfer done\n\n");
s3c_hsmmc_finish_command(host);
s3c_hsmmc_finish_data(host);
intsts &= ~S3C_HSMMC_NIS_DMA;
}
#ifdef CONFIG_HSMMC_SCATTERGATHER
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443)
if (intsts & S3C_HSMMC_NIS_DMA) {
struct s3c_hsmmc_dma_blk *dblk;
dblk = &host->dblk[host->next_blk];
if (dblk->original) {
/* on read */
if (host->dma_dir == DMA_FROM_DEVICE) {
memcpy(dblk->original, phys_to_virt(dblk->dma_address), dblk->length);
}
dma_unmap_single(NULL, dblk->dma_address, dblk->length, host->dma_dir);
dblk->original = 0;
}
host->next_blk++;
dblk = &host->dblk[host->next_blk];
if (host->next_blk == (host->dma_blk-1)) {
u32 reg;
reg = s3c_hsmmc_readl(S3C_HSMMC_NORINTSTSEN);
reg &= ~S3C_HSMMC_NIS_DMA;
s3c_hsmmc_writel(reg, S3C_HSMMC_NORINTSTSEN);
}
/* We do not handle DMA boundaries, so set it to max (512 KiB) */
s3c_hsmmc_writew((dblk->boundary<<12 | 0x200), S3C_HSMMC_BLKSIZE);
s3c_hsmmc_writel(dblk->dma_address, S3C_HSMMC_SYSAD);
}
#endif
#endif
} else {
DBG("command FAIL : found bad irq [0x%8x]\n", intsts);
DBG("\n");
if (intsts & S3C_HSMMC_INT_CMD_MASK) {
s3c_hsmmc_writel(intsts & S3C_HSMMC_INT_CMD_MASK, S3C_HSMMC_NORINTSTS);
s3c_hsmmc_cmd_irq(host, intsts & S3C_HSMMC_INT_CMD_MASK);
}
if (intsts & S3C_HSMMC_INT_DATA_MASK) {
s3c_hsmmc_writel(intsts & S3C_HSMMC_INT_DATA_MASK, S3C_HSMMC_NORINTSTS);
s3c_hsmmc_finish_command(host);
s3c_hsmmc_data_irq(host, intsts & S3C_HSMMC_INT_DATA_MASK);
}
intsts &= ~(S3C_HSMMC_INT_CMD_MASK | S3C_HSMMC_INT_DATA_MASK);
}
/* XXX: fix later by scsuh */
#if 0
if (intsts & S3C_HSMMC_INT_BUS_POWER) {
printk(KERN_ERR "%s: Card is consuming too much power!\n",
mmc_hostname(host->mmc));
s3c_hsmmc_writel(S3C_HSMMC_INT_BUS_POWER, S3C_HSMMC_NORINTSTS);
}
intsts &= S3C_HSMMC_INT_BUS_POWER;
if (intsts) {
printk(KERN_ERR "%s: Unexpected interrupt 0x%08x.\n",
mmc_hostname(host->mmc), intsts);
s3c_hsmmc_writel(intsts, S3C_HSMMC_NORINTSTS);
}
#endif
#ifdef CONFIG_HSMMC_S3C_IRQ_WORKAROUND
for (i=0; i<0x1000; i++) {
if (org_irq_sts != s3c_hsmmc_readl(S3C_HSMMC_NORINTSTS))
break;
}
#endif
insert:
result = IRQ_HANDLED;
mmiowb();
out:
spin_unlock(&host->lock);
return result;
}
#include <linux/cyio.h>
int Cyio_PushEvent(char eventId, char unique);
static irqreturn_t s3c_hsmmc_irq_cd (int irq, void *dev_id)
{
struct s3c_hsmmc_host *host = dev_id;
int ext_CD_int = 0;
#if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
ext_CD_int = readl(S3C2410_GPFDAT);
ext_CD_int &= 0x2; /* GPF1 */
#elif defined(CONFIG_CPU_S3C6410)
ext_CD_int = readl(S3C_GPNDAT);
ext_CD_int &= 0x2000; /* GPN13 */
#endif
//printk("\ns3c_hsmmc_irq_cd, ext_CD_int: %d, card_detect: %d, card_detect2: %d\n", ext_CD_int, card_detect, card_detect2);
if(ext_CD_int && card_detect) {
printk("s3c-hsmmc channel-0(EXT): card removed.\n");
/*Qisda Qube for sd card detect20091124*/
//set_irq_type(host->irq_cd, IRQT_FALLING);
set_irq_type(host->irq_cd, IRQT_LOW);
/*Qisda Qube for sd card detect20091124*/
card_detect = 0;
//Qisda, Asaku Chen, 2009/11/03, SD power off
s3c_hsmmc_power_switch(0, 0);
card_detect2 = 2;
/* Send CyIO event */
Cyio_PushEvent(CYEVENT_SD_OUT, 1);
}
else if(!ext_CD_int && !card_detect) {
printk("s3c-hsmmc channel-0(EXT): card inserted.\n");
/*Qisda Qube for sd card detect20091124*/
//set_irq_type(host->irq_cd, IRQT_RISING);
set_irq_type(host->irq_cd, IRQT_HIGH);
/*Qisda Qube for sd card detect20091124*/
//Qisda, Asaku Chen, 2009/11/03, SD power on
if (inResume!=1)//howard, 2009/12/22, fix resume card-detect
{
card_detect = 1;
s3c_hsmmc_power_switch(0, 1);
}
if(card_detect2 == 3){
printk("resume host first\n");
s3c_hsmmc_ios_init(host);
mmc_resume_host(host->mmc);
card_detect2 = 2;
}
/* Send CyIO event */
Cyio_PushEvent(CYEVENT_SD_IN, 1);
}
//Qisda, Asaku Chen, 2009/11/03 {
else if(card_detect2 == 1){
printk("SD power on\n");
s3c_hsmmc_power_switch(0, 1);
card_detect2 = 2;
}
else if(card_detect2 == 0){
printk("SD power off\n");
s3c_hsmmc_power_switch(0, 0);
card_detect2 = -1;
}
//Qisda, Asaku Chen, 2009/11/03 }
else
return IRQ_HANDLED;
tasklet_schedule(&host->card_tasklet);
mmiowb();
spin_unlock(&host->lock);
return IRQ_HANDLED;
}
static void s3c_hsmmc_check_status (unsigned long data)
{
struct s3c_hsmmc_host *host = (struct s3c_hsmmc_host *)data;
s3c_hsmmc_irq(0, host);
}
static void s3c_hsmmc_request (struct mmc_host *mmc, struct mmc_request *mrq)
{
struct s3c_hsmmc_host *host = mmc_priv(mmc);
unsigned long flags;
DBG("hsmmc(%d) request: [CMD] opcode:%d arg:0x%08x flags:0x%02x retries:%u\n",
host->plat_data->hwport,
mrq->cmd->opcode, mrq->cmd->arg, mrq->cmd->flags, mrq->cmd->retries);
spin_lock_irqsave(&host->lock, flags);
WARN_ON(host->mrq != NULL);
s3c_hsmmc_activate_led(host);
host->mrq = mrq;
if ((s3c_hsmmc_readl(S3C_HSMMC_PRNSTS) & S3C_HSMMC_CARD_PRESENT) || card_detect) {
s3c_hsmmc_send_command(host, mrq->cmd);
} else {
host->mrq->cmd->error = MMC_ERR_TIMEOUT;
/* tasklet_schedule(&host->finish_tasklet); */
s3c_hsmmc_tasklet_finish((unsigned long) host);
}
mmiowb();
spin_unlock_irqrestore(&host->lock, flags);
}
/* return 0: OK
* return -1: error
*/
static int s3c_set_bus_width (struct s3c_hsmmc_host *host, uint width)
{
u8 reg;
reg = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL);
switch (width) {
case MMC_BUS_WIDTH_1:
reg &= ~(S3C_HSMMC_CTRL_4BIT | S3C_HSMMC_CTRL_8BIT);
DBG("bus width: 1 bit\n");
break;
case MMC_BUS_WIDTH_4:
DBG("bus width: 4 bit\n");
reg &= ~(S3C_HSMMC_CTRL_8BIT);
reg |= S3C_HSMMC_CTRL_4BIT;
break;
default:
DBG("bus width: Error\n");
return -1;
}
s3c_hsmmc_writeb(reg, S3C_HSMMC_HOSTCTL);
DBG("HOSTCTL(0x28) = 0x%02x\n", s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL));
return 0;
}
static void s3c_hsmmc_set_clock (struct s3c_hsmmc_host *host, ulong clock)
{
struct s3c_hsmmc_cfg *cfg = host->plat_data;
//int cardtype = host->mmc->mode;
int cardtype = MMC_MODE_MMC;
u32 val = 0, tmp_clk = 0, clk_src = 0, i, j;
u16 div = (u16)-1;
ulong timeout;
u8 ctrl;
/* if we already set, just out. */
if (clock == host->clock) {
printk("%p:host->clock0 : %d Hz\n", host->base, host->clock);
return;
}
/* before setting clock, clkcon must be disabled. */
s3c_hsmmc_writew(0, S3C_HSMMC_CLKCON);
s3c_hsmmc_writeb(S3C_HSMMC_TIMEOUT_MAX, S3C_HSMMC_TIMEOUTCON);
/* change the edge type according to frequency */
ctrl = s3c_hsmmc_readb(S3C_HSMMC_HOSTCTL);
if (cfg->highspeed)
ctrl |= S3C_HSMMC_CTRL_HIGHSPEED;
else
ctrl &= ~S3C_HSMMC_CTRL_HIGHSPEED;
s3c_hsmmc_writeb(ctrl, S3C_HSMMC_HOSTCTL);
if (clock == 0) {
DBG(" In case of 0 Hz of clock, I'm afraid DO NOTHING ..\n");
return;
}
/* calculate optimal clock. by scsuh */
DBG("Requested clock is %ld Hz\n", clock);
for (i=0; i<NUM_OF_HSMMC_CLKSOURCES; i++) {
if ((tmp_clk = clk_get_rate(host->clk[i])) <= clock) {
if (tmp_clk >= val) {
val = tmp_clk;
div = 0;
clk_src = i+1;
}
}
for (j=0x1; j<=0x80; j <<= 1) {
tmp_clk = clk_get_rate(host->clk[i]) / (j<<1);
if ((val < tmp_clk) && (tmp_clk <= clock)) {
val = tmp_clk;
div = j;
clk_src = i+1;
break;
}
}
DBG(" tmp_val[%d]: %d\n", i, val);
}
DBG("Optimal clock : %d Hz, div: 0x%x, SelBaseclk_src: %d\n", val, div, clk_src);
/* CONTROL2 */
s3c_hsmmc_writel(cfg->fd_ctrl[cardtype].ctrl2 | (clk_src << 4), S3C_HSMMC_CONTROL2);
/* CONTROL3 */
if (clock > 25000000)
s3c_hsmmc_writel(cfg->fd_ctrl[cardtype].ctrl3[SPEED_HIGH], S3C_HSMMC_CONTROL3);
else
s3c_hsmmc_writel(cfg->fd_ctrl[cardtype].ctrl3[SPEED_NORMAL], S3C_HSMMC_CONTROL3);
#if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
s3c_hsmmc_writel(cfg->fd_ctrl[cardtype].ctrl4 << 16, S3C_HSMMC_CONTROL4);
#endif
s3c_hsmmc_writew(((div << 8) | S3C_HSMMC_CLOCK_INT_EN), S3C_HSMMC_CLKCON);
timeout = 10;
while (!((val = s3c_hsmmc_readw(S3C_HSMMC_CLKCON))
& S3C_HSMMC_CLOCK_INT_STABLE)) {
if (!timeout) {
printk("Error in INTERNAL clock stabilization: %08x\n", val);
return;
}
timeout--;
mdelay(1);
}
s3c_hsmmc_writew(val | S3C_HSMMC_CLOCK_CARD_EN, S3C_HSMMC_CLKCON);
timeout = 10;
while (!((val = s3c_hsmmc_readw(S3C_HSMMC_CLKCON))
& S3C_HSMMC_CLOCK_EXT_STABLE)) {
if (!timeout) {
printk("Error in EXTERNAL clock stabilization: %08x\n", val);
return;
}
timeout--;
mdelay(1);
}
}
static void s3c_hsmmc_set_ios(struct mmc_host *mmc, struct mmc_ios *ios)
{
struct s3c_hsmmc_host *host = mmc_priv(mmc);
unsigned long iflags;
spin_lock_irqsave(&host->lock, iflags);
/*
* Reset the chip on each power off.
* Should clear out any weird states.
*/
if (ios->power_mode == MMC_POWER_OFF) {
s3c_hsmmc_writew(0, S3C_HSMMC_NORINTSIGEN);
s3c_hsmmc_ios_init(host);
//Qisda, Asaku Chen, 2009/11/03 {
if(host->plat_data->hwport==0){
//printk("== s3c_hsmmc_set_ios: %d, MMC_POWER_OFF\n", host->plat_data->hwport);
s3c_hsmmc_power_switch(0, 0);
}
}
else{
//Enable SD power
if((host->plat_data->hwport==0) && (card_detect2 > 0)){
//printk("== s3c_hsmmc_set_ios: %d, MMC_POWER_ON\n", host->plat_data->hwport);
s3c_hsmmc_power_switch(0, 1);
}
}
if((ios->power_mode == MMC_POWER_OFF) && (host->plat_data->hwport==0)){
//do nothing
}else{
if (host->plat_data->enabled)
hsmmc_set_gpio(host->plat_data->hwport, host->plat_data->bus_width);
}
if((host->plat_data->hwport == 1) && (ios->clock > 26000000)){
ios->clock = 26000000;
}
DBG("ios->clock: %d Hz\n", ios->clock);
s3c_hsmmc_set_clock(host, ios->clock);
DBG("ios->bus_width: %d\n", ios->bus_width);
s3c_set_bus_width(host, ios->bus_width);
DBG("S3C_HSMMC_CONTROL2(0x80) = 0x%08x\n", s3c_hsmmc_readl(S3C_HSMMC_CONTROL2));
DBG("S3C_HSMMC_CONTROL3(0x84) = 0x%08x\n", s3c_hsmmc_readl(S3C_HSMMC_CONTROL3));
DBG("S3C_HSMMC_CLKCON (0x2c) = 0x%04x\n", s3c_hsmmc_readw(S3C_HSMMC_CLKCON));
if (ios->power_mode == MMC_POWER_OFF)
s3c_hsmmc_writeb(S3C_HSMMC_POWER_OFF, S3C_HSMMC_PWRCON);
else
s3c_hsmmc_writeb(S3C_HSMMC_POWER_ON_ALL, S3C_HSMMC_PWRCON);
udelay(1000);
spin_unlock_irqrestore(&host->lock, iflags);
}
static struct mmc_host_ops s3c_hsmmc_ops = {
.request = s3c_hsmmc_request,
.set_ios = s3c_hsmmc_set_ios,
};
//Qisda, Asaku Chen, 2009/08/20, for wifi power {
static int s3c_wifi_open(struct inode *inode, struct file *file)
{
return 0;
}
#define S3C_WIFI_POWER_ON 1
#define S3C_WIFI_POWER_OFF 2
#define S3C_READ_TILT_STATUS 3
#define S3C_READ_GPIO_VALUE 4
#define S3C_SET_GPIO_ADDR 5
#define S3C_WRITE_GPIO_VALUE 6
#define S3C_READ_HW_VERSION 7
#define S3C_TCON_ERASE 10
#define S3C_TCON_ID 11
#define S3C_TCON_READ 12
#define S3C_TCON_BYTE_COUNT 13
#define S3C_TCON_WRITE 14
#define MMC_DEVICE_POWER_STATE 20
#define S3C_BT_POWER_ON 1
#define S3C_BT_POWER_OFF 2
static unsigned char s3c_tcon_read_byte(void)
{
/*
Unsigned char Get data () //Receive a byte from Serial flash
{
unsigned char k, temdata =0, i;
for ( k = 0; k++; k< 8)
{
if ( SO == 1 )
{
i = (0x80 >> k);
temdata = (temdata || i);
}
SCLK = 1;
SCLK = 0;
}
return (temdata);
}
*/
unsigned char k, temdata =0, i=0;
u32 read_pin;
for(k=0; k<8; k++){
read_pin = (readl(S3C2410_GPEDAT) & (1<<11));
//printk(" [%d]: 0x%X 0x%X\n", k, read_pin, readl(S3C2410_GPEDAT));
if(read_pin){
i = (0x80 >> k);
temdata = (temdata | i);
}
s3c2410_gpio_setpin(S3C2410_GPE13, 1);
udelay(2);
s3c2410_gpio_setpin(S3C2410_GPE13, 0);
udelay(2);
}
//printk("s3c_tcon_read_byte: 0x%2X\n", temdata);
return (temdata);
}
static void s3c_tcon_write_byte(char w_data)
{
/*
Send data (unsigned char indata) // Send 1 byte data to Serial flash
{
unsigned char k;
for ( k = 0; k++; k< 8)
{
if( (indata & 0x80) == 0x80)
SI = 1;
else
SI = 0;
SCLK = 1;
SCLK = 0;
indata = (indata << 1);
}
}
*/
unsigned char k;
//printk("s3c_tcon_write_byte: 0x%2X\n", w_data);
for(k=0; k<8; k++){
if((w_data & 0x80))
s3c2410_gpio_setpin(S3C2410_GPE12, 1);
else
s3c2410_gpio_setpin(S3C2410_GPE12, 0);
s3c2410_gpio_setpin(S3C2410_GPE13, 1);
udelay(2);
s3c2410_gpio_setpin(S3C2410_GPE13, 0);
udelay(2);
w_data = (w_data << 1);
}
}
extern void mmc_rescan(struct work_struct *work);
static int s3c_wifi_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
static char s3c_power_off = 0;
static unsigned int gpio_addr = 0;
static unsigned int tcon_byte_count;
static unsigned int tcon_address;
unsigned char tcon_buffer[128];
unsigned char r_data;
int ret, i;
u32 read_pin;
switch(cmd){
#ifdef CONFIG_QISDA_BK060B00
case S3C_WIFI_POWER_ON:
printk("BCM4329-WIFI_BT_POWER_ON\n");
//TCON Flash disable, and select WiFi
s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH6, 0);
//bcm4329 module power on control
//GPK7, WIFI PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<15)) | (1<<14)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) | (1<<7)), S3C2416_GPKDAT);
//GPK6, PA 1.8V
writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) | (1<<6)), S3C2416_GPKDAT);
//GPK5, PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) | (1<<5)), S3C2416_GPKDAT);
//GPK9 (green light), pull high
writel(((readl(S3C2416_GPKCON) & ~(1<<19)) | (1<<18)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) | (1<<9)), S3C2416_GPKDAT);
//WIFI_BT_EN (GPD11) pull high
s3c2410_gpio_cfgpin(S3C2410_GPD11, S3C2410_GPD11_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD11, 0);
//writel(((readl(S3C2416_GPGCON) & ~(1<<9)) | (1<<8)), S3C2416_GPGCON);
//writel((readl(S3C2416_GPGDAT) & ~(1<<4)), S3C2416_GPGDAT);
mdelay(1);
s3c2410_gpio_setpin(S3C2410_GPD11, 1);
//writel((readl(S3C2416_GPGDAT) | (1<<4)), S3C2416_GPGDAT);
mdelay(20);
//EXT_N_WIFI_RST (GPD8) pull high
s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD8, 0);
//writel(((readl(S3C2416_GPDCON) & ~(1<<17)) | (1<<16)), S3C2416_GPDCON);
//writel((readl(S3C2416_GPDDAT) & ~(1<<8)), S3C2416_GPDDAT);
mdelay(1);
s3c2410_gpio_setpin(S3C2410_GPD8, 1);
//writel((readl(S3C2416_GPDDAT) | (1<<8)), S3C2416_GPDDAT);
mdelay(20);
//EXT_N_BT_RST (GPA2) pull high
s3c2410_gpio_cfgpin(S3C2410_GPA2, S3C2410_GPA2_OUT);
s3c2410_gpio_setpin(S3C2410_GPA2, 0);
mdelay(1);
s3c2410_gpio_setpin(S3C2410_GPA2, 1);
mdelay(20);
//UART1 CTS1 (GPH10) set
s3c2410_gpio_cfgpin(S3C2410_GPH10, S3C2410_GPH10_CLKOUT1);
//writel(((readl(S3C2416_GPHCON) | (1<<21)) & ~ (1<<20)), S3C2416_GPHCON);
//writel(((readl(S3C2416_GPHUDP) & ~ (1<<21)) & ~ (1<<20)), S3C2416_GPHUDP);
//writel((readl(S3C2416_GPHDAT) & ~(1<<10)), S3C2416_GPHDAT);
mdelay(1);
//UART1 RTS1 (GPH11) set
s3c2410_gpio_cfgpin(S3C2410_GPH11, S3C2410_GPH11_nRTS);
//writel(((readl(S3C2416_GPHCON) | (1<<23)) & ~(1<<22)), S3C2416_GPHCON);
//writel((readl(S3C2416_GPHDAT) & ~(1<<11)), S3C2416_GPHDAT);
//writel(((readl(S3C2416_GPHUDP) & ~ (1<<23)) & ~ (1<<22)), S3C2416_GPHUDP);
mdelay(1);
break;
case S3C_WIFI_POWER_OFF:
printk("BCM4329-WIFI_POWER_OFF\n");
//TCON Flash disable, and select WiFi
s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH6, 0);
//bcm4329 module power off control
//EXT_N_WIFI_RST (GPD8) pull down
s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD8, 0);
//WIFI_BT_EN (GPD11) pull down
s3c2410_gpio_cfgpin(S3C2410_GPD11, S3C2410_GPD11_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD11, 0);
//EXT_N_BT_RST (GPA2) pull down
s3c2410_gpio_cfgpin(S3C2410_GPA2, S3C2410_GPA2_OUT);
s3c2410_gpio_setpin(S3C2410_GPA2, 0);
mdelay(20);
//GPK9 (green light), pull down
writel(((readl(S3C2416_GPKCON) & ~(1<<19)) | (1<<18)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<9)), S3C2416_GPKDAT);
//GPK7, WIFI PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<15)) | (1<<14)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<7)), S3C2416_GPKDAT);
//GPK6, PA 1.8V
writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<6)), S3C2416_GPKDAT);
//GPK5, PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<5)), S3C2416_GPKDAT);
break;
#else
case S3C_WIFI_POWER_ON:
printk("WIFI_POWER_ON\n");
//TCON Flash disable, and select WiFi
s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH6, 0);
//GPL13, SPI_CS
s3c2410_gpio_cfgpin(S3C2410_GPL13, S3C2410_GPL13_SS0);
//SPI DI
s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_SPIMISO0);
//SPI DO
s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_SPIMOSI0);
//SPI CLK
s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_SPICLK0);
//GPK6, PA 1.8V
writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) | (1<<6)), S3C2416_GPKDAT);
//GPK5, PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) | (1<<5)), S3C2416_GPKDAT);
//GPD8, RESET
s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD8, 0);
mdelay(1);
s3c2410_gpio_setpin(S3C2410_GPD8, 1);
mdelay(20);
break;
case S3C_WIFI_POWER_OFF:
printk("WIFI_POWER_OFF\n");
//TCON Flash disable, and select WiFi
s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH6, 0);
//GPK5, PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<5)), S3C2416_GPKDAT);
//GPK6, PA 1.8V
writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<6)), S3C2416_GPKDAT);
//GPD8, RESET
s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD8, 0);
//GPL13, SPI_CS
s3c2410_gpio_cfgpin(S3C2410_GPL13, S3C2410_GPL13_OUTP);
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
//SPI DI
s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_INP);
s3c2410_gpio_pullup(S3C2410_GPE11, 1);
//SPI DO
s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_INP);
s3c2410_gpio_pullup(S3C2410_GPE12, 1);
//SPI CLK
s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_INP);
s3c2410_gpio_pullup(S3C2410_GPE13, 1);
break;
#endif
case S3C_READ_TILT_STATUS:
if( ((readl(S3C2410_GPDDAT) & (0x3<<10)) >> 10) == 2){
return 270;
}
else if( ((readl(S3C2410_GPDDAT) & (0x3<<10)) >> 10) == 1){
return 90;
}
else if( ((readl(S3C2410_GPDDAT) & (0x3<<10)) >> 10) == 3){
return 180;
}
else{
return 0;
}
break;
case S3C_READ_GPIO_VALUE:
printk("S3C_READ_GPIO_VALUE\n");
printk("addr: 0x%x\n", arg+0x56000000);
arg = arg + S3C24XX_VA_GPIO;
printk("value: 0x%x\n", readl(arg));
break;
case S3C_SET_GPIO_ADDR:
printk("S3C_SET_GPIO_ADDR\n");
printk("set addr: 0x%x\n", arg + 0x56000000);
gpio_addr = arg + S3C24XX_VA_GPIO;
break;
case S3C_WRITE_GPIO_VALUE:
printk("S3C_WRITE_GPIO_VALUE\n");
printk("set addr: 0x%x, value: 0x%x\n", (gpio_addr-0xA0400000), arg);
writel(arg, gpio_addr);
break;
case MMC_DEVICE_POWER_STATE:
printk("MMC_DEVICE_POWER_STATE: %d\n", arg);
if(arg == 0){
card_detect2 = 1;
s3c_hsmmc_irq_cd (0, global_host[0]);
}
else if(arg == 3){
card_detect2 = 0;
s3c_hsmmc_irq_cd (0, global_host[0]);
}
else{
printk("no such state\n");
}
break;
case S3C_READ_HW_VERSION:
//printk("S3C_READ_HW_VERSION\n");
read_pin = readl(S3C2416_GPKDAT) & 0x1E;
//printk(" read_pin: 0x%x\n", read_pin);
ret = 0;
if((read_pin & (1<<4)))
ret = ret + 0x1000;
if((read_pin & (1<<3)))
ret = ret + 0x0100;
if((read_pin & (1<<2)))
ret = ret + 0x0010;
if((read_pin & (1<<1)))
ret = ret + 0x0001;
printk("Board ID: 0x%04x\n", ret);
return ret;
break;
case S3C_TCON_ERASE:
printk("S3C_TCON_ERASE\n");
//WiFi off
//GPK5, PA 3.3V off
writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<5)), S3C2416_GPKDAT);
//GPK6, PA 1.8V
writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<6)), S3C2416_GPKDAT);
//GPD8, RESET
s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD8, 0);
//GPH6, TCON_FLASH enable, and select TCON flash
s3c2410_gpio_cfgpin(S3C2410_GPH6, S3C2410_GPH6_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH6, 1);
mdelay(200);
//Set GPIO for TCON update
//Chip Select
s3c2410_gpio_cfgpin(S3C2410_GPL13, S3C2410_GPL13_OUTP);
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
//GPE11 SPIMISO0
s3c2410_gpio_cfgpin(S3C2410_GPE11, S3C2410_GPE11_INP);
s3c2410_gpio_pullup(S3C2410_GPE11, 0);
//GPE12 SPIMOSI0
s3c2410_gpio_cfgpin(S3C2410_GPE12, S3C2410_GPE12_OUTP);
s3c2410_gpio_setpin(S3C2410_GPE12, 1);
//GPE13 SPI CLK
s3c2410_gpio_cfgpin(S3C2410_GPE13, S3C2410_GPE13_OUTP);
s3c2410_gpio_setpin(S3C2410_GPE13, 0);
msleep(50);
//Erase TCON Flash
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
udelay(50);
s3c_tcon_write_byte(0x06); //Setting Write Enable Latch bit
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
udelay(50);
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
udelay(50);
s3c_tcon_write_byte(0x60); //Write Chip Erase command
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
msleep(500);
do{
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
udelay(50);
s3c_tcon_write_byte(0x05); //Write Read Status command
r_data = s3c_tcon_read_byte();
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
//printk("r_data: 0x%02x\n", r_data);
msleep(500);
}while(r_data & 0x01);
tcon_byte_count = 0;
tcon_address = 0;
break;
case S3C_TCON_ID:
printk("S3C_TCON_ID: ");
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
udelay(50);
s3c_tcon_write_byte(0x9F);
r_data = s3c_tcon_read_byte();
printk("0x%02X ", r_data);
r_data = s3c_tcon_read_byte();
printk("0x%02X ", r_data);
r_data = s3c_tcon_read_byte();
printk("0x%02X\n", r_data);
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
break;
case S3C_TCON_READ:
printk("S3C_TCON_READ\n");
printk("\n\ns3c_tcon_read_content 1\n");
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
udelay(50);
s3c_tcon_write_byte(0x03);
s3c_tcon_write_byte(0x00);
s3c_tcon_write_byte(0x00);
s3c_tcon_write_byte(0x00);
for(i=0; i<300; i++){
printk("0x%02x\n", s3c_tcon_read_byte());
}
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
msleep(50);
printk("\n\ns3c_tcon_read_content 2\n");
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
udelay(50);
s3c_tcon_write_byte(0x03);
s3c_tcon_write_byte(0x02);
s3c_tcon_write_byte(0x0F);
s3c_tcon_write_byte(0xA0);
for(i=0; i<40; i++){
printk("0x%02x\n", s3c_tcon_read_byte());
}
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
break;
case S3C_TCON_BYTE_COUNT:
tcon_byte_count = arg;
//printk("S3C_TCON_BYTE_COUNT: %d\n", tcon_byte_count);
break;
case S3C_TCON_WRITE:
//printk("+ S3C_TCON_WRITE: %d\n ", tcon_address);
copy_from_user(tcon_buffer, arg, tcon_byte_count);
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
udelay(20);
s3c_tcon_write_byte(0x06); //Setting Write Enable Latch bit
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
udelay(20);
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
udelay(20);
//printk("0x%x 0x%x 0x%x\n", (tcon_address & 0xFF0000) >> 16, (tcon_address & 0xFF00) >> 8, (tcon_address & 0xFF));
s3c_tcon_write_byte(0x02); //Write Page Program command
s3c_tcon_write_byte( (tcon_address & 0xFF0000) >> 16 );
s3c_tcon_write_byte( (tcon_address & 0xFF00) >> 8 );
s3c_tcon_write_byte( (tcon_address & 0xFF) );
for(i=0; i<tcon_byte_count; i++){
//printk("0x%02X ", tcon_buffer[i]);
s3c_tcon_write_byte(tcon_buffer[i]);
}
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
udelay(400);
do{
s3c2410_gpio_setpin(S3C2410_GPL13, 0);
udelay(50);
s3c_tcon_write_byte(0x05); //Write Read Status command
r_data = s3c_tcon_read_byte();
s3c2410_gpio_setpin(S3C2410_GPL13, 1);
//printk("r_data: 0x%02x\n", r_data);
udelay(100);
}while(r_data & 0x01);
tcon_address = tcon_address + tcon_byte_count;
//printk("\n- S3C_TCON_WRITE: %d\n\n", tcon_address);
break;
default:
printk("no this command\n");
}
return 0;
}
#ifdef CONFIG_QISDA_BK060B00
static int s3c_bt_open(struct inode *inode, struct file *file);
static int s3c_bt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg);
/* File operations struct for character device */
static const struct file_operations s3c_bt_fops = {
.owner = THIS_MODULE,
.ioctl = s3c_bt_ioctl,
.open = s3c_bt_open,
.release = NULL
};
//Qisda, Ralph Chang, 2010/02/09, for bt power }
//Qisda, Ralph Chang, 2010/02/09, for bt power {
static int s3c_bt_open(struct inode *inode, struct file *file)
{
return 0;
}
static int s3c_bt_ioctl(struct inode *inode, struct file *file, unsigned int cmd, unsigned long arg)
{
switch(cmd){
case S3C_BT_POWER_ON:
printk("BCM4329-BT_POWER_ON\n");
//bcm4329 module power on control
//GPK7, WIFI PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<15)) | (1<<14)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) | (1<<7)), S3C2416_GPKDAT);
//GPK6, PA 1.8V
writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) | (1<<6)), S3C2416_GPKDAT);
//GPK5, PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) | (1<<5)), S3C2416_GPKDAT);
mdelay(1);
//GPL8 (blue light), pull high
//printk ("\n GPL8 (blue light), pull high\n");
//writel(((readl(S3C2416_GPLCON) & ~(1<<17)) | (1<<16)), S3C2416_GPLCON);
//writel((readl(S3C2416_GPLDAT) | (1<<8)), S3C2416_GPLDAT);
//writel(((readl(S3C2416_GPLUDP) | (1<<17)) & ~(1<<16)), S3C2416_GPLUDP); //pull up enable
//s3c2410_gpio_cfgpin(S3C2410_GPL8, S3C2410_GPL8_OUTP);
//s3c2410_gpio_setpin(S3C2410_GPL8, 1);
//s3c2410_gpio_pullup(S3C2410_GPL8, 1);
//WIFI_BT_EN (GPD11) pull high
s3c2410_gpio_cfgpin(S3C2410_GPD11, S3C2410_GPD11_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD11, 0);
//writel(((readl(S3C2416_GPGCON) & ~(1<<9)) | (1<<8)), S3C2416_GPGCON);
//writel((readl(S3C2416_GPGDAT) & ~(1<<4)), S3C2416_GPGDAT);
mdelay(1);
s3c2410_gpio_setpin(S3C2410_GPD11, 1);
//writel((readl(S3C2416_GPGDAT) | (1<<4)), S3C2416_GPGDAT);
mdelay(20);
/*
printk("EXT_N_WIFI_RST (GPD8) set high\n");
//s3c2410_gpio_cfgpin(S3C2410_GPD8, S3C2410_GPD8_OUTP);
//s3c2410_gpio_setpin(S3C2410_GPD8, 0);
writel(((readl(S3C2416_GPDCON) & ~(1<<17)) | (1<<16)), S3C2416_GPDCON);
writel((readl(S3C2416_GPDDAT) & ~(1<<8)), S3C2416_GPDDAT);
mdelay(1);
//s3c2410_gpio_setpin(S3C2410_GPD8, 1);
writel((readl(S3C2416_GPDDAT) | (1<<8)), S3C2416_GPDDAT);
mdelay(20);
*/
//EXT_N_BT_RST (GPA2) pull high
s3c2410_gpio_cfgpin(S3C2410_GPA2, S3C2410_GPA2_OUT);
s3c2410_gpio_setpin(S3C2410_GPA2, 0);
mdelay(1);
s3c2410_gpio_setpin(S3C2410_GPA2, 1);
mdelay(20);
//UART1 CTS1 (GPH10) set
s3c2410_gpio_cfgpin(S3C2410_GPH10, S3C2410_GPH10_CLKOUT1);
//writel(((readl(S3C2416_GPHCON) | (1<<21)) & ~ (1<<20)), S3C2416_GPHCON);
//writel(((readl(S3C2416_GPHUDP) & ~ (1<<21)) & ~ (1<<20)), S3C2416_GPHUDP);
//writel((readl(S3C2416_GPHDAT) & ~(1<<10)), S3C2416_GPHDAT);
mdelay(1);
//"UART1 RTS1 (GPH11) set
s3c2410_gpio_cfgpin(S3C2410_GPH11, S3C2410_GPH11_nRTS);
//writel(((readl(S3C2416_GPHCON) | (1<<23)) & ~(1<<22)), S3C2416_GPHCON);
//writel((readl(S3C2416_GPHDAT) & ~(1<<11)), S3C2416_GPHDAT);
//writel(((readl(S3C2416_GPHUDP) & ~ (1<<23)) & ~ (1<<22)), S3C2416_GPHUDP);
mdelay(1);
break;
case S3C_BT_POWER_OFF:
printk("BCM4329-BT_POWER_OFF\n");
//bcm4329 module power off control
//EXT_N_BT_RST (GPA2) pull down
s3c2410_gpio_cfgpin(S3C2410_GPA2, S3C2410_GPA2_OUT);
s3c2410_gpio_setpin(S3C2410_GPA2, 0);
mdelay(20);
//WIFI_BT_EN (GPD11) pull down
s3c2410_gpio_cfgpin(S3C2410_GPD11, S3C2410_GPD11_OUTP);
s3c2410_gpio_setpin(S3C2410_GPD11, 0);
//GPL8 (blue light), pull down
//writel(((readl(S3C2416_GPLCON) & ~(1<<17)) | (1<<16)), S3C2416_GPLCON);
//writel((readl(S3C2416_GPLDAT) & ~(1<<8)), S3C2416_GPLDAT);
//s3c2410_gpio_cfgpin(S3C2410_GPL8, S3C2410_GPL8_OUTP);
//s3c2410_gpio_setpin(S3C2410_GPL8, 0);
//GPK7, WIFI PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<15)) | (1<<14)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<7)), S3C2416_GPKDAT);
//GPK6, PA 1.8V
writel(((readl(S3C2416_GPKCON) & ~(1<<13)) | (1<<12)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<6)), S3C2416_GPKDAT);
//GPK5, PA 3.3V
writel(((readl(S3C2416_GPKCON) & ~(1<<11)) | (1<<10)), S3C2416_GPKCON);
writel((readl(S3C2416_GPKDAT) & ~(1<<5)), S3C2416_GPKDAT);
break;
default:
printk("no this command\n");
}
return 0;
}
#endif
/* File operations struct for character device */
static const struct file_operations s3c_wifi_fops = {
.owner = THIS_MODULE,
.ioctl = s3c_wifi_ioctl,
.open = s3c_wifi_open,
.release = NULL
};
//Qisda, Asaku Chen, 2009/08/20, for wifi power }
static int s3c_hsmmc_probe (struct platform_device *pdev)
{
struct mmc_host *mmc;
struct s3c_hsmmc_host *host;
struct s3c_hsmmc_cfg *plat_data;
uint i;
int ret;
#if 0
unsigned long gpdat;
unsigned long gpcon;
/* Trevor Fix QISDA WIFIPMP SD_GPIO, Move to hsmmc_set_gpio*/
gpcon = __raw_readl(S3C_GPL1CON);
gpcon = (gpcon & ~(0xf<<12)) | (1<<12); // SD_GPIO(GPL11) Enable control
__raw_writel(gpcon, S3C_GPL1CON);
gpdat = __raw_readl(S3C_GPLDAT);
gpdat |= (1<<11);
__raw_writel(gpdat, S3C_GPLDAT);
#endif
mmc = mmc_alloc_host(sizeof(struct s3c_hsmmc_host), &pdev->dev);
if (!mmc)
ret = -ENOMEM;
plat_data = s3c_hsmmc_get_platdata(&pdev->dev);
//Qisda, Asaku Chen, 2009/08/20, for wifi power {
if(plat_data->hwport==1){
if (register_chrdev (S3C_WIFI_MAJOR, "s3c_wifi_cmd", &s3c_wifi_fops)) {
printk("unable to get major S3C_WIFI_MAJOR\n");
}
}
//Qisda, Asaku Chen, 2009/08/20, for wifi power }
#ifdef CONFIG_QISDA_BK060B00
//Qisda, Ralph Chang, 2010/02/09, for bt power {
if(plat_data->hwport==1){
if (register_chrdev (S3C_BT_MAJOR, "s3c_bt_cmd", &s3c_bt_fops)) {
printk("unable to get major S3C_BT_MAJOR\n");
}
}
//Qisda, Asaku Chen, 2009/08/20, for wifi power }
#endif
host = mmc_priv(mmc);
host->mmc = mmc;
host->plat_data = plat_data;
host->mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!host->mem) {
printk("Failed to get io memory region resouce.\n");
ret = -ENOENT;
goto probe_free_host;
}
host->mem = request_mem_region(host->mem->start,
RESSIZE(host->mem), pdev->name);
if (!host->mem) {
printk("Failed to request io memory region.\n");
ret = -ENOENT;
goto probe_free_host;
}
host->base = ioremap(host->mem->start, (host->mem->end - host->mem->start)+1);
if (host->base == NULL) {
printk(KERN_ERR "Failed to remap register block\n");
return -ENOMEM;
}
host->irq = platform_get_irq(pdev, 0);
if (host->irq == 0) {
printk("Failed to get interrupt resouce.\n");
ret = -EINVAL;
goto untasklet;
}
DBG("platform_device(pdev): pdev(0x%p), name(%s)\n", pdev, pdev->name);
DBG("s3c_hsmmc_host(host): mem->start(0x%08x), base(0x%p), size(%d)\n", host->mem->start, host->base, RESSIZE(host->mem));
DBG("s3c_hsmmc_cfg(plat_data): enabled(%d) hwport(%d)\n", plat_data->enabled, plat_data->hwport);
/* To detect a card inserted on channel 0, an external interrupt is used. */
/* Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card { */
#ifdef CONFIG_SD_SW_DEBOUNCE
if( plat_data->hwport == 0)
{
init_timer(&sd_detect_timer);
sd_detect_timer.function=sdcard_debounce_timer;
}
#endif /* CONFIG_SD_SW_DEBOUNCE */
/* } Qisda, howard hsu, 2010/01/21, SW_DEBOUNCE for sd card */
/* Qisda, howard hsu, 2010/01/21, fix start slot for SD card { */
if( plat_data->hwport == 0)
{
mmc->first_devidx = 1;
}
else
{
mmc->first_devidx = 0;
}
/* } Qisda, howard hsu, 2010/01/21, fix start slot for SD card */
if ((plat_data->enabled == 1) && (plat_data->hwport == 0)) {
host->irq_cd = platform_get_irq(pdev, 1);
if (host->irq_cd == 0) {
printk("Failed to get interrupt resouce.\n");
ret = -EINVAL;
goto untasklet;
}
#if defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
set_irq_type(host->irq_cd, IRQT_BOTHEDGE);
#elif defined(CONFIG_CPU_S3C6410)
set_irq_type(host->irq_cd, IRQT_LOW);
#endif
}
host->flags |= S3C_HSMMC_USE_DMA;
#ifdef CONFIG_HSMMC_SCATTERGATHER
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443)
for (i=0; i<S3C_HSMMC_MAX_SUB_BUF; i++)
host->sub_block[i] = kmalloc(S3C_HSMMC_MALLOC_SIZE, GFP_KERNEL);
#endif
#endif
s3c_hsmmc_reset(host, S3C_HSMMC_RESET_ALL);
/* register some clock sources if exist */
for (i=0; i<NUM_OF_HSMMC_CLKSOURCES; i++) {
host->clk[i] = clk_get(&pdev->dev, plat_data->clk_name[i]);
if (IS_ERR(host->clk[i])) {
ret = PTR_ERR(host->clk[i]);
host->clk[i] = ERR_PTR(-ENOENT);
}
if (clk_enable(host->clk[i])) {
host->clk[i] = ERR_PTR(-ENOENT);
}
if (!IS_ERR(host->clk[i])) {
DBG("MMC clock source[%d], %s is %ld Hz\n",i, plat_data->clk_name[i], clk_get_rate(host->clk[i]));
}
}
mmc->ops = &s3c_hsmmc_ops;
mmc->ocr_avail = MMC_VDD_32_33|MMC_VDD_33_34;
mmc->f_min = 400 * 1000; /* at least 400kHz */
/* you must make sure that our hsmmc block can support
* up to 52MHz. by scsuh
*/
//mmc->f_max = 100 * MHZ;
mmc->f_max = 25 * MHZ;
mmc->caps = plat_data->host_caps;
DBG("mmc->caps: %08lx\n", mmc->caps);
printk(KERN_INFO "mmc->caps: %08lx\n", mmc->caps);
spin_lock_init(&host->lock);
/*
* Maximum number of segments. Hardware cannot do scatter lists.
* XXX: must modify later. by scsuh
*/
#ifdef CONFIG_HSMMC_SCATTERGATHER
mmc->max_hw_segs = CONFIG_S3C_HSMMC_MAX_HW_SEGS;
mmc->max_phys_segs = CONFIG_S3C_HSMMC_MAX_HW_SEGS;
#else
mmc->max_hw_segs = 1;
#endif
/*
* Maximum number of sectors in one transfer. Limited by DMA boundary
* size (512KiB), which means (512 KiB/512=) 1024 entries.
*/
//mmc->max_sectors = 128; /* 65535/512=128 */
/*
* Maximum segment size. Could be one segment with the maximum number
* of sectors.
*/
mmc->max_blk_size = 512;
mmc->max_seg_size = 128 * mmc->max_blk_size;
mmc->max_blk_count = 128;
mmc->max_req_size = mmc->max_seg_size;
init_timer(&host->timer);
host->timer.data = (unsigned long)host;
host->timer.function = s3c_hsmmc_check_status;
host->timer.expires = jiffies + HZ;
/*
* Init tasklets.
*/
tasklet_init(&host->card_tasklet,
s3c_hsmmc_tasklet_card, (unsigned long)host);
#if 0
tasklet_init(&host->finish_tasklet,
s3c_hsmmc_tasklet_finish, (unsigned long)host);
#endif
ret = request_irq(host->irq, s3c_hsmmc_irq, 0, DRIVER_NAME, host);
if (ret)
goto untasklet;
#if defined(CONFIG_CPU_S3C6410) || defined(CONFIG_CPU_S3C2450) || defined(CONFIG_CPU_S3C2416)
if ((plat_data->enabled == 1) && (plat_data->hwport == 0)) {
ret = request_irq(host->irq_cd, s3c_hsmmc_irq_cd, 0, DRIVER_NAME, host);
if (ret)
goto untasklet;
}
#endif
s3c_hsmmc_ios_init(host);
DBG("before mmc_add_host()\n");
mmc_add_host(mmc);
DBG("after mmc_add_host()\n");
#if defined(CONFIG_PM)
global_host[plat_data->hwport] = host;
#endif
printk(KERN_INFO "[s3c_hsmmc_probe]: %s.%d: at 0x%p with irq %d. clk src:",
pdev->name, pdev->id, host->base, host->irq);
for (i=0; i<NUM_OF_HSMMC_CLKSOURCES; i++) {
if (!IS_ERR(host->clk[i]))
printk(" %s", host->clk[i]->name);
}
printk("\n");
return 0;
untasklet:
tasklet_kill(&host->card_tasklet);
/* tasklet_kill(&host->finish_tasklet); */
for (i=0; i<NUM_OF_HSMMC_CLKSOURCES; i++) {
if (host->clk[i] != ERR_PTR(-ENOENT)) {
clk_disable(host->clk[i]);
clk_put(host->clk[i]);
}
}
probe_free_host:
mmc_free_host(mmc);
return ret;
}
static int s3c_hsmmc_remove(struct platform_device *dev)
{
struct mmc_host *mmc = platform_get_drvdata(dev);
struct s3c_hsmmc_host *host = mmc_priv(mmc);
int i;
mmc = host->mmc;
mmc_remove_host(mmc);
s3c_hsmmc_reset(host, S3C_HSMMC_RESET_ALL);
for (i=0; i<NUM_OF_HSMMC_CLKSOURCES; i++) {
clk_disable(host->clk[i]);
clk_put(host->clk[i]);
}
free_irq(host->irq, host);
del_timer_sync(&host->timer);
tasklet_kill(&host->card_tasklet);
/* tasklet_kill(&host->finish_tasklet); */
#ifdef CONFIG_HSMMC_SCATTERGATHER
#if defined(CONFIG_CPU_S3C6400) || defined(CONFIG_CPU_S3C2443)
kfree(host->sub_block);
#endif
#endif
mmc_free_host(mmc);
return 0;
}
#ifdef CONFIG_PM
static int s3c_hsmmc_suspend(struct platform_device *pdev, pm_message_t state)
{
// struct mmc_card *card;
struct s3c_hsmmc_host *s3c_host = global_host[pdev->id];
struct mmc_host *host = s3c_host->mmc;
#if 0
/*
* Fix me later by jsgood
*/
list_for_each_entry(card, &host->cards, node) {
if (card->state & MMC_STATE_PRESENT)
card->state &= ~MMC_STATE_PRESENT;
}
#endif
//USB20_EN
//writel((readl(S3C2410_GPHCON) & ~(1<<29) | 1<<28), S3C2410_GPHCON);
//writel((readl(S3C2410_GPHDAT) &~(1<<14)), S3C2410_GPHDAT);
if(((readl(S3C2410_GPGDAT) &(1<<1))==0)&&((readl(S3C2410_GPHDAT) &(1<<11))==0))
{
//if((readl(S3C2410_GPHDAT) &(1<<11))==0)
{
printk("\nUSB_UDC_DEVICE_s3c_hsmmc_suspend: usb\n");
// writel((readl(S3C2410_GPHCON) & ~(1<<29) | (1<<28)), S3C2410_GPHCON);
s3c2410_gpio_pullup(S3C2443_GPH14,1);
s3c2410_gpio_cfgpin(S3C2443_GPH14, S3C2443_GPH14_OUTP);
s3c2410_gpio_setpin(S3C2443_GPH14, 0); /* usb power enbale */
s3c2410_gpio_pullup(S3C2410_GPD13,1);
s3c2410_gpio_cfgpin(S3C2410_GPD13, S3C2410_GPD13_INP);
}
}
if((readl(S3C2410_GPHDAT) &(1<<11))==0)
{
printk("\nUSB_UDC_DEVICE_s3c_hsmmc_suspend: 3g\n");
s3c2410_gpio_pullup(S3C2410_GPH11,1);
s3c2410_gpio_cfgpin(S3C2410_GPH11, S3C2410_GPH11_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH11, 0); /* usb power enbale */
s3c2410_gpio_pullup(S3C2410_GPH8,1);
s3c2410_gpio_cfgpin(S3C2410_GPH8, S3C2410_GPH8_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH8, 0); /* usb power enbale */
s3c2410_gpio_cfgpin(S3C2410_GPH8, S3C2410_GPH9_OUTP);
s3c2410_gpio_setpin(S3C2410_GPH9, 0); /* usb power enbale */
// s3c2410_gpio_pullup(S3C2410_GPG3,1);
// s3c2410_gpio_cfgpin(S3C2410_GPG3, S3C2410_GPG3_OUTP);
// s3c2410_gpio_setpin(S3C2410_GPG3, 0); /* usb power enbale */
}
mmc_suspend_host(host, state);
//mmc suspend GPIO config
s3c_hsmmc_power_switch(s3c_host->plat_data->hwport, 0);
/* Qisda, Howard, 2009/12/22, fix resume card-detect { */
if(s3c_host->plat_data->hwport==0)
{
inResume = 1;
card_detect2 = 2;
}
/* } Qisda, Howard, 2009/12/22, fix resume card-detect */
return 0;
}
static int s3c_hsmmc_resume(struct platform_device *pdev)
{
struct s3c_hsmmc_host *s3c_host = global_host[pdev->id];
struct mmc_host *host = s3c_host->mmc;
int read_pin;
//USB20_EN
//writel((readl(S3C2410_GPHCON) & ~(1<<29) | 1<<28), S3C2410_GPHCON);
//writel((readl(S3C2410_GPHDAT) |(1<<14)), S3C2410_GPHDAT);
if((readl(S3C2410_GPGDAT) &(1<<1))!=0)
{
printk("\nUSB_UDC_DEVICE_POWER_STATE: %d\n");
writel((readl(S3C2410_GPHCON) & ~(1<<29) | 1<<28), S3C2410_GPHCON);
s3c2410_gpio_pullup(S3C2443_GPH14,2);
s3c2410_gpio_cfgpin(S3C2443_GPH14,S3C2443_GPH14_OUTP);
s3c2410_gpio_setpin(S3C2443_GPH14,1); /* usb power enbale */
}
//PWREN_SD
if(s3c_host->plat_data->hwport==0){
read_pin = readl(S3C2410_GPFDAT) & (1<<1);
if(read_pin && !card_detect){
printk("\nNO CARD\n");
s3c_hsmmc_power_switch(0, 0);
/* Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger { */
//set_irq_type(s3c_host->irq_cd, IRQT_FALLING);
set_irq_type(s3c_host->irq_cd, IRQT_LOW);
/* } Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger */
card_detect2 = 3;
}
else{
if(read_pin){
printk("no card\n");
/* Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger { */
//set_irq_type(s3c_host->irq_cd, IRQT_FALLING);
set_irq_type(s3c_host->irq_cd, IRQT_LOW);
/* } Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger */
card_detect = 0;
//s3c_hsmmc_power_switch(0, 0);
}
else{
printk("card in\n");
/* Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger { */
//set_irq_type(s3c_host->irq_cd, IRQT_RISING);
set_irq_type(s3c_host->irq_cd, IRQT_HIGH);
/* } Qisda, Howard Hsu, 2009/12/17, card detect by level-trigger */
card_detect = 1;
//s3c_hsmmc_power_switch(0, 1);
}
card_detect2 = 2;
s3c_hsmmc_power_switch(0, 1);
s3c_hsmmc_ios_init(s3c_host);
mmc_resume_host(host);
}
inResume = 0; //howard, 2009/12/22, fix resume card-detect
}
else if(s3c_host->plat_data->hwport==1){
s3c_hsmmc_power_switch(1, 1);
s3c_hsmmc_ios_init(s3c_host);
mmc_resume_host(host);
}
#if 0
/* Qisda, Daniel Lee, 2009/07/23, e600 { */
// For EVT0
// Set MMC insert/remove irq back.
if(card_detect)
set_irq_type(s3c_host->irq_cd, IRQT_RISING);
else
set_irq_type(s3c_host->irq_cd, IRQT_FALLING);
/* Qisda, Daniel Lee, 2009/07/23, e600 } */
#endif
/*}Qisda,2009/7/28,Leo SJ Yang*/
//s3c_hsmmc_ios_init(s3c_host);
//mmc_resume_host(host);
/*Qisda,2009/7/28,Leo SJ Yang {*/
/* Add SD Card,WIFI,USB power management*/
if(s3c_host->plat_data->hwport==0)
{
//card_detect=1;
}
/*}Qisda,2009/7/28,Leo SJ Yang*/
return 0;
}
#else
#define s3c_hsmmc_suspend NULL
#define s3c_hsmmc_resume NULL
#endif
static struct platform_driver s3c_hsmmc_driver =
{
.probe = s3c_hsmmc_probe,
.remove = s3c_hsmmc_remove,
.suspend = s3c_hsmmc_suspend,
.resume = s3c_hsmmc_resume,
.driver = {
.name = "s3c-hsmmc",
.owner = THIS_MODULE,
},
};
static int __init s3c_hsmmc_drv_init(void)
{
//Disable SD power
s3c_hsmmc_power_switch(0, 0);
/*Qisda,2009/7/28,Leo SJ Yang{*/
//s3c2410_gpio_cfgpin(S3C2410_GPF1,S3C2410_GPF1_EINT1);
s3c2410_gpio_cfgpin(S3C2410_GPF1,S3C2410_GPF1_INP);
s3c2410_gpio_pullup(S3C2410_GPF1, 2);
/*}Qisda,2009/7/28,Leo SJ Yang*/
return platform_driver_register(&s3c_hsmmc_driver);
}
static void __exit s3c_hsmmc_drv_exit(void)
{
platform_driver_unregister(&s3c_hsmmc_driver);
}
void s3c_moviNAND_power_off(void)
{
struct s3c_hsmmc_host *host = global_host[1];
printk("s3c_moviNAND_power_off");
s3c_hsmmc_writeb(S3C_HSMMC_POWER_OFF, S3C_HSMMC_PWRCON);
}
module_init(s3c_hsmmc_drv_init);
module_exit(s3c_hsmmc_drv_exit);
MODULE_DESCRIPTION("S3C SD HOST I/F 1.0 and 2.0 Driver");
MODULE_LICENSE("GPL");