/*
*
* Bluetooth HCI UART driver for Intel 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>
#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 <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 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
struct hci_lpm_pkt {
__u8 opcode;
__u8 dlen;
__u8 data[0];
} __packed;
struct intel_device {
struct list_head list;
struct platform_device *pdev;
struct gpio_desc *reset;
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;
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_dev_err(hu->hdev, "Device boot interrupted");
return -EINTR;
}
if (err) {
bt_dev_err(hu->hdev, "Device boot timeout");
return -ETIMEDOUT;
}
return err;
}
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");
return IRQ_HANDLED;
}
static int intel_set_power(struct hci_uart *hu, bool powered)
{
struct list_head *p;
int err = -ENODEV;
mutex_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);
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->