2017-09-06 17:15:55 +05:30
/**
* SDHCI Controller driver for TI ' s OMAP SoCs
*
* Copyright ( C ) 2017 Texas Instruments
* Author : Kishon Vijay Abraham I < kishon @ ti . com >
*
* This program is free software : you can redistribute it and / or modify
* it under the terms of the GNU General Public License version 2 of
* the License as published by the Free Software Foundation .
*
* This program is distributed in the hope that it will be useful ,
* but WITHOUT ANY WARRANTY ; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE . See the
* GNU General Public License for more details .
*
* You should have received a copy of the GNU General Public License
* along with this program . If not , see < http : //www.gnu.org/licenses/>.
*/
# include <linux/delay.h>
# include <linux/mmc/slot-gpio.h>
# include <linux/module.h>
# include <linux/of.h>
# include <linux/of_device.h>
# include <linux/platform_device.h>
# include <linux/pm_runtime.h>
# include <linux/regulator/consumer.h>
2018-02-05 18:20:19 +05:30
# include <linux/pinctrl/consumer.h>
2018-04-27 17:17:12 +05:30
# include <linux/sys_soc.h>
2017-09-06 17:15:55 +05:30
# include "sdhci-pltfm.h"
# define SDHCI_OMAP_CON 0x12c
# define CON_DW8 BIT(5)
# define CON_DMA_MASTER BIT(20)
2018-02-05 18:20:16 +05:30
# define CON_DDR BIT(19)
2018-02-05 18:20:15 +05:30
# define CON_CLKEXTFREE BIT(16)
# define CON_PADEN BIT(15)
2018-04-27 17:17:21 +05:30
# define CON_CTPL BIT(11)
2017-09-06 17:15:55 +05:30
# define CON_INIT BIT(1)
# define CON_OD BIT(0)
2018-02-05 18:20:17 +05:30
# define SDHCI_OMAP_DLL 0x0134
# define DLL_SWT BIT(20)
# define DLL_FORCE_SR_C_SHIFT 13
# define DLL_FORCE_SR_C_MASK (0x7f << DLL_FORCE_SR_C_SHIFT)
# define DLL_FORCE_VALUE BIT(12)
# define DLL_CALIB BIT(1)
2017-09-06 17:15:55 +05:30
# define SDHCI_OMAP_CMD 0x20c
2018-02-05 18:20:15 +05:30
# define SDHCI_OMAP_PSTATE 0x0224
# define PSTATE_DLEV_DAT0 BIT(20)
# define PSTATE_DATI BIT(1)
2017-09-06 17:15:55 +05:30
# define SDHCI_OMAP_HCTL 0x228
# define HCTL_SDBP BIT(8)
# define HCTL_SDVS_SHIFT 9
# define HCTL_SDVS_MASK (0x7 << HCTL_SDVS_SHIFT)
# define HCTL_SDVS_33 (0x7 << HCTL_SDVS_SHIFT)
# define HCTL_SDVS_30 (0x6 << HCTL_SDVS_SHIFT)
# define HCTL_SDVS_18 (0x5 << HCTL_SDVS_SHIFT)
# define SDHCI_OMAP_SYSCTL 0x22c
# define SYSCTL_CEN BIT(2)
# define SYSCTL_CLKD_SHIFT 6
# define SYSCTL_CLKD_MASK 0x3ff
# define SDHCI_OMAP_STAT 0x230
# define SDHCI_OMAP_IE 0x234
# define INT_CC_EN BIT(0)
# define SDHCI_OMAP_AC12 0x23c
# define AC12_V1V8_SIGEN BIT(19)
2018-02-05 18:20:17 +05:30
# define AC12_SCLK_SEL BIT(23)
2017-09-06 17:15:55 +05:30
# define SDHCI_OMAP_CAPA 0x240
# define CAPA_VS33 BIT(24)
# define CAPA_VS30 BIT(25)
# define CAPA_VS18 BIT(26)
2018-02-05 18:20:17 +05:30
# define SDHCI_OMAP_CAPA2 0x0244
# define CAPA2_TSDR50 BIT(13)
2017-09-06 17:15:55 +05:30
# define SDHCI_OMAP_TIMEOUT 1 /* 1 msec */
# define SYSCTL_CLKD_MAX 0x3FF
# define IOV_1V8 1800000 /* 180000 uV */
# define IOV_3V0 3000000 /* 300000 uV */
# define IOV_3V3 3300000 /* 330000 uV */
2018-02-05 18:20:17 +05:30
# define MAX_PHASE_DELAY 0x7C
2018-02-05 18:20:19 +05:30
/* sdhci-omap controller flags */
# define SDHCI_OMAP_REQUIRE_IODELAY BIT(0)
2017-09-06 17:15:55 +05:30
struct sdhci_omap_data {
u32 offset ;
2018-02-05 18:20:19 +05:30
u8 flags ;
2017-09-06 17:15:55 +05:30
} ;
struct sdhci_omap_host {
2018-04-27 17:17:12 +05:30
char * version ;
2017-09-06 17:15:55 +05:30
void __iomem * base ;
struct device * dev ;
struct regulator * pbias ;
bool pbias_enabled ;
struct sdhci_host * host ;
u8 bus_mode ;
u8 power_mode ;
2018-02-05 18:20:19 +05:30
u8 timing ;
u8 flags ;
struct pinctrl * pinctrl ;
struct pinctrl_state * * pinctrl_state ;
2017-09-06 17:15:55 +05:30
} ;
2018-02-05 18:20:19 +05:30
static void sdhci_omap_start_clock ( struct sdhci_omap_host * omap_host ) ;
static void sdhci_omap_stop_clock ( struct sdhci_omap_host * omap_host ) ;
2017-09-06 17:15:55 +05:30
static inline u32 sdhci_omap_readl ( struct sdhci_omap_host * host ,
unsigned int offset )
{
return readl ( host - > base + offset ) ;
}
static inline void sdhci_omap_writel ( struct sdhci_omap_host * host ,
unsigned int offset , u32 data )
{
writel ( data , host - > base + offset ) ;
}
static int sdhci_omap_set_pbias ( struct sdhci_omap_host * omap_host ,
bool power_on , unsigned int iov )
{
int ret ;
struct device * dev = omap_host - > dev ;
if ( IS_ERR ( omap_host - > pbias ) )
return 0 ;
if ( power_on ) {
ret = regulator_set_voltage ( omap_host - > pbias , iov , iov ) ;
if ( ret ) {
dev_err ( dev , " pbias set voltage failed \n " ) ;
return ret ;
}
if ( omap_host - > pbias_enabled )
return 0 ;
ret = regulator_enable ( omap_host - > pbias ) ;
if ( ret ) {
dev_err ( dev , " pbias reg enable fail \n " ) ;
return ret ;
}
omap_host - > pbias_enabled = true ;
} else {
if ( ! omap_host - > pbias_enabled )
return 0 ;
ret = regulator_disable ( omap_host - > pbias ) ;
if ( ret ) {
dev_err ( dev , " pbias reg disable fail \n " ) ;
return ret ;
}
omap_host - > pbias_enabled = false ;
}
return 0 ;
}
static int sdhci_omap_enable_iov ( struct sdhci_omap_host * omap_host ,
unsigned int iov )
{
int ret ;
struct sdhci_host * host = omap_host - > host ;
struct mmc_host * mmc = host - > mmc ;
ret = sdhci_omap_set_pbias ( omap_host , false , 0 ) ;
if ( ret )
return ret ;
if ( ! IS_ERR ( mmc - > supply . vqmmc ) ) {
ret = regulator_set_voltage ( mmc - > supply . vqmmc , iov , iov ) ;
if ( ret ) {
dev_err ( mmc_dev ( mmc ) , " vqmmc set voltage failed \n " ) ;
return ret ;
}
}
ret = sdhci_omap_set_pbias ( omap_host , true , iov ) ;
if ( ret )
return ret ;
return 0 ;
}
static void sdhci_omap_conf_bus_power ( struct sdhci_omap_host * omap_host ,
unsigned char signal_voltage )
{
u32 reg ;
ktime_t timeout ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_HCTL ) ;
reg & = ~ HCTL_SDVS_MASK ;
if ( signal_voltage = = MMC_SIGNAL_VOLTAGE_330 )
reg | = HCTL_SDVS_33 ;
else
reg | = HCTL_SDVS_18 ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_HCTL , reg ) ;
reg | = HCTL_SDBP ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_HCTL , reg ) ;
/* wait 1ms */
timeout = ktime_add_ms ( ktime_get ( ) , SDHCI_OMAP_TIMEOUT ) ;
while ( ! ( sdhci_omap_readl ( omap_host , SDHCI_OMAP_HCTL ) & HCTL_SDBP ) ) {
if ( WARN_ON ( ktime_after ( ktime_get ( ) , timeout ) ) )
return ;
usleep_range ( 5 , 10 ) ;
}
}
2018-04-27 17:17:21 +05:30
static void sdhci_omap_enable_sdio_irq ( struct mmc_host * mmc , int enable )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_omap_host * omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
u32 reg ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CON ) ;
if ( enable )
reg | = ( CON_CTPL | CON_CLKEXTFREE ) ;
else
reg & = ~ ( CON_CTPL | CON_CLKEXTFREE ) ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CON , reg ) ;
sdhci_enable_sdio_irq ( mmc , enable ) ;
}
2018-02-05 18:20:17 +05:30
static inline void sdhci_omap_set_dll ( struct sdhci_omap_host * omap_host ,
int count )
{
int i ;
u32 reg ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_DLL ) ;
reg | = DLL_FORCE_VALUE ;
reg & = ~ DLL_FORCE_SR_C_MASK ;
reg | = ( count < < DLL_FORCE_SR_C_SHIFT ) ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_DLL , reg ) ;
reg | = DLL_CALIB ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_DLL , reg ) ;
for ( i = 0 ; i < 1000 ; i + + ) {
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_DLL ) ;
if ( reg & DLL_CALIB )
break ;
}
reg & = ~ DLL_CALIB ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_DLL , reg ) ;
}
static void sdhci_omap_disable_tuning ( struct sdhci_omap_host * omap_host )
{
u32 reg ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_AC12 ) ;
reg & = ~ AC12_SCLK_SEL ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_AC12 , reg ) ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_DLL ) ;
reg & = ~ ( DLL_FORCE_VALUE | DLL_SWT ) ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_DLL , reg ) ;
}
static int sdhci_omap_execute_tuning ( struct mmc_host * mmc , u32 opcode )
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_omap_host * omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
struct device * dev = omap_host - > dev ;
struct mmc_ios * ios = & mmc - > ios ;
u32 start_window = 0 , max_window = 0 ;
2018-11-21 16:03:55 +05:30
bool dcrc_was_enabled = false ;
2018-02-05 18:20:17 +05:30
u8 cur_match , prev_match = 0 ;
u32 length = 0 , max_len = 0 ;
u32 phase_delay = 0 ;
int ret = 0 ;
u32 reg ;
pltfm_host = sdhci_priv ( host ) ;
omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
dev = omap_host - > dev ;
/* clock tuning is not needed for upto 52MHz */
if ( ios - > clock < = 52000000 )
return 0 ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CAPA2 ) ;
if ( ios - > timing = = MMC_TIMING_UHS_SDR50 & & ! ( reg & CAPA2_TSDR50 ) )
return 0 ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_DLL ) ;
reg | = DLL_SWT ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_DLL , reg ) ;
2018-02-05 18:20:18 +05:30
/*
* OMAP5 / DRA74X / DRA72x Errata i802 :
* DCRC error interrupts ( MMCHS_STAT [ 21 ] DCRC = 0x1 ) can occur
* during the tuning procedure . So disable it during the
* tuning procedure .
*/
2018-11-21 16:03:55 +05:30
if ( host - > ier & SDHCI_INT_DATA_CRC ) {
host - > ier & = ~ SDHCI_INT_DATA_CRC ;
dcrc_was_enabled = true ;
}
2018-02-05 18:20:18 +05:30
2018-02-05 18:20:17 +05:30
while ( phase_delay < = MAX_PHASE_DELAY ) {
sdhci_omap_set_dll ( omap_host , phase_delay ) ;
cur_match = ! mmc_send_tuning ( mmc , opcode , NULL ) ;
if ( cur_match ) {
if ( prev_match ) {
length + + ;
} else {
start_window = phase_delay ;
length = 1 ;
}
}
if ( length > max_len ) {
max_window = start_window ;
max_len = length ;
}
prev_match = cur_match ;
phase_delay + = 4 ;
}
if ( ! max_len ) {
dev_err ( dev , " Unable to find match \n " ) ;
ret = - EIO ;
goto tuning_error ;
}
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_AC12 ) ;
if ( ! ( reg & AC12_SCLK_SEL ) ) {
ret = - EIO ;
goto tuning_error ;
}
phase_delay = max_window + 4 * ( max_len > > 1 ) ;
sdhci_omap_set_dll ( omap_host , phase_delay ) ;
goto ret ;
tuning_error :
dev_err ( dev , " Tuning failed \n " ) ;
sdhci_omap_disable_tuning ( omap_host ) ;
ret :
sdhci_reset ( host , SDHCI_RESET_CMD | SDHCI_RESET_DATA ) ;
2018-11-21 16:03:55 +05:30
/* Reenable forbidden interrupt */
if ( dcrc_was_enabled )
host - > ier | = SDHCI_INT_DATA_CRC ;
2018-02-05 18:20:18 +05:30
sdhci_writel ( host , host - > ier , SDHCI_INT_ENABLE ) ;
sdhci_writel ( host , host - > ier , SDHCI_SIGNAL_ENABLE ) ;
2018-02-05 18:20:17 +05:30
return ret ;
}
2018-02-05 18:20:15 +05:30
static int sdhci_omap_card_busy ( struct mmc_host * mmc )
{
u32 reg , ac12 ;
int ret = false ;
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_omap_host * omap_host ;
u32 ier = host - > ier ;
pltfm_host = sdhci_priv ( host ) ;
omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CON ) ;
ac12 = sdhci_omap_readl ( omap_host , SDHCI_OMAP_AC12 ) ;
reg & = ~ CON_CLKEXTFREE ;
if ( ac12 & AC12_V1V8_SIGEN )
reg | = CON_CLKEXTFREE ;
reg | = CON_PADEN ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CON , reg ) ;
disable_irq ( host - > irq ) ;
ier | = SDHCI_INT_CARD_INT ;
sdhci_writel ( host , ier , SDHCI_INT_ENABLE ) ;
sdhci_writel ( host , ier , SDHCI_SIGNAL_ENABLE ) ;
/*
* Delay is required for PSTATE to correctly reflect
* DLEV / CLEV values after PADEN is set .
*/
usleep_range ( 50 , 100 ) ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_PSTATE ) ;
if ( ( reg & PSTATE_DATI ) | | ! ( reg & PSTATE_DLEV_DAT0 ) )
ret = true ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CON ) ;
reg & = ~ ( CON_CLKEXTFREE | CON_PADEN ) ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CON , reg ) ;
sdhci_writel ( host , host - > ier , SDHCI_INT_ENABLE ) ;
sdhci_writel ( host , host - > ier , SDHCI_SIGNAL_ENABLE ) ;
enable_irq ( host - > irq ) ;
return ret ;
}
2017-09-06 17:15:55 +05:30
static int sdhci_omap_start_signal_voltage_switch ( struct mmc_host * mmc ,
struct mmc_ios * ios )
{
u32 reg ;
int ret ;
unsigned int iov ;
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_omap_host * omap_host ;
struct device * dev ;
pltfm_host = sdhci_priv ( host ) ;
omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
dev = omap_host - > dev ;
if ( ios - > signal_voltage = = MMC_SIGNAL_VOLTAGE_330 ) {
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CAPA ) ;
if ( ! ( reg & CAPA_VS33 ) )
return - EOPNOTSUPP ;
sdhci_omap_conf_bus_power ( omap_host , ios - > signal_voltage ) ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_AC12 ) ;
reg & = ~ AC12_V1V8_SIGEN ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_AC12 , reg ) ;
iov = IOV_3V3 ;
} else if ( ios - > signal_voltage = = MMC_SIGNAL_VOLTAGE_180 ) {
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CAPA ) ;
if ( ! ( reg & CAPA_VS18 ) )
return - EOPNOTSUPP ;
sdhci_omap_conf_bus_power ( omap_host , ios - > signal_voltage ) ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_AC12 ) ;
reg | = AC12_V1V8_SIGEN ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_AC12 , reg ) ;
iov = IOV_1V8 ;
} else {
return - EOPNOTSUPP ;
}
ret = sdhci_omap_enable_iov ( omap_host , iov ) ;
if ( ret ) {
dev_err ( dev , " failed to switch IO voltage to %dmV \n " , iov ) ;
return ret ;
}
dev_dbg ( dev , " IO voltage switched to %dmV \n " , iov ) ;
return 0 ;
}
2018-02-05 18:20:19 +05:30
static void sdhci_omap_set_timing ( struct sdhci_omap_host * omap_host , u8 timing )
{
int ret ;
struct pinctrl_state * pinctrl_state ;
struct device * dev = omap_host - > dev ;
if ( ! ( omap_host - > flags & SDHCI_OMAP_REQUIRE_IODELAY ) )
return ;
if ( omap_host - > timing = = timing )
return ;
sdhci_omap_stop_clock ( omap_host ) ;
pinctrl_state = omap_host - > pinctrl_state [ timing ] ;
ret = pinctrl_select_state ( omap_host - > pinctrl , pinctrl_state ) ;
if ( ret ) {
dev_err ( dev , " failed to select pinctrl state \n " ) ;
return ;
}
sdhci_omap_start_clock ( omap_host ) ;
omap_host - > timing = timing ;
}
2018-02-05 18:20:14 +05:30
static void sdhci_omap_set_power_mode ( struct sdhci_omap_host * omap_host ,
u8 power_mode )
{
2018-02-05 18:20:17 +05:30
if ( omap_host - > bus_mode = = MMC_POWER_OFF )
sdhci_omap_disable_tuning ( omap_host ) ;
2018-02-05 18:20:14 +05:30
omap_host - > power_mode = power_mode ;
}
2017-09-06 17:15:55 +05:30
static void sdhci_omap_set_bus_mode ( struct sdhci_omap_host * omap_host ,
unsigned int mode )
{
u32 reg ;
if ( omap_host - > bus_mode = = mode )
return ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CON ) ;
if ( mode = = MMC_BUSMODE_OPENDRAIN )
reg | = CON_OD ;
else
reg & = ~ CON_OD ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CON , reg ) ;
omap_host - > bus_mode = mode ;
}
2017-09-26 15:55:46 +01:00
static void sdhci_omap_set_ios ( struct mmc_host * mmc , struct mmc_ios * ios )
2017-09-06 17:15:55 +05:30
{
struct sdhci_host * host = mmc_priv ( mmc ) ;
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_omap_host * omap_host ;
pltfm_host = sdhci_priv ( host ) ;
omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
sdhci_omap_set_bus_mode ( omap_host , ios - > bus_mode ) ;
2018-02-05 18:20:19 +05:30
sdhci_omap_set_timing ( omap_host , ios - > timing ) ;
2017-09-06 17:15:55 +05:30
sdhci_set_ios ( mmc , ios ) ;
2018-02-05 18:20:14 +05:30
sdhci_omap_set_power_mode ( omap_host , ios - > power_mode ) ;
2017-09-06 17:15:55 +05:30
}
static u16 sdhci_omap_calc_divisor ( struct sdhci_pltfm_host * host ,
unsigned int clock )
{
u16 dsor ;
dsor = DIV_ROUND_UP ( clk_get_rate ( host - > clk ) , clock ) ;
if ( dsor > SYSCTL_CLKD_MAX )
dsor = SYSCTL_CLKD_MAX ;
return dsor ;
}
static void sdhci_omap_start_clock ( struct sdhci_omap_host * omap_host )
{
u32 reg ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_SYSCTL ) ;
reg | = SYSCTL_CEN ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_SYSCTL , reg ) ;
}
static void sdhci_omap_stop_clock ( struct sdhci_omap_host * omap_host )
{
u32 reg ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_SYSCTL ) ;
reg & = ~ SYSCTL_CEN ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_SYSCTL , reg ) ;
}
static void sdhci_omap_set_clock ( struct sdhci_host * host , unsigned int clock )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_omap_host * omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
unsigned long clkdiv ;
sdhci_omap_stop_clock ( omap_host ) ;
if ( ! clock )
return ;
clkdiv = sdhci_omap_calc_divisor ( pltfm_host , clock ) ;
clkdiv = ( clkdiv & SYSCTL_CLKD_MASK ) < < SYSCTL_CLKD_SHIFT ;
sdhci_enable_clk ( host , clkdiv ) ;
sdhci_omap_start_clock ( omap_host ) ;
}
2017-09-26 15:55:46 +01:00
static void sdhci_omap_set_power ( struct sdhci_host * host , unsigned char mode ,
2017-09-06 17:15:55 +05:30
unsigned short vdd )
{
struct mmc_host * mmc = host - > mmc ;
mmc_regulator_set_ocr ( mmc , mmc - > supply . vmmc , vdd ) ;
}
static int sdhci_omap_enable_dma ( struct sdhci_host * host )
{
u32 reg ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_omap_host * omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CON ) ;
reg | = CON_DMA_MASTER ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CON , reg ) ;
return 0 ;
}
2017-09-26 15:55:46 +01:00
static unsigned int sdhci_omap_get_min_clock ( struct sdhci_host * host )
2017-09-06 17:15:55 +05:30
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
return clk_get_rate ( pltfm_host - > clk ) / SYSCTL_CLKD_MAX ;
}
static void sdhci_omap_set_bus_width ( struct sdhci_host * host , int width )
{
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_omap_host * omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
u32 reg ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CON ) ;
if ( width = = MMC_BUS_WIDTH_8 )
reg | = CON_DW8 ;
else
reg & = ~ CON_DW8 ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CON , reg ) ;
sdhci_set_bus_width ( host , width ) ;
}
static void sdhci_omap_init_74_clocks ( struct sdhci_host * host , u8 power_mode )
{
u32 reg ;
ktime_t timeout ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_omap_host * omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
if ( omap_host - > power_mode = = power_mode )
return ;
if ( power_mode ! = MMC_POWER_ON )
return ;
disable_irq ( host - > irq ) ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CON ) ;
reg | = CON_INIT ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CON , reg ) ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CMD , 0x0 ) ;
/* wait 1ms */
timeout = ktime_add_ms ( ktime_get ( ) , SDHCI_OMAP_TIMEOUT ) ;
while ( ! ( sdhci_omap_readl ( omap_host , SDHCI_OMAP_STAT ) & INT_CC_EN ) ) {
if ( WARN_ON ( ktime_after ( ktime_get ( ) , timeout ) ) )
return ;
usleep_range ( 5 , 10 ) ;
}
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CON ) ;
reg & = ~ CON_INIT ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CON , reg ) ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_STAT , INT_CC_EN ) ;
enable_irq ( host - > irq ) ;
}
2018-02-05 18:20:16 +05:30
static void sdhci_omap_set_uhs_signaling ( struct sdhci_host * host ,
unsigned int timing )
{
u32 reg ;
struct sdhci_pltfm_host * pltfm_host = sdhci_priv ( host ) ;
struct sdhci_omap_host * omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
sdhci_omap_stop_clock ( omap_host ) ;
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CON ) ;
if ( timing = = MMC_TIMING_UHS_DDR50 | | timing = = MMC_TIMING_MMC_DDR52 )
reg | = CON_DDR ;
else
reg & = ~ CON_DDR ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CON , reg ) ;
sdhci_set_uhs_signaling ( host , timing ) ;
sdhci_omap_start_clock ( omap_host ) ;
}
2017-09-06 17:15:55 +05:30
static struct sdhci_ops sdhci_omap_ops = {
. set_clock = sdhci_omap_set_clock ,
. set_power = sdhci_omap_set_power ,
. enable_dma = sdhci_omap_enable_dma ,
. get_max_clock = sdhci_pltfm_clk_get_max_clock ,
. get_min_clock = sdhci_omap_get_min_clock ,
. set_bus_width = sdhci_omap_set_bus_width ,
. platform_send_init_74_clocks = sdhci_omap_init_74_clocks ,
. reset = sdhci_reset ,
2018-02-05 18:20:16 +05:30
. set_uhs_signaling = sdhci_omap_set_uhs_signaling ,
2017-09-06 17:15:55 +05:30
} ;
static int sdhci_omap_set_capabilities ( struct sdhci_omap_host * omap_host )
{
u32 reg ;
int ret = 0 ;
struct device * dev = omap_host - > dev ;
struct regulator * vqmmc ;
vqmmc = regulator_get ( dev , " vqmmc " ) ;
if ( IS_ERR ( vqmmc ) ) {
ret = PTR_ERR ( vqmmc ) ;
goto reg_put ;
}
/* voltage capabilities might be set by boot loader, clear it */
reg = sdhci_omap_readl ( omap_host , SDHCI_OMAP_CAPA ) ;
reg & = ~ ( CAPA_VS18 | CAPA_VS30 | CAPA_VS33 ) ;
if ( regulator_is_supported_voltage ( vqmmc , IOV_3V3 , IOV_3V3 ) )
reg | = CAPA_VS33 ;
if ( regulator_is_supported_voltage ( vqmmc , IOV_1V8 , IOV_1V8 ) )
reg | = CAPA_VS18 ;
sdhci_omap_writel ( omap_host , SDHCI_OMAP_CAPA , reg ) ;
reg_put :
regulator_put ( vqmmc ) ;
return ret ;
}
static const struct sdhci_pltfm_data sdhci_omap_pdata = {
. quirks = SDHCI_QUIRK_BROKEN_CARD_DETECTION |
SDHCI_QUIRK_DATA_TIMEOUT_USES_SDCLK |
SDHCI_QUIRK_CAP_CLOCK_BASE_BROKEN |
SDHCI_QUIRK_NO_HISPD_BIT |
SDHCI_QUIRK_BROKEN_ADMA_ZEROLEN_DESC ,
2018-02-05 18:20:20 +05:30
. quirks2 = SDHCI_QUIRK2_ACMD23_BROKEN |
SDHCI_QUIRK2_PRESET_VALUE_BROKEN |
2018-04-27 17:17:18 +05:30
SDHCI_QUIRK2_RSP_136_HAS_CRC |
SDHCI_QUIRK2_DISABLE_HW_TIMEOUT ,
2017-09-06 17:15:55 +05:30
. ops = & sdhci_omap_ops ,
} ;
2018-04-27 17:17:20 +05:30
static const struct sdhci_omap_data k2g_data = {
. offset = 0x200 ,
} ;
2017-09-06 17:15:55 +05:30
static const struct sdhci_omap_data dra7_data = {
. offset = 0x200 ,
2018-02-05 18:20:19 +05:30
. flags = SDHCI_OMAP_REQUIRE_IODELAY ,
2017-09-06 17:15:55 +05:30
} ;
static const struct of_device_id omap_sdhci_match [ ] = {
{ . compatible = " ti,dra7-sdhci " , . data = & dra7_data } ,
2018-04-27 17:17:20 +05:30
{ . compatible = " ti,k2g-sdhci " , . data = & k2g_data } ,
2017-09-06 17:15:55 +05:30
{ } ,
} ;
MODULE_DEVICE_TABLE ( of , omap_sdhci_match ) ;
2018-02-05 18:20:19 +05:30
static struct pinctrl_state
* sdhci_omap_iodelay_pinctrl_state ( struct sdhci_omap_host * omap_host , char * mode ,
u32 * caps , u32 capmask )
{
struct device * dev = omap_host - > dev ;
2018-04-27 17:17:12 +05:30
char * version = omap_host - > version ;
2018-02-05 18:20:19 +05:30
struct pinctrl_state * pinctrl_state = ERR_PTR ( - ENODEV ) ;
2018-04-27 17:17:12 +05:30
char str [ 20 ] ;
2018-02-05 18:20:19 +05:30
if ( ! ( * caps & capmask ) )
goto ret ;
2018-04-27 17:17:12 +05:30
if ( version ) {
snprintf ( str , 20 , " %s-%s " , mode , version ) ;
pinctrl_state = pinctrl_lookup_state ( omap_host - > pinctrl , str ) ;
}
if ( IS_ERR ( pinctrl_state ) )
pinctrl_state = pinctrl_lookup_state ( omap_host - > pinctrl , mode ) ;
2018-02-05 18:20:19 +05:30
if ( IS_ERR ( pinctrl_state ) ) {
dev_err ( dev , " no pinctrl state for %s mode " , mode ) ;
* caps & = ~ capmask ;
}
ret :
return pinctrl_state ;
}
static int sdhci_omap_config_iodelay_pinctrl_state ( struct sdhci_omap_host
* omap_host )
{
struct device * dev = omap_host - > dev ;
struct sdhci_host * host = omap_host - > host ;
struct mmc_host * mmc = host - > mmc ;
u32 * caps = & mmc - > caps ;
u32 * caps2 = & mmc - > caps2 ;
struct pinctrl_state * state ;
struct pinctrl_state * * pinctrl_state ;
if ( ! ( omap_host - > flags & SDHCI_OMAP_REQUIRE_IODELAY ) )
return 0 ;
treewide: devm_kzalloc() -> devm_kcalloc()
The devm_kzalloc() function has a 2-factor argument form, devm_kcalloc().
This patch replaces cases of:
devm_kzalloc(handle, a * b, gfp)
with:
devm_kcalloc(handle, a * b, gfp)
as well as handling cases of:
devm_kzalloc(handle, a * b * c, gfp)
with:
devm_kzalloc(handle, array3_size(a, b, c), gfp)
as it's slightly less ugly than:
devm_kcalloc(handle, array_size(a, b), c, gfp)
This does, however, attempt to ignore constant size factors like:
devm_kzalloc(handle, 4 * 1024, gfp)
though any constants defined via macros get caught up in the conversion.
Any factors with a sizeof() of "unsigned char", "char", and "u8" were
dropped, since they're redundant.
Some manual whitespace fixes were needed in this patch, as Coccinelle
really liked to write "=devm_kcalloc..." instead of "= devm_kcalloc...".
The Coccinelle script used for this was:
// Fix redundant parens around sizeof().
@@
expression HANDLE;
type TYPE;
expression THING, E;
@@
(
devm_kzalloc(HANDLE,
- (sizeof(TYPE)) * E
+ sizeof(TYPE) * E
, ...)
|
devm_kzalloc(HANDLE,
- (sizeof(THING)) * E
+ sizeof(THING) * E
, ...)
)
// Drop single-byte sizes and redundant parens.
@@
expression HANDLE;
expression COUNT;
typedef u8;
typedef __u8;
@@
(
devm_kzalloc(HANDLE,
- sizeof(u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * (COUNT)
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(__u8) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(char) * COUNT
+ COUNT
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(unsigned char) * COUNT
+ COUNT
, ...)
)
// 2-factor product with sizeof(type/expression) and identifier or constant.
@@
expression HANDLE;
type TYPE;
expression THING;
identifier COUNT_ID;
constant COUNT_CONST;
@@
(
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_ID)
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_ID
+ COUNT_ID, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (COUNT_CONST)
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * COUNT_CONST
+ COUNT_CONST, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_ID)
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_ID
+ COUNT_ID, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (COUNT_CONST)
+ COUNT_CONST, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * COUNT_CONST
+ COUNT_CONST, sizeof(THING)
, ...)
)
// 2-factor product, only identifiers.
@@
expression HANDLE;
identifier SIZE, COUNT;
@@
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- SIZE * COUNT
+ COUNT, SIZE
, ...)
// 3-factor product with 1 sizeof(type) or sizeof(expression), with
// redundant parens removed.
@@
expression HANDLE;
expression THING;
identifier STRIDE, COUNT;
type TYPE;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(TYPE))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * (COUNT) * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * (STRIDE)
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING) * COUNT * STRIDE
+ array3_size(COUNT, STRIDE, sizeof(THING))
, ...)
)
// 3-factor product with 2 sizeof(variable), with redundant parens removed.
@@
expression HANDLE;
expression THING1, THING2;
identifier COUNT;
type TYPE1, TYPE2;
@@
(
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(TYPE2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(TYPE2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(THING1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(THING1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * COUNT
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
|
devm_kzalloc(HANDLE,
- sizeof(TYPE1) * sizeof(THING2) * (COUNT)
+ array3_size(COUNT, sizeof(TYPE1), sizeof(THING2))
, ...)
)
// 3-factor product, only identifiers, with redundant parens removed.
@@
expression HANDLE;
identifier STRIDE, SIZE, COUNT;
@@
(
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * STRIDE * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- (COUNT) * (STRIDE) * (SIZE)
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
|
devm_kzalloc(HANDLE,
- COUNT * STRIDE * SIZE
+ array3_size(COUNT, STRIDE, SIZE)
, ...)
)
// Any remaining multi-factor products, first at least 3-factor products,
// when they're not all constants...
@@
expression HANDLE;
expression E1, E2, E3;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE,
- (E1) * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * E3
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- (E1) * (E2) * (E3)
+ array3_size(E1, E2, E3)
, ...)
|
devm_kzalloc(HANDLE,
- E1 * E2 * E3
+ array3_size(E1, E2, E3)
, ...)
)
// And then all remaining 2 factors products when they're not all constants,
// keeping sizeof() as the second factor argument.
@@
expression HANDLE;
expression THING, E1, E2;
type TYPE;
constant C1, C2, C3;
@@
(
devm_kzalloc(HANDLE, sizeof(THING) * C2, ...)
|
devm_kzalloc(HANDLE, sizeof(TYPE) * C2, ...)
|
devm_kzalloc(HANDLE, C1 * C2 * C3, ...)
|
devm_kzalloc(HANDLE, C1 * C2, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * (E2)
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(TYPE) * E2
+ E2, sizeof(TYPE)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * (E2)
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- sizeof(THING) * E2
+ E2, sizeof(THING)
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * E2
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- (E1) * (E2)
+ E1, E2
, ...)
|
- devm_kzalloc
+ devm_kcalloc
(HANDLE,
- E1 * E2
+ E1, E2
, ...)
)
Signed-off-by: Kees Cook <keescook@chromium.org>
2018-06-12 14:07:58 -07:00
pinctrl_state = devm_kcalloc ( dev ,
MMC_TIMING_MMC_HS200 + 1 ,
sizeof ( * pinctrl_state ) ,
GFP_KERNEL ) ;
2018-02-05 18:20:19 +05:30
if ( ! pinctrl_state )
return - ENOMEM ;
omap_host - > pinctrl = devm_pinctrl_get ( omap_host - > dev ) ;
if ( IS_ERR ( omap_host - > pinctrl ) ) {
dev_err ( dev , " Cannot get pinctrl \n " ) ;
return PTR_ERR ( omap_host - > pinctrl ) ;
}
state = pinctrl_lookup_state ( omap_host - > pinctrl , " default " ) ;
if ( IS_ERR ( state ) ) {
dev_err ( dev , " no pinctrl state for default mode \n " ) ;
return PTR_ERR ( state ) ;
}
pinctrl_state [ MMC_TIMING_LEGACY ] = state ;
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " sdr104 " , caps ,
MMC_CAP_UHS_SDR104 ) ;
if ( ! IS_ERR ( state ) )
pinctrl_state [ MMC_TIMING_UHS_SDR104 ] = state ;
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " ddr50 " , caps ,
MMC_CAP_UHS_DDR50 ) ;
if ( ! IS_ERR ( state ) )
pinctrl_state [ MMC_TIMING_UHS_DDR50 ] = state ;
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " sdr50 " , caps ,
MMC_CAP_UHS_SDR50 ) ;
if ( ! IS_ERR ( state ) )
pinctrl_state [ MMC_TIMING_UHS_SDR50 ] = state ;
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " sdr25 " , caps ,
MMC_CAP_UHS_SDR25 ) ;
if ( ! IS_ERR ( state ) )
pinctrl_state [ MMC_TIMING_UHS_SDR25 ] = state ;
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " sdr12 " , caps ,
MMC_CAP_UHS_SDR12 ) ;
if ( ! IS_ERR ( state ) )
pinctrl_state [ MMC_TIMING_UHS_SDR12 ] = state ;
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " ddr_1_8v " , caps ,
MMC_CAP_1_8V_DDR ) ;
2018-04-27 17:17:23 +05:30
if ( ! IS_ERR ( state ) ) {
2018-02-05 18:20:19 +05:30
pinctrl_state [ MMC_TIMING_MMC_DDR52 ] = state ;
2018-04-27 17:17:23 +05:30
} else {
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " ddr_3_3v " ,
caps ,
MMC_CAP_3_3V_DDR ) ;
if ( ! IS_ERR ( state ) )
pinctrl_state [ MMC_TIMING_MMC_DDR52 ] = state ;
}
2018-02-05 18:20:19 +05:30
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " hs " , caps ,
MMC_CAP_SD_HIGHSPEED ) ;
if ( ! IS_ERR ( state ) )
pinctrl_state [ MMC_TIMING_SD_HS ] = state ;
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " hs " , caps ,
MMC_CAP_MMC_HIGHSPEED ) ;
if ( ! IS_ERR ( state ) )
pinctrl_state [ MMC_TIMING_MMC_HS ] = state ;
state = sdhci_omap_iodelay_pinctrl_state ( omap_host , " hs200_1_8v " , caps2 ,
MMC_CAP2_HS200_1_8V_SDR ) ;
if ( ! IS_ERR ( state ) )
pinctrl_state [ MMC_TIMING_MMC_HS200 ] = state ;
omap_host - > pinctrl_state = pinctrl_state ;
return 0 ;
}
2018-04-27 17:17:12 +05:30
static const struct soc_device_attribute sdhci_omap_soc_devices [ ] = {
{
. machine = " DRA7[45]* " ,
. revision = " ES1.[01] " ,
} ,
{
/* sentinel */
}
} ;
2017-09-06 17:15:55 +05:30
static int sdhci_omap_probe ( struct platform_device * pdev )
{
int ret ;
u32 offset ;
struct device * dev = & pdev - > dev ;
struct sdhci_host * host ;
struct sdhci_pltfm_host * pltfm_host ;
struct sdhci_omap_host * omap_host ;
struct mmc_host * mmc ;
const struct of_device_id * match ;
struct sdhci_omap_data * data ;
2018-04-27 17:17:12 +05:30
const struct soc_device_attribute * soc ;
2017-09-06 17:15:55 +05:30
match = of_match_device ( omap_sdhci_match , dev ) ;
if ( ! match )
return - EINVAL ;
data = ( struct sdhci_omap_data * ) match - > data ;
if ( ! data ) {
dev_err ( dev , " no sdhci omap data \n " ) ;
return - EINVAL ;
}
offset = data - > offset ;
host = sdhci_pltfm_init ( pdev , & sdhci_omap_pdata ,
sizeof ( * omap_host ) ) ;
if ( IS_ERR ( host ) ) {
dev_err ( dev , " Failed sdhci_pltfm_init \n " ) ;
return PTR_ERR ( host ) ;
}
pltfm_host = sdhci_priv ( host ) ;
omap_host = sdhci_pltfm_priv ( pltfm_host ) ;
omap_host - > host = host ;
omap_host - > base = host - > ioaddr ;
omap_host - > dev = dev ;
2018-02-05 18:20:14 +05:30
omap_host - > power_mode = MMC_POWER_UNDEFINED ;
2018-02-05 18:20:19 +05:30
omap_host - > timing = MMC_TIMING_LEGACY ;
omap_host - > flags = data - > flags ;
2017-09-06 17:15:55 +05:30
host - > ioaddr + = offset ;
mmc = host - > mmc ;
2018-04-27 17:17:13 +05:30
sdhci_get_of_property ( pdev ) ;
2017-09-06 17:15:55 +05:30
ret = mmc_of_parse ( mmc ) ;
if ( ret )
goto err_pltfm_free ;
2018-04-27 17:17:12 +05:30
soc = soc_device_match ( sdhci_omap_soc_devices ) ;
if ( soc ) {
omap_host - > version = " rev11 " ;
if ( ! strcmp ( dev_name ( dev ) , " 4809c000.mmc " ) )
mmc - > f_max = 96000000 ;
if ( ! strcmp ( dev_name ( dev ) , " 480b4000.mmc " ) )
mmc - > f_max = 48000000 ;
if ( ! strcmp ( dev_name ( dev ) , " 480ad000.mmc " ) )
mmc - > f_max = 48000000 ;
}
2017-09-06 17:15:55 +05:30
pltfm_host - > clk = devm_clk_get ( dev , " fck " ) ;
if ( IS_ERR ( pltfm_host - > clk ) ) {
ret = PTR_ERR ( pltfm_host - > clk ) ;
goto err_pltfm_free ;
}
ret = clk_set_rate ( pltfm_host - > clk , mmc - > f_max ) ;
if ( ret ) {
dev_err ( dev , " failed to set clock to %d \n " , mmc - > f_max ) ;
goto err_pltfm_free ;
}
omap_host - > pbias = devm_regulator_get_optional ( dev , " pbias " ) ;
if ( IS_ERR ( omap_host - > pbias ) ) {
ret = PTR_ERR ( omap_host - > pbias ) ;
if ( ret ! = - ENODEV )
goto err_pltfm_free ;
dev_dbg ( dev , " unable to get pbias regulator %d \n " , ret ) ;
}
omap_host - > pbias_enabled = false ;
/*
* omap_device_pm_domain has callbacks to enable the main
* functional clock , interface clock and also configure the
* SYSCONFIG register of omap devices . The callback will be invoked
* as part of pm_runtime_get_sync .
*/
pm_runtime_enable ( dev ) ;
ret = pm_runtime_get_sync ( dev ) ;
if ( ret < 0 ) {
dev_err ( dev , " pm_runtime_get_sync failed \n " ) ;
pm_runtime_put_noidle ( dev ) ;
goto err_rpm_disable ;
}
ret = sdhci_omap_set_capabilities ( omap_host ) ;
if ( ret ) {
dev_err ( dev , " failed to set system capabilities \n " ) ;
goto err_put_sync ;
}
host - > mmc_host_ops . get_ro = mmc_gpio_get_ro ;
host - > mmc_host_ops . start_signal_voltage_switch =
sdhci_omap_start_signal_voltage_switch ;
host - > mmc_host_ops . set_ios = sdhci_omap_set_ios ;
2018-02-05 18:20:15 +05:30
host - > mmc_host_ops . card_busy = sdhci_omap_card_busy ;
2018-02-05 18:20:17 +05:30
host - > mmc_host_ops . execute_tuning = sdhci_omap_execute_tuning ;
2018-04-27 17:17:21 +05:30
host - > mmc_host_ops . enable_sdio_irq = sdhci_omap_enable_sdio_irq ;
2017-09-06 17:15:55 +05:30
2018-04-27 17:17:10 +05:30
ret = sdhci_setup_host ( host ) ;
2017-09-06 17:15:55 +05:30
if ( ret )
goto err_put_sync ;
2018-04-27 17:17:10 +05:30
ret = sdhci_omap_config_iodelay_pinctrl_state ( omap_host ) ;
if ( ret )
goto err_cleanup_host ;
ret = __sdhci_add_host ( host ) ;
if ( ret )
goto err_cleanup_host ;
2017-09-06 17:15:55 +05:30
return 0 ;
2018-04-27 17:17:10 +05:30
err_cleanup_host :
sdhci_cleanup_host ( host ) ;
2017-09-06 17:15:55 +05:30
err_put_sync :
pm_runtime_put_sync ( dev ) ;
err_rpm_disable :
pm_runtime_disable ( dev ) ;
err_pltfm_free :
sdhci_pltfm_free ( pdev ) ;
return ret ;
}
static int sdhci_omap_remove ( struct platform_device * pdev )
{
struct device * dev = & pdev - > dev ;
struct sdhci_host * host = platform_get_drvdata ( pdev ) ;
sdhci_remove_host ( host , true ) ;
pm_runtime_put_sync ( dev ) ;
pm_runtime_disable ( dev ) ;
sdhci_pltfm_free ( pdev ) ;
return 0 ;
}
static struct platform_driver sdhci_omap_driver = {
. probe = sdhci_omap_probe ,
. remove = sdhci_omap_remove ,
. driver = {
. name = " sdhci-omap " ,
. of_match_table = omap_sdhci_match ,
} ,
} ;
module_platform_driver ( sdhci_omap_driver ) ;
MODULE_DESCRIPTION ( " SDHCI driver for OMAP SoCs " ) ;
MODULE_AUTHOR ( " Texas Instruments Inc. " ) ;
MODULE_LICENSE ( " GPL v2 " ) ;
MODULE_ALIAS ( " platform:sdhci_omap " ) ;