2015-04-05 02:13:03 +03:00
/*
*
* Bluetooth HCI UART driver for Broadcom devices
*
* Copyright ( C ) 2015 Intel 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 . , 59 Temple Place , Suite 330 , Boston , MA 02111 - 1307 USA
*
*/
# include <linux/kernel.h>
# include <linux/errno.h>
# include <linux/skbuff.h>
2015-05-28 12:25:01 +03:00
# include <linux/firmware.h>
2015-08-11 17:35:35 +03:00
# include <linux/module.h>
# include <linux/acpi.h>
# include <linux/platform_device.h>
# include <linux/clk.h>
# include <linux/gpio/consumer.h>
# include <linux/tty.h>
2015-09-04 16:35:44 +03:00
# include <linux/interrupt.h>
2015-09-23 19:18:08 +03:00
# include <linux/dmi.h>
2015-09-23 19:18:11 +03:00
# include <linux/pm_runtime.h>
2015-04-05 02:13:03 +03:00
# include <net/bluetooth/bluetooth.h>
# include <net/bluetooth/hci_core.h>
2015-04-06 08:52:18 +03:00
# include "btbcm.h"
2015-04-05 02:13:03 +03:00
# include "hci_uart.h"
2015-04-06 08:52:18 +03:00
2015-10-07 20:12:54 +03:00
# define BCM_LM_DIAG_PKT 0x07
# define BCM_LM_DIAG_SIZE 63
2015-09-23 19:18:11 +03:00
# define BCM_AUTOSUSPEND_DELAY 5000 /* default autosleep delay */
2015-08-11 17:35:35 +03:00
struct bcm_device {
struct list_head list ;
struct platform_device * pdev ;
const char * name ;
struct gpio_desc * device_wakeup ;
struct gpio_desc * shutdown ;
struct clk * clk ;
bool clk_enabled ;
2015-08-11 17:35:37 +03:00
u32 init_speed ;
2015-09-04 16:35:44 +03:00
int irq ;
u8 irq_polarity ;
2015-08-11 17:35:38 +03:00
2015-09-23 19:18:09 +03:00
# ifdef CONFIG_PM
2015-08-11 17:35:38 +03:00
struct hci_uart * hu ;
bool is_suspended ; /* suspend/resume flag */
# endif
2015-08-11 17:35:35 +03:00
} ;
2015-04-06 08:52:18 +03:00
struct bcm_data {
2015-08-11 17:35:35 +03:00
struct sk_buff * rx_skb ;
struct sk_buff_head txq ;
struct bcm_device * dev ;
2015-04-06 08:52:18 +03:00
} ;
2015-08-11 17:35:35 +03:00
/* List of BCM BT UART devices */
2015-09-01 13:13:35 +03:00
static DEFINE_MUTEX ( bcm_device_lock ) ;
2015-08-11 17:35:35 +03:00
static LIST_HEAD ( bcm_device_list ) ;
2015-06-09 17:15:37 +03:00
static int bcm_set_baudrate ( struct hci_uart * hu , unsigned int speed )
{
struct hci_dev * hdev = hu - > hdev ;
struct sk_buff * skb ;
struct bcm_update_uart_baud_rate param ;
if ( speed > 3000000 ) {
struct bcm_write_uart_clock_setting clock ;
clock . type = BCM_UART_CLOCK_48MHZ ;
2015-09-01 13:13:36 +03:00
bt_dev_dbg ( hdev , " Set Controller clock (%d) " , clock . type ) ;
2015-06-09 17:15:37 +03:00
/* This Broadcom specific command changes the UART's controller
* clock for baud rate > 3000000.
*/
skb = __hci_cmd_sync ( hdev , 0xfc45 , 1 , & clock , HCI_INIT_TIMEOUT ) ;
if ( IS_ERR ( skb ) ) {
int err = PTR_ERR ( skb ) ;
2015-09-01 13:13:36 +03:00
bt_dev_err ( hdev , " BCM: failed to write clock (%d) " ,
err ) ;
2015-06-09 17:15:37 +03:00
return err ;
}
kfree_skb ( skb ) ;
}
2015-09-01 13:13:36 +03:00
bt_dev_dbg ( hdev , " Set Controller UART speed to %d bit/s " , speed ) ;
2015-06-09 17:15:37 +03:00
param . zero = cpu_to_le16 ( 0 ) ;
param . baud_rate = cpu_to_le32 ( speed ) ;
/* This Broadcom specific command changes the UART's controller baud
* rate .
*/
skb = __hci_cmd_sync ( hdev , 0xfc18 , sizeof ( param ) , & param ,
HCI_INIT_TIMEOUT ) ;
if ( IS_ERR ( skb ) ) {
int err = PTR_ERR ( skb ) ;
2015-09-01 13:13:36 +03:00
bt_dev_err ( hdev , " BCM: failed to write update baudrate (%d) " ,
err ) ;
2015-06-09 17:15:37 +03:00
return err ;
}
kfree_skb ( skb ) ;
return 0 ;
}
2015-08-28 16:44:00 +03:00
/* bcm_device_exists should be protected by bcm_device_lock */
2015-08-11 17:35:35 +03:00
static bool bcm_device_exists ( struct bcm_device * device )
{
struct list_head * p ;
list_for_each ( p , & bcm_device_list ) {
struct bcm_device * dev = list_entry ( p , struct bcm_device , list ) ;
if ( device = = dev )
return true ;
}
return false ;
}
static int bcm_gpio_set_power ( struct bcm_device * dev , bool powered )
{
if ( powered & & ! IS_ERR ( dev - > clk ) & & ! dev - > clk_enabled )
2017-03-15 15:20:05 +03:00
clk_prepare_enable ( dev - > clk ) ;
2015-08-11 17:35:35 +03:00
2015-08-17 17:00:21 +03:00
gpiod_set_value ( dev - > shutdown , powered ) ;
gpiod_set_value ( dev - > device_wakeup , powered ) ;
2015-08-11 17:35:35 +03:00
if ( ! powered & & ! IS_ERR ( dev - > clk ) & & dev - > clk_enabled )
2017-03-15 15:20:05 +03:00
clk_disable_unprepare ( dev - > clk ) ;
2015-08-11 17:35:35 +03:00
dev - > clk_enabled = powered ;
return 0 ;
}
2015-09-23 19:18:09 +03:00
# ifdef CONFIG_PM
2015-09-04 16:35:44 +03:00
static irqreturn_t bcm_host_wake ( int irq , void * data )
{
struct bcm_device * bdev = data ;
bt_dev_dbg ( bdev , " Host wake IRQ " ) ;
2015-09-23 19:18:11 +03:00
pm_runtime_get ( & bdev - > pdev - > dev ) ;
pm_runtime_mark_last_busy ( & bdev - > pdev - > dev ) ;
pm_runtime_put_autosuspend ( & bdev - > pdev - > dev ) ;
2015-09-04 16:35:44 +03:00
return IRQ_HANDLED ;
}
static int bcm_request_irq ( struct bcm_data * bcm )
{
struct bcm_device * bdev = bcm - > dev ;
int err = 0 ;
/* If this is not a platform device, do not enable PM functionalities */
mutex_lock ( & bcm_device_lock ) ;
if ( ! bcm_device_exists ( bdev ) ) {
err = - ENODEV ;
goto unlock ;
}
if ( bdev - > irq > 0 ) {
err = devm_request_irq ( & bdev - > pdev - > dev , bdev - > irq ,
bcm_host_wake , IRQF_TRIGGER_RISING ,
" host_wake " , bdev ) ;
if ( err )
goto unlock ;
device_init_wakeup ( & bdev - > pdev - > dev , true ) ;
2015-09-23 19:18:11 +03:00
pm_runtime_set_autosuspend_delay ( & bdev - > pdev - > dev ,
BCM_AUTOSUSPEND_DELAY ) ;
pm_runtime_use_autosuspend ( & bdev - > pdev - > dev ) ;
pm_runtime_set_active ( & bdev - > pdev - > dev ) ;
pm_runtime_enable ( & bdev - > pdev - > dev ) ;
2015-09-04 16:35:44 +03:00
}
unlock :
mutex_unlock ( & bcm_device_lock ) ;
return err ;
}
static const struct bcm_set_sleep_mode default_sleep_params = {
. sleep_mode = 1 , /* 0=Disabled, 1=UART, 2=Reserved, 3=USB */
. idle_host = 2 , /* idle threshold HOST, in 300ms */
. idle_dev = 2 , /* idle threshold device, in 300ms */
. bt_wake_active = 1 , /* BT_WAKE active mode: 1 = high, 0 = low */
. host_wake_active = 0 , /* HOST_WAKE active mode: 1 = high, 0 = low */
. allow_host_sleep = 1 , /* Allow host sleep in SCO flag */
2015-09-23 19:18:11 +03:00
. combine_modes = 1 , /* Combine sleep and LPM flag */
2015-09-04 16:35:44 +03:00
. tristate_control = 0 , /* Allow tri-state control of UART tx flag */
/* Irrelevant USB flags */
. usb_auto_sleep = 0 ,
. usb_resume_timeout = 0 ,
. pulsed_host_wake = 0 ,
. break_to_host = 0
} ;
static int bcm_setup_sleep ( struct hci_uart * hu )
{
struct bcm_data * bcm = hu - > priv ;
struct sk_buff * skb ;
struct bcm_set_sleep_mode sleep_params = default_sleep_params ;
sleep_params . host_wake_active = ! bcm - > dev - > irq_polarity ;
skb = __hci_cmd_sync ( hu - > hdev , 0xfc27 , sizeof ( sleep_params ) ,
& sleep_params , HCI_INIT_TIMEOUT ) ;
if ( IS_ERR ( skb ) ) {
int err = PTR_ERR ( skb ) ;
bt_dev_err ( hu - > hdev , " Sleep VSC failed (%d) " , err ) ;
return err ;
}
kfree_skb ( skb ) ;
bt_dev_dbg ( hu - > hdev , " Set Sleep Parameters VSC succeeded " ) ;
return 0 ;
}
# else
static inline int bcm_request_irq ( struct bcm_data * bcm ) { return 0 ; }
static inline int bcm_setup_sleep ( struct hci_uart * hu ) { return 0 ; }
# endif
2015-10-07 21:08:26 +03:00
static int bcm_set_diag ( struct hci_dev * hdev , bool enable )
{
struct hci_uart * hu = hci_get_drvdata ( hdev ) ;
struct bcm_data * bcm = hu - > priv ;
struct sk_buff * skb ;
if ( ! test_bit ( HCI_RUNNING , & hdev - > flags ) )
return - ENETDOWN ;
skb = bt_skb_alloc ( 3 , GFP_KERNEL ) ;
2015-10-22 12:06:09 +03:00
if ( ! skb )
return - ENOMEM ;
2015-10-07 21:08:26 +03:00
networking: add and use skb_put_u8()
Joe and Bjørn suggested that it'd be nicer to not have the
cast in the fairly common case of doing
*(u8 *)skb_put(skb, 1) = c;
Add skb_put_u8() for this case, and use it across the code,
using the following spatch:
@@
expression SKB, C, S;
typedef u8;
identifier fn = {skb_put};
fresh identifier fn2 = fn ## "_u8";
@@
- *(u8 *)fn(SKB, S) = C;
+ fn2(SKB, C);
Note that due to the "S", the spatch isn't perfect, it should
have checked that S is 1, but there's also places that use a
sizeof expression like sizeof(var) or sizeof(u8) etc. Turns
out that nobody ever did something like
*(u8 *)skb_put(skb, 2) = c;
which would be wrong anyway since the second byte wouldn't be
initialized.
Suggested-by: Joe Perches <joe@perches.com>
Suggested-by: Bjørn Mork <bjorn@mork.no>
Signed-off-by: Johannes Berg <johannes.berg@intel.com>
Signed-off-by: David S. Miller <davem@davemloft.net>
2017-06-16 15:29:24 +03:00
skb_put_u8 ( skb , BCM_LM_DIAG_PKT ) ;
skb_put_u8 ( skb , 0xf0 ) ;
skb_put_u8 ( skb , enable ) ;
2015-10-07 21:08:26 +03:00
skb_queue_tail ( & bcm - > txq , skb ) ;
hci_uart_tx_wakeup ( hu ) ;
return 0 ;
}
2015-04-06 08:52:18 +03:00
static int bcm_open ( struct hci_uart * hu )
{
struct bcm_data * bcm ;
2015-08-11 17:35:35 +03:00
struct list_head * p ;
2015-04-06 08:52:18 +03:00
2015-09-01 13:13:36 +03:00
bt_dev_dbg ( hu - > hdev , " hu %p " , hu ) ;
2015-04-06 08:52:18 +03:00
bcm = kzalloc ( sizeof ( * bcm ) , GFP_KERNEL ) ;
if ( ! bcm )
return - ENOMEM ;
skb_queue_head_init ( & bcm - > txq ) ;
hu - > priv = bcm ;
2015-08-11 17:35:35 +03:00
2017-03-29 19:15:27 +03:00
if ( ! hu - > tty - > dev )
goto out ;
2015-09-01 13:13:35 +03:00
mutex_lock ( & bcm_device_lock ) ;
2015-08-11 17:35:35 +03:00
list_for_each ( p , & bcm_device_list ) {
struct bcm_device * dev = list_entry ( p , struct bcm_device , list ) ;
/* Retrieve saved bcm_device based on parent of the
* platform device ( saved during device probe ) and
* parent of tty device used by hci_uart
*/
if ( hu - > tty - > dev - > parent = = dev - > pdev - > dev . parent ) {
bcm - > dev = dev ;
2015-08-11 17:35:37 +03:00
hu - > init_speed = dev - > init_speed ;
2015-09-23 19:18:09 +03:00
# ifdef CONFIG_PM
2015-08-11 17:35:38 +03:00
dev - > hu = hu ;
# endif
2015-09-04 16:35:44 +03:00
bcm_gpio_set_power ( bcm - > dev , true ) ;
2015-08-11 17:35:35 +03:00
break ;
}
}
2015-09-01 13:13:35 +03:00
mutex_unlock ( & bcm_device_lock ) ;
2017-03-29 19:15:27 +03:00
out :
2015-04-06 08:52:18 +03:00
return 0 ;
}
static int bcm_close ( struct hci_uart * hu )
{
struct bcm_data * bcm = hu - > priv ;
2015-09-04 16:35:44 +03:00
struct bcm_device * bdev = bcm - > dev ;
2015-04-06 08:52:18 +03:00
2015-09-01 13:13:36 +03:00
bt_dev_dbg ( hu - > hdev , " hu %p " , hu ) ;
2015-04-06 08:52:18 +03:00
2015-08-11 17:35:35 +03:00
/* Protect bcm->dev against removal of the device or driver */
2015-09-01 13:13:35 +03:00
mutex_lock ( & bcm_device_lock ) ;
2015-09-04 16:35:44 +03:00
if ( bcm_device_exists ( bdev ) ) {
bcm_gpio_set_power ( bdev , false ) ;
2015-09-23 19:18:09 +03:00
# ifdef CONFIG_PM
2015-09-23 19:18:11 +03:00
pm_runtime_disable ( & bdev - > pdev - > dev ) ;
pm_runtime_set_suspended ( & bdev - > pdev - > dev ) ;
2015-09-04 16:35:44 +03:00
if ( device_can_wakeup ( & bdev - > pdev - > dev ) ) {
devm_free_irq ( & bdev - > pdev - > dev , bdev - > irq , bdev ) ;
device_init_wakeup ( & bdev - > pdev - > dev , false ) ;
}
bdev - > hu = NULL ;
2015-08-11 17:35:38 +03:00
# endif
}
2015-09-01 13:13:35 +03:00
mutex_unlock ( & bcm_device_lock ) ;
2015-08-11 17:35:35 +03:00
2015-04-06 08:52:18 +03:00
skb_queue_purge ( & bcm - > txq ) ;
kfree_skb ( bcm - > rx_skb ) ;
kfree ( bcm ) ;
hu - > priv = NULL ;
return 0 ;
}
static int bcm_flush ( struct hci_uart * hu )
{
struct bcm_data * bcm = hu - > priv ;
2015-09-01 13:13:36 +03:00
bt_dev_dbg ( hu - > hdev , " hu %p " , hu ) ;
2015-04-06 08:52:18 +03:00
skb_queue_purge ( & bcm - > txq ) ;
return 0 ;
}
static int bcm_setup ( struct hci_uart * hu )
{
2015-09-04 16:35:44 +03:00
struct bcm_data * bcm = hu - > priv ;
2015-05-28 12:25:05 +03:00
char fw_name [ 64 ] ;
const struct firmware * fw ;
2015-06-18 13:43:27 +03:00
unsigned int speed ;
2015-05-28 12:25:05 +03:00
int err ;
2015-09-01 13:13:36 +03:00
bt_dev_dbg ( hu - > hdev , " hu %p " , hu ) ;
2015-04-06 08:52:18 +03:00
2015-10-07 21:08:26 +03:00
hu - > hdev - > set_diag = bcm_set_diag ;
2015-04-06 08:52:18 +03:00
hu - > hdev - > set_bdaddr = btbcm_set_bdaddr ;
2015-05-28 12:25:05 +03:00
err = btbcm_initialize ( hu - > hdev , fw_name , sizeof ( fw_name ) ) ;
if ( err )
return err ;
err = request_firmware ( & fw , fw_name , & hu - > hdev - > dev ) ;
if ( err < 0 ) {
2015-09-01 13:13:36 +03:00
bt_dev_info ( hu - > hdev , " BCM: Patch %s not found " , fw_name ) ;
2015-05-28 12:25:05 +03:00
return 0 ;
}
err = btbcm_patchram ( hu - > hdev , fw ) ;
if ( err ) {
2015-09-01 13:13:36 +03:00
bt_dev_info ( hu - > hdev , " BCM: Patch failed (%d) " , err ) ;
2015-05-28 12:25:05 +03:00
goto finalize ;
}
2015-06-18 13:43:27 +03:00
/* Init speed if any */
if ( hu - > init_speed )
speed = hu - > init_speed ;
else if ( hu - > proto - > init_speed )
speed = hu - > proto - > init_speed ;
else
speed = 0 ;
if ( speed )
hci_uart_set_baudrate ( hu , speed ) ;
/* Operational speed if any */
if ( hu - > oper_speed )
speed = hu - > oper_speed ;
else if ( hu - > proto - > oper_speed )
speed = hu - > proto - > oper_speed ;
else
speed = 0 ;
if ( speed ) {
err = bcm_set_baudrate ( hu , speed ) ;
2015-06-09 17:15:37 +03:00
if ( ! err )
2015-06-18 13:43:27 +03:00
hci_uart_set_baudrate ( hu , speed ) ;
2015-06-09 17:15:37 +03:00
}
2015-05-28 12:25:05 +03:00
finalize :
release_firmware ( fw ) ;
err = btbcm_finalize ( hu - > hdev ) ;
2015-09-04 16:35:44 +03:00
if ( err )
return err ;
2017-06-27 20:15:07 +03:00
if ( ! bcm_request_irq ( bcm ) )
2015-09-04 16:35:44 +03:00
err = bcm_setup_sleep ( hu ) ;
2015-05-28 12:25:05 +03:00
return err ;
2015-04-06 08:52:18 +03:00
}
2015-10-07 20:12:54 +03:00
# define BCM_RECV_LM_DIAG \
. type = BCM_LM_DIAG_PKT , \
. hlen = BCM_LM_DIAG_SIZE , \
. loff = 0 , \
. lsize = 0 , \
. maxlen = BCM_LM_DIAG_SIZE
2015-04-06 09:44:59 +03:00
static const struct h4_recv_pkt bcm_recv_pkts [ ] = {
2015-10-07 20:12:54 +03:00
{ H4_RECV_ACL , . recv = hci_recv_frame } ,
{ H4_RECV_SCO , . recv = hci_recv_frame } ,
{ H4_RECV_EVENT , . recv = hci_recv_frame } ,
{ BCM_RECV_LM_DIAG , . recv = hci_recv_diag } ,
2015-04-06 09:44:59 +03:00
} ;
2015-04-06 08:52:18 +03:00
static int bcm_recv ( struct hci_uart * hu , const void * data , int count )
{
struct bcm_data * bcm = hu - > priv ;
if ( ! test_bit ( HCI_UART_REGISTERED , & hu - > flags ) )
return - EUNATCH ;
2015-04-06 09:44:59 +03:00
bcm - > rx_skb = h4_recv_buf ( hu - > hdev , bcm - > rx_skb , data , count ,
bcm_recv_pkts , ARRAY_SIZE ( bcm_recv_pkts ) ) ;
2015-04-06 08:52:18 +03:00
if ( IS_ERR ( bcm - > rx_skb ) ) {
int err = PTR_ERR ( bcm - > rx_skb ) ;
2015-09-01 13:13:36 +03:00
bt_dev_err ( hu - > hdev , " Frame reassembly failed (%d) " , err ) ;
2015-06-17 15:10:39 +03:00
bcm - > rx_skb = NULL ;
2015-04-06 08:52:18 +03:00
return err ;
2015-09-23 19:18:11 +03:00
} else if ( ! bcm - > rx_skb ) {
/* Delay auto-suspend when receiving completed packet */
mutex_lock ( & bcm_device_lock ) ;
if ( bcm - > dev & & bcm_device_exists ( bcm - > dev ) ) {
pm_runtime_get ( & bcm - > dev - > pdev - > dev ) ;
pm_runtime_mark_last_busy ( & bcm - > dev - > pdev - > dev ) ;
pm_runtime_put_autosuspend ( & bcm - > dev - > pdev - > dev ) ;
}
mutex_unlock ( & bcm_device_lock ) ;
2015-04-06 08:52:18 +03:00
}
return count ;
}
static int bcm_enqueue ( struct hci_uart * hu , struct sk_buff * skb )
{
struct bcm_data * bcm = hu - > priv ;
2015-09-01 13:13:36 +03:00
bt_dev_dbg ( hu - > hdev , " hu %p skb %p " , hu , skb ) ;
2015-04-06 08:52:18 +03:00
/* Prepend skb with frame type */
2015-11-05 09:33:56 +03:00
memcpy ( skb_push ( skb , 1 ) , & hci_skb_pkt_type ( skb ) , 1 ) ;
2015-04-06 08:52:18 +03:00
skb_queue_tail ( & bcm - > txq , skb ) ;
return 0 ;
}
static struct sk_buff * bcm_dequeue ( struct hci_uart * hu )
{
struct bcm_data * bcm = hu - > priv ;
2015-09-23 19:18:11 +03:00
struct sk_buff * skb = NULL ;
struct bcm_device * bdev = NULL ;
mutex_lock ( & bcm_device_lock ) ;
if ( bcm_device_exists ( bcm - > dev ) ) {
bdev = bcm - > dev ;
pm_runtime_get_sync ( & bdev - > pdev - > dev ) ;
/* Shall be resumed here */
}
skb = skb_dequeue ( & bcm - > txq ) ;
if ( bdev ) {
pm_runtime_mark_last_busy ( & bdev - > pdev - > dev ) ;
pm_runtime_put_autosuspend ( & bdev - > pdev - > dev ) ;
}
2015-04-06 08:52:18 +03:00
2015-09-23 19:18:11 +03:00
mutex_unlock ( & bcm_device_lock ) ;
return skb ;
2015-04-06 08:52:18 +03:00
}
2015-09-23 19:18:09 +03:00
# ifdef CONFIG_PM
static int bcm_suspend_device ( struct device * dev )
2015-08-11 17:35:38 +03:00
{
struct bcm_device * bdev = platform_get_drvdata ( to_platform_device ( dev ) ) ;
2015-09-23 19:18:09 +03:00
bt_dev_dbg ( bdev , " " ) ;
2015-08-28 16:44:00 +03:00
2015-09-23 19:18:09 +03:00
if ( ! bdev - > is_suspended & & bdev - > hu ) {
2015-08-11 17:35:38 +03:00
hci_uart_set_flow_control ( bdev - > hu , true ) ;
2015-09-23 19:18:09 +03:00
/* Once this returns, driver suspends BT via GPIO */
2015-08-11 17:35:38 +03:00
bdev - > is_suspended = true ;
}
/* Suspend the device */
if ( bdev - > device_wakeup ) {
gpiod_set_value ( bdev - > device_wakeup , false ) ;
2015-09-01 13:13:36 +03:00
bt_dev_dbg ( bdev , " suspend, delaying 15 ms " ) ;
2015-08-11 17:35:38 +03:00
mdelay ( 15 ) ;
}
2015-09-23 19:18:09 +03:00
return 0 ;
}
static int bcm_resume_device ( struct device * dev )
{
struct bcm_device * bdev = platform_get_drvdata ( to_platform_device ( dev ) ) ;
bt_dev_dbg ( bdev , " " ) ;
if ( bdev - > device_wakeup ) {
gpiod_set_value ( bdev - > device_wakeup , true ) ;
bt_dev_dbg ( bdev , " resume, delaying 15 ms " ) ;
mdelay ( 15 ) ;
}
/* When this executes, the device has woken up already */
if ( bdev - > is_suspended & & bdev - > hu ) {
bdev - > is_suspended = false ;
hci_uart_set_flow_control ( bdev - > hu , false ) ;
}
return 0 ;
}
# endif
# ifdef CONFIG_PM_SLEEP
/* Platform suspend callback */
static int bcm_suspend ( struct device * dev )
{
struct bcm_device * bdev = platform_get_drvdata ( to_platform_device ( dev ) ) ;
int error ;
bt_dev_dbg ( bdev , " suspend: is_suspended %d " , bdev - > is_suspended ) ;
/* bcm_suspend can be called at any time as long as platform device is
* bound , so it should use bcm_device_lock to protect access to hci_uart
* and device_wake - up GPIO .
*/
mutex_lock ( & bcm_device_lock ) ;
if ( ! bdev - > hu )
goto unlock ;
2015-09-23 19:18:11 +03:00
if ( pm_runtime_active ( dev ) )
bcm_suspend_device ( dev ) ;
2015-09-23 19:18:09 +03:00
2015-09-04 16:35:44 +03:00
if ( device_may_wakeup ( & bdev - > pdev - > dev ) ) {
error = enable_irq_wake ( bdev - > irq ) ;
if ( ! error )
bt_dev_dbg ( bdev , " BCM irq: enabled " ) ;
}
2015-08-28 16:44:00 +03:00
unlock :
2015-09-01 13:13:35 +03:00
mutex_unlock ( & bcm_device_lock ) ;
2015-08-28 16:44:00 +03:00
2015-08-11 17:35:38 +03:00
return 0 ;
}
/* Platform resume callback */
static int bcm_resume ( struct device * dev )
{
struct bcm_device * bdev = platform_get_drvdata ( to_platform_device ( dev ) ) ;
2015-09-01 13:13:36 +03:00
bt_dev_dbg ( bdev , " resume: is_suspended %d " , bdev - > is_suspended ) ;
2015-08-11 17:35:38 +03:00
2015-09-23 19:18:09 +03:00
/* bcm_resume can be called at any time as long as platform device is
* bound , so it should use bcm_device_lock to protect access to hci_uart
* and device_wake - up GPIO .
*/
2015-09-01 13:13:35 +03:00
mutex_lock ( & bcm_device_lock ) ;
2015-08-28 16:44:00 +03:00
if ( ! bdev - > hu )
goto unlock ;
2015-09-04 16:35:44 +03:00
if ( device_may_wakeup ( & bdev - > pdev - > dev ) ) {
disable_irq_wake ( bdev - > irq ) ;
bt_dev_dbg ( bdev , " BCM irq: disabled " ) ;
}
2015-09-23 19:18:09 +03:00
bcm_resume_device ( dev ) ;
2015-08-11 17:35:38 +03:00
2015-08-28 16:44:00 +03:00
unlock :
2015-09-01 13:13:35 +03:00
mutex_unlock ( & bcm_device_lock ) ;
2015-08-28 16:44:00 +03:00
2015-09-23 19:18:11 +03:00
pm_runtime_disable ( dev ) ;
pm_runtime_set_active ( dev ) ;
pm_runtime_enable ( dev ) ;
2015-08-11 17:35:38 +03:00
return 0 ;
}
# endif
2017-01-05 20:10:54 +03:00
static const struct acpi_gpio_params int_last_device_wakeup_gpios = { 0 , 0 , false } ;
static const struct acpi_gpio_params int_last_shutdown_gpios = { 1 , 0 , false } ;
static const struct acpi_gpio_params int_last_host_wakeup_gpios = { 2 , 0 , false } ;
static const struct acpi_gpio_mapping acpi_bcm_int_last_gpios [ ] = {
{ " device-wakeup-gpios " , & int_last_device_wakeup_gpios , 1 } ,
{ " shutdown-gpios " , & int_last_shutdown_gpios , 1 } ,
{ " host-wakeup-gpios " , & int_last_host_wakeup_gpios , 1 } ,
{ } ,
} ;
static const struct acpi_gpio_params int_first_host_wakeup_gpios = { 0 , 0 , false } ;
static const struct acpi_gpio_params int_first_device_wakeup_gpios = { 1 , 0 , false } ;
static const struct acpi_gpio_params int_first_shutdown_gpios = { 2 , 0 , false } ;
static const struct acpi_gpio_mapping acpi_bcm_int_first_gpios [ ] = {
{ " device-wakeup-gpios " , & int_first_device_wakeup_gpios , 1 } ,
{ " shutdown-gpios " , & int_first_shutdown_gpios , 1 } ,
{ " host-wakeup-gpios " , & int_first_host_wakeup_gpios , 1 } ,
2015-08-11 17:35:35 +03:00
{ } ,
} ;
2015-08-12 13:46:01 +03:00
# ifdef CONFIG_ACPI
2015-09-23 19:18:08 +03:00
static u8 acpi_active_low = ACPI_ACTIVE_LOW ;
/* IRQ polarity of some chipsets are not defined correctly in ACPI table. */
static const struct dmi_system_id bcm_wrong_irq_dmi_table [ ] = {
{
. ident = " Asus T100TA " ,
. matches = {
DMI_EXACT_MATCH ( DMI_SYS_VENDOR ,
" ASUSTeK COMPUTER INC. " ) ,
DMI_EXACT_MATCH ( DMI_PRODUCT_NAME , " T100TA " ) ,
} ,
. driver_data = & acpi_active_low ,
} ,
2017-06-29 15:21:32 +03:00
{
. ident = " Asus T100CHI " ,
. matches = {
DMI_EXACT_MATCH ( DMI_SYS_VENDOR ,
" ASUSTeK COMPUTER INC. " ) ,
DMI_EXACT_MATCH ( DMI_PRODUCT_NAME , " T100CHI " ) ,
} ,
. driver_data = & acpi_active_low ,
} ,
2016-10-09 16:51:05 +03:00
{ /* Handle ThinkPad 8 tablets with BCM2E55 chipset ACPI ID */
. ident = " Lenovo ThinkPad 8 " ,
. matches = {
DMI_EXACT_MATCH ( DMI_SYS_VENDOR , " LENOVO " ) ,
DMI_EXACT_MATCH ( DMI_PRODUCT_VERSION , " ThinkPad 8 " ) ,
} ,
. driver_data = & acpi_active_low ,
} ,
2015-09-23 19:18:08 +03:00
{ }
} ;
2015-08-11 17:35:37 +03:00
static int bcm_resource ( struct acpi_resource * ares , void * data )
{
struct bcm_device * dev = data ;
2015-09-04 16:35:44 +03:00
struct acpi_resource_extended_irq * irq ;
struct acpi_resource_gpio * gpio ;
struct acpi_resource_uart_serialbus * sb ;
switch ( ares - > type ) {
case ACPI_RESOURCE_TYPE_EXTENDED_IRQ :
irq = & ares - > data . extended_irq ;
dev - > irq_polarity = irq - > polarity ;
break ;
case ACPI_RESOURCE_TYPE_GPIO :
gpio = & ares - > data . gpio ;
if ( gpio - > connection_type = = ACPI_RESOURCE_GPIO_TYPE_INT )
dev - > irq_polarity = gpio - > polarity ;
break ;
case ACPI_RESOURCE_TYPE_SERIAL_BUS :
2015-08-11 17:35:37 +03:00
sb = & ares - > data . uart_serial_bus ;
if ( sb - > type = = ACPI_RESOURCE_SERIAL_TYPE_UART )
dev - > init_speed = sb - > default_baud_rate ;
2015-09-04 16:35:44 +03:00
break ;
default :
break ;
2015-08-11 17:35:37 +03:00
}
/* Always tell the ACPI core to skip this resource */
return 1 ;
}
2017-03-10 15:28:20 +03:00
# endif /* CONFIG_ACPI */
2015-08-11 17:35:37 +03:00
2017-03-10 15:28:20 +03:00
static int bcm_platform_probe ( struct bcm_device * dev )
2015-08-11 17:35:35 +03:00
{
struct platform_device * pdev = dev - > pdev ;
dev - > name = dev_name ( & pdev - > dev ) ;
2017-01-05 20:10:54 +03:00
2015-08-11 17:35:35 +03:00
dev - > clk = devm_clk_get ( & pdev - > dev , NULL ) ;
2015-08-12 10:20:56 +03:00
dev - > device_wakeup = devm_gpiod_get_optional ( & pdev - > dev ,
" device-wakeup " ,
GPIOD_OUT_LOW ) ;
if ( IS_ERR ( dev - > device_wakeup ) )
return PTR_ERR ( dev - > device_wakeup ) ;
2015-08-11 17:35:35 +03:00
2015-08-12 10:20:56 +03:00
dev - > shutdown = devm_gpiod_get_optional ( & pdev - > dev , " shutdown " ,
GPIOD_OUT_LOW ) ;
if ( IS_ERR ( dev - > shutdown ) )
return PTR_ERR ( dev - > shutdown ) ;
2015-08-11 17:35:35 +03:00
2015-09-04 16:35:44 +03:00
/* IRQ can be declared in ACPI table as Interrupt or GpioInt */
dev - > irq = platform_get_irq ( pdev , 0 ) ;
if ( dev - > irq < = 0 ) {
struct gpio_desc * gpio ;
gpio = devm_gpiod_get_optional ( & pdev - > dev , " host-wakeup " ,
GPIOD_IN ) ;
if ( IS_ERR ( gpio ) )
return PTR_ERR ( gpio ) ;
dev - > irq = gpiod_to_irq ( gpio ) ;
}
dev_info ( & pdev - > dev , " BCM irq: %d \n " , dev - > irq ) ;
2015-08-11 17:35:35 +03:00
/* Make sure at-least one of the GPIO is defined and that
* a name is specified for this instance
*/
if ( ( ! dev - > device_wakeup & & ! dev - > shutdown ) | | ! dev - > name ) {
dev_err ( & pdev - > dev , " invalid platform data \n " ) ;
return - EINVAL ;
}
2017-03-10 15:28:20 +03:00
return 0 ;
}
# ifdef CONFIG_ACPI
static int bcm_acpi_probe ( struct bcm_device * dev )
{
struct platform_device * pdev = dev - > pdev ;
LIST_HEAD ( resources ) ;
const struct dmi_system_id * dmi_id ;
const struct acpi_gpio_mapping * gpio_mapping = acpi_bcm_int_last_gpios ;
const struct acpi_device_id * id ;
int ret ;
/* Retrieve GPIO data */
id = acpi_match_device ( pdev - > dev . driver - > acpi_match_table , & pdev - > dev ) ;
if ( id )
gpio_mapping = ( const struct acpi_gpio_mapping * ) id - > driver_data ;
2017-06-06 17:47:17 +03:00
ret = devm_acpi_dev_add_driver_gpios ( & pdev - > dev , gpio_mapping ) ;
2017-03-10 15:28:20 +03:00
if ( ret )
return ret ;
ret = bcm_platform_probe ( dev ) ;
if ( ret )
return ret ;
2015-08-11 17:35:37 +03:00
/* Retrieve UART ACPI info */
2015-09-30 16:26:45 +03:00
ret = acpi_dev_get_resources ( ACPI_COMPANION ( & dev - > pdev - > dev ) ,
& resources , bcm_resource , dev ) ;
2015-09-30 16:26:42 +03:00
if ( ret < 0 )
return ret ;
2015-09-30 16:26:41 +03:00
acpi_dev_free_resource_list ( & resources ) ;
2015-08-11 17:35:37 +03:00
2015-09-23 19:18:08 +03:00
dmi_id = dmi_first_match ( bcm_wrong_irq_dmi_table ) ;
if ( dmi_id ) {
bt_dev_warn ( dev , " %s: Overwriting IRQ polarity to active low " ,
dmi_id - > ident ) ;
dev - > irq_polarity = * ( u8 * ) dmi_id - > driver_data ;
}
2015-08-11 17:35:35 +03:00
return 0 ;
}
2015-08-12 13:46:01 +03:00
# else
static int bcm_acpi_probe ( struct bcm_device * dev )
{
return - EINVAL ;
}
# endif /* CONFIG_ACPI */
2015-08-11 17:35:35 +03:00
static int bcm_probe ( struct platform_device * pdev )
{
struct bcm_device * dev ;
int ret ;
dev = devm_kzalloc ( & pdev - > dev , sizeof ( * dev ) , GFP_KERNEL ) ;
if ( ! dev )
return - ENOMEM ;
dev - > pdev = pdev ;
2017-03-10 15:28:20 +03:00
if ( has_acpi_companion ( & pdev - > dev ) )
ret = bcm_acpi_probe ( dev ) ;
else
ret = bcm_platform_probe ( dev ) ;
2015-09-30 16:26:44 +03:00
if ( ret )
return ret ;
2015-08-11 17:35:35 +03:00
platform_set_drvdata ( pdev , dev ) ;
dev_info ( & pdev - > dev , " %s device registered. \n " , dev - > name ) ;
/* Place this instance on the device list */
2015-09-01 13:13:35 +03:00
mutex_lock ( & bcm_device_lock ) ;
2015-08-11 17:35:35 +03:00
list_add_tail ( & dev - > list , & bcm_device_list ) ;
2015-09-01 13:13:35 +03:00
mutex_unlock ( & bcm_device_lock ) ;
2015-08-11 17:35:35 +03:00
bcm_gpio_set_power ( dev , false ) ;
return 0 ;
}
static int bcm_remove ( struct platform_device * pdev )
{
struct bcm_device * dev = platform_get_drvdata ( pdev ) ;
2015-09-01 13:13:35 +03:00
mutex_lock ( & bcm_device_lock ) ;
2015-08-11 17:35:35 +03:00
list_del ( & dev - > list ) ;
2015-09-01 13:13:35 +03:00
mutex_unlock ( & bcm_device_lock ) ;
2015-08-11 17:35:35 +03:00
dev_info ( & pdev - > dev , " %s device unregistered. \n " , dev - > name ) ;
return 0 ;
}
2015-04-06 08:52:18 +03:00
static const struct hci_uart_proto bcm_proto = {
. id = HCI_UART_BCM ,
2016-09-19 13:05:12 +03:00
. name = " Broadcom " ,
2015-10-20 22:30:45 +03:00
. manufacturer = 15 ,
2015-06-09 17:15:37 +03:00
. init_speed = 115200 ,
. oper_speed = 4000000 ,
2015-04-06 08:52:18 +03:00
. open = bcm_open ,
. close = bcm_close ,
. flush = bcm_flush ,
. setup = bcm_setup ,
2015-06-09 17:15:37 +03:00
. set_baudrate = bcm_set_baudrate ,
2015-04-06 08:52:18 +03:00
. recv = bcm_recv ,
. enqueue = bcm_enqueue ,
. dequeue = bcm_dequeue ,
} ;
2015-08-11 17:35:35 +03:00
# ifdef CONFIG_ACPI
static const struct acpi_device_id bcm_acpi_match [ ] = {
2017-01-05 20:10:54 +03:00
{ " BCM2E1A " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E39 " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E3A " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E3D " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E3F " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E40 " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E54 " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E55 " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E64 " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E65 " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E67 " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E71 " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E7B " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E7C " , ( kernel_ulong_t ) & acpi_bcm_int_last_gpios } ,
{ " BCM2E95 " , ( kernel_ulong_t ) & acpi_bcm_int_first_gpios } ,
{ " BCM2E96 " , ( kernel_ulong_t ) & acpi_bcm_int_first_gpios } ,
2015-08-11 17:35:35 +03:00
{ } ,
} ;
MODULE_DEVICE_TABLE ( acpi , bcm_acpi_match ) ;
# endif
2015-08-11 17:35:38 +03:00
/* Platform suspend and resume callbacks */
2015-09-23 19:18:11 +03:00
static const struct dev_pm_ops bcm_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS ( bcm_suspend , bcm_resume )
SET_RUNTIME_PM_OPS ( bcm_suspend_device , bcm_resume_device , NULL )
} ;
2015-08-11 17:35:38 +03:00
2015-08-11 17:35:35 +03:00
static struct platform_driver bcm_driver = {
. probe = bcm_probe ,
. remove = bcm_remove ,
. driver = {
. name = " hci_bcm " ,
. acpi_match_table = ACPI_PTR ( bcm_acpi_match ) ,
2015-08-11 17:35:38 +03:00
. pm = & bcm_pm_ops ,
2015-08-11 17:35:35 +03:00
} ,
} ;
2015-04-06 08:52:18 +03:00
int __init bcm_init ( void )
{
2015-08-11 17:35:35 +03:00
platform_driver_register ( & bcm_driver ) ;
2015-04-06 08:52:18 +03:00
return hci_uart_register_proto ( & bcm_proto ) ;
}
int __exit bcm_deinit ( void )
{
2015-08-11 17:35:35 +03:00
platform_driver_unregister ( & bcm_driver ) ;
2015-04-06 08:52:18 +03:00
return hci_uart_unregister_proto ( & bcm_proto ) ;
}