2019-05-19 16:51:31 +03:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-05-17 01:41:48 +04:00
/*
* Copyright ( c ) 2011 , NVIDIA Corporation .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
2018-08-14 01:35:24 +03:00
# include <linux/mod_devicetable.h>
2011-05-17 01:41:48 +04:00
# include <linux/rfkill.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/slab.h>
2013-10-16 14:53:43 +04:00
# include <linux/acpi.h>
2013-11-26 14:05:46 +04:00
# include <linux/gpio/consumer.h>
2011-05-17 01:41:48 +04:00
struct rfkill_gpio_data {
2013-10-16 14:53:42 +04:00
const char * name ;
enum rfkill_type type ;
2013-11-26 14:05:46 +04:00
struct gpio_desc * reset_gpio ;
struct gpio_desc * shutdown_gpio ;
2013-10-16 14:53:42 +04:00
struct rfkill * rfkill_dev ;
struct clk * clk ;
bool clk_enabled ;
2011-05-17 01:41:48 +04:00
} ;
static int rfkill_gpio_set_power ( void * data , bool blocked )
{
struct rfkill_gpio_data * rfkill = data ;
2014-05-07 13:38:11 +04:00
if ( ! blocked & & ! IS_ERR ( rfkill - > clk ) & & ! rfkill - > clk_enabled )
clk_enable ( rfkill - > clk ) ;
gpiod_set_value_cansleep ( rfkill - > shutdown_gpio , ! blocked ) ;
gpiod_set_value_cansleep ( rfkill - > reset_gpio , ! blocked ) ;
if ( blocked & & ! IS_ERR ( rfkill - > clk ) & & rfkill - > clk_enabled )
clk_disable ( rfkill - > clk ) ;
2011-05-17 01:41:48 +04:00
2014-09-16 16:53:58 +04:00
rfkill - > clk_enabled = ! blocked ;
2011-05-17 01:41:48 +04:00
return 0 ;
}
static const struct rfkill_ops rfkill_gpio_ops = {
. set_block = rfkill_gpio_set_power ,
} ;
2014-10-27 13:15:14 +03:00
static const struct acpi_gpio_params reset_gpios = { 0 , 0 , false } ;
static const struct acpi_gpio_params shutdown_gpios = { 1 , 0 , false } ;
static const struct acpi_gpio_mapping acpi_rfkill_default_gpios [ ] = {
{ " reset-gpios " , & reset_gpios , 1 } ,
{ " shutdown-gpios " , & shutdown_gpios , 1 } ,
{ } ,
} ;
2013-10-16 14:53:43 +04:00
static int rfkill_gpio_acpi_probe ( struct device * dev ,
struct rfkill_gpio_data * rfkill )
{
const struct acpi_device_id * id ;
id = acpi_match_device ( dev - > driver - > acpi_match_table , dev ) ;
if ( ! id )
return - ENODEV ;
rfkill - > type = ( unsigned ) id - > driver_data ;
2017-06-10 22:10:34 +03:00
return devm_acpi_dev_add_driver_gpios ( dev , acpi_rfkill_default_gpios ) ;
2013-10-16 14:53:43 +04:00
}
2011-05-17 01:41:48 +04:00
static int rfkill_gpio_probe ( struct platform_device * pdev )
{
2013-10-16 14:53:42 +04:00
struct rfkill_gpio_data * rfkill ;
2013-11-26 14:05:46 +04:00
struct gpio_desc * gpio ;
2016-01-25 12:03:47 +03:00
const char * type_name ;
2013-11-26 14:05:46 +04:00
int ret ;
2011-05-17 01:41:48 +04:00
2013-10-16 14:53:42 +04:00
rfkill = devm_kzalloc ( & pdev - > dev , sizeof ( * rfkill ) , GFP_KERNEL ) ;
if ( ! rfkill )
return - ENOMEM ;
2016-01-25 12:03:47 +03:00
device_property_read_string ( & pdev - > dev , " name " , & rfkill - > name ) ;
device_property_read_string ( & pdev - > dev , " type " , & type_name ) ;
if ( ! rfkill - > name )
rfkill - > name = dev_name ( & pdev - > dev ) ;
rfkill - > type = rfkill_find_type ( type_name ) ;
2013-10-16 14:53:43 +04:00
if ( ACPI_HANDLE ( & pdev - > dev ) ) {
ret = rfkill_gpio_acpi_probe ( & pdev - > dev , rfkill ) ;
if ( ret )
return ret ;
2011-05-17 01:41:48 +04:00
}
2014-04-01 18:02:53 +04:00
rfkill - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2011-05-17 01:41:48 +04:00
2015-05-28 12:46:12 +03:00
gpio = devm_gpiod_get_optional ( & pdev - > dev , " reset " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( gpio ) )
return PTR_ERR ( gpio ) ;
2013-11-26 14:05:46 +04:00
2015-05-28 12:46:12 +03:00
rfkill - > reset_gpio = gpio ;
gpio = devm_gpiod_get_optional ( & pdev - > dev , " shutdown " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( gpio ) )
return PTR_ERR ( gpio ) ;
rfkill - > shutdown_gpio = gpio ;
2011-05-17 01:41:48 +04:00
2016-01-25 12:03:47 +03:00
/* Make sure at-least one GPIO is defined for this instance */
if ( ! rfkill - > reset_gpio & & ! rfkill - > shutdown_gpio ) {
2013-11-26 14:05:46 +04:00
dev_err ( & pdev - > dev , " invalid platform data \n " ) ;
return - EINVAL ;
}
2013-10-16 14:53:42 +04:00
rfkill - > rfkill_dev = rfkill_alloc ( rfkill - > name , & pdev - > dev ,
rfkill - > type , & rfkill_gpio_ops ,
rfkill ) ;
2013-10-16 14:53:39 +04:00
if ( ! rfkill - > rfkill_dev )
return - ENOMEM ;
2011-05-17 01:41:48 +04:00
ret = rfkill_register ( rfkill - > rfkill_dev ) ;
if ( ret < 0 )
2018-04-26 10:31:52 +03:00
goto err_destroy ;
2011-05-17 01:41:48 +04:00
platform_set_drvdata ( pdev , rfkill ) ;
2013-10-16 14:53:42 +04:00
dev_info ( & pdev - > dev , " %s device registered. \n " , rfkill - > name ) ;
2011-05-17 01:41:48 +04:00
return 0 ;
2018-04-26 10:31:52 +03:00
err_destroy :
rfkill_destroy ( rfkill - > rfkill_dev ) ;
return ret ;
2011-05-17 01:41:48 +04:00
}
static int rfkill_gpio_remove ( struct platform_device * pdev )
{
struct rfkill_gpio_data * rfkill = platform_get_drvdata ( pdev ) ;
rfkill_unregister ( rfkill - > rfkill_dev ) ;
rfkill_destroy ( rfkill - > rfkill_dev ) ;
return 0 ;
}
2014-04-01 18:02:55 +04:00
# ifdef CONFIG_ACPI
2013-10-16 14:53:43 +04:00
static const struct acpi_device_id rfkill_acpi_match [ ] = {
{ " BCM4752 " , RFKILL_TYPE_GPS } ,
2014-04-01 18:02:54 +04:00
{ " LNV4752 " , RFKILL_TYPE_GPS } ,
2013-10-16 14:53:43 +04:00
{ } ,
} ;
2014-09-12 23:49:28 +04:00
MODULE_DEVICE_TABLE ( acpi , rfkill_acpi_match ) ;
2014-04-01 18:02:55 +04:00
# endif
2013-10-16 14:53:43 +04:00
2011-05-17 01:41:48 +04:00
static struct platform_driver rfkill_gpio_driver = {
. probe = rfkill_gpio_probe ,
2012-12-03 18:56:26 +04:00
. remove = rfkill_gpio_remove ,
2011-05-17 01:41:48 +04:00
. driver = {
2013-10-16 14:53:42 +04:00
. name = " rfkill_gpio " ,
2013-10-16 14:53:43 +04:00
. acpi_match_table = ACPI_PTR ( rfkill_acpi_match ) ,
2011-05-17 01:41:48 +04:00
} ,
} ;
2011-11-28 13:15:04 +04:00
module_platform_driver ( rfkill_gpio_driver ) ;
2011-05-17 01:41:48 +04:00
MODULE_DESCRIPTION ( " gpio rfkill " ) ;
MODULE_AUTHOR ( " NVIDIA " ) ;
MODULE_LICENSE ( " GPL " ) ;