1225 lines · 29.3 KB · C
RawDownload
// SPDX-License-Identifier: GPL-2.0-or-later/* * *  Bluetooth HCI UART driver for Intel devices * *  Copyright (C) 2015  Intel Corporation */ #include <linux/kernel.h>#include <linux/errno.h>#include <linux/skbuff.h>#include <linux/firmware.h>#include <linux/module.h>#include <linux/wait.h>#include <linux/tty.h>#include <linux/platform_device.h>#include <linux/gpio/consumer.h>#include <linux/acpi.h>#include <linux/interrupt.h>#include <linux/pm_runtime.h> #include <net/bluetooth/bluetooth.h>#include <net/bluetooth/hci_core.h> #include "hci_uart.h"#include "btintel.h" #define STATE_BOOTLOADER	0#define STATE_DOWNLOADING	1#define STATE_FIRMWARE_LOADED	2#define STATE_FIRMWARE_FAILED	3#define STATE_BOOTING		4#define STATE_LPM_ENABLED	5#define STATE_TX_ACTIVE		6#define STATE_SUSPENDED		7#define STATE_LPM_TRANSACTION	8 #define HCI_LPM_WAKE_PKT 0xf0#define HCI_LPM_PKT 0xf1#define HCI_LPM_MAX_SIZE 10#define HCI_LPM_HDR_SIZE HCI_EVENT_HDR_SIZE #define LPM_OP_TX_NOTIFY 0x00#define LPM_OP_SUSPEND_ACK 0x02#define LPM_OP_RESUME_ACK 0x03 #define LPM_SUSPEND_DELAY_MS 1000 struct hci_lpm_pkt {	__u8 opcode;	__u8 dlen;	__u8 data[];} __packed; struct intel_device {	struct list_head list;	struct platform_device *pdev;	struct gpio_desc *reset;	struct hci_uart *hu;	struct mutex hu_lock;	int irq;}; static LIST_HEAD(intel_device_list);static DEFINE_MUTEX(intel_device_list_lock); struct intel_data {	struct sk_buff *rx_skb;	struct sk_buff_head txq;	struct work_struct busy_work;	struct hci_uart *hu;	unsigned long flags;}; static u8 intel_convert_speed(unsigned int speed){	switch (speed) {	case 9600:		return 0x00;	case 19200:		return 0x01;	case 38400:		return 0x02;	case 57600:		return 0x03;	case 115200:		return 0x04;	case 230400:		return 0x05;	case 460800:		return 0x06;	case 921600:		return 0x07;	case 1843200:		return 0x08;	case 3250000:		return 0x09;	case 2000000:		return 0x0a;	case 3000000:		return 0x0b;	default:		return 0xff;	}} static int intel_wait_booting(struct hci_uart *hu){	struct intel_data *intel = hu->priv;	int err; 	err = wait_on_bit_timeout(&intel->flags, STATE_BOOTING,				  TASK_INTERRUPTIBLE,				  msecs_to_jiffies(1000)); 	if (err == -EINTR) {		bt_dev_err(hu->hdev, "Device boot interrupted");		return -EINTR;	} 	if (err) {		bt_dev_err(hu->hdev, "Device boot timeout");		return -ETIMEDOUT;	} 	return err;} static int intel_wait_lpm_transaction(struct hci_uart *hu){	struct intel_data *intel = hu->priv;	int err; 	err = wait_on_bit_timeout(&intel->flags, STATE_LPM_TRANSACTION,				  TASK_INTERRUPTIBLE,				  msecs_to_jiffies(1000)); 	if (err == -EINTR) {		bt_dev_err(hu->hdev, "LPM transaction interrupted");		return -EINTR;	} 	if (err) {		bt_dev_err(hu->hdev, "LPM transaction timeout");		return -ETIMEDOUT;	} 	return err;} static int intel_lpm_suspend(struct hci_uart *hu){	static const u8 suspend[] = { 0x01, 0x01, 0x01 };	struct intel_data *intel = hu->priv;	struct sk_buff *skb; 	if (!test_bit(STATE_LPM_ENABLED, &intel->flags) ||	    test_bit(STATE_SUSPENDED, &intel->flags))		return 0; 	if (test_bit(STATE_TX_ACTIVE, &intel->flags))		return -EAGAIN; 	bt_dev_dbg(hu->hdev, "Suspending"); 	skb = bt_skb_alloc(sizeof(suspend), GFP_KERNEL);	if (!skb) {		bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet");		return -ENOMEM;	} 	skb_put_data(skb, suspend, sizeof(suspend));	hci_skb_pkt_type(skb) = HCI_LPM_PKT; 	set_bit(STATE_LPM_TRANSACTION, &intel->flags); 	/* LPM flow is a priority, enqueue packet at list head */	skb_queue_head(&intel->txq, skb);	hci_uart_tx_wakeup(hu); 	intel_wait_lpm_transaction(hu);	/* Even in case of failure, continue and test the suspended flag */ 	clear_bit(STATE_LPM_TRANSACTION, &intel->flags); 	if (!test_bit(STATE_SUSPENDED, &intel->flags)) {		bt_dev_err(hu->hdev, "Device suspend error");		return -EINVAL;	} 	bt_dev_dbg(hu->hdev, "Suspended"); 	hci_uart_set_flow_control(hu, true); 	return 0;} static int intel_lpm_resume(struct hci_uart *hu){	struct intel_data *intel = hu->priv;	struct sk_buff *skb; 	if (!test_bit(STATE_LPM_ENABLED, &intel->flags) ||	    !test_bit(STATE_SUSPENDED, &intel->flags))		return 0; 	bt_dev_dbg(hu->hdev, "Resuming"); 	hci_uart_set_flow_control(hu, false); 	skb = bt_skb_alloc(0, GFP_KERNEL);	if (!skb) {		bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet");		return -ENOMEM;	} 	hci_skb_pkt_type(skb) = HCI_LPM_WAKE_PKT; 	set_bit(STATE_LPM_TRANSACTION, &intel->flags); 	/* LPM flow is a priority, enqueue packet at list head */	skb_queue_head(&intel->txq, skb);	hci_uart_tx_wakeup(hu); 	intel_wait_lpm_transaction(hu);	/* Even in case of failure, continue and test the suspended flag */ 	clear_bit(STATE_LPM_TRANSACTION, &intel->flags); 	if (test_bit(STATE_SUSPENDED, &intel->flags)) {		bt_dev_err(hu->hdev, "Device resume error");		return -EINVAL;	} 	bt_dev_dbg(hu->hdev, "Resumed"); 	return 0;} static int intel_lpm_host_wake(struct hci_uart *hu){	static const u8 lpm_resume_ack[] = { LPM_OP_RESUME_ACK, 0x00 };	struct intel_data *intel = hu->priv;	struct sk_buff *skb; 	hci_uart_set_flow_control(hu, false); 	clear_bit(STATE_SUSPENDED, &intel->flags); 	skb = bt_skb_alloc(sizeof(lpm_resume_ack), GFP_KERNEL);	if (!skb) {		bt_dev_err(hu->hdev, "Failed to alloc memory for LPM packet");		return -ENOMEM;	} 	skb_put_data(skb, lpm_resume_ack, sizeof(lpm_resume_ack));	hci_skb_pkt_type(skb) = HCI_LPM_PKT; 	/* LPM flow is a priority, enqueue packet at list head */	skb_queue_head(&intel->txq, skb);	hci_uart_tx_wakeup(hu); 	bt_dev_dbg(hu->hdev, "Resumed by controller"); 	return 0;} static irqreturn_t intel_irq(int irq, void *dev_id){	struct intel_device *idev = dev_id; 	dev_info(&idev->pdev->dev, "hci_intel irq\n"); 	mutex_lock(&idev->hu_lock);	if (idev->hu)		intel_lpm_host_wake(idev->hu);	mutex_unlock(&idev->hu_lock); 	/* Host/Controller are now LPM resumed, trigger a new delayed suspend */	pm_runtime_get(&idev->pdev->dev);	pm_runtime_put_autosuspend(&idev->pdev->dev); 	return IRQ_HANDLED;} static int intel_set_power(struct hci_uart *hu, bool powered){	struct intel_device *idev;	int err = -ENODEV; 	if (!hu->tty->dev)		return err; 	mutex_lock(&intel_device_list_lock); 	list_for_each_entry(idev, &intel_device_list, list) {		/* tty device and pdev device should share the same parent		 * which is the UART port.		 */		if (hu->tty->dev->parent != idev->pdev->dev.parent)			continue; 		if (!idev->reset) {			err = -ENOTSUPP;			break;		} 		BT_INFO("hu %p, Switching compatible pm device (%s) to %u",			hu, dev_name(&idev->pdev->dev), powered); 		gpiod_set_value(idev->reset, powered); 		/* Provide to idev a hu reference which is used to run LPM		 * transactions (lpm suspend/resume) from PM callbacks.		 * hu needs to be protected against concurrent removing during		 * these PM ops.		 */		mutex_lock(&idev->hu_lock);		idev->hu = powered ? hu : NULL;		mutex_unlock(&idev->hu_lock); 		if (idev->irq < 0)			break; 		if (powered && device_can_wakeup(&idev->pdev->dev)) {			err = devm_request_threaded_irq(&idev->pdev->dev,							idev->irq, NULL,							intel_irq,							IRQF_ONESHOT,							"bt-host-wake", idev);			if (err) {				BT_ERR("hu %p, unable to allocate irq-%d",				       hu, idev->irq);				break;			} 			device_wakeup_enable(&idev->pdev->dev); 			pm_runtime_set_active(&idev->pdev->dev);			pm_runtime_use_autosuspend(&idev->pdev->dev);			pm_runtime_set_autosuspend_delay(&idev->pdev->dev,							 LPM_SUSPEND_DELAY_MS);			pm_runtime_enable(&idev->pdev->dev);		} else if (!powered && device_may_wakeup(&idev->pdev->dev)) {			devm_free_irq(&idev->pdev->dev, idev->irq, idev);			device_wakeup_disable(&idev->pdev->dev); 			pm_runtime_dont_use_autosuspend(&idev->pdev->dev);			pm_runtime_disable(&idev->pdev->dev);		}	} 	mutex_unlock(&intel_device_list_lock); 	return err;} static void intel_busy_work(struct work_struct *work){	struct intel_data *intel = container_of(work, struct intel_data,						busy_work);	struct intel_device *idev; 	if (!intel->hu->tty->dev)		return; 	/* Link is busy, delay the suspend */	mutex_lock(&intel_device_list_lock);	list_for_each_entry(idev, &intel_device_list, list) {		if (intel->hu->tty->dev->parent == idev->pdev->dev.parent) {			pm_runtime_get(&idev->pdev->dev);			pm_runtime_put_autosuspend(&idev->pdev->dev);			break;		}	}	mutex_unlock(&intel_device_list_lock);} static int intel_open(struct hci_uart *hu){	struct intel_data *intel; 	BT_DBG("hu %p", hu); 	if (!hci_uart_has_flow_control(hu))		return -EOPNOTSUPP; 	intel = kzalloc_obj(*intel);	if (!intel)		return -ENOMEM; 	skb_queue_head_init(&intel->txq);	INIT_WORK(&intel->busy_work, intel_busy_work); 	intel->hu = hu; 	hu->priv = intel; 	if (!intel_set_power(hu, true))		set_bit(STATE_BOOTING, &intel->flags); 	return 0;} static int intel_close(struct hci_uart *hu){	struct intel_data *intel = hu->priv; 	BT_DBG("hu %p", hu); 	cancel_work_sync(&intel->busy_work); 	intel_set_power(hu, false); 	skb_queue_purge(&intel->txq);	kfree_skb(intel->rx_skb);	kfree(intel); 	hu->priv = NULL;	return 0;} static int intel_flush(struct hci_uart *hu){	struct intel_data *intel = hu->priv; 	BT_DBG("hu %p", hu); 	skb_queue_purge(&intel->txq); 	return 0;} static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode){	struct sk_buff *skb;	struct hci_event_hdr *hdr;	struct hci_ev_cmd_complete *evt; 	skb = bt_skb_alloc(sizeof(*hdr) + sizeof(*evt) + 1, GFP_KERNEL);	if (!skb)		return -ENOMEM; 	hdr = skb_put(skb, sizeof(*hdr));	hdr->evt = HCI_EV_CMD_COMPLETE;	hdr->plen = sizeof(*evt) + 1; 	evt = skb_put(skb, sizeof(*evt));	evt->ncmd = 0x01;	evt->opcode = cpu_to_le16(opcode); 	skb_put_u8(skb, 0x00); 	hci_skb_pkt_type(skb) = HCI_EVENT_PKT; 	return hci_recv_frame(hdev, skb);} static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed){	struct intel_data *intel = hu->priv;	struct hci_dev *hdev = hu->hdev;	u8 speed_cmd[] = { 0x06, 0xfc, 0x01, 0x00 };	struct sk_buff *skb;	int err; 	/* This can be the first command sent to the chip, check	 * that the controller is ready.	 */	err = intel_wait_booting(hu); 	clear_bit(STATE_BOOTING, &intel->flags); 	/* In case of timeout, try to continue anyway */	if (err && err != -ETIMEDOUT)		return err; 	bt_dev_info(hdev, "Change controller speed to %d", speed); 	speed_cmd[3] = intel_convert_speed(speed);	if (speed_cmd[3] == 0xff) {		bt_dev_err(hdev, "Unsupported speed");		return -EINVAL;	} 	/* Device will not accept speed change if Intel version has not been	 * previously requested.	 */	skb = __hci_cmd_sync(hdev, 0xfc05, 0, NULL, HCI_CMD_TIMEOUT);	if (IS_ERR(skb)) {		bt_dev_err(hdev, "Reading Intel version information failed (%ld)",			   PTR_ERR(skb));		return PTR_ERR(skb);	}	kfree_skb(skb); 	skb = bt_skb_alloc(sizeof(speed_cmd), GFP_KERNEL);	if (!skb) {		bt_dev_err(hdev, "Failed to alloc memory for baudrate packet");		return -ENOMEM;

Lines 1–500 of 1225.