|
@@ -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,12 +44,108 @@
|
|
|
#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;
|
|
|
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 == 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;
|
|
@@ -58,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;
|
|
|
}
|
|
|
|
|
@@ -67,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);
|
|
@@ -111,6 +218,68 @@ static int inject_cmd_complete(struct hci_dev *hdev, __u16 opcode)
|
|
|
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_INFO("%s: Change controller speed to %d", hdev->name, speed);
|
|
|
+
|
|
|
+ speed_cmd[3] = intel_convert_speed(speed);
|
|
|
+ if (speed_cmd[3] == 0xff) {
|
|
|
+ BT_ERR("%s: Unsupported speed", hdev->name);
|
|
|
+ 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_INIT_TIMEOUT);
|
|
|
+ if (IS_ERR(skb)) {
|
|
|
+ BT_ERR("%s: Reading Intel version information failed (%ld)",
|
|
|
+ hdev->name, PTR_ERR(skb));
|
|
|
+ return PTR_ERR(skb);
|
|
|
+ }
|
|
|
+ kfree_skb(skb);
|
|
|
+
|
|
|
+ skb = bt_skb_alloc(sizeof(speed_cmd), GFP_KERNEL);
|
|
|
+ if (!skb) {
|
|
|
+ BT_ERR("%s: Failed to allocate memory for baudrate packet",
|
|
|
+ hdev->name);
|
|
|
+ return -ENOMEM;
|
|
|
+ }
|
|
|
+
|
|
|
+ memcpy(skb_put(skb, sizeof(speed_cmd)), speed_cmd, sizeof(speed_cmd));
|
|
|
+ bt_cb(skb)->pkt_type = HCI_COMMAND_PKT;
|
|
|
+
|
|
|
+ hci_uart_set_flow_control(hu, true);
|
|
|
+
|
|
|
+ skb_queue_tail(&intel->txq, skb);
|
|
|
+ hci_uart_tx_wakeup(hu);
|
|
|
+
|
|
|
+ /* wait 100ms to change baudrate on controller side */
|
|
|
+ msleep(100);
|
|
|
+
|
|
|
+ hci_uart_set_baudrate(hu, speed);
|
|
|
+ hci_uart_set_flow_control(hu, false);
|
|
|
+
|
|
|
+ return 0;
|
|
|
+}
|
|
|
+
|
|
|
static int intel_setup(struct hci_uart *hu)
|
|
|
{
|
|
|
static const u8 reset_param[] = { 0x00, 0x01, 0x00, 0x01,
|
|
@@ -126,6 +295,8 @@ static int intel_setup(struct hci_uart *hu)
|
|
|
u32 frag_len;
|
|
|
ktime_t calltime, delta, rettime;
|
|
|
unsigned long long duration;
|
|
|
+ unsigned int init_speed, oper_speed;
|
|
|
+ int speed_change = 0;
|
|
|
int err;
|
|
|
|
|
|
BT_DBG("%s", hdev->name);
|
|
@@ -134,6 +305,28 @@ static int intel_setup(struct hci_uart *hu)
|
|
|
|
|
|
calltime = ktime_get();
|
|
|
|
|
|
+ if (hu->init_speed)
|
|
|
+ init_speed = hu->init_speed;
|
|
|
+ else
|
|
|
+ init_speed = hu->proto->init_speed;
|
|
|
+
|
|
|
+ if (hu->oper_speed)
|
|
|
+ oper_speed = hu->oper_speed;
|
|
|
+ else
|
|
|
+ oper_speed = hu->proto->oper_speed;
|
|
|
+
|
|
|
+ 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
|
|
@@ -416,6 +609,13 @@ done:
|
|
|
if (err < 0)
|
|
|
return err;
|
|
|
|
|
|
+ /* We need to restore the default speed before Intel reset */
|
|
|
+ if (speed_change) {
|
|
|
+ err = intel_set_baudrate(hu, init_speed);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
calltime = ktime_get();
|
|
|
|
|
|
set_bit(STATE_BOOTING, &intel->flags);
|
|
@@ -436,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);
|
|
@@ -456,6 +648,19 @@ done:
|
|
|
|
|
|
BT_INFO("%s: Device booted in %llu usecs", hdev->name, duration);
|
|
|
|
|
|
+ skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_CMD_TIMEOUT);
|
|
|
+ if (IS_ERR(skb))
|
|
|
+ return PTR_ERR(skb);
|
|
|
+ kfree_skb(skb);
|
|
|
+
|
|
|
+ if (speed_change) {
|
|
|
+ err = intel_set_baudrate(hu, oper_speed);
|
|
|
+ if (err)
|
|
|
+ return err;
|
|
|
+ }
|
|
|
+
|
|
|
+ BT_INFO("%s: Setup complete", hdev->name);
|
|
|
+
|
|
|
clear_bit(STATE_BOOTLOADER, &intel->flags);
|
|
|
|
|
|
return 0;
|
|
@@ -467,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;
|
|
@@ -572,21 +778,110 @@ static const struct hci_uart_proto intel_proto = {
|
|
|
.id = HCI_UART_INTEL,
|
|
|
.name = "Intel",
|
|
|
.init_speed = 115200,
|
|
|
+ .oper_speed = 3000000,
|
|
|
.open = intel_open,
|
|
|
.close = intel_close,
|
|
|
.flush = intel_flush,
|
|
|
.setup = intel_setup,
|
|
|
+ .set_baudrate = intel_set_baudrate,
|
|
|
.recv = intel_recv,
|
|
|
.enqueue = intel_enqueue,
|
|
|
.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);
|
|
|
}
|