2019-05-19 15:51:31 +02:00
// SPDX-License-Identifier: GPL-2.0-or-later
2011-05-16 14:41:48 -07:00
/*
* Copyright ( c ) 2011 , NVIDIA Corporation .
*/
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
2018-08-14 00:35:24 +02:00
# include <linux/mod_devicetable.h>
2011-05-16 14:41:48 -07:00
# include <linux/rfkill.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/slab.h>
2013-10-16 13:53:43 +03:00
# include <linux/acpi.h>
2013-11-26 12:05:46 +02:00
# include <linux/gpio/consumer.h>
2011-05-16 14:41:48 -07:00
struct rfkill_gpio_data {
2013-10-16 13:53:42 +03:00
const char * name ;
enum rfkill_type type ;
2013-11-26 12:05:46 +02:00
struct gpio_desc * reset_gpio ;
struct gpio_desc * shutdown_gpio ;
2013-10-16 13:53:42 +03:00
struct rfkill * rfkill_dev ;
struct clk * clk ;
bool clk_enabled ;
2011-05-16 14:41:48 -07:00
} ;
static int rfkill_gpio_set_power ( void * data , bool blocked )
{
struct rfkill_gpio_data * rfkill = data ;
2014-05-07 11:38:11 +02: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-16 14:41:48 -07:00
2014-09-16 14:53:58 +02:00
rfkill - > clk_enabled = ! blocked ;
2011-05-16 14:41:48 -07:00
return 0 ;
}
static const struct rfkill_ops rfkill_gpio_ops = {
. set_block = rfkill_gpio_set_power ,
} ;
2014-10-27 12:15:14 +02: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 13:53:43 +03: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 13:53:43 +03:00
}
2011-05-16 14:41:48 -07:00
static int rfkill_gpio_probe ( struct platform_device * pdev )
{
2013-10-16 13:53:42 +03:00
struct rfkill_gpio_data * rfkill ;
2013-11-26 12:05:46 +02:00
struct gpio_desc * gpio ;
2016-01-25 12:03:47 +03:00
const char * type_name ;
2013-11-26 12:05:46 +02:00
int ret ;
2011-05-16 14:41:48 -07:00
2013-10-16 13:53:42 +03: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 13:53:43 +03:00
if ( ACPI_HANDLE ( & pdev - > dev ) ) {
ret = rfkill_gpio_acpi_probe ( & pdev - > dev , rfkill ) ;
if ( ret )
return ret ;
2011-05-16 14:41:48 -07:00
}
2014-04-01 17:02:53 +03:00
rfkill - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2011-05-16 14:41:48 -07:00
2015-05-28 11:46:12 +02:00
gpio = devm_gpiod_get_optional ( & pdev - > dev , " reset " , GPIOD_OUT_LOW ) ;
if ( IS_ERR ( gpio ) )
return PTR_ERR ( gpio ) ;
2013-11-26 12:05:46 +02:00
2015-05-28 11:46:12 +02: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-16 14:41:48 -07: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 12:05:46 +02:00
dev_err ( & pdev - > dev , " invalid platform data \n " ) ;
return - EINVAL ;
}
2013-10-16 13:53:42 +03:00
rfkill - > rfkill_dev = rfkill_alloc ( rfkill - > name , & pdev - > dev ,
rfkill - > type , & rfkill_gpio_ops ,
rfkill ) ;
2013-10-16 13:53:39 +03:00
if ( ! rfkill - > rfkill_dev )
return - ENOMEM ;
2011-05-16 14:41:48 -07:00
ret = rfkill_register ( rfkill - > rfkill_dev ) ;
if ( ret < 0 )
2018-04-26 09:31:52 +02:00
goto err_destroy ;
2011-05-16 14:41:48 -07:00
platform_set_drvdata ( pdev , rfkill ) ;
2013-10-16 13:53:42 +03:00
dev_info ( & pdev - > dev , " %s device registered. \n " , rfkill - > name ) ;
2011-05-16 14:41:48 -07:00
return 0 ;
2018-04-26 09:31:52 +02:00
err_destroy :
rfkill_destroy ( rfkill - > rfkill_dev ) ;
return ret ;
2011-05-16 14:41:48 -07: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 17:02:55 +03:00
# ifdef CONFIG_ACPI
2013-10-16 13:53:43 +03:00
static const struct acpi_device_id rfkill_acpi_match [ ] = {
{ " BCM4752 " , RFKILL_TYPE_GPS } ,
2014-04-01 17:02:54 +03:00
{ " LNV4752 " , RFKILL_TYPE_GPS } ,
2013-10-16 13:53:43 +03:00
{ } ,
} ;
2014-09-12 21:49:28 +02:00
MODULE_DEVICE_TABLE ( acpi , rfkill_acpi_match ) ;
2014-04-01 17:02:55 +03:00
# endif
2013-10-16 13:53:43 +03:00
2011-05-16 14:41:48 -07:00
static struct platform_driver rfkill_gpio_driver = {
. probe = rfkill_gpio_probe ,
2012-12-03 09:56:26 -05:00
. remove = rfkill_gpio_remove ,
2011-05-16 14:41:48 -07:00
. driver = {
2013-10-16 13:53:42 +03:00
. name = " rfkill_gpio " ,
2013-10-16 13:53:43 +03:00
. acpi_match_table = ACPI_PTR ( rfkill_acpi_match ) ,
2011-05-16 14:41:48 -07:00
} ,
} ;
2011-11-28 17:15:04 +08:00
module_platform_driver ( rfkill_gpio_driver ) ;
2011-05-16 14:41:48 -07:00
MODULE_DESCRIPTION ( " gpio rfkill " ) ;
MODULE_AUTHOR ( " NVIDIA " ) ;
MODULE_LICENSE ( " GPL " ) ;