2616 lines
72 KiB
C
2616 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>
|
|
|
|
#include <linux/cyevent.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;
|
|
}
|
|
|
|
static irqreturn_t s3c_hsmmc_irq_cd (int irq, void *dev_id)
|
|
{
|
|
struct s3c_hsmmc_host *host = dev_id;
|
|
int ext_CD_int = 0;
|
|
CyEvent_t CyEvent = NEW_CYEVENT(CYEVENT_TYPE_SYSTEM);
|
|
CyEvent.flags |= CYEVENT_FLAG_SD_MESSAGE;
|
|
#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 */
|
|
/* SD OUT */
|
|
CyEvent_PushNewUniqueEvent(&CyEvent);
|
|
|
|
}
|
|
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 */
|
|
/* SD IN */
|
|
CyEvent.flags |= CYEVENT_FLAG_SYS_STATUS;
|
|
CyEvent_PushNewUniqueEvent(&CyEvent);
|
|
|
|
}
|
|
//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;
|
|
enable_irq_wake(host->irq_cd);
|
|
}
|
|
#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");
|