|
@@ -25,7 +25,12 @@
|
|
|
#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 <net/bluetooth/bluetooth.h>
|
|
|
#include <net/bluetooth/hci_core.h>
|
|
@@ -39,6 +44,15 @@
|
|
|
#define STATE_FIRMWARE_FAILED 3
|
|
|
#define STATE_BOOTING 4
|
|
|
|
|
|
+struct intel_device {
|
|
|
+ struct list_head list;
|
|
|
+ struct platform_device *pdev;
|
|
|
+ struct gpio_desc *reset;
|
|
|
+};
|
|
|
+
|
|
|
+static LIST_HEAD(intel_device_list);
|
|
|
+static DEFINE_SPINLOCK(intel_device_list_lock);
|
|
|
+
|
|
|
struct intel_data {
|
|
|
struct sk_buff *rx_skb;
|
|
|
struct sk_buff_head txq;
|
|
@@ -77,6 +91,61 @@ static u8 intel_convert_speed(unsigned int speed)
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+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 == 1) {
|
|
|
+ BT_ERR("%s: Device boot interrupted", hu->hdev->name);
|
|
|
+ return -EINTR;
|
|
|
+ }
|
|
|
+
|
|
|
+ if (err) {
|
|
|
+ BT_ERR("%s: Device boot timeout", hu->hdev->name);
|
|
|
+ return -ETIMEDOUT;
|
|
|
+ }
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
+static int intel_set_power(struct hci_uart *hu, bool powered)
|
|
|
+{
|
|
|
+ struct list_head *p;
|
|
|
+ int err = -ENODEV;
|
|
|
+
|
|
|
+ spin_lock(&intel_device_list_lock);
|
|
|
+
|
|
|
+ list_for_each(p, &intel_device_list) {
|
|
|
+ struct intel_device *idev = list_entry(p, struct intel_device,
|
|
|
+ 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);
|
|
|
+ }
|
|
|
+
|
|
|
+ spin_unlock(&intel_device_list_lock);
|
|
|
+
|
|
|
+ return err;
|
|
|
+}
|
|
|
+
|
|
|
static int intel_open(struct hci_uart *hu)
|
|
|
{
|
|
|
struct intel_data *intel;
|
|
@@ -90,6 +159,10 @@ static int intel_open(struct hci_uart *hu)
|
|
|
skb_queue_head_init(&intel->txq);
|
|
|
|
|
|
hu->priv = intel;
|
|
|
+
|
|
|
+ if (!intel_set_power(hu, true))
|
|
|
+ set_bit(STATE_BOOTING, &intel->flags);
|
|
|
+
|
|
|
return 0;
|
|
|
}
|
|
|
|
|
@@ -99,6 +172,8 @@ static int intel_close(struct hci_uart *hu)
|
|
|
|
|
|
BT_DBG("hu %p", hu);
|
|
|
|
|
|
+ intel_set_power(hu, false);
|
|
|
+
|
|
|
skb_queue_purge(&intel->txq);
|
|
|
kfree_skb(intel->rx_skb);
|
|
|
kfree(intel);
|
|
@@ -149,6 +224,18 @@ static int intel_set_baudrate(struct hci_uart *hu, unsigned int speed)
|
|
|
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_INFO("%s: Change controller speed to %d", hdev->name, speed);
|
|
|
|
|
@@ -231,6 +318,15 @@ static int intel_setup(struct hci_uart *hu)
|
|
|
if (oper_speed && init_speed && oper_speed != init_speed)
|
|
|
speed_change = 1;
|
|
|
|
|
|
+ /* 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;
|
|
|
+
|
|
|
set_bit(STATE_BOOTLOADER, &intel->flags);
|
|
|
|
|
|
/* Read the Intel version information to determine if the device
|
|
@@ -540,19 +636,11 @@ done:
|
|
|
*/
|
|
|
BT_INFO("%s: Waiting for device to boot", hdev->name);
|
|
|
|
|
|
- err = wait_on_bit_timeout(&intel->flags, STATE_BOOTING,
|
|
|
- TASK_INTERRUPTIBLE,
|
|
|
- msecs_to_jiffies(1000));
|
|
|
-
|
|
|
- if (err == 1) {
|
|
|
- BT_ERR("%s: Device boot interrupted", hdev->name);
|
|
|
- return -EINTR;
|
|
|
- }
|
|
|
+ err = intel_wait_booting(hu);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
|
|
|
- if (err) {
|
|
|
- BT_ERR("%s: Device boot timeout", hdev->name);
|
|
|
- return -ETIMEDOUT;
|
|
|
- }
|
|
|
+ clear_bit(STATE_BOOTING, &intel->flags);
|
|
|
|
|
|
rettime = ktime_get();
|
|
|
delta = ktime_sub(rettime, calltime);
|
|
@@ -584,7 +672,8 @@ static int intel_recv_event(struct hci_dev *hdev, struct sk_buff *skb)
|
|
|
struct intel_data *intel = hu->priv;
|
|
|
struct hci_event_hdr *hdr;
|
|
|
|
|
|
- if (!test_bit(STATE_BOOTLOADER, &intel->flags))
|
|
|
+ if (!test_bit(STATE_BOOTLOADER, &intel->flags) &&
|
|
|
+ !test_bit(STATE_BOOTING, &intel->flags))
|
|
|
goto recv;
|
|
|
|
|
|
hdr = (void *)skb->data;
|
|
@@ -700,12 +789,99 @@ static const struct hci_uart_proto intel_proto = {
|
|
|
.dequeue = intel_dequeue,
|
|
|
};
|
|
|
|
|
|
+#ifdef CONFIG_ACPI
|
|
|
+static const struct acpi_device_id intel_acpi_match[] = {
|
|
|
+ { "INT33E1", 0 },
|
|
|
+ { },
|
|
|
+};
|
|
|
+MODULE_DEVICE_TABLE(acpi, intel_acpi_match);
|
|
|
+
|
|
|
+static int intel_acpi_probe(struct intel_device *idev)
|
|
|
+{
|
|
|
+ const struct acpi_device_id *id;
|
|
|
+
|
|
|
+ id = acpi_match_device(intel_acpi_match, &idev->pdev->dev);
|
|
|
+ if (!id)
|
|
|
+ return -ENODEV;
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+#else
|
|
|
+static int intel_acpi_probe(struct intel_device *idev)
|
|
|
+{
|
|
|
+ return -ENODEV;
|
|
|
+}
|
|
|
+#endif
|
|
|
+
|
|
|
+static int intel_probe(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ struct intel_device *idev;
|
|
|
+
|
|
|
+ idev = devm_kzalloc(&pdev->dev, sizeof(*idev), GFP_KERNEL);
|
|
|
+ if (!idev)
|
|
|
+ return -ENOMEM;
|
|
|
+
|
|
|
+ idev->pdev = pdev;
|
|
|
+
|
|
|
+ if (ACPI_HANDLE(&pdev->dev)) {
|
|
|
+ int err = intel_acpi_probe(idev);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ } else {
|
|
|
+ return -ENODEV;
|
|
|
+ }
|
|
|
+
|
|
|
+ idev->reset = devm_gpiod_get_optional(&pdev->dev, "reset",
|
|
|
+ GPIOD_OUT_LOW);
|
|
|
+ if (IS_ERR(idev->reset)) {
|
|
|
+ dev_err(&pdev->dev, "Unable to retrieve gpio\n");
|
|
|
+ return PTR_ERR(idev->reset);
|
|
|
+ }
|
|
|
+
|
|
|
+ platform_set_drvdata(pdev, idev);
|
|
|
+
|
|
|
+ /* Place this instance on the device list */
|
|
|
+ spin_lock(&intel_device_list_lock);
|
|
|
+ list_add_tail(&idev->list, &intel_device_list);
|
|
|
+ spin_unlock(&intel_device_list_lock);
|
|
|
+
|
|
|
+ dev_info(&pdev->dev, "registered.\n");
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static int intel_remove(struct platform_device *pdev)
|
|
|
+{
|
|
|
+ struct intel_device *idev = platform_get_drvdata(pdev);
|
|
|
+
|
|
|
+ spin_lock(&intel_device_list_lock);
|
|
|
+ list_del(&idev->list);
|
|
|
+ spin_unlock(&intel_device_list_lock);
|
|
|
+
|
|
|
+ dev_info(&pdev->dev, "unregistered.\n");
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
+static struct platform_driver intel_driver = {
|
|
|
+ .probe = intel_probe,
|
|
|
+ .remove = intel_remove,
|
|
|
+ .driver = {
|
|
|
+ .name = "hci_intel",
|
|
|
+ .acpi_match_table = ACPI_PTR(intel_acpi_match),
|
|
|
+ },
|
|
|
+};
|
|
|
+
|
|
|
int __init intel_init(void)
|
|
|
{
|
|
|
+ platform_driver_register(&intel_driver);
|
|
|
+
|
|
|
return hci_uart_register_proto(&intel_proto);
|
|
|
}
|
|
|
|
|
|
int __exit intel_deinit(void)
|
|
|
{
|
|
|
+ platform_driver_unregister(&intel_driver);
|
|
|
+
|
|
|
return hci_uart_unregister_proto(&intel_proto);
|
|
|
}
|