2019-06-04 11:11:33 +03:00
// SPDX-License-Identifier: GPL-2.0-only
2011-12-26 00:36:02 +04:00
/*
* Generic GPIO card - detect helper
*
* Copyright ( C ) 2011 , Guennadi Liakhovetski < g . liakhovetski @ gmx . de >
*/
# include <linux/err.h>
2014-03-10 17:02:39 +04:00
# include <linux/gpio/consumer.h>
2011-12-26 00:36:02 +04:00
# include <linux/interrupt.h>
# include <linux/jiffies.h>
# include <linux/mmc/host.h>
2012-05-01 01:31:57 +04:00
# include <linux/mmc/slot-gpio.h>
2011-12-26 00:36:02 +04:00
# include <linux/module.h>
# include <linux/slab.h>
2014-12-18 17:44:34 +03:00
# include "slot-gpio.h"
2012-05-01 01:31:57 +04:00
struct mmc_gpio {
2014-03-10 17:02:39 +04:00
struct gpio_desc * ro_gpio ;
struct gpio_desc * cd_gpio ;
2015-01-12 22:23:18 +03:00
irqreturn_t ( * cd_gpio_isr ) ( int irq , void * dev_id ) ;
2012-05-01 18:59:38 +04:00
char * ro_label ;
2018-11-12 17:12:30 +03:00
char * cd_label ;
2018-04-24 03:42:57 +03:00
u32 cd_debounce_delay_ms ;
2023-02-15 00:41:19 +03:00
int cd_irq ;
2011-12-26 00:36:02 +04:00
} ;
2012-05-01 01:31:57 +04:00
static irqreturn_t mmc_gpio_cd_irqt ( int irq , void * dev_id )
2011-12-26 00:36:02 +04:00
{
/* Schedule a card detection after a debounce timeout */
2012-12-04 19:51:36 +04:00
struct mmc_host * host = dev_id ;
2018-04-24 03:42:57 +03:00
struct mmc_gpio * ctx = host - > slot . handler_priv ;
2012-12-04 19:51:36 +04:00
2014-04-09 02:19:43 +04:00
host - > trigger_card_event = true ;
2018-04-24 03:42:57 +03:00
mmc_detect_change ( host , msecs_to_jiffies ( ctx - > cd_debounce_delay_ms ) ) ;
2012-12-04 19:51:36 +04:00
2011-12-26 00:36:02 +04:00
return IRQ_HANDLED ;
}
2014-12-18 17:44:34 +03:00
int mmc_gpio_alloc ( struct mmc_host * host )
2012-05-01 18:51:38 +04:00
{
2021-09-29 14:17:56 +03:00
const char * devname = dev_name ( host - > parent ) ;
struct mmc_gpio * ctx ;
ctx = devm_kzalloc ( host - > parent , sizeof ( * ctx ) , GFP_KERNEL ) ;
if ( ! ctx )
return - ENOMEM ;
ctx - > cd_debounce_delay_ms = 200 ;
ctx - > cd_label = devm_kasprintf ( host - > parent , GFP_KERNEL , " %s cd " , devname ) ;
if ( ! ctx - > cd_label )
return - ENOMEM ;
ctx - > ro_label = devm_kasprintf ( host - > parent , GFP_KERNEL , " %s ro " , devname ) ;
if ( ! ctx - > ro_label )
return - ENOMEM ;
2023-02-15 00:41:19 +03:00
ctx - > cd_irq = - EINVAL ;
2021-09-29 14:17:56 +03:00
host - > slot . handler_priv = ctx ;
host - > slot . cd_irq = - EINVAL ;
return 0 ;
2012-05-01 18:51:38 +04:00
}
2023-02-15 00:41:19 +03:00
void mmc_gpio_set_cd_irq ( struct mmc_host * host , int irq )
{
struct mmc_gpio * ctx = host - > slot . handler_priv ;
if ( ! ctx | | irq < 0 )
return ;
ctx - > cd_irq = irq ;
}
EXPORT_SYMBOL ( mmc_gpio_set_cd_irq ) ;
2012-05-01 18:59:38 +04:00
int mmc_gpio_get_ro ( struct mmc_host * host )
{
struct mmc_gpio * ctx = host - > slot . handler_priv ;
2024-02-06 11:39:12 +03:00
int cansleep ;
2012-05-01 18:59:38 +04:00
2014-03-10 17:02:39 +04:00
if ( ! ctx | | ! ctx - > ro_gpio )
2012-05-01 18:59:38 +04:00
return - ENOSYS ;
2024-02-06 11:39:12 +03:00
cansleep = gpiod_cansleep ( ctx - > ro_gpio ) ;
return cansleep ?
gpiod_get_value_cansleep ( ctx - > ro_gpio ) :
gpiod_get_value ( ctx - > ro_gpio ) ;
2012-05-01 18:59:38 +04:00
}
EXPORT_SYMBOL ( mmc_gpio_get_ro ) ;
2012-05-01 18:27:25 +04:00
int mmc_gpio_get_cd ( struct mmc_host * host )
{
struct mmc_gpio * ctx = host - > slot . handler_priv ;
2018-05-25 22:25:23 +03:00
int cansleep ;
2012-05-01 18:27:25 +04:00
2014-03-10 17:02:39 +04:00
if ( ! ctx | | ! ctx - > cd_gpio )
2012-05-01 18:27:25 +04:00
return - ENOSYS ;
2018-05-25 22:25:23 +03:00
cansleep = gpiod_cansleep ( ctx - > cd_gpio ) ;
return cansleep ?
gpiod_get_value_cansleep ( ctx - > cd_gpio ) :
gpiod_get_value ( ctx - > cd_gpio ) ;
2012-05-01 18:27:25 +04:00
}
EXPORT_SYMBOL ( mmc_gpio_get_cd ) ;
2014-03-10 17:02:41 +04:00
void mmc_gpiod_request_cd_irq ( struct mmc_host * host )
2014-03-10 17:02:40 +04:00
{
struct mmc_gpio * ctx = host - > slot . handler_priv ;
2017-11-08 09:34:54 +03:00
int irq = - EINVAL ;
int ret ;
2014-03-10 17:02:40 +04:00
if ( host - > slot . cd_irq > = 0 | | ! ctx | | ! ctx - > cd_gpio )
return ;
/*
2017-11-08 09:34:54 +03:00
* Do not use IRQ if the platform prefers to poll , e . g . , because that
* IRQ number is already used by another unit and cannot be shared .
2014-03-10 17:02:40 +04:00
*/
2023-02-15 00:41:19 +03:00
if ( ctx - > cd_irq > = 0 )
irq = ctx - > cd_irq ;
else if ( ! ( host - > caps & MMC_CAP_NEEDS_POLL ) )
2017-11-08 09:34:54 +03:00
irq = gpiod_to_irq ( ctx - > cd_gpio ) ;
2014-03-10 17:02:40 +04:00
if ( irq > = 0 ) {
2015-01-12 22:23:18 +03:00
if ( ! ctx - > cd_gpio_isr )
ctx - > cd_gpio_isr = mmc_gpio_cd_irqt ;
2014-12-18 17:44:33 +03:00
ret = devm_request_threaded_irq ( host - > parent , irq ,
2015-01-12 22:23:18 +03:00
NULL , ctx - > cd_gpio_isr ,
2014-03-10 17:02:40 +04:00
IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT ,
ctx - > cd_label , host ) ;
if ( ret < 0 )
irq = ret ;
}
host - > slot . cd_irq = irq ;
if ( irq < 0 )
host - > caps | = MMC_CAP_NEEDS_POLL ;
}
2014-03-10 17:02:41 +04:00
EXPORT_SYMBOL ( mmc_gpiod_request_cd_irq ) ;
2014-03-10 17:02:40 +04:00
2018-02-27 15:51:25 +03:00
int mmc_gpio_set_cd_wake ( struct mmc_host * host , bool on )
{
int ret = 0 ;
if ( ! ( host - > caps & MMC_CAP_CD_WAKE ) | |
host - > slot . cd_irq < 0 | |
on = = host - > slot . cd_wake_enabled )
return 0 ;
if ( on ) {
ret = enable_irq_wake ( host - > slot . cd_irq ) ;
host - > slot . cd_wake_enabled = ! ret ;
} else {
disable_irq_wake ( host - > slot . cd_irq ) ;
host - > slot . cd_wake_enabled = false ;
}
return ret ;
}
EXPORT_SYMBOL ( mmc_gpio_set_cd_wake ) ;
2015-01-12 22:23:18 +03:00
/* Register an alternate interrupt service routine for
* the card - detect GPIO .
*/
void mmc_gpio_set_cd_isr ( struct mmc_host * host ,
irqreturn_t ( * isr ) ( int irq , void * dev_id ) )
{
struct mmc_gpio * ctx = host - > slot . handler_priv ;
WARN_ON ( ctx - > cd_gpio_isr ) ;
ctx - > cd_gpio_isr = isr ;
}
EXPORT_SYMBOL ( mmc_gpio_set_cd_isr ) ;
2014-03-10 17:02:41 +04:00
/**
* mmc_gpiod_request_cd - request a gpio descriptor for card - detection
* @ host : mmc host
* @ con_id : function within the GPIO consumer
* @ idx : index of the GPIO to obtain in the consumer
* @ override_active_level : ignore % GPIO_ACTIVE_LOW flag
* @ debounce : debounce time in microseconds
*
2018-12-02 11:43:28 +03:00
* Note that this must be called prior to mmc_add_host ( )
2014-03-10 17:02:41 +04:00
* otherwise the caller must also call mmc_gpiod_request_cd_irq ( ) .
*
* Returns zero on success , else an error .
*/
int mmc_gpiod_request_cd ( struct mmc_host * host , const char * con_id ,
unsigned int idx , bool override_active_level ,
2019-12-11 05:40:56 +03:00
unsigned int debounce )
2014-03-10 17:02:41 +04:00
{
2014-12-18 17:44:36 +03:00
struct mmc_gpio * ctx = host - > slot . handler_priv ;
2014-03-10 17:02:41 +04:00
struct gpio_desc * desc ;
int ret ;
2014-08-27 15:00:50 +04:00
desc = devm_gpiod_get_index ( host - > parent , con_id , idx , GPIOD_IN ) ;
2014-03-10 17:02:41 +04:00
if ( IS_ERR ( desc ) )
return PTR_ERR ( desc ) ;
2021-09-29 14:17:57 +03:00
/* Update default label if no con_id provided */
if ( ! con_id )
gpiod_set_consumer_name ( desc , ctx - > cd_label ) ;
2014-03-10 17:02:41 +04:00
if ( debounce ) {
ret = gpiod_set_debounce ( desc , debounce ) ;
if ( ret < 0 )
2018-09-28 15:20:40 +03:00
ctx - > cd_debounce_delay_ms = debounce / 1000 ;
2014-03-10 17:02:41 +04:00
}
2019-12-11 05:40:56 +03:00
/* override forces default (active-low) polarity ... */
if ( override_active_level & & ! gpiod_is_active_low ( desc ) )
gpiod_toggle_active_low ( desc ) ;
/* ... or active-high */
if ( host - > caps2 & MMC_CAP2_CD_ACTIVE_HIGH )
gpiod_toggle_active_low ( desc ) ;
2014-03-10 17:02:41 +04:00
ctx - > cd_gpio = desc ;
return 0 ;
}
EXPORT_SYMBOL ( mmc_gpiod_request_cd ) ;
2016-10-12 05:50:37 +03:00
bool mmc_can_gpio_cd ( struct mmc_host * host )
{
struct mmc_gpio * ctx = host - > slot . handler_priv ;
return ctx - > cd_gpio ? true : false ;
}
EXPORT_SYMBOL ( mmc_can_gpio_cd ) ;
2014-08-27 15:00:51 +04:00
/**
* mmc_gpiod_request_ro - request a gpio descriptor for write protection
* @ host : mmc host
* @ con_id : function within the GPIO consumer
* @ idx : index of the GPIO to obtain in the consumer
* @ debounce : debounce time in microseconds
*
* Returns zero on success , else an error .
*/
int mmc_gpiod_request_ro ( struct mmc_host * host , const char * con_id ,
2019-12-11 05:40:56 +03:00
unsigned int idx , unsigned int debounce )
2014-08-27 15:00:51 +04:00
{
2014-12-18 17:44:36 +03:00
struct mmc_gpio * ctx = host - > slot . handler_priv ;
2014-08-27 15:00:51 +04:00
struct gpio_desc * desc ;
int ret ;
desc = devm_gpiod_get_index ( host - > parent , con_id , idx , GPIOD_IN ) ;
if ( IS_ERR ( desc ) )
return PTR_ERR ( desc ) ;
2021-09-29 14:17:57 +03:00
/* Update default label if no con_id provided */
if ( ! con_id )
gpiod_set_consumer_name ( desc , ctx - > ro_label ) ;
2014-08-27 15:00:51 +04:00
if ( debounce ) {
ret = gpiod_set_debounce ( desc , debounce ) ;
if ( ret < 0 )
return ret ;
}
2019-12-11 05:40:55 +03:00
if ( host - > caps2 & MMC_CAP2_RO_ACTIVE_HIGH )
gpiod_toggle_active_low ( desc ) ;
2014-08-27 15:00:51 +04:00
ctx - > ro_gpio = desc ;
return 0 ;
}
EXPORT_SYMBOL ( mmc_gpiod_request_ro ) ;
2018-01-17 19:28:05 +03:00
bool mmc_can_gpio_ro ( struct mmc_host * host )
{
struct mmc_gpio * ctx = host - > slot . handler_priv ;
return ctx - > ro_gpio ? true : false ;
}
EXPORT_SYMBOL ( mmc_can_gpio_ro ) ;