2009-01-23 03:05:23 +03:00
/*
* drivers / mmc / host / omap_hsmmc . c
*
* Driver for OMAP2430 / 3430 MMC controller .
*
* Copyright ( C ) 2007 Texas Instruments .
*
* Authors :
* Syed Mohammed Khasim < x0khasim @ ti . com >
* Madhusudhan < madhu . cr @ ti . com >
* Mohit Jalori < mjalori @ ti . com >
*
* This file is licensed under the terms of the GNU General Public License
* version 2. This program is licensed " as is " without any warranty of any
* kind , whether express or implied .
*/
# include <linux/module.h>
# include <linux/init.h>
2011-05-10 16:51:54 +04:00
# include <linux/kernel.h>
2009-09-23 03:44:38 +04:00
# include <linux/debugfs.h>
# include <linux/seq_file.h>
2009-01-23 03:05:23 +03:00
# include <linux/interrupt.h>
# include <linux/delay.h>
# include <linux/dma-mapping.h>
# include <linux/platform_device.h>
# include <linux/timer.h>
# include <linux/clk.h>
2012-03-12 19:02:37 +04:00
# include <linux/of.h>
# include <linux/of_gpio.h>
# include <linux/of_device.h>
2009-01-23 03:05:23 +03:00
# include <linux/mmc/host.h>
2009-09-23 03:44:53 +04:00
# include <linux/mmc/core.h>
2010-08-12 01:17:48 +04:00
# include <linux/mmc/mmc.h>
2009-01-23 03:05:23 +03:00
# include <linux/io.h>
# include <linux/semaphore.h>
2010-02-15 21:03:34 +03:00
# include <linux/gpio.h>
# include <linux/regulator/consumer.h>
2011-07-01 20:39:35 +04:00
# include <linux/pm_runtime.h>
2009-10-20 20:40:47 +04:00
# include <plat/dma.h>
2009-01-23 03:05:23 +03:00
# include <mach/hardware.h>
2009-10-20 20:40:47 +04:00
# include <plat/board.h>
# include <plat/mmc.h>
# include <plat/cpu.h>
2009-01-23 03:05:23 +03:00
/* OMAP HSMMC Host Controller Registers */
# define OMAP_HSMMC_SYSCONFIG 0x0010
2009-09-23 03:44:43 +04:00
# define OMAP_HSMMC_SYSSTATUS 0x0014
2009-01-23 03:05:23 +03:00
# define OMAP_HSMMC_CON 0x002C
# define OMAP_HSMMC_BLK 0x0104
# define OMAP_HSMMC_ARG 0x0108
# define OMAP_HSMMC_CMD 0x010C
# define OMAP_HSMMC_RSP10 0x0110
# define OMAP_HSMMC_RSP32 0x0114
# define OMAP_HSMMC_RSP54 0x0118
# define OMAP_HSMMC_RSP76 0x011C
# define OMAP_HSMMC_DATA 0x0120
# define OMAP_HSMMC_HCTL 0x0128
# define OMAP_HSMMC_SYSCTL 0x012C
# define OMAP_HSMMC_STAT 0x0130
# define OMAP_HSMMC_IE 0x0134
# define OMAP_HSMMC_ISE 0x0138
# define OMAP_HSMMC_CAPA 0x0140
# define VS18 (1 << 26)
# define VS30 (1 << 25)
# define SDVS18 (0x5 << 9)
# define SDVS30 (0x6 << 9)
2009-02-18 01:49:01 +03:00
# define SDVS33 (0x7 << 9)
2009-02-20 15:10:08 +03:00
# define SDVS_MASK 0x00000E00
2009-01-23 03:05:23 +03:00
# define SDVSCLR 0xFFFFF1FF
# define SDVSDET 0x00000400
# define AUTOIDLE 0x1
# define SDBP (1 << 8)
# define DTO 0xe
# define ICE 0x1
# define ICS 0x2
# define CEN (1 << 2)
# define CLKD_MASK 0x0000FFC0
# define CLKD_SHIFT 6
# define DTO_MASK 0x000F0000
# define DTO_SHIFT 16
# define INT_EN_MASK 0x307F0033
2009-09-23 03:44:21 +04:00
# define BWR_ENABLE (1 << 4)
# define BRR_ENABLE (1 << 5)
2010-08-12 01:17:48 +04:00
# define DTO_ENABLE (1 << 20)
2009-01-23 03:05:23 +03:00
# define INIT_STREAM (1 << 1)
2012-04-09 10:38:32 +04:00
# define ACEN_ACMD12 (1 << 2)
2009-01-23 03:05:23 +03:00
# define DP_SELECT (1 << 21)
# define DDIR (1 << 4)
# define DMA_EN 0x1
# define MSBS (1 << 5)
# define BCE (1 << 1)
# define FOUR_BIT (1 << 1)
2012-04-09 10:38:33 +04:00
# define DDR (1 << 19)
2008-11-21 17:49:54 +03:00
# define DW8 (1 << 5)
2009-01-23 03:05:23 +03:00
# define CC 0x1
# define TC 0x02
# define OD 0x1
# define ERR (1 << 15)
# define CMD_TIMEOUT (1 << 16)
# define DATA_TIMEOUT (1 << 20)
# define CMD_CRC (1 << 17)
# define DATA_CRC (1 << 21)
# define CARD_ERR (1 << 28)
# define STAT_CLEAR 0xFFFFFFFF
# define INIT_STREAM_CMD 0x00000000
# define DUAL_VOLT_OCR_BIT 7
# define SRC (1 << 25)
# define SRD (1 << 26)
2009-09-23 03:44:43 +04:00
# define SOFTRESET (1 << 1)
# define RESETDONE (1 << 0)
2009-01-23 03:05:23 +03:00
2011-07-01 20:39:35 +04:00
# define MMC_AUTOSUSPEND_DELAY 100
2009-01-23 03:05:23 +03:00
# define MMC_TIMEOUT_MS 20
2011-07-13 19:16:29 +04:00
# define OMAP_MMC_MIN_CLOCK 400000
# define OMAP_MMC_MAX_CLOCK 52000000
2011-02-28 18:18:05 +03:00
# define DRIVER_NAME "omap_hsmmc"
2009-01-23 03:05:23 +03:00
2012-04-09 10:38:32 +04:00
# define AUTO_CMD12 (1 << 0) /* Auto CMD12 support */
2009-01-23 03:05:23 +03:00
/*
* One controller can have multiple slots , like on some omap boards using
* omap . c controller driver . Luckily this is not currently done on any known
* omap_hsmmc . c device .
*/
# define mmc_slot(host) (host->pdata->slots[host->slot_id])
/*
* MMC Host controller read / write API ' s
*/
# define OMAP_HSMMC_READ(base, reg) \
__raw_readl ( ( base ) + OMAP_HSMMC_ # # reg )
# define OMAP_HSMMC_WRITE(base, reg, val) \
__raw_writel ( ( val ) , ( base ) + OMAP_HSMMC_ # # reg )
2011-07-01 20:55:23 +04:00
struct omap_hsmmc_next {
unsigned int dma_len ;
s32 cookie ;
} ;
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host {
2009-01-23 03:05:23 +03:00
struct device * dev ;
struct mmc_host * mmc ;
struct mmc_request * mrq ;
struct mmc_command * cmd ;
struct mmc_data * data ;
struct clk * fclk ;
struct clk * dbclk ;
2010-02-15 21:03:34 +03:00
/*
* vcc = = configured supply
* vcc_aux = = optional
* - MMC1 , supply for DAT4 . . DAT7
* - MMC2 / MMC2 , external level shifter voltage supply , for
* chip ( SDIO , eMMC , etc ) or transceiver ( MMC2 only )
*/
struct regulator * vcc ;
struct regulator * vcc_aux ;
2009-01-23 03:05:23 +03:00
void __iomem * base ;
resource_size_t mapbase ;
2009-09-23 03:44:58 +04:00
spinlock_t irq_lock ; /* Prevent races with irq handler */
2009-01-23 03:05:23 +03:00
unsigned int dma_len ;
2008-11-14 16:22:00 +03:00
unsigned int dma_sg_idx ;
2009-01-23 03:05:23 +03:00
unsigned char bus_mode ;
2009-09-23 03:44:42 +04:00
unsigned char power_mode ;
2009-01-23 03:05:23 +03:00
u32 * buffer ;
u32 bytesleft ;
int suspended ;
int irq ;
int use_dma , dma_ch ;
2009-01-03 13:36:13 +03:00
int dma_line_tx , dma_line_rx ;
2009-01-23 03:05:23 +03:00
int slot_id ;
2009-09-23 03:45:02 +04:00
int got_dbclk ;
2009-01-12 17:13:08 +03:00
int response_busy ;
2009-09-23 03:44:43 +04:00
int context_loss ;
2009-09-23 03:44:51 +04:00
int vdd ;
2009-09-23 03:45:01 +04:00
int protect_card ;
int reqs_blocked ;
2010-02-15 21:03:34 +03:00
int use_reg ;
2010-05-27 01:42:06 +04:00
int req_in_progress ;
2012-04-09 10:38:32 +04:00
unsigned int flags ;
2011-07-01 20:55:23 +04:00
struct omap_hsmmc_next next_data ;
2009-09-23 03:44:43 +04:00
2009-01-23 03:05:23 +03:00
struct omap_mmc_platform_data * pdata ;
} ;
2010-02-15 21:03:34 +03:00
static int omap_hsmmc_card_detect ( struct device * dev , int slot )
{
struct omap_mmc_platform_data * mmc = dev - > platform_data ;
/* NOTE: assumes card detect signal is active-low */
return ! gpio_get_value_cansleep ( mmc - > slots [ 0 ] . switch_pin ) ;
}
static int omap_hsmmc_get_wp ( struct device * dev , int slot )
{
struct omap_mmc_platform_data * mmc = dev - > platform_data ;
/* NOTE: assumes write protect signal is active-high */
return gpio_get_value_cansleep ( mmc - > slots [ 0 ] . gpio_wp ) ;
}
static int omap_hsmmc_get_cover_state ( struct device * dev , int slot )
{
struct omap_mmc_platform_data * mmc = dev - > platform_data ;
/* NOTE: assumes card detect signal is active-low */
return ! gpio_get_value_cansleep ( mmc - > slots [ 0 ] . switch_pin ) ;
}
# ifdef CONFIG_PM
static int omap_hsmmc_suspend_cdirq ( struct device * dev , int slot )
{
struct omap_mmc_platform_data * mmc = dev - > platform_data ;
disable_irq ( mmc - > slots [ 0 ] . card_detect_irq ) ;
return 0 ;
}
static int omap_hsmmc_resume_cdirq ( struct device * dev , int slot )
{
struct omap_mmc_platform_data * mmc = dev - > platform_data ;
enable_irq ( mmc - > slots [ 0 ] . card_detect_irq ) ;
return 0 ;
}
# else
# define omap_hsmmc_suspend_cdirq NULL
# define omap_hsmmc_resume_cdirq NULL
# endif
2010-02-15 21:03:35 +03:00
# ifdef CONFIG_REGULATOR
2012-03-07 18:55:30 +04:00
static int omap_hsmmc_set_power ( struct device * dev , int slot , int power_on ,
2010-02-15 21:03:34 +03:00
int vdd )
{
struct omap_hsmmc_host * host =
platform_get_drvdata ( to_platform_device ( dev ) ) ;
int ret = 0 ;
/*
* If we don ' t see a Vcc regulator , assume it ' s a fixed
* voltage always - on regulator .
*/
if ( ! host - > vcc )
return 0 ;
2012-03-12 19:02:38 +04:00
/*
* With DT , never turn OFF the regulator . This is because
* the pbias cell programming support is still missing when
* booting with Device tree
*/
2012-04-11 14:03:13 +04:00
if ( dev - > of_node & & ! vdd )
2012-03-12 19:02:38 +04:00
return 0 ;
2010-02-15 21:03:34 +03:00
if ( mmc_slot ( host ) . before_set_reg )
mmc_slot ( host ) . before_set_reg ( dev , slot , power_on , vdd ) ;
/*
* Assume Vcc regulator is used only to power the card . . . OMAP
* VDDS is used to power the pins , optionally with a transceiver to
* support cards using voltages other than VDDS ( 1.8 V nominal ) . When a
* transceiver is used , DAT3 . .7 are muxed as transceiver control pins .
*
* In some cases this regulator won ' t support enable / disable ;
* e . g . it ' s a fixed rail for a WLAN chip .
*
* In other cases vcc_aux switches interface power . Example , for
* eMMC cards it represents VccQ . Sometimes transceivers or SDIO
* chips / cards need an interface voltage rail too .
*/
if ( power_on ) {
2010-09-29 09:08:27 +04:00
ret = mmc_regulator_set_ocr ( host - > mmc , host - > vcc , vdd ) ;
2010-02-15 21:03:34 +03:00
/* Enable interface voltage rail, if needed */
if ( ret = = 0 & & host - > vcc_aux ) {
ret = regulator_enable ( host - > vcc_aux ) ;
if ( ret < 0 )
2010-09-29 09:08:27 +04:00
ret = mmc_regulator_set_ocr ( host - > mmc ,
host - > vcc , 0 ) ;
2010-02-15 21:03:34 +03:00
}
} else {
2010-09-29 09:08:27 +04:00
/* Shut down the rail */
2010-02-15 21:03:34 +03:00
if ( host - > vcc_aux )
ret = regulator_disable ( host - > vcc_aux ) ;
2010-09-29 09:08:27 +04:00
if ( ! ret ) {
/* Then proceed to shut down the local regulator */
ret = mmc_regulator_set_ocr ( host - > mmc ,
host - > vcc , 0 ) ;
}
2010-02-15 21:03:34 +03:00
}
if ( mmc_slot ( host ) . after_set_reg )
mmc_slot ( host ) . after_set_reg ( dev , slot , power_on , vdd ) ;
return ret ;
}
static int omap_hsmmc_reg_get ( struct omap_hsmmc_host * host )
{
struct regulator * reg ;
2010-10-02 03:35:28 +04:00
int ocr_value = 0 ;
2010-02-15 21:03:34 +03:00
2012-03-07 18:55:31 +04:00
mmc_slot ( host ) . set_power = omap_hsmmc_set_power ;
2010-02-15 21:03:34 +03:00
reg = regulator_get ( host - > dev , " vmmc " ) ;
if ( IS_ERR ( reg ) ) {
dev_dbg ( host - > dev , " vmmc regulator missing \n " ) ;
} else {
host - > vcc = reg ;
2010-10-02 03:35:28 +04:00
ocr_value = mmc_regulator_get_ocrmask ( reg ) ;
if ( ! mmc_slot ( host ) . ocr_mask ) {
mmc_slot ( host ) . ocr_mask = ocr_value ;
} else {
if ( ! ( mmc_slot ( host ) . ocr_mask & ocr_value ) ) {
2012-02-23 15:32:20 +04:00
dev_err ( host - > dev , " ocrmask %x is not supported \n " ,
2012-03-07 18:55:31 +04:00
mmc_slot ( host ) . ocr_mask ) ;
2010-10-02 03:35:28 +04:00
mmc_slot ( host ) . ocr_mask = 0 ;
return - EINVAL ;
}
}
2010-02-15 21:03:34 +03:00
/* Allow an aux regulator */
reg = regulator_get ( host - > dev , " vmmc_aux " ) ;
host - > vcc_aux = IS_ERR ( reg ) ? NULL : reg ;
2011-05-30 18:25:34 +04:00
/* For eMMC do not power off when not in sleep state */
if ( mmc_slot ( host ) . no_regulator_off_init )
return 0 ;
2010-02-15 21:03:34 +03:00
/*
* UGLY HACK : workaround regulator framework bugs .
* When the bootloader leaves a supply active , it ' s
* initialized with zero usecount . . . and we can ' t
* disable it without first enabling it . Until the
* framework is fixed , we need a workaround like this
* ( which is safe for MMC , but not in general ) .
*/
2011-05-06 13:14:10 +04:00
if ( regulator_is_enabled ( host - > vcc ) > 0 | |
( host - > vcc_aux & & regulator_is_enabled ( host - > vcc_aux ) ) ) {
int vdd = ffs ( mmc_slot ( host ) . ocr_mask ) - 1 ;
mmc_slot ( host ) . set_power ( host - > dev , host - > slot_id ,
1 , vdd ) ;
mmc_slot ( host ) . set_power ( host - > dev , host - > slot_id ,
0 , 0 ) ;
2010-02-15 21:03:34 +03:00
}
}
return 0 ;
}
static void omap_hsmmc_reg_put ( struct omap_hsmmc_host * host )
{
regulator_put ( host - > vcc ) ;
regulator_put ( host - > vcc_aux ) ;
mmc_slot ( host ) . set_power = NULL ;
}
2010-02-15 21:03:35 +03:00
static inline int omap_hsmmc_have_reg ( void )
{
return 1 ;
}
# else
static inline int omap_hsmmc_reg_get ( struct omap_hsmmc_host * host )
{
return - EINVAL ;
}
static inline void omap_hsmmc_reg_put ( struct omap_hsmmc_host * host )
{
}
static inline int omap_hsmmc_have_reg ( void )
{
return 0 ;
}
# endif
static int omap_hsmmc_gpio_init ( struct omap_mmc_platform_data * pdata )
{
int ret ;
if ( gpio_is_valid ( pdata - > slots [ 0 ] . switch_pin ) ) {
if ( pdata - > slots [ 0 ] . cover )
pdata - > slots [ 0 ] . get_cover_state =
omap_hsmmc_get_cover_state ;
else
pdata - > slots [ 0 ] . card_detect = omap_hsmmc_card_detect ;
pdata - > slots [ 0 ] . card_detect_irq =
gpio_to_irq ( pdata - > slots [ 0 ] . switch_pin ) ;
ret = gpio_request ( pdata - > slots [ 0 ] . switch_pin , " mmc_cd " ) ;
if ( ret )
return ret ;
ret = gpio_direction_input ( pdata - > slots [ 0 ] . switch_pin ) ;
if ( ret )
goto err_free_sp ;
} else
pdata - > slots [ 0 ] . switch_pin = - EINVAL ;
if ( gpio_is_valid ( pdata - > slots [ 0 ] . gpio_wp ) ) {
pdata - > slots [ 0 ] . get_ro = omap_hsmmc_get_wp ;
ret = gpio_request ( pdata - > slots [ 0 ] . gpio_wp , " mmc_wp " ) ;
if ( ret )
goto err_free_cd ;
ret = gpio_direction_input ( pdata - > slots [ 0 ] . gpio_wp ) ;
if ( ret )
goto err_free_wp ;
} else
pdata - > slots [ 0 ] . gpio_wp = - EINVAL ;
return 0 ;
err_free_wp :
gpio_free ( pdata - > slots [ 0 ] . gpio_wp ) ;
err_free_cd :
if ( gpio_is_valid ( pdata - > slots [ 0 ] . switch_pin ) )
err_free_sp :
gpio_free ( pdata - > slots [ 0 ] . switch_pin ) ;
return ret ;
}
static void omap_hsmmc_gpio_free ( struct omap_mmc_platform_data * pdata )
{
if ( gpio_is_valid ( pdata - > slots [ 0 ] . gpio_wp ) )
gpio_free ( pdata - > slots [ 0 ] . gpio_wp ) ;
if ( gpio_is_valid ( pdata - > slots [ 0 ] . switch_pin ) )
gpio_free ( pdata - > slots [ 0 ] . switch_pin ) ;
}
2011-05-06 13:14:05 +04:00
/*
* Start clock to the card
*/
static void omap_hsmmc_start_clock ( struct omap_hsmmc_host * host )
{
OMAP_HSMMC_WRITE ( host - > base , SYSCTL ,
OMAP_HSMMC_READ ( host - > base , SYSCTL ) | CEN ) ;
}
2009-01-23 03:05:23 +03:00
/*
* Stop clock to the card
*/
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_stop_clock ( struct omap_hsmmc_host * host )
2009-01-23 03:05:23 +03:00
{
OMAP_HSMMC_WRITE ( host - > base , SYSCTL ,
OMAP_HSMMC_READ ( host - > base , SYSCTL ) & ~ CEN ) ;
if ( ( OMAP_HSMMC_READ ( host - > base , SYSCTL ) & CEN ) ! = 0x0 )
dev_dbg ( mmc_dev ( host - > mmc ) , " MMC Clock is not stoped \n " ) ;
}
2010-08-12 01:17:48 +04:00
static void omap_hsmmc_enable_irq ( struct omap_hsmmc_host * host ,
struct mmc_command * cmd )
2010-05-27 01:42:06 +04:00
{
unsigned int irq_mask ;
if ( host - > use_dma )
irq_mask = INT_EN_MASK & ~ ( BRR_ENABLE | BWR_ENABLE ) ;
else
irq_mask = INT_EN_MASK ;
2010-08-12 01:17:48 +04:00
/* Disable timeout for erases */
if ( cmd - > opcode = = MMC_ERASE )
irq_mask & = ~ DTO_ENABLE ;
2010-05-27 01:42:06 +04:00
OMAP_HSMMC_WRITE ( host - > base , STAT , STAT_CLEAR ) ;
OMAP_HSMMC_WRITE ( host - > base , ISE , irq_mask ) ;
OMAP_HSMMC_WRITE ( host - > base , IE , irq_mask ) ;
}
static void omap_hsmmc_disable_irq ( struct omap_hsmmc_host * host )
{
OMAP_HSMMC_WRITE ( host - > base , ISE , 0 ) ;
OMAP_HSMMC_WRITE ( host - > base , IE , 0 ) ;
OMAP_HSMMC_WRITE ( host - > base , STAT , STAT_CLEAR ) ;
}
2011-05-10 16:51:54 +04:00
/* Calculate divisor for the given clock frequency */
2011-12-20 13:42:00 +04:00
static u16 calc_divisor ( struct omap_hsmmc_host * host , struct mmc_ios * ios )
2011-05-10 16:51:54 +04:00
{
u16 dsor = 0 ;
if ( ios - > clock ) {
2011-12-20 13:42:00 +04:00
dsor = DIV_ROUND_UP ( clk_get_rate ( host - > fclk ) , ios - > clock ) ;
2011-05-10 16:51:54 +04:00
if ( dsor > 250 )
dsor = 250 ;
}
return dsor ;
}
2011-05-06 13:14:06 +04:00
static void omap_hsmmc_set_clock ( struct omap_hsmmc_host * host )
{
struct mmc_ios * ios = & host - > mmc - > ios ;
unsigned long regval ;
unsigned long timeout ;
dev_dbg ( mmc_dev ( host - > mmc ) , " Set clock to %uHz \n " , ios - > clock ) ;
omap_hsmmc_stop_clock ( host ) ;
regval = OMAP_HSMMC_READ ( host - > base , SYSCTL ) ;
regval = regval & ~ ( CLKD_MASK | DTO_MASK ) ;
2011-12-20 13:42:00 +04:00
regval = regval | ( calc_divisor ( host , ios ) < < 6 ) | ( DTO < < 16 ) ;
2011-05-06 13:14:06 +04:00
OMAP_HSMMC_WRITE ( host - > base , SYSCTL , regval ) ;
OMAP_HSMMC_WRITE ( host - > base , SYSCTL ,
OMAP_HSMMC_READ ( host - > base , SYSCTL ) | ICE ) ;
/* Wait till the ICS bit is set */
timeout = jiffies + msecs_to_jiffies ( MMC_TIMEOUT_MS ) ;
while ( ( OMAP_HSMMC_READ ( host - > base , SYSCTL ) & ICS ) ! = ICS
& & time_before ( jiffies , timeout ) )
cpu_relax ( ) ;
omap_hsmmc_start_clock ( host ) ;
}
2011-07-13 19:31:15 +04:00
static void omap_hsmmc_set_bus_width ( struct omap_hsmmc_host * host )
{
struct mmc_ios * ios = & host - > mmc - > ios ;
u32 con ;
con = OMAP_HSMMC_READ ( host - > base , CON ) ;
2012-04-09 10:38:33 +04:00
if ( ios - > timing = = MMC_TIMING_UHS_DDR50 )
con | = DDR ; /* configure in DDR mode */
else
con & = ~ DDR ;
2011-07-13 19:31:15 +04:00
switch ( ios - > bus_width ) {
case MMC_BUS_WIDTH_8 :
OMAP_HSMMC_WRITE ( host - > base , CON , con | DW8 ) ;
break ;
case MMC_BUS_WIDTH_4 :
OMAP_HSMMC_WRITE ( host - > base , CON , con & ~ DW8 ) ;
OMAP_HSMMC_WRITE ( host - > base , HCTL ,
OMAP_HSMMC_READ ( host - > base , HCTL ) | FOUR_BIT ) ;
break ;
case MMC_BUS_WIDTH_1 :
OMAP_HSMMC_WRITE ( host - > base , CON , con & ~ DW8 ) ;
OMAP_HSMMC_WRITE ( host - > base , HCTL ,
OMAP_HSMMC_READ ( host - > base , HCTL ) & ~ FOUR_BIT ) ;
break ;
}
}
static void omap_hsmmc_set_bus_mode ( struct omap_hsmmc_host * host )
{
struct mmc_ios * ios = & host - > mmc - > ios ;
u32 con ;
con = OMAP_HSMMC_READ ( host - > base , CON ) ;
if ( ios - > bus_mode = = MMC_BUSMODE_OPENDRAIN )
OMAP_HSMMC_WRITE ( host - > base , CON , con | OD ) ;
else
OMAP_HSMMC_WRITE ( host - > base , CON , con & ~ OD ) ;
}
2009-09-23 03:44:43 +04:00
# ifdef CONFIG_PM
/*
* Restore the MMC host context , if it was lost as result of a
* power state change .
*/
2009-09-23 03:44:59 +04:00
static int omap_hsmmc_context_restore ( struct omap_hsmmc_host * host )
2009-09-23 03:44:43 +04:00
{
struct mmc_ios * ios = & host - > mmc - > ios ;
struct omap_mmc_platform_data * pdata = host - > pdata ;
int context_loss = 0 ;
2011-07-13 19:31:15 +04:00
u32 hctl , capa ;
2009-09-23 03:44:43 +04:00
unsigned long timeout ;
if ( pdata - > get_context_loss_count ) {
context_loss = pdata - > get_context_loss_count ( host - > dev ) ;
if ( context_loss < 0 )
return 1 ;
}
dev_dbg ( mmc_dev ( host - > mmc ) , " context was %slost \n " ,
context_loss = = host - > context_loss ? " not " : " " ) ;
if ( host - > context_loss = = context_loss )
return 1 ;
/* Wait for hardware reset */
timeout = jiffies + msecs_to_jiffies ( MMC_TIMEOUT_MS ) ;
while ( ( OMAP_HSMMC_READ ( host - > base , SYSSTATUS ) & RESETDONE ) ! = RESETDONE
& & time_before ( jiffies , timeout ) )
;
/* Do software reset */
OMAP_HSMMC_WRITE ( host - > base , SYSCONFIG , SOFTRESET ) ;
timeout = jiffies + msecs_to_jiffies ( MMC_TIMEOUT_MS ) ;
while ( ( OMAP_HSMMC_READ ( host - > base , SYSSTATUS ) & RESETDONE ) ! = RESETDONE
& & time_before ( jiffies , timeout ) )
;
OMAP_HSMMC_WRITE ( host - > base , SYSCONFIG ,
OMAP_HSMMC_READ ( host - > base , SYSCONFIG ) | AUTOIDLE ) ;
2012-03-07 18:55:30 +04:00
if ( host - > pdata - > controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT ) {
2009-09-23 03:44:43 +04:00
if ( host - > power_mode ! = MMC_POWER_OFF & &
( 1 < < ios - > vdd ) < = MMC_VDD_23_24 )
hctl = SDVS18 ;
else
hctl = SDVS30 ;
capa = VS30 | VS18 ;
} else {
hctl = SDVS18 ;
capa = VS18 ;
}
OMAP_HSMMC_WRITE ( host - > base , HCTL ,
OMAP_HSMMC_READ ( host - > base , HCTL ) | hctl ) ;
OMAP_HSMMC_WRITE ( host - > base , CAPA ,
OMAP_HSMMC_READ ( host - > base , CAPA ) | capa ) ;
OMAP_HSMMC_WRITE ( host - > base , HCTL ,
OMAP_HSMMC_READ ( host - > base , HCTL ) | SDBP ) ;
timeout = jiffies + msecs_to_jiffies ( MMC_TIMEOUT_MS ) ;
while ( ( OMAP_HSMMC_READ ( host - > base , HCTL ) & SDBP ) ! = SDBP
& & time_before ( jiffies , timeout ) )
;
2010-05-27 01:42:06 +04:00
omap_hsmmc_disable_irq ( host ) ;
2009-09-23 03:44:43 +04:00
/* Do not initialize card-specific things if the power is off */
if ( host - > power_mode = = MMC_POWER_OFF )
goto out ;
2011-07-13 19:31:15 +04:00
omap_hsmmc_set_bus_width ( host ) ;
2009-09-23 03:44:43 +04:00
2011-05-06 13:14:06 +04:00
omap_hsmmc_set_clock ( host ) ;
2009-09-23 03:44:43 +04:00
2011-07-13 19:31:15 +04:00
omap_hsmmc_set_bus_mode ( host ) ;
2009-09-23 03:44:43 +04:00
out :
host - > context_loss = context_loss ;
dev_dbg ( mmc_dev ( host - > mmc ) , " context is restored \n " ) ;
return 0 ;
}
/*
* Save the MMC host context ( store the number of power state changes so far ) .
*/
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_context_save ( struct omap_hsmmc_host * host )
2009-09-23 03:44:43 +04:00
{
struct omap_mmc_platform_data * pdata = host - > pdata ;
int context_loss ;
if ( pdata - > get_context_loss_count ) {
context_loss = pdata - > get_context_loss_count ( host - > dev ) ;
if ( context_loss < 0 )
return ;
host - > context_loss = context_loss ;
}
}
# else
2009-09-23 03:44:59 +04:00
static int omap_hsmmc_context_restore ( struct omap_hsmmc_host * host )
2009-09-23 03:44:43 +04:00
{
return 0 ;
}
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_context_save ( struct omap_hsmmc_host * host )
2009-09-23 03:44:43 +04:00
{
}
# endif
2009-01-23 03:05:23 +03:00
/*
* Send init stream sequence to card
* before sending IDLE command
*/
2009-09-23 03:44:59 +04:00
static void send_init_stream ( struct omap_hsmmc_host * host )
2009-01-23 03:05:23 +03:00
{
int reg = 0 ;
unsigned long timeout ;
2009-09-23 03:45:01 +04:00
if ( host - > protect_card )
return ;
2009-01-23 03:05:23 +03:00
disable_irq ( host - > irq ) ;
2010-05-27 01:42:06 +04:00
OMAP_HSMMC_WRITE ( host - > base , IE , INT_EN_MASK ) ;
2009-01-23 03:05:23 +03:00
OMAP_HSMMC_WRITE ( host - > base , CON ,
OMAP_HSMMC_READ ( host - > base , CON ) | INIT_STREAM ) ;
OMAP_HSMMC_WRITE ( host - > base , CMD , INIT_STREAM_CMD ) ;
timeout = jiffies + msecs_to_jiffies ( MMC_TIMEOUT_MS ) ;
while ( ( reg ! = CC ) & & time_before ( jiffies , timeout ) )
reg = OMAP_HSMMC_READ ( host - > base , STAT ) & CC ;
OMAP_HSMMC_WRITE ( host - > base , CON ,
OMAP_HSMMC_READ ( host - > base , CON ) & ~ INIT_STREAM ) ;
2009-09-23 03:44:56 +04:00
OMAP_HSMMC_WRITE ( host - > base , STAT , STAT_CLEAR ) ;
OMAP_HSMMC_READ ( host - > base , STAT ) ;
2009-01-23 03:05:23 +03:00
enable_irq ( host - > irq ) ;
}
static inline
2009-09-23 03:44:59 +04:00
int omap_hsmmc_cover_is_closed ( struct omap_hsmmc_host * host )
2009-01-23 03:05:23 +03:00
{
int r = 1 ;
2009-09-23 03:44:55 +04:00
if ( mmc_slot ( host ) . get_cover_state )
r = mmc_slot ( host ) . get_cover_state ( host - > dev , host - > slot_id ) ;
2009-01-23 03:05:23 +03:00
return r ;
}
static ssize_t
2009-09-23 03:44:59 +04:00
omap_hsmmc_show_cover_switch ( struct device * dev , struct device_attribute * attr ,
2009-01-23 03:05:23 +03:00
char * buf )
{
struct mmc_host * mmc = container_of ( dev , struct mmc_host , class_dev ) ;
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
2009-01-23 03:05:23 +03:00
2009-09-23 03:44:59 +04:00
return sprintf ( buf , " %s \n " ,
omap_hsmmc_cover_is_closed ( host ) ? " closed " : " open " ) ;
2009-01-23 03:05:23 +03:00
}
2009-09-23 03:44:59 +04:00
static DEVICE_ATTR ( cover_switch , S_IRUGO , omap_hsmmc_show_cover_switch , NULL ) ;
2009-01-23 03:05:23 +03:00
static ssize_t
2009-09-23 03:44:59 +04:00
omap_hsmmc_show_slot_name ( struct device * dev , struct device_attribute * attr ,
2009-01-23 03:05:23 +03:00
char * buf )
{
struct mmc_host * mmc = container_of ( dev , struct mmc_host , class_dev ) ;
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
2009-01-23 03:05:23 +03:00
2009-09-23 03:44:55 +04:00
return sprintf ( buf , " %s \n " , mmc_slot ( host ) . name ) ;
2009-01-23 03:05:23 +03:00
}
2009-09-23 03:44:59 +04:00
static DEVICE_ATTR ( slot_name , S_IRUGO , omap_hsmmc_show_slot_name , NULL ) ;
2009-01-23 03:05:23 +03:00
/*
* Configure the response type and send the cmd .
*/
static void
2009-09-23 03:44:59 +04:00
omap_hsmmc_start_command ( struct omap_hsmmc_host * host , struct mmc_command * cmd ,
2009-01-23 03:05:23 +03:00
struct mmc_data * data )
{
int cmdreg = 0 , resptype = 0 , cmdtype = 0 ;
dev_dbg ( mmc_dev ( host - > mmc ) , " %s: CMD%d, argument 0x%08x \n " ,
mmc_hostname ( host - > mmc ) , cmd - > opcode , cmd - > arg ) ;
host - > cmd = cmd ;
2010-08-12 01:17:48 +04:00
omap_hsmmc_enable_irq ( host , cmd ) ;
2009-01-23 03:05:23 +03:00
2009-01-12 17:13:08 +03:00
host - > response_busy = 0 ;
2009-01-23 03:05:23 +03:00
if ( cmd - > flags & MMC_RSP_PRESENT ) {
if ( cmd - > flags & MMC_RSP_136 )
resptype = 1 ;
2009-01-12 17:13:08 +03:00
else if ( cmd - > flags & MMC_RSP_BUSY ) {
resptype = 3 ;
host - > response_busy = 1 ;
} else
2009-01-23 03:05:23 +03:00
resptype = 2 ;
}
/*
* Unlike OMAP1 controller , the cmdtype does not seem to be based on
* ac , bc , adtc , bcr . Only commands ending an open ended transfer need
* a val of 0x3 , rest 0x0 .
*/
if ( cmd = = host - > mrq - > stop )
cmdtype = 0x3 ;
cmdreg = ( cmd - > opcode < < 24 ) | ( resptype < < 16 ) | ( cmdtype < < 22 ) ;
2012-04-09 10:38:32 +04:00
if ( ( host - > flags & AUTO_CMD12 ) & & mmc_op_multi ( cmd - > opcode ) )
cmdreg | = ACEN_ACMD12 ;
2009-01-23 03:05:23 +03:00
if ( data ) {
cmdreg | = DP_SELECT | MSBS | BCE ;
if ( data - > flags & MMC_DATA_READ )
cmdreg | = DDIR ;
else
cmdreg & = ~ ( DDIR ) ;
}
if ( host - > use_dma )
cmdreg | = DMA_EN ;
2010-05-27 01:42:06 +04:00
host - > req_in_progress = 1 ;
2009-09-23 03:44:58 +04:00
2009-01-23 03:05:23 +03:00
OMAP_HSMMC_WRITE ( host - > base , ARG , cmd - > arg ) ;
OMAP_HSMMC_WRITE ( host - > base , CMD , cmdreg ) ;
}
2008-11-14 16:22:00 +03:00
static int
2009-09-23 03:44:59 +04:00
omap_hsmmc_get_dma_dir ( struct omap_hsmmc_host * host , struct mmc_data * data )
2008-11-14 16:22:00 +03:00
{
if ( data - > flags & MMC_DATA_WRITE )
return DMA_TO_DEVICE ;
else
return DMA_FROM_DEVICE ;
}
2010-05-27 01:42:06 +04:00
static void omap_hsmmc_request_done ( struct omap_hsmmc_host * host , struct mmc_request * mrq )
{
int dma_ch ;
2012-04-09 10:38:34 +04:00
unsigned long flags ;
2010-05-27 01:42:06 +04:00
2012-04-09 10:38:34 +04:00
spin_lock_irqsave ( & host - > irq_lock , flags ) ;
2010-05-27 01:42:06 +04:00
host - > req_in_progress = 0 ;
dma_ch = host - > dma_ch ;
2012-04-09 10:38:34 +04:00
spin_unlock_irqrestore ( & host - > irq_lock , flags ) ;
2010-05-27 01:42:06 +04:00
omap_hsmmc_disable_irq ( host ) ;
/* Do not complete the request if DMA is still in progress */
if ( mrq - > data & & host - > use_dma & & dma_ch ! = - 1 )
return ;
host - > mrq = NULL ;
mmc_request_done ( host - > mmc , mrq ) ;
}
2009-01-23 03:05:23 +03:00
/*
* Notify the transfer complete to MMC core
*/
static void
2009-09-23 03:44:59 +04:00
omap_hsmmc_xfer_done ( struct omap_hsmmc_host * host , struct mmc_data * data )
2009-01-23 03:05:23 +03:00
{
2009-01-12 17:13:08 +03:00
if ( ! data ) {
struct mmc_request * mrq = host - > mrq ;
2009-09-23 03:44:57 +04:00
/* TC before CC from CMD6 - don't know why, but it happens */
if ( host - > cmd & & host - > cmd - > opcode = = 6 & &
host - > response_busy ) {
host - > response_busy = 0 ;
return ;
}
2010-05-27 01:42:06 +04:00
omap_hsmmc_request_done ( host , mrq ) ;
2009-01-12 17:13:08 +03:00
return ;
}
2009-01-23 03:05:23 +03:00
host - > data = NULL ;
if ( ! data - > error )
data - > bytes_xfered + = data - > blocks * ( data - > blksz ) ;
else
data - > bytes_xfered = 0 ;
2012-04-09 10:38:32 +04:00
if ( data - > stop & & ( ( ! ( host - > flags & AUTO_CMD12 ) ) | | data - > error ) ) {
omap_hsmmc_start_command ( host , data - > stop , NULL ) ;
} else {
if ( data - > stop )
data - > stop - > resp [ 0 ] = OMAP_HSMMC_READ ( host - > base ,
RSP76 ) ;
2010-05-27 01:42:06 +04:00
omap_hsmmc_request_done ( host , data - > mrq ) ;
2009-01-23 03:05:23 +03:00
}
}
/*
* Notify the core about command completion
*/
static void
2009-09-23 03:44:59 +04:00
omap_hsmmc_cmd_done ( struct omap_hsmmc_host * host , struct mmc_command * cmd )
2009-01-23 03:05:23 +03:00
{
host - > cmd = NULL ;
if ( cmd - > flags & MMC_RSP_PRESENT ) {
if ( cmd - > flags & MMC_RSP_136 ) {
/* response type 2 */
cmd - > resp [ 3 ] = OMAP_HSMMC_READ ( host - > base , RSP10 ) ;
cmd - > resp [ 2 ] = OMAP_HSMMC_READ ( host - > base , RSP32 ) ;
cmd - > resp [ 1 ] = OMAP_HSMMC_READ ( host - > base , RSP54 ) ;
cmd - > resp [ 0 ] = OMAP_HSMMC_READ ( host - > base , RSP76 ) ;
} else {
/* response types 1, 1b, 3, 4, 5, 6 */
cmd - > resp [ 0 ] = OMAP_HSMMC_READ ( host - > base , RSP10 ) ;
}
}
2010-05-27 01:42:06 +04:00
if ( ( host - > data = = NULL & & ! host - > response_busy ) | | cmd - > error )
omap_hsmmc_request_done ( host , cmd - > mrq ) ;
2009-01-23 03:05:23 +03:00
}
/*
* DMA clean up for command errors
*/
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_dma_cleanup ( struct omap_hsmmc_host * host , int errno )
2009-01-23 03:05:23 +03:00
{
2010-05-27 01:42:06 +04:00
int dma_ch ;
2012-04-09 10:38:34 +04:00
unsigned long flags ;
2010-05-27 01:42:06 +04:00
2008-12-05 13:31:46 +03:00
host - > data - > error = errno ;
2009-01-23 03:05:23 +03:00
2012-04-09 10:38:34 +04:00
spin_lock_irqsave ( & host - > irq_lock , flags ) ;
2010-05-27 01:42:06 +04:00
dma_ch = host - > dma_ch ;
host - > dma_ch = - 1 ;
2012-04-09 10:38:34 +04:00
spin_unlock_irqrestore ( & host - > irq_lock , flags ) ;
2010-05-27 01:42:06 +04:00
if ( host - > use_dma & & dma_ch ! = - 1 ) {
2011-06-17 22:14:21 +04:00
dma_unmap_sg ( mmc_dev ( host - > mmc ) , host - > data - > sg ,
host - > data - > sg_len ,
2009-09-23 03:44:59 +04:00
omap_hsmmc_get_dma_dir ( host , host - > data ) ) ;
2010-05-27 01:42:06 +04:00
omap_free_dma ( dma_ch ) ;
2011-11-07 20:25:11 +04:00
host - > data - > host_cookie = 0 ;
2009-01-23 03:05:23 +03:00
}
host - > data = NULL ;
}
/*
* Readable error output
*/
# ifdef CONFIG_MMC_DEBUG
2011-05-06 13:14:01 +04:00
static void omap_hsmmc_dbg_report_irq ( struct omap_hsmmc_host * host , u32 status )
2009-01-23 03:05:23 +03:00
{
/* --- means reserved bit without definition at documentation */
2009-09-23 03:44:59 +04:00
static const char * omap_hsmmc_status_bits [ ] = {
2011-05-06 13:14:01 +04:00
" CC " , " TC " , " BGE " , " --- " , " BWR " , " BRR " , " --- " , " --- " ,
" CIRQ " , " OBI " , " --- " , " --- " , " --- " , " --- " , " --- " , " ERRI " ,
" CTO " , " CCRC " , " CEB " , " CIE " , " DTO " , " DCRC " , " DEB " , " --- " ,
" ACE " , " --- " , " --- " , " --- " , " CERR " , " BADA " , " --- " , " --- "
2009-01-23 03:05:23 +03:00
} ;
char res [ 256 ] ;
char * buf = res ;
int len , i ;
len = sprintf ( buf , " MMC IRQ 0x%x : " , status ) ;
buf + = len ;
2009-09-23 03:44:59 +04:00
for ( i = 0 ; i < ARRAY_SIZE ( omap_hsmmc_status_bits ) ; i + + )
2009-01-23 03:05:23 +03:00
if ( status & ( 1 < < i ) ) {
2009-09-23 03:44:59 +04:00
len = sprintf ( buf , " %s " , omap_hsmmc_status_bits [ i ] ) ;
2009-01-23 03:05:23 +03:00
buf + = len ;
}
dev_dbg ( mmc_dev ( host - > mmc ) , " %s \n " , res ) ;
}
2011-05-06 13:14:01 +04:00
# else
static inline void omap_hsmmc_dbg_report_irq ( struct omap_hsmmc_host * host ,
u32 status )
{
}
2009-01-23 03:05:23 +03:00
# endif /* CONFIG_MMC_DEBUG */
2009-02-06 18:42:51 +03:00
/*
* MMC controller internal state machines reset
*
* Used to reset command or data internal state machines , using respectively
* SRC or SRD bit of SYSCTL register
* Can be called from interrupt context
*/
2009-09-23 03:44:59 +04:00
static inline void omap_hsmmc_reset_controller_fsm ( struct omap_hsmmc_host * host ,
unsigned long bit )
2009-02-06 18:42:51 +03:00
{
unsigned long i = 0 ;
unsigned long limit = ( loops_per_jiffy *
msecs_to_jiffies ( MMC_TIMEOUT_MS ) ) ;
OMAP_HSMMC_WRITE ( host - > base , SYSCTL ,
OMAP_HSMMC_READ ( host - > base , SYSCTL ) | bit ) ;
2010-10-02 03:35:25 +04:00
/*
* OMAP4 ES2 and greater has an updated reset logic .
* Monitor a 0 - > 1 transition first
*/
if ( mmc_slot ( host ) . features & HSMMC_HAS_UPDATED_RESET ) {
2010-11-18 06:35:32 +03:00
while ( ( ! ( OMAP_HSMMC_READ ( host - > base , SYSCTL ) & bit ) )
2010-10-02 03:35:25 +04:00
& & ( i + + < limit ) )
cpu_relax ( ) ;
}
i = 0 ;
2009-02-06 18:42:51 +03:00
while ( ( OMAP_HSMMC_READ ( host - > base , SYSCTL ) & bit ) & &
( i + + < limit ) )
cpu_relax ( ) ;
if ( OMAP_HSMMC_READ ( host - > base , SYSCTL ) & bit )
dev_err ( mmc_dev ( host - > mmc ) ,
" Timeout waiting on controller reset in %s \n " ,
__func__ ) ;
}
2009-01-23 03:05:23 +03:00
2010-05-27 01:42:06 +04:00
static void omap_hsmmc_do_irq ( struct omap_hsmmc_host * host , int status )
2009-01-23 03:05:23 +03:00
{
struct mmc_data * data ;
2010-05-27 01:42:06 +04:00
int end_cmd = 0 , end_trans = 0 ;
if ( ! host - > req_in_progress ) {
do {
OMAP_HSMMC_WRITE ( host - > base , STAT , status ) ;
/* Flush posted write */
status = OMAP_HSMMC_READ ( host - > base , STAT ) ;
} while ( status & INT_EN_MASK ) ;
return ;
2009-01-23 03:05:23 +03:00
}
data = host - > data ;
dev_dbg ( mmc_dev ( host - > mmc ) , " IRQ Status is %x \n " , status ) ;
if ( status & ERR ) {
2011-05-06 13:14:01 +04:00
omap_hsmmc_dbg_report_irq ( host , status ) ;
2009-01-23 03:05:23 +03:00
if ( ( status & CMD_TIMEOUT ) | |
( status & CMD_CRC ) ) {
if ( host - > cmd ) {
if ( status & CMD_TIMEOUT ) {
2009-09-23 03:44:59 +04:00
omap_hsmmc_reset_controller_fsm ( host ,
SRC ) ;
2009-01-23 03:05:23 +03:00
host - > cmd - > error = - ETIMEDOUT ;
} else {
host - > cmd - > error = - EILSEQ ;
}
end_cmd = 1 ;
}
2009-01-12 17:13:08 +03:00
if ( host - > data | | host - > response_busy ) {
if ( host - > data )
2009-09-23 03:44:59 +04:00
omap_hsmmc_dma_cleanup ( host ,
- ETIMEDOUT ) ;
2009-01-12 17:13:08 +03:00
host - > response_busy = 0 ;
2009-09-23 03:44:59 +04:00
omap_hsmmc_reset_controller_fsm ( host , SRD ) ;
2009-02-12 00:11:39 +03:00
}
2009-01-23 03:05:23 +03:00
}
if ( ( status & DATA_TIMEOUT ) | |
( status & DATA_CRC ) ) {
2009-01-12 17:13:08 +03:00
if ( host - > data | | host - > response_busy ) {
int err = ( status & DATA_TIMEOUT ) ?
- ETIMEDOUT : - EILSEQ ;
if ( host - > data )
2009-09-23 03:44:59 +04:00
omap_hsmmc_dma_cleanup ( host , err ) ;
2009-01-23 03:05:23 +03:00
else
2009-01-12 17:13:08 +03:00
host - > mrq - > cmd - > error = err ;
host - > response_busy = 0 ;
2009-09-23 03:44:59 +04:00
omap_hsmmc_reset_controller_fsm ( host , SRD ) ;
2009-01-23 03:05:23 +03:00
end_trans = 1 ;
}
}
if ( status & CARD_ERR ) {
dev_dbg ( mmc_dev ( host - > mmc ) ,
" Ignoring card err CMD%d \n " , host - > cmd - > opcode ) ;
if ( host - > cmd )
end_cmd = 1 ;
if ( host - > data )
end_trans = 1 ;
}
}
OMAP_HSMMC_WRITE ( host - > base , STAT , status ) ;
2009-04-08 12:18:32 +04:00
if ( end_cmd | | ( ( status & CC ) & & host - > cmd ) )
2009-09-23 03:44:59 +04:00
omap_hsmmc_cmd_done ( host , host - > cmd ) ;
2009-09-23 03:44:54 +04:00
if ( ( end_trans | | ( status & TC ) ) & & host - > mrq )
2009-09-23 03:44:59 +04:00
omap_hsmmc_xfer_done ( host , data ) ;
2010-05-27 01:42:06 +04:00
}
2009-01-23 03:05:23 +03:00
2010-05-27 01:42:06 +04:00
/*
* MMC controller IRQ handler
*/
static irqreturn_t omap_hsmmc_irq ( int irq , void * dev_id )
{
struct omap_hsmmc_host * host = dev_id ;
int status ;
status = OMAP_HSMMC_READ ( host - > base , STAT ) ;
do {
omap_hsmmc_do_irq ( host , status ) ;
/* Flush posted write */
status = OMAP_HSMMC_READ ( host - > base , STAT ) ;
} while ( status & INT_EN_MASK ) ;
2009-09-23 03:44:58 +04:00
2009-01-23 03:05:23 +03:00
return IRQ_HANDLED ;
}
2009-09-23 03:44:59 +04:00
static void set_sd_bus_power ( struct omap_hsmmc_host * host )
2009-03-12 18:08:26 +03:00
{
unsigned long i ;
OMAP_HSMMC_WRITE ( host - > base , HCTL ,
OMAP_HSMMC_READ ( host - > base , HCTL ) | SDBP ) ;
for ( i = 0 ; i < loops_per_jiffy ; i + + ) {
if ( OMAP_HSMMC_READ ( host - > base , HCTL ) & SDBP )
break ;
cpu_relax ( ) ;
}
}
2009-01-23 03:05:23 +03:00
/*
2009-02-18 01:49:01 +03:00
* Switch MMC interface voltage . . . only relevant for MMC1 .
*
* MMC2 and MMC3 use fixed 1.8 V levels , and maybe a transceiver .
* The MMC2 transceiver controls are used instead of DAT4 . . DAT7 .
* Some chips , like eMMC ones , use internal transceivers .
2009-01-23 03:05:23 +03:00
*/
2009-09-23 03:44:59 +04:00
static int omap_hsmmc_switch_opcond ( struct omap_hsmmc_host * host , int vdd )
2009-01-23 03:05:23 +03:00
{
u32 reg_val = 0 ;
int ret ;
/* Disable the clocks */
2011-07-01 20:39:35 +04:00
pm_runtime_put_sync ( host - > dev ) ;
2009-09-23 03:45:02 +04:00
if ( host - > got_dbclk )
clk_disable ( host - > dbclk ) ;
2009-01-23 03:05:23 +03:00
/* Turn the power off */
ret = mmc_slot ( host ) . set_power ( host - > dev , host - > slot_id , 0 , 0 ) ;
/* Turn the power ON with given VDD 1.8 or 3.0v */
2009-09-23 03:45:02 +04:00
if ( ! ret )
ret = mmc_slot ( host ) . set_power ( host - > dev , host - > slot_id , 1 ,
vdd ) ;
2011-07-01 20:39:35 +04:00
pm_runtime_get_sync ( host - > dev ) ;
2009-09-23 03:45:02 +04:00
if ( host - > got_dbclk )
clk_enable ( host - > dbclk ) ;
2009-01-23 03:05:23 +03:00
if ( ret ! = 0 )
goto err ;
OMAP_HSMMC_WRITE ( host - > base , HCTL ,
OMAP_HSMMC_READ ( host - > base , HCTL ) & SDVSCLR ) ;
reg_val = OMAP_HSMMC_READ ( host - > base , HCTL ) ;
2009-02-18 01:49:01 +03:00
2009-01-23 03:05:23 +03:00
/*
* If a MMC dual voltage card is detected , the set_ios fn calls
* this fn with VDD bit set for 1.8 V . Upon card removal from the
2009-09-23 03:44:59 +04:00
* slot , omap_hsmmc_set_ios sets the VDD back to 3 V on MMC_POWER_OFF .
2009-01-23 03:05:23 +03:00
*
2009-02-18 01:49:01 +03:00
* Cope with a bit of slop in the range . . . per data sheets :
* - " 1.8V " for vdds_mmc1 / vdds_mmc1a can be up to 2.45 V max ,
* but recommended values are 1.71 V to 1.89 V
* - " 3.0V " for vdds_mmc1 / vdds_mmc1a can be up to 3.5 V max ,
* but recommended values are 2.7 V to 3.3 V
*
* Board setup code shouldn ' t permit anything very out - of - range .
* TWL4030 - family VMMC1 and VSIM regulators are fine ( avoiding the
* middle range ) but VSIM can ' t power DAT4 . . DAT7 at more than 3 V .
2009-01-23 03:05:23 +03:00
*/
2009-02-18 01:49:01 +03:00
if ( ( 1 < < vdd ) < = MMC_VDD_23_24 )
2009-01-23 03:05:23 +03:00
reg_val | = SDVS18 ;
2009-02-18 01:49:01 +03:00
else
reg_val | = SDVS30 ;
2009-01-23 03:05:23 +03:00
OMAP_HSMMC_WRITE ( host - > base , HCTL , reg_val ) ;
2009-03-12 18:08:26 +03:00
set_sd_bus_power ( host ) ;
2009-01-23 03:05:23 +03:00
return 0 ;
err :
dev_dbg ( mmc_dev ( host - > mmc ) , " Unable to switch operating voltage \n " ) ;
return ret ;
}
2009-09-23 03:45:01 +04:00
/* Protect the card while the cover is open */
static void omap_hsmmc_protect_card ( struct omap_hsmmc_host * host )
{
if ( ! mmc_slot ( host ) . get_cover_state )
return ;
host - > reqs_blocked = 0 ;
if ( mmc_slot ( host ) . get_cover_state ( host - > dev , host - > slot_id ) ) {
if ( host - > protect_card ) {
2012-02-23 15:32:20 +04:00
dev_info ( host - > dev , " %s: cover is closed, "
2009-09-23 03:45:01 +04:00
" card is now accessible \n " ,
mmc_hostname ( host - > mmc ) ) ;
host - > protect_card = 0 ;
}
} else {
if ( ! host - > protect_card ) {
2012-02-23 15:32:20 +04:00
dev_info ( host - > dev , " %s: cover is open, "
2009-09-23 03:45:01 +04:00
" card is now inaccessible \n " ,
mmc_hostname ( host - > mmc ) ) ;
host - > protect_card = 1 ;
}
}
}
2009-01-23 03:05:23 +03:00
/*
2011-12-30 05:35:13 +04:00
* irq handler to notify the core about card insertion / removal
2009-01-23 03:05:23 +03:00
*/
2011-12-30 05:35:13 +04:00
static irqreturn_t omap_hsmmc_detect ( int irq , void * dev_id )
2009-01-23 03:05:23 +03:00
{
2011-12-30 05:35:13 +04:00
struct omap_hsmmc_host * host = dev_id ;
2009-02-05 01:42:03 +03:00
struct omap_mmc_slot_data * slot = & mmc_slot ( host ) ;
2009-09-23 03:44:45 +04:00
int carddetect ;
if ( host - > suspended )
2011-12-30 05:35:13 +04:00
return IRQ_HANDLED ;
2009-09-23 03:44:45 +04:00
sysfs_notify ( & host - > mmc - > class_dev . kobj , NULL , " cover_switch " ) ;
2009-02-05 01:42:03 +03:00
2009-09-23 03:44:55 +04:00
if ( slot - > card_detect )
2010-02-15 21:03:34 +03:00
carddetect = slot - > card_detect ( host - > dev , host - > slot_id ) ;
2009-09-23 03:45:01 +04:00
else {
omap_hsmmc_protect_card ( host ) ;
2009-09-23 03:44:45 +04:00
carddetect = - ENOSYS ;
2009-09-23 03:45:01 +04:00
}
2009-01-23 03:05:23 +03:00
2010-04-07 01:34:49 +04:00
if ( carddetect )
2009-01-23 03:05:23 +03:00
mmc_detect_change ( host - > mmc , ( HZ * 200 ) / 1000 ) ;
2010-04-07 01:34:49 +04:00
else
2009-01-23 03:05:23 +03:00
mmc_detect_change ( host - > mmc , ( HZ * 50 ) / 1000 ) ;
return IRQ_HANDLED ;
}
2009-09-23 03:44:59 +04:00
static int omap_hsmmc_get_dma_sync_dev ( struct omap_hsmmc_host * host ,
2008-11-14 16:22:00 +03:00
struct mmc_data * data )
{
int sync_dev ;
2009-01-03 13:36:13 +03:00
if ( data - > flags & MMC_DATA_WRITE )
sync_dev = host - > dma_line_tx ;
else
sync_dev = host - > dma_line_rx ;
2008-11-14 16:22:00 +03:00
return sync_dev ;
}
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_config_dma_params ( struct omap_hsmmc_host * host ,
2008-11-14 16:22:00 +03:00
struct mmc_data * data ,
struct scatterlist * sgl )
{
int blksz , nblk , dma_ch ;
dma_ch = host - > dma_ch ;
if ( data - > flags & MMC_DATA_WRITE ) {
omap_set_dma_dest_params ( dma_ch , 0 , OMAP_DMA_AMODE_CONSTANT ,
( host - > mapbase + OMAP_HSMMC_DATA ) , 0 , 0 ) ;
omap_set_dma_src_params ( dma_ch , 0 , OMAP_DMA_AMODE_POST_INC ,
sg_dma_address ( sgl ) , 0 , 0 ) ;
} else {
omap_set_dma_src_params ( dma_ch , 0 , OMAP_DMA_AMODE_CONSTANT ,
2009-09-23 03:44:55 +04:00
( host - > mapbase + OMAP_HSMMC_DATA ) , 0 , 0 ) ;
2008-11-14 16:22:00 +03:00
omap_set_dma_dest_params ( dma_ch , 0 , OMAP_DMA_AMODE_POST_INC ,
sg_dma_address ( sgl ) , 0 , 0 ) ;
}
blksz = host - > data - > blksz ;
nblk = sg_dma_len ( sgl ) / blksz ;
omap_set_dma_transfer_params ( dma_ch , OMAP_DMA_DATA_TYPE_S32 ,
blksz / 4 , nblk , OMAP_DMA_SYNC_FRAME ,
2009-09-23 03:44:59 +04:00
omap_hsmmc_get_dma_sync_dev ( host , data ) ,
2008-11-14 16:22:00 +03:00
! ( data - > flags & MMC_DATA_WRITE ) ) ;
omap_start_dma ( dma_ch ) ;
}
2009-01-23 03:05:23 +03:00
/*
* DMA call back function
*/
2010-05-27 01:42:06 +04:00
static void omap_hsmmc_dma_cb ( int lch , u16 ch_status , void * cb_data )
2009-01-23 03:05:23 +03:00
{
2010-05-27 01:42:06 +04:00
struct omap_hsmmc_host * host = cb_data ;
2011-05-06 13:14:11 +04:00
struct mmc_data * data ;
2010-05-27 01:42:06 +04:00
int dma_ch , req_in_progress ;
2012-04-09 10:38:34 +04:00
unsigned long flags ;
2009-01-23 03:05:23 +03:00
2010-08-11 05:01:54 +04:00
if ( ! ( ch_status & OMAP_DMA_BLOCK_IRQ ) ) {
dev_warn ( mmc_dev ( host - > mmc ) , " unexpected dma status %x \n " ,
ch_status ) ;
return ;
}
2009-01-23 03:05:23 +03:00
2012-04-09 10:38:34 +04:00
spin_lock_irqsave ( & host - > irq_lock , flags ) ;
2010-05-27 01:42:06 +04:00
if ( host - > dma_ch < 0 ) {
2012-04-09 10:38:34 +04:00
spin_unlock_irqrestore ( & host - > irq_lock , flags ) ;
2009-01-23 03:05:23 +03:00
return ;
2010-05-27 01:42:06 +04:00
}
2009-01-23 03:05:23 +03:00
2011-05-06 13:14:11 +04:00
data = host - > mrq - > data ;
2008-11-14 16:22:00 +03:00
host - > dma_sg_idx + + ;
if ( host - > dma_sg_idx < host - > dma_len ) {
/* Fire up the next transfer. */
2010-05-27 01:42:06 +04:00
omap_hsmmc_config_dma_params ( host , data ,
data - > sg + host - > dma_sg_idx ) ;
2012-04-09 10:38:34 +04:00
spin_unlock_irqrestore ( & host - > irq_lock , flags ) ;
2008-11-14 16:22:00 +03:00
return ;
}
2011-07-01 20:55:23 +04:00
if ( ! data - > host_cookie )
dma_unmap_sg ( mmc_dev ( host - > mmc ) , data - > sg , data - > sg_len ,
omap_hsmmc_get_dma_dir ( host , data ) ) ;
2010-05-27 01:42:06 +04:00
req_in_progress = host - > req_in_progress ;
dma_ch = host - > dma_ch ;
2009-01-23 03:05:23 +03:00
host - > dma_ch = - 1 ;
2012-04-09 10:38:34 +04:00
spin_unlock_irqrestore ( & host - > irq_lock , flags ) ;
2010-05-27 01:42:06 +04:00
omap_free_dma ( dma_ch ) ;
/* If DMA has finished after TC, complete the request */
if ( ! req_in_progress ) {
struct mmc_request * mrq = host - > mrq ;
host - > mrq = NULL ;
mmc_request_done ( host - > mmc , mrq ) ;
}
2009-01-23 03:05:23 +03:00
}
2011-07-01 20:55:23 +04:00
static int omap_hsmmc_pre_dma_transfer ( struct omap_hsmmc_host * host ,
struct mmc_data * data ,
struct omap_hsmmc_next * next )
{
int dma_len ;
if ( ! next & & data - > host_cookie & &
data - > host_cookie ! = host - > next_data . cookie ) {
2012-02-23 15:32:20 +04:00
dev_warn ( host - > dev , " [%s] invalid cookie: data->host_cookie %d "
2011-07-01 20:55:23 +04:00
" host->next_data.cookie %d \n " ,
__func__ , data - > host_cookie , host - > next_data . cookie ) ;
data - > host_cookie = 0 ;
}
/* Check if next job is already prepared */
if ( next | |
( ! next & & data - > host_cookie ! = host - > next_data . cookie ) ) {
dma_len = dma_map_sg ( mmc_dev ( host - > mmc ) , data - > sg ,
data - > sg_len ,
omap_hsmmc_get_dma_dir ( host , data ) ) ;
} else {
dma_len = host - > next_data . dma_len ;
host - > next_data . dma_len = 0 ;
}
if ( dma_len = = 0 )
return - EINVAL ;
if ( next ) {
next - > dma_len = dma_len ;
data - > host_cookie = + + next - > cookie < 0 ? 1 : next - > cookie ;
} else
host - > dma_len = dma_len ;
return 0 ;
}
2009-01-23 03:05:23 +03:00
/*
* Routine to configure and start DMA for the MMC card
*/
2009-09-23 03:44:59 +04:00
static int omap_hsmmc_start_dma_transfer ( struct omap_hsmmc_host * host ,
struct mmc_request * req )
2009-01-23 03:05:23 +03:00
{
2010-05-27 01:42:06 +04:00
int dma_ch = 0 , ret = 0 , i ;
2009-01-23 03:05:23 +03:00
struct mmc_data * data = req - > data ;
2008-11-14 16:22:00 +03:00
/* Sanity check: all the SG entries must be aligned by block size. */
2009-09-23 03:44:46 +04:00
for ( i = 0 ; i < data - > sg_len ; i + + ) {
2008-11-14 16:22:00 +03:00
struct scatterlist * sgl ;
sgl = data - > sg + i ;
if ( sgl - > length % data - > blksz )
return - EINVAL ;
}
if ( ( data - > blksz % 4 ) ! = 0 )
/* REVISIT: The MMC buffer increments only when MSB is written.
* Return error for blksz which is non multiple of four .
*/
return - EINVAL ;
2010-05-27 01:42:06 +04:00
BUG_ON ( host - > dma_ch ! = - 1 ) ;
2009-01-23 03:05:23 +03:00
2009-09-23 03:44:59 +04:00
ret = omap_request_dma ( omap_hsmmc_get_dma_sync_dev ( host , data ) ,
" MMC/SD " , omap_hsmmc_dma_cb , host , & dma_ch ) ;
2009-01-23 03:05:23 +03:00
if ( ret ! = 0 ) {
2008-11-14 16:22:00 +03:00
dev_err ( mmc_dev ( host - > mmc ) ,
2009-01-23 03:05:23 +03:00
" %s: omap_request_dma() failed with %d \n " ,
mmc_hostname ( host - > mmc ) , ret ) ;
return ret ;
}
2011-07-01 20:55:23 +04:00
ret = omap_hsmmc_pre_dma_transfer ( host , data , NULL ) ;
if ( ret )
return ret ;
2009-01-23 03:05:23 +03:00
host - > dma_ch = dma_ch ;
2008-11-14 16:22:00 +03:00
host - > dma_sg_idx = 0 ;
2009-01-23 03:05:23 +03:00
2009-09-23 03:44:59 +04:00
omap_hsmmc_config_dma_params ( host , data , data - > sg ) ;
2009-01-23 03:05:23 +03:00
return 0 ;
}
2009-09-23 03:44:59 +04:00
static void set_data_timeout ( struct omap_hsmmc_host * host ,
2009-09-23 03:45:03 +04:00
unsigned int timeout_ns ,
unsigned int timeout_clks )
2009-01-23 03:05:23 +03:00
{
unsigned int timeout , cycle_ns ;
uint32_t reg , clkd , dto = 0 ;
reg = OMAP_HSMMC_READ ( host - > base , SYSCTL ) ;
clkd = ( reg & CLKD_MASK ) > > CLKD_SHIFT ;
if ( clkd = = 0 )
clkd = 1 ;
cycle_ns = 1000000000 / ( clk_get_rate ( host - > fclk ) / clkd ) ;
2009-09-23 03:45:03 +04:00
timeout = timeout_ns / cycle_ns ;
timeout + = timeout_clks ;
2009-01-23 03:05:23 +03:00
if ( timeout ) {
while ( ( timeout & 0x80000000 ) = = 0 ) {
dto + = 1 ;
timeout < < = 1 ;
}
dto = 31 - dto ;
timeout < < = 1 ;
if ( timeout & & dto )
dto + = 1 ;
if ( dto > = 13 )
dto - = 13 ;
else
dto = 0 ;
if ( dto > 14 )
dto = 14 ;
}
reg & = ~ DTO_MASK ;
reg | = dto < < DTO_SHIFT ;
OMAP_HSMMC_WRITE ( host - > base , SYSCTL , reg ) ;
}
/*
* Configure block length for MMC / SD cards and initiate the transfer .
*/
static int
2009-09-23 03:44:59 +04:00
omap_hsmmc_prepare_data ( struct omap_hsmmc_host * host , struct mmc_request * req )
2009-01-23 03:05:23 +03:00
{
int ret ;
host - > data = req - > data ;
if ( req - > data = = NULL ) {
OMAP_HSMMC_WRITE ( host - > base , BLK , 0 ) ;
2009-09-23 03:45:03 +04:00
/*
* Set an arbitrary 100 ms data timeout for commands with
* busy signal .
*/
if ( req - > cmd - > flags & MMC_RSP_BUSY )
set_data_timeout ( host , 100000000U , 0 ) ;
2009-01-23 03:05:23 +03:00
return 0 ;
}
OMAP_HSMMC_WRITE ( host - > base , BLK , ( req - > data - > blksz )
| ( req - > data - > blocks < < 16 ) ) ;
2009-09-23 03:45:03 +04:00
set_data_timeout ( host , req - > data - > timeout_ns , req - > data - > timeout_clks ) ;
2009-01-23 03:05:23 +03:00
if ( host - > use_dma ) {
2009-09-23 03:44:59 +04:00
ret = omap_hsmmc_start_dma_transfer ( host , req ) ;
2009-01-23 03:05:23 +03:00
if ( ret ! = 0 ) {
dev_dbg ( mmc_dev ( host - > mmc ) , " MMC start dma failure \n " ) ;
return ret ;
}
}
return 0 ;
}
2011-07-01 20:55:23 +04:00
static void omap_hsmmc_post_req ( struct mmc_host * mmc , struct mmc_request * mrq ,
int err )
{
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
struct mmc_data * data = mrq - > data ;
if ( host - > use_dma ) {
2011-11-07 20:25:11 +04:00
if ( data - > host_cookie )
dma_unmap_sg ( mmc_dev ( host - > mmc ) , data - > sg ,
data - > sg_len ,
omap_hsmmc_get_dma_dir ( host , data ) ) ;
2011-07-01 20:55:23 +04:00
data - > host_cookie = 0 ;
}
}
static void omap_hsmmc_pre_req ( struct mmc_host * mmc , struct mmc_request * mrq ,
bool is_first_req )
{
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
if ( mrq - > data - > host_cookie ) {
mrq - > data - > host_cookie = 0 ;
return ;
}
if ( host - > use_dma )
if ( omap_hsmmc_pre_dma_transfer ( host , mrq - > data ,
& host - > next_data ) )
mrq - > data - > host_cookie = 0 ;
}
2009-01-23 03:05:23 +03:00
/*
* Request function . for read / write operation
*/
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_request ( struct mmc_host * mmc , struct mmc_request * req )
2009-01-23 03:05:23 +03:00
{
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
2009-09-23 03:44:46 +04:00
int err ;
2009-01-23 03:05:23 +03:00
2010-05-27 01:42:06 +04:00
BUG_ON ( host - > req_in_progress ) ;
BUG_ON ( host - > dma_ch ! = - 1 ) ;
if ( host - > protect_card ) {
if ( host - > reqs_blocked < 3 ) {
/*
* Ensure the controller is left in a consistent
* state by resetting the command and data state
* machines .
*/
omap_hsmmc_reset_controller_fsm ( host , SRD ) ;
omap_hsmmc_reset_controller_fsm ( host , SRC ) ;
host - > reqs_blocked + = 1 ;
}
req - > cmd - > error = - EBADF ;
if ( req - > data )
req - > data - > error = - EBADF ;
req - > cmd - > retries = 0 ;
mmc_request_done ( mmc , req ) ;
return ;
} else if ( host - > reqs_blocked )
host - > reqs_blocked = 0 ;
2009-01-23 03:05:23 +03:00
WARN_ON ( host - > mrq ! = NULL ) ;
host - > mrq = req ;
2009-09-23 03:44:59 +04:00
err = omap_hsmmc_prepare_data ( host , req ) ;
2009-09-23 03:44:46 +04:00
if ( err ) {
req - > cmd - > error = err ;
if ( req - > data )
req - > data - > error = err ;
host - > mrq = NULL ;
mmc_request_done ( mmc , req ) ;
return ;
}
2009-09-23 03:44:59 +04:00
omap_hsmmc_start_command ( host , req - > cmd , req - > data ) ;
2009-01-23 03:05:23 +03:00
}
/* Routine to configure clock values. Exposed API to core */
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_set_ios ( struct mmc_host * mmc , struct mmc_ios * ios )
2009-01-23 03:05:23 +03:00
{
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
2009-09-23 03:44:42 +04:00
int do_send_init_stream = 0 ;
2009-01-23 03:05:23 +03:00
2011-07-01 20:39:35 +04:00
pm_runtime_get_sync ( host - > dev ) ;
2009-09-23 03:44:39 +04:00
2009-09-23 03:44:42 +04:00
if ( ios - > power_mode ! = host - > power_mode ) {
switch ( ios - > power_mode ) {
case MMC_POWER_OFF :
mmc_slot ( host ) . set_power ( host - > dev , host - > slot_id ,
0 , 0 ) ;
2009-09-23 03:44:51 +04:00
host - > vdd = 0 ;
2009-09-23 03:44:42 +04:00
break ;
case MMC_POWER_UP :
mmc_slot ( host ) . set_power ( host - > dev , host - > slot_id ,
1 , ios - > vdd ) ;
2009-09-23 03:44:51 +04:00
host - > vdd = ios - > vdd ;
2009-09-23 03:44:42 +04:00
break ;
case MMC_POWER_ON :
do_send_init_stream = 1 ;
break ;
}
host - > power_mode = ios - > power_mode ;
2009-01-23 03:05:23 +03:00
}
2009-09-23 03:44:49 +04:00
/* FIXME: set registers based only on changes to ios */
2011-07-13 19:31:15 +04:00
omap_hsmmc_set_bus_width ( host ) ;
2009-01-23 03:05:23 +03:00
2011-02-28 18:18:04 +03:00
if ( host - > pdata - > controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT ) {
2009-02-18 01:49:01 +03:00
/* Only MMC1 can interface at 3V without some flavor
* of external transceiver ; but they all handle 1.8 V .
*/
2009-01-23 03:05:23 +03:00
if ( ( OMAP_HSMMC_READ ( host - > base , HCTL ) & SDVSDET ) & &
2012-03-12 19:02:38 +04:00
( ios - > vdd = = DUAL_VOLT_OCR_BIT ) & &
/*
* With pbias cell programming missing , this
* can ' t be allowed when booting with device
* tree .
*/
2012-04-11 14:03:13 +04:00
! host - > dev - > of_node ) {
2009-01-23 03:05:23 +03:00
/*
* The mmc_select_voltage fn of the core does
* not seem to set the power_mode to
* MMC_POWER_UP upon recalculating the voltage .
* vdd 1.8 v .
*/
2009-09-23 03:44:59 +04:00
if ( omap_hsmmc_switch_opcond ( host , ios - > vdd ) ! = 0 )
dev_dbg ( mmc_dev ( host - > mmc ) ,
2009-01-23 03:05:23 +03:00
" Switch operation failed \n " ) ;
}
}
2011-05-06 13:14:06 +04:00
omap_hsmmc_set_clock ( host ) ;
2009-01-23 03:05:23 +03:00
2009-09-23 03:44:42 +04:00
if ( do_send_init_stream )
2009-01-23 03:05:23 +03:00
send_init_stream ( host ) ;
2011-07-13 19:31:15 +04:00
omap_hsmmc_set_bus_mode ( host ) ;
2009-09-23 03:44:39 +04:00
2011-07-01 20:39:35 +04:00
pm_runtime_put_autosuspend ( host - > dev ) ;
2009-01-23 03:05:23 +03:00
}
static int omap_hsmmc_get_cd ( struct mmc_host * mmc )
{
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
2009-01-23 03:05:23 +03:00
2009-09-23 03:44:55 +04:00
if ( ! mmc_slot ( host ) . card_detect )
2009-01-23 03:05:23 +03:00
return - ENOSYS ;
2010-02-15 21:03:34 +03:00
return mmc_slot ( host ) . card_detect ( host - > dev , host - > slot_id ) ;
2009-01-23 03:05:23 +03:00
}
static int omap_hsmmc_get_ro ( struct mmc_host * mmc )
{
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
2009-01-23 03:05:23 +03:00
2009-09-23 03:44:55 +04:00
if ( ! mmc_slot ( host ) . get_ro )
2009-01-23 03:05:23 +03:00
return - ENOSYS ;
2009-09-23 03:44:55 +04:00
return mmc_slot ( host ) . get_ro ( host - > dev , 0 ) ;
2009-01-23 03:05:23 +03:00
}
2010-08-11 05:01:52 +04:00
static void omap_hsmmc_init_card ( struct mmc_host * mmc , struct mmc_card * card )
{
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
if ( mmc_slot ( host ) . init_card )
mmc_slot ( host ) . init_card ( card ) ;
}
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_conf_bus_power ( struct omap_hsmmc_host * host )
2009-02-20 15:10:08 +03:00
{
u32 hctl , capa , value ;
/* Only MMC1 supports 3.0V */
2011-02-28 18:18:04 +03:00
if ( host - > pdata - > controller_flags & OMAP_HSMMC_SUPPORTS_DUAL_VOLT ) {
2009-02-20 15:10:08 +03:00
hctl = SDVS30 ;
capa = VS30 | VS18 ;
} else {
hctl = SDVS18 ;
capa = VS18 ;
}
value = OMAP_HSMMC_READ ( host - > base , HCTL ) & ~ SDVS_MASK ;
OMAP_HSMMC_WRITE ( host - > base , HCTL , value | hctl ) ;
value = OMAP_HSMMC_READ ( host - > base , CAPA ) ;
OMAP_HSMMC_WRITE ( host - > base , CAPA , value | capa ) ;
/* Set the controller to AUTO IDLE mode */
value = OMAP_HSMMC_READ ( host - > base , SYSCONFIG ) ;
OMAP_HSMMC_WRITE ( host - > base , SYSCONFIG , value | AUTOIDLE ) ;
/* Set SD bus power bit */
2009-03-12 18:08:26 +03:00
set_sd_bus_power ( host ) ;
2009-02-20 15:10:08 +03:00
}
2009-09-23 03:44:59 +04:00
static int omap_hsmmc_enable_fclk ( struct mmc_host * mmc )
2009-09-23 03:44:49 +04:00
{
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
2009-09-23 03:44:49 +04:00
2011-07-01 20:39:35 +04:00
pm_runtime_get_sync ( host - > dev ) ;
2009-09-23 03:44:49 +04:00
return 0 ;
}
2012-02-29 11:17:21 +04:00
static int omap_hsmmc_disable_fclk ( struct mmc_host * mmc )
2009-09-23 03:44:49 +04:00
{
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
2009-09-23 03:44:49 +04:00
2011-07-01 20:39:35 +04:00
pm_runtime_mark_last_busy ( host - > dev ) ;
pm_runtime_put_autosuspend ( host - > dev ) ;
2009-09-23 03:44:49 +04:00
return 0 ;
}
2009-09-23 03:44:59 +04:00
static const struct mmc_host_ops omap_hsmmc_ops = {
. enable = omap_hsmmc_enable_fclk ,
. disable = omap_hsmmc_disable_fclk ,
2011-07-01 20:55:23 +04:00
. post_req = omap_hsmmc_post_req ,
. pre_req = omap_hsmmc_pre_req ,
2009-09-23 03:44:59 +04:00
. request = omap_hsmmc_request ,
. set_ios = omap_hsmmc_set_ios ,
2009-09-23 03:44:49 +04:00
. get_cd = omap_hsmmc_get_cd ,
. get_ro = omap_hsmmc_get_ro ,
2010-08-11 05:01:52 +04:00
. init_card = omap_hsmmc_init_card ,
2009-09-23 03:44:49 +04:00
/* NYET -- enable_sdio_irq */
} ;
2009-09-23 03:44:38 +04:00
# ifdef CONFIG_DEBUG_FS
2009-09-23 03:44:59 +04:00
static int omap_hsmmc_regs_show ( struct seq_file * s , void * data )
2009-09-23 03:44:38 +04:00
{
struct mmc_host * mmc = s - > private ;
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = mmc_priv ( mmc ) ;
2009-09-23 03:44:43 +04:00
int context_loss = 0 ;
2009-09-23 03:44:59 +04:00
if ( host - > pdata - > get_context_loss_count )
context_loss = host - > pdata - > get_context_loss_count ( host - > dev ) ;
2009-09-23 03:44:38 +04:00
2012-02-29 11:17:21 +04:00
seq_printf ( s , " mmc%d: \n ctx_loss: \t %d:%d \n \n regs: \n " ,
mmc - > index , host - > context_loss , context_loss ) ;
2009-09-23 03:44:39 +04:00
2011-07-01 20:39:34 +04:00
if ( host - > suspended ) {
2009-09-23 03:44:49 +04:00
seq_printf ( s , " host suspended, can't read registers \n " ) ;
return 0 ;
}
2011-07-01 20:39:35 +04:00
pm_runtime_get_sync ( host - > dev ) ;
2009-09-23 03:44:38 +04:00
seq_printf ( s , " SYSCONFIG: \t 0x%08x \n " ,
OMAP_HSMMC_READ ( host - > base , SYSCONFIG ) ) ;
seq_printf ( s , " CON: \t \t 0x%08x \n " ,
OMAP_HSMMC_READ ( host - > base , CON ) ) ;
seq_printf ( s , " HCTL: \t \t 0x%08x \n " ,
OMAP_HSMMC_READ ( host - > base , HCTL ) ) ;
seq_printf ( s , " SYSCTL: \t \t 0x%08x \n " ,
OMAP_HSMMC_READ ( host - > base , SYSCTL ) ) ;
seq_printf ( s , " IE: \t \t 0x%08x \n " ,
OMAP_HSMMC_READ ( host - > base , IE ) ) ;
seq_printf ( s , " ISE: \t \t 0x%08x \n " ,
OMAP_HSMMC_READ ( host - > base , ISE ) ) ;
seq_printf ( s , " CAPA: \t \t 0x%08x \n " ,
OMAP_HSMMC_READ ( host - > base , CAPA ) ) ;
2009-09-23 03:44:39 +04:00
2011-07-01 20:39:35 +04:00
pm_runtime_mark_last_busy ( host - > dev ) ;
pm_runtime_put_autosuspend ( host - > dev ) ;
2009-09-23 03:44:49 +04:00
2009-09-23 03:44:38 +04:00
return 0 ;
}
2009-09-23 03:44:59 +04:00
static int omap_hsmmc_regs_open ( struct inode * inode , struct file * file )
2009-09-23 03:44:38 +04:00
{
2009-09-23 03:44:59 +04:00
return single_open ( file , omap_hsmmc_regs_show , inode - > i_private ) ;
2009-09-23 03:44:38 +04:00
}
static const struct file_operations mmc_regs_fops = {
2009-09-23 03:44:59 +04:00
. open = omap_hsmmc_regs_open ,
2009-09-23 03:44:38 +04:00
. read = seq_read ,
. llseek = seq_lseek ,
. release = single_release ,
} ;
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_debugfs ( struct mmc_host * mmc )
2009-09-23 03:44:38 +04:00
{
if ( mmc - > debugfs_root )
debugfs_create_file ( " regs " , S_IRUSR , mmc - > debugfs_root ,
mmc , & mmc_regs_fops ) ;
}
# else
2009-09-23 03:44:59 +04:00
static void omap_hsmmc_debugfs ( struct mmc_host * mmc )
2009-09-23 03:44:38 +04:00
{
}
# endif
2012-03-12 19:02:37 +04:00
# ifdef CONFIG_OF
static u16 omap4_reg_offset = 0x100 ;
static const struct of_device_id omap_mmc_of_match [ ] = {
{
. compatible = " ti,omap2-hsmmc " ,
} ,
{
. compatible = " ti,omap3-hsmmc " ,
} ,
{
. compatible = " ti,omap4-hsmmc " ,
. data = & omap4_reg_offset ,
} ,
{ } ,
2012-04-10 17:57:36 +04:00
} ;
2012-03-12 19:02:37 +04:00
MODULE_DEVICE_TABLE ( of , omap_mmc_of_match ) ;
static struct omap_mmc_platform_data * of_get_hsmmc_pdata ( struct device * dev )
{
struct omap_mmc_platform_data * pdata ;
struct device_node * np = dev - > of_node ;
u32 bus_width ;
pdata = devm_kzalloc ( dev , sizeof ( * pdata ) , GFP_KERNEL ) ;
if ( ! pdata )
return NULL ; /* out of memory */
if ( of_find_property ( np , " ti,dual-volt " , NULL ) )
pdata - > controller_flags | = OMAP_HSMMC_SUPPORTS_DUAL_VOLT ;
/* This driver only supports 1 slot */
pdata - > nr_slots = 1 ;
pdata - > slots [ 0 ] . switch_pin = of_get_named_gpio ( np , " cd-gpios " , 0 ) ;
pdata - > slots [ 0 ] . gpio_wp = of_get_named_gpio ( np , " wp-gpios " , 0 ) ;
if ( of_find_property ( np , " ti,non-removable " , NULL ) ) {
pdata - > slots [ 0 ] . nonremovable = true ;
pdata - > slots [ 0 ] . no_regulator_off_init = true ;
}
of_property_read_u32 ( np , " ti,bus-width " , & bus_width ) ;
if ( bus_width = = 4 )
pdata - > slots [ 0 ] . caps | = MMC_CAP_4_BIT_DATA ;
else if ( bus_width = = 8 )
pdata - > slots [ 0 ] . caps | = MMC_CAP_8_BIT_DATA ;
if ( of_find_property ( np , " ti,needs-special-reset " , NULL ) )
pdata - > slots [ 0 ] . features | = HSMMC_HAS_UPDATED_RESET ;
return pdata ;
}
# else
static inline struct omap_mmc_platform_data
* of_get_hsmmc_pdata ( struct device * dev )
{
return NULL ;
}
# endif
2012-03-14 13:18:28 +04:00
static int __devinit omap_hsmmc_probe ( struct platform_device * pdev )
2009-01-23 03:05:23 +03:00
{
struct omap_mmc_platform_data * pdata = pdev - > dev . platform_data ;
struct mmc_host * mmc ;
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = NULL ;
2009-01-23 03:05:23 +03:00
struct resource * res ;
2010-02-15 21:03:34 +03:00
int ret , irq ;
2012-03-12 19:02:37 +04:00
const struct of_device_id * match ;
match = of_match_device ( of_match_ptr ( omap_mmc_of_match ) , & pdev - > dev ) ;
if ( match ) {
pdata = of_get_hsmmc_pdata ( & pdev - > dev ) ;
if ( match - > data ) {
u16 * offsetp = match - > data ;
pdata - > reg_offset = * offsetp ;
}
}
2009-01-23 03:05:23 +03:00
if ( pdata = = NULL ) {
dev_err ( & pdev - > dev , " Platform Data is missing \n " ) ;
return - ENXIO ;
}
if ( pdata - > nr_slots = = 0 ) {
dev_err ( & pdev - > dev , " No Slots \n " ) ;
return - ENXIO ;
}
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
irq = platform_get_irq ( pdev , 0 ) ;
if ( res = = NULL | | irq < 0 )
return - ENXIO ;
2011-03-23 02:34:42 +03:00
res = request_mem_region ( res - > start , resource_size ( res ) , pdev - > name ) ;
2009-01-23 03:05:23 +03:00
if ( res = = NULL )
return - EBUSY ;
2010-02-15 21:03:34 +03:00
ret = omap_hsmmc_gpio_init ( pdata ) ;
if ( ret )
goto err ;
2009-09-23 03:44:59 +04:00
mmc = mmc_alloc_host ( sizeof ( struct omap_hsmmc_host ) , & pdev - > dev ) ;
2009-01-23 03:05:23 +03:00
if ( ! mmc ) {
ret = - ENOMEM ;
2010-02-15 21:03:34 +03:00
goto err_alloc ;
2009-01-23 03:05:23 +03:00
}
host = mmc_priv ( mmc ) ;
host - > mmc = mmc ;
host - > pdata = pdata ;
host - > dev = & pdev - > dev ;
host - > use_dma = 1 ;
host - > dev - > dma_mask = & pdata - > dma_mask ;
host - > dma_ch = - 1 ;
host - > irq = irq ;
host - > slot_id = 0 ;
2012-04-02 10:56:47 +04:00
host - > mapbase = res - > start + pdata - > reg_offset ;
2009-01-23 03:05:23 +03:00
host - > base = ioremap ( host - > mapbase , SZ_4K ) ;
2010-02-15 21:03:34 +03:00
host - > power_mode = MMC_POWER_OFF ;
2012-04-09 10:38:32 +04:00
host - > flags = AUTO_CMD12 ;
2011-07-01 20:55:23 +04:00
host - > next_data . cookie = 1 ;
2009-01-23 03:05:23 +03:00
platform_set_drvdata ( pdev , host ) ;
2011-07-01 20:39:34 +04:00
mmc - > ops = & omap_hsmmc_ops ;
2009-09-23 03:44:49 +04:00
2010-02-15 21:03:34 +03:00
/*
* If regulator_disable can only put vcc_aux to sleep then there is
* no off state .
*/
if ( mmc_slot ( host ) . vcc_aux_disable_is_sleep )
mmc_slot ( host ) . no_off = 1 ;
2012-02-19 16:20:33 +04:00
mmc - > f_min = OMAP_MMC_MIN_CLOCK ;
if ( pdata - > max_freq > 0 )
mmc - > f_max = pdata - > max_freq ;
else
mmc - > f_max = OMAP_MMC_MAX_CLOCK ;
2009-01-23 03:05:23 +03:00
2009-09-23 03:44:58 +04:00
spin_lock_init ( & host - > irq_lock ) ;
2009-01-23 03:05:23 +03:00
2009-01-28 13:22:50 +03:00
host - > fclk = clk_get ( & pdev - > dev , " fck " ) ;
2009-01-23 03:05:23 +03:00
if ( IS_ERR ( host - > fclk ) ) {
ret = PTR_ERR ( host - > fclk ) ;
host - > fclk = NULL ;
goto err1 ;
}
2011-10-07 00:50:35 +04:00
if ( host - > pdata - > controller_flags & OMAP_HSMMC_BROKEN_MULTIBLOCK_READ ) {
dev_info ( & pdev - > dev , " multiblock reads disabled due to 35xx erratum 2.1.1.128; MMC read performance may suffer \n " ) ;
mmc - > caps2 | = MMC_CAP2_NO_MULTI_READ ;
}
2009-09-23 03:44:49 +04:00
2011-07-01 20:39:35 +04:00
pm_runtime_enable ( host - > dev ) ;
pm_runtime_get_sync ( host - > dev ) ;
pm_runtime_set_autosuspend_delay ( host - > dev , MMC_AUTOSUSPEND_DELAY ) ;
pm_runtime_use_autosuspend ( host - > dev ) ;
2009-01-23 03:05:23 +03:00
2012-02-24 19:44:34 +04:00
omap_hsmmc_context_save ( host ) ;
2009-09-23 03:45:02 +04:00
if ( cpu_is_omap2430 ( ) ) {
host - > dbclk = clk_get ( & pdev - > dev , " mmchsdb_fck " ) ;
/*
* MMC can still work without debounce clock .
*/
if ( IS_ERR ( host - > dbclk ) )
dev_warn ( mmc_dev ( host - > mmc ) ,
" Failed to get debounce clock \n " ) ;
2009-01-23 03:05:23 +03:00
else
2009-09-23 03:45:02 +04:00
host - > got_dbclk = 1 ;
if ( host - > got_dbclk )
if ( clk_enable ( host - > dbclk ) ! = 0 )
dev_dbg ( mmc_dev ( host - > mmc ) , " Enabling debounce "
" clk failed \n " ) ;
}
2009-01-23 03:05:23 +03:00
2008-11-14 16:22:00 +03:00
/* Since we do only SG emulation, we can have as many segs
* as we want . */
2010-09-10 09:33:59 +04:00
mmc - > max_segs = 1024 ;
2008-11-14 16:22:00 +03:00
2009-01-23 03:05:23 +03:00
mmc - > max_blk_size = 512 ; /* Block Length at max can be 1024 */
mmc - > max_blk_count = 0xFFFF ; /* No. of Blocks is 16 bits */
mmc - > max_req_size = mmc - > max_blk_size * mmc - > max_blk_count ;
mmc - > max_seg_size = mmc - > max_req_size ;
2009-09-23 03:44:53 +04:00
mmc - > caps | = MMC_CAP_MMC_HIGHSPEED | MMC_CAP_SD_HIGHSPEED |
2010-08-12 01:17:48 +04:00
MMC_CAP_WAIT_WHILE_BUSY | MMC_CAP_ERASE ;
2009-01-23 03:05:23 +03:00
2010-09-15 18:49:23 +04:00
mmc - > caps | = mmc_slot ( host ) . caps ;
if ( mmc - > caps & MMC_CAP_8_BIT_DATA )
2009-01-23 03:05:23 +03:00
mmc - > caps | = MMC_CAP_4_BIT_DATA ;
2009-09-23 03:44:55 +04:00
if ( mmc_slot ( host ) . nonremovable )
2009-09-23 03:44:48 +04:00
mmc - > caps | = MMC_CAP_NONREMOVABLE ;
2011-11-22 18:02:18 +04:00
mmc - > pm_caps = mmc_slot ( host ) . pm_caps ;
2009-09-23 03:44:59 +04:00
omap_hsmmc_conf_bus_power ( host ) ;
2009-01-23 03:05:23 +03:00
2012-03-07 18:55:30 +04:00
res = platform_get_resource_byname ( pdev , IORESOURCE_DMA , " tx " ) ;
if ( ! res ) {
dev_err ( mmc_dev ( host - > mmc ) , " cannot get DMA TX channel \n " ) ;
goto err_irq ;
}
host - > dma_line_tx = res - > start ;
res = platform_get_resource_byname ( pdev , IORESOURCE_DMA , " rx " ) ;
if ( ! res ) {
dev_err ( mmc_dev ( host - > mmc ) , " cannot get DMA RX channel \n " ) ;
2009-01-03 13:36:13 +03:00
goto err_irq ;
}
2012-03-07 18:55:30 +04:00
host - > dma_line_rx = res - > start ;
2009-01-23 03:05:23 +03:00
/* Request IRQ for MMC operations */
2011-09-22 12:59:04 +04:00
ret = request_irq ( host - > irq , omap_hsmmc_irq , 0 ,
2009-01-23 03:05:23 +03:00
mmc_hostname ( mmc ) , host ) ;
if ( ret ) {
dev_dbg ( mmc_dev ( host - > mmc ) , " Unable to grab HSMMC IRQ \n " ) ;
goto err_irq ;
}
if ( pdata - > init ! = NULL ) {
if ( pdata - > init ( & pdev - > dev ) ! = 0 ) {
2009-09-23 03:44:59 +04:00
dev_dbg ( mmc_dev ( host - > mmc ) ,
" Unable to configure MMC IRQs \n " ) ;
2009-01-23 03:05:23 +03:00
goto err_irq_cd_init ;
}
}
2010-02-15 21:03:34 +03:00
2010-02-15 21:03:35 +03:00
if ( omap_hsmmc_have_reg ( ) & & ! mmc_slot ( host ) . set_power ) {
2010-02-15 21:03:34 +03:00
ret = omap_hsmmc_reg_get ( host ) ;
if ( ret )
goto err_reg ;
host - > use_reg = 1 ;
}
2009-05-29 01:04:03 +04:00
mmc - > ocr_avail = mmc_slot ( host ) . ocr_mask ;
2009-01-23 03:05:23 +03:00
/* Request IRQ for card detect */
2009-01-26 14:17:25 +03:00
if ( ( mmc_slot ( host ) . card_detect_irq ) ) {
2011-12-30 05:35:13 +04:00
ret = request_threaded_irq ( mmc_slot ( host ) . card_detect_irq ,
NULL ,
omap_hsmmc_detect ,
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING ,
mmc_hostname ( mmc ) , host ) ;
2009-01-23 03:05:23 +03:00
if ( ret ) {
dev_dbg ( mmc_dev ( host - > mmc ) ,
" Unable to grab MMC CD IRQ \n " ) ;
goto err_irq_cd ;
}
2010-09-24 21:13:20 +04:00
pdata - > suspend = omap_hsmmc_suspend_cdirq ;
pdata - > resume = omap_hsmmc_resume_cdirq ;
2009-01-23 03:05:23 +03:00
}
2010-05-27 01:42:06 +04:00
omap_hsmmc_disable_irq ( host ) ;
2009-01-23 03:05:23 +03:00
2009-09-23 03:45:01 +04:00
omap_hsmmc_protect_card ( host ) ;
2009-01-23 03:05:23 +03:00
mmc_add_host ( mmc ) ;
2009-09-23 03:44:55 +04:00
if ( mmc_slot ( host ) . name ! = NULL ) {
2009-01-23 03:05:23 +03:00
ret = device_create_file ( & mmc - > class_dev , & dev_attr_slot_name ) ;
if ( ret < 0 )
goto err_slot_name ;
}
2009-09-23 03:44:55 +04:00
if ( mmc_slot ( host ) . card_detect_irq & & mmc_slot ( host ) . get_cover_state ) {
2009-01-23 03:05:23 +03:00
ret = device_create_file ( & mmc - > class_dev ,
& dev_attr_cover_switch ) ;
if ( ret < 0 )
2010-02-15 21:03:34 +03:00
goto err_slot_name ;
2009-01-23 03:05:23 +03:00
}
2009-09-23 03:44:59 +04:00
omap_hsmmc_debugfs ( mmc ) ;
2011-07-01 20:39:35 +04:00
pm_runtime_mark_last_busy ( host - > dev ) ;
pm_runtime_put_autosuspend ( host - > dev ) ;
2009-09-23 03:44:38 +04:00
2009-01-23 03:05:23 +03:00
return 0 ;
err_slot_name :
mmc_remove_host ( mmc ) ;
free_irq ( mmc_slot ( host ) . card_detect_irq , host ) ;
2010-02-15 21:03:34 +03:00
err_irq_cd :
if ( host - > use_reg )
omap_hsmmc_reg_put ( host ) ;
err_reg :
if ( host - > pdata - > cleanup )
host - > pdata - > cleanup ( & pdev - > dev ) ;
2009-01-23 03:05:23 +03:00
err_irq_cd_init :
free_irq ( host - > irq , host ) ;
err_irq :
2012-02-24 19:44:33 +04:00
pm_runtime_put_sync ( host - > dev ) ;
2012-03-09 08:41:35 +04:00
pm_runtime_disable ( host - > dev ) ;
2009-01-23 03:05:23 +03:00
clk_put ( host - > fclk ) ;
2009-09-23 03:45:02 +04:00
if ( host - > got_dbclk ) {
2009-01-23 03:05:23 +03:00
clk_disable ( host - > dbclk ) ;
clk_put ( host - > dbclk ) ;
}
err1 :
iounmap ( host - > base ) ;
2010-02-15 21:03:34 +03:00
platform_set_drvdata ( pdev , NULL ) ;
mmc_free_host ( mmc ) ;
err_alloc :
omap_hsmmc_gpio_free ( pdata ) ;
2009-01-23 03:05:23 +03:00
err :
2011-03-23 02:34:42 +03:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2009-01-23 03:05:23 +03:00
return ret ;
}
2012-03-14 13:18:28 +04:00
static int __devexit omap_hsmmc_remove ( struct platform_device * pdev )
2009-01-23 03:05:23 +03:00
{
2009-09-23 03:44:59 +04:00
struct omap_hsmmc_host * host = platform_get_drvdata ( pdev ) ;
2009-01-23 03:05:23 +03:00
struct resource * res ;
2012-03-14 13:18:27 +04:00
pm_runtime_get_sync ( host - > dev ) ;
mmc_remove_host ( host - > mmc ) ;
if ( host - > use_reg )
omap_hsmmc_reg_put ( host ) ;
if ( host - > pdata - > cleanup )
host - > pdata - > cleanup ( & pdev - > dev ) ;
free_irq ( host - > irq , host ) ;
if ( mmc_slot ( host ) . card_detect_irq )
free_irq ( mmc_slot ( host ) . card_detect_irq , host ) ;
2009-01-23 03:05:23 +03:00
2012-03-14 13:18:27 +04:00
pm_runtime_put_sync ( host - > dev ) ;
pm_runtime_disable ( host - > dev ) ;
clk_put ( host - > fclk ) ;
if ( host - > got_dbclk ) {
clk_disable ( host - > dbclk ) ;
clk_put ( host - > dbclk ) ;
2009-01-23 03:05:23 +03:00
}
2012-03-14 13:18:27 +04:00
mmc_free_host ( host - > mmc ) ;
iounmap ( host - > base ) ;
omap_hsmmc_gpio_free ( pdev - > dev . platform_data ) ;
2009-01-23 03:05:23 +03:00
res = platform_get_resource ( pdev , IORESOURCE_MEM , 0 ) ;
if ( res )
2011-03-23 02:34:42 +03:00
release_mem_region ( res - > start , resource_size ( res ) ) ;
2009-01-23 03:05:23 +03:00
platform_set_drvdata ( pdev , NULL ) ;
return 0 ;
}
# ifdef CONFIG_PM
2010-05-27 01:42:07 +04:00
static int omap_hsmmc_suspend ( struct device * dev )
2009-01-23 03:05:23 +03:00
{
int ret = 0 ;
2012-03-14 13:18:27 +04:00
struct omap_hsmmc_host * host = dev_get_drvdata ( dev ) ;
2009-01-23 03:05:23 +03:00
2012-03-14 13:18:27 +04:00
if ( ! host )
2009-01-23 03:05:23 +03:00
return 0 ;
2012-03-14 13:18:27 +04:00
if ( host & & host - > suspended )
return 0 ;
2011-07-01 20:39:35 +04:00
2012-03-14 13:18:27 +04:00
pm_runtime_get_sync ( host - > dev ) ;
host - > suspended = 1 ;
if ( host - > pdata - > suspend ) {
ret = host - > pdata - > suspend ( dev , host - > slot_id ) ;
2011-11-22 18:02:17 +04:00
if ( ret ) {
2012-03-14 13:18:27 +04:00
dev_dbg ( dev , " Unable to handle MMC board "
" level suspend \n " ) ;
2009-09-23 03:44:45 +04:00
host - > suspended = 0 ;
2012-03-14 13:18:27 +04:00
return ret ;
2009-09-23 03:44:45 +04:00
}
2012-03-14 13:18:27 +04:00
}
ret = mmc_suspend_host ( host - > mmc ) ;
2011-11-22 18:02:17 +04:00
2012-03-14 13:18:27 +04:00
if ( ret ) {
host - > suspended = 0 ;
if ( host - > pdata - > resume ) {
ret = host - > pdata - > resume ( dev , host - > slot_id ) ;
if ( ret )
dev_dbg ( dev , " Unmask interrupt failed \n " ) ;
2011-11-22 18:02:17 +04:00
}
2012-03-14 13:18:27 +04:00
goto err ;
}
2011-11-22 18:02:17 +04:00
2012-03-14 13:18:27 +04:00
if ( ! ( host - > mmc - > pm_flags & MMC_PM_KEEP_POWER ) ) {
omap_hsmmc_disable_irq ( host ) ;
OMAP_HSMMC_WRITE ( host - > base , HCTL ,
OMAP_HSMMC_READ ( host - > base , HCTL ) & ~ SDBP ) ;
2009-01-23 03:05:23 +03:00
}
2012-03-14 13:18:27 +04:00
if ( host - > got_dbclk )
clk_disable ( host - > dbclk ) ;
2011-11-22 18:02:17 +04:00
err :
pm_runtime_put_sync ( host - > dev ) ;
2009-01-23 03:05:23 +03:00
return ret ;
}
/* Routine to resume the MMC device */
2010-05-27 01:42:07 +04:00
static int omap_hsmmc_resume ( struct device * dev )
2009-01-23 03:05:23 +03:00
{
int ret = 0 ;
2012-03-14 13:18:27 +04:00
struct omap_hsmmc_host * host = dev_get_drvdata ( dev ) ;
if ( ! host )
return 0 ;
2009-01-23 03:05:23 +03:00
if ( host & & ! host - > suspended )
return 0 ;
2012-03-14 13:18:27 +04:00
pm_runtime_get_sync ( host - > dev ) ;
2009-09-23 03:44:43 +04:00
2012-03-14 13:18:27 +04:00
if ( host - > got_dbclk )
clk_enable ( host - > dbclk ) ;
2009-09-23 03:45:02 +04:00
2012-03-14 13:18:27 +04:00
if ( ! ( host - > mmc - > pm_flags & MMC_PM_KEEP_POWER ) )
omap_hsmmc_conf_bus_power ( host ) ;
2009-02-20 15:10:08 +03:00
2012-03-14 13:18:27 +04:00
if ( host - > pdata - > resume ) {
ret = host - > pdata - > resume ( dev , host - > slot_id ) ;
if ( ret )
dev_dbg ( dev , " Unmask interrupt failed \n " ) ;
}
2009-01-23 03:05:23 +03:00
2012-03-14 13:18:27 +04:00
omap_hsmmc_protect_card ( host ) ;
2009-09-23 03:45:01 +04:00
2012-03-14 13:18:27 +04:00
/* Notify the core to resume the host */
ret = mmc_resume_host ( host - > mmc ) ;
if ( ret = = 0 )
host - > suspended = 0 ;
2011-07-01 20:39:35 +04:00
2012-03-14 13:18:27 +04:00
pm_runtime_mark_last_busy ( host - > dev ) ;
pm_runtime_put_autosuspend ( host - > dev ) ;
2009-01-23 03:05:23 +03:00
return ret ;
}
# else
2009-09-23 03:44:59 +04:00
# define omap_hsmmc_suspend NULL
# define omap_hsmmc_resume NULL
2009-01-23 03:05:23 +03:00
# endif
2011-07-01 20:39:35 +04:00
static int omap_hsmmc_runtime_suspend ( struct device * dev )
{
struct omap_hsmmc_host * host ;
host = platform_get_drvdata ( to_platform_device ( dev ) ) ;
omap_hsmmc_context_save ( host ) ;
2012-03-14 13:18:27 +04:00
dev_dbg ( dev , " disabled \n " ) ;
2011-07-01 20:39:35 +04:00
return 0 ;
}
static int omap_hsmmc_runtime_resume ( struct device * dev )
{
struct omap_hsmmc_host * host ;
host = platform_get_drvdata ( to_platform_device ( dev ) ) ;
omap_hsmmc_context_restore ( host ) ;
2012-03-14 13:18:27 +04:00
dev_dbg ( dev , " enabled \n " ) ;
2011-07-01 20:39:35 +04:00
return 0 ;
}
2010-05-27 01:42:07 +04:00
static struct dev_pm_ops omap_hsmmc_dev_pm_ops = {
2009-09-23 03:44:59 +04:00
. suspend = omap_hsmmc_suspend ,
. resume = omap_hsmmc_resume ,
2011-07-01 20:39:35 +04:00
. runtime_suspend = omap_hsmmc_runtime_suspend ,
. runtime_resume = omap_hsmmc_runtime_resume ,
2010-05-27 01:42:07 +04:00
} ;
static struct platform_driver omap_hsmmc_driver = {
2012-03-14 13:18:28 +04:00
. probe = omap_hsmmc_probe ,
. remove = __devexit_p ( omap_hsmmc_remove ) ,
2009-01-23 03:05:23 +03:00
. driver = {
. name = DRIVER_NAME ,
. owner = THIS_MODULE ,
2010-05-27 01:42:07 +04:00
. pm = & omap_hsmmc_dev_pm_ops ,
2012-03-12 19:02:37 +04:00
. of_match_table = of_match_ptr ( omap_mmc_of_match ) ,
2009-01-23 03:05:23 +03:00
} ,
} ;
2012-03-14 13:18:32 +04:00
module_platform_driver ( omap_hsmmc_driver ) ;
2009-01-23 03:05:23 +03:00
MODULE_DESCRIPTION ( " OMAP High Speed Multimedia Card driver " ) ;
MODULE_LICENSE ( " GPL " ) ;
MODULE_ALIAS ( " platform: " DRIVER_NAME ) ;
MODULE_AUTHOR ( " Texas Instruments Inc " ) ;