2011-05-17 01:41:48 +04:00
/*
* Copyright ( c ) 2011 , NVIDIA Corporation .
*
* This program is free software ; you can redistribute it and / or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation ; either version 2 of the License , or
* ( at your option ) any later version .
*
* 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 , write to the Free Software Foundation , Inc . ,
* 51 Franklin Street , Fifth Floor , Boston , MA 02110 - 1301 , USA .
*/
# include <linux/gpio.h>
# include <linux/init.h>
# include <linux/kernel.h>
# include <linux/module.h>
# 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
# include <linux/rfkill-gpio.h>
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 - > name = dev_name ( dev ) ;
rfkill - > type = ( unsigned ) id - > driver_data ;
2014-10-27 13:15:14 +03:00
return acpi_dev_add_driver_gpios ( ACPI_COMPANION ( 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 )
{
struct rfkill_gpio_platform_data * pdata = pdev - > dev . platform_data ;
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 ;
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 ;
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 ;
} else if ( pdata ) {
2013-10-16 14:53:42 +04:00
rfkill - > name = pdata - > name ;
rfkill - > type = pdata - > type ;
} else {
return - ENODEV ;
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
2013-11-26 14:05:46 +04:00
/* Make sure at-least one of the GPIO is defined and that
* a name is specified for this instance
*/
if ( ( ! rfkill - > reset_gpio & & ! rfkill - > shutdown_gpio ) | | ! rfkill - > name ) {
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 )
2013-10-16 14:53:39 +04:00
return ret ;
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 ;
}
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 ) ;
2014-10-27 13:15:14 +03:00
acpi_dev_remove_driver_gpios ( ACPI_COMPANION ( & pdev - > dev ) ) ;
2011-05-17 01:41:48 +04:00
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 [ ] = {
2014-04-01 18:02:55 +04:00
{ " BCM2E1A " , RFKILL_TYPE_BLUETOOTH } ,
{ " BCM2E3D " , RFKILL_TYPE_BLUETOOTH } ,
2015-01-29 19:26:00 +03:00
{ " BCM2E40 " , RFKILL_TYPE_BLUETOOTH } ,
2014-08-19 16:41:32 +04:00
{ " BCM2E64 " , RFKILL_TYPE_BLUETOOTH } ,
2013-10-16 14:53:43 +04:00
{ " 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 " ) ;