staging/bluetooth: Add hci_h4p driver
Add hci_h4p bluetooth driver to staging tree. This device is used for example on Nokia N900 cell phone. Signed-off-by: Pali Rohár <pali.rohar@gmail.com> Signed-off-by: Pavel Machek <pavel@ucw.cz> Thanks-to: Sebastian Reichel <sre@debian.org> Thanks-to: Joe Perches <joe@perches.com> Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
Родитель
be973fcd81
Коммит
91eef3e2fe
|
@ -148,4 +148,6 @@ source "drivers/staging/dgap/Kconfig"
|
|||
|
||||
source "drivers/staging/gs_fpgaboot/Kconfig"
|
||||
|
||||
source "drivers/staging/nokia_h4p/Kconfig"
|
||||
|
||||
endif # STAGING
|
||||
|
|
|
@ -66,3 +66,4 @@ obj-$(CONFIG_DGNC) += dgnc/
|
|||
obj-$(CONFIG_DGAP) += dgap/
|
||||
obj-$(CONFIG_MTD_SPINAND_MT29F) += mt29f_spinand/
|
||||
obj-$(CONFIG_GS_FPGABOOT) += gs_fpgaboot/
|
||||
obj-$(CONFIG_BT_NOKIA_H4P) += nokia_h4p/
|
||||
|
|
|
@ -0,0 +1,9 @@
|
|||
config BT_NOKIA_H4P
|
||||
tristate "HCI driver with H4 Nokia extensions"
|
||||
depends on BT && ARCH_OMAP
|
||||
help
|
||||
Bluetooth HCI driver with H4 extensions. This driver provides
|
||||
support for H4+ Bluetooth chip with vendor-specific H4 extensions.
|
||||
|
||||
Say Y here to compile support for h4 extended devices into the kernel
|
||||
or say M to compile it as module (btnokia_h4p).
|
|
@ -0,0 +1,6 @@
|
|||
|
||||
obj-$(CONFIG_BT_NOKIA_H4P) += btnokia_h4p.o
|
||||
btnokia_h4p-objs := nokia_core.o nokia_fw.o nokia_uart.o nokia_fw-csr.o \
|
||||
nokia_fw-bcm.o nokia_fw-ti1273.o
|
||||
|
||||
ccflags-y += -D__CHECK_ENDIAN__
|
|
@ -0,0 +1,140 @@
|
|||
Few attempts to submission have been made, last review comments were received in
|
||||
|
||||
Date: Wed, 15 Jan 2014 19:01:51 -0800
|
||||
From: Marcel Holtmann <marcel@holtmann.org>
|
||||
Subject: Re: [PATCH v6] Bluetooth: Add hci_h4p driver
|
||||
|
||||
Some code refactoring is still needed.
|
||||
|
||||
TODO:
|
||||
|
||||
> +++ b/drivers/bluetooth/hci_h4p.h
|
||||
|
||||
can we please get the naming straight. File names do not start with
|
||||
hci_ anymore. We moved away from it since that term is too generic.
|
||||
|
||||
> +#define FW_NAME_TI1271_LE "ti1273_le.bin"
|
||||
> +#define FW_NAME_TI1271 "ti1273.bin"
|
||||
> +#define FW_NAME_BCM2048 "bcmfw.bin"
|
||||
> +#define FW_NAME_CSR "bc4fw.bin"
|
||||
|
||||
We do these have to be global in a header file. This should be
|
||||
confined to the specific firmware part.
|
||||
|
||||
> +struct hci_h4p_info {
|
||||
|
||||
Can we please get rid of the hci_ prefix for everything. Copying from
|
||||
drivers that are over 10 years old is not a good idea. Please look at
|
||||
recent ones.
|
||||
|
||||
> + struct timer_list lazy_release;
|
||||
|
||||
Timer? Not delayed work?
|
||||
|
||||
> +void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val);
|
||||
> +u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset);
|
||||
> +void hci_h4p_set_rts(struct hci_h4p_info *info, int active);
|
||||
> +int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active, int timeout_ms);
|
||||
> +void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
|
||||
> +void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
|
||||
> +void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed);
|
||||
> +int hci_h4p_reset_uart(struct hci_h4p_info *info);
|
||||
> +void hci_h4p_init_uart(struct hci_h4p_info *info);
|
||||
> +void hci_h4p_enable_tx(struct hci_h4p_info *info);
|
||||
> +void hci_h4p_store_regs(struct hci_h4p_info *info);
|
||||
> +void hci_h4p_restore_regs(struct hci_h4p_info *info);
|
||||
> +void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable);
|
||||
|
||||
These are a lot of public functions. Are they all really needed or can
|
||||
the code be done smart.
|
||||
|
||||
> +static ssize_t hci_h4p_store_bdaddr(struct device *dev,
|
||||
> + struct device_attribute *attr,
|
||||
> + const char *buf, size_t count)
|
||||
> +{
|
||||
> + struct hci_h4p_info *info = dev_get_drvdata(dev);
|
||||
|
||||
Since none of these devices can function without having a valid
|
||||
address, the way this should work is that we should not register the
|
||||
HCI device when probing the platform device.
|
||||
|
||||
The HCI device should be registered once a valid address has been
|
||||
written into the sysfs file. I do not want to play the tricks with
|
||||
bringing up the device without a valid address.
|
||||
|
||||
> + hdev->close = hci_h4p_hci_close;
|
||||
> + hdev->flush = hci_h4p_hci_flush;
|
||||
> + hdev->send = hci_h4p_hci_send_frame;
|
||||
|
||||
It needs to use hdev->setup to load the firmware. I assume the
|
||||
firmware only needs to be loaded once. That is exactly what
|
||||
hdev->setup does. It gets executed once.
|
||||
|
||||
> + set_bit(HCI_QUIRK_RESET_ON_CLOSE, &hdev->quirks);
|
||||
|
||||
Is this quirk really needed? Normally only Bluetooth 1.1 and early
|
||||
devices qualify for it.
|
||||
|
||||
> +static int hci_h4p_bcm_set_bdaddr(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||
> +{
|
||||
> + int i;
|
||||
> + static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
|
||||
> + int not_valid;
|
||||
|
||||
Has this actually been confirmed that we can just randomly set an
|
||||
address out of the Nokia range. I do not think so. This is a pretty
|
||||
bad idea.
|
||||
|
||||
I have no interest in merging a driver with such a hack.
|
||||
|
||||
> + not_valid = 1;
|
||||
> + for (i = 0; i < 6; i++) {
|
||||
> + if (info->bd_addr[i] != 0x00) {
|
||||
> + not_valid = 0;
|
||||
> + break;
|
||||
> + }
|
||||
> + }
|
||||
|
||||
Anybody every heard of memcmp or bacmp and BDADDR_ANY?
|
||||
|
||||
> + if (not_valid) {
|
||||
> + dev_info(info->dev, "Valid bluetooth address not found,"
|
||||
> + " setting some random\n");
|
||||
> + /* When address is not valid, use some random */
|
||||
> + memcpy(info->bd_addr, nokia_oui, 3);
|
||||
> + get_random_bytes(info->bd_addr + 3, 3);
|
||||
> + }
|
||||
|
||||
|
||||
And why does every single chip firmware does this differently. Seriously, this is a mess.
|
||||
|
||||
> +void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||
> +{
|
||||
> + switch (info->man_id) {
|
||||
> + case H4P_ID_CSR:
|
||||
> + hci_h4p_bc4_parse_fw_event(info, skb);
|
||||
> + break;
|
||||
...
|
||||
> +}
|
||||
|
||||
We have proper HCI sync command handling in recent kernels. I really
|
||||
do not know why this is hand coded these days. Check how the Intel
|
||||
firmware loading inside btusb.c does it.
|
||||
|
||||
> +inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
|
||||
> +{
|
||||
> + return __raw_readb(info->uart_base + (offset << 2));
|
||||
> +}
|
||||
|
||||
Inline in a *.c file for a non-static function. Makes no sense to me.
|
||||
|
||||
> +/**
|
||||
> + * struct hci_h4p_platform data - hci_h4p Platform data structure
|
||||
> + */
|
||||
> +struct hci_h4p_platform_data {
|
||||
|
||||
please have a proper name here. For example
|
||||
btnokia_h4p_platform_data.
|
||||
|
||||
Please send patches to Greg Kroah-Hartman <greg@kroah.com> and Cc:
|
||||
Pavel Machek <pavel@ucw.cz>
|
|
@ -0,0 +1,228 @@
|
|||
/*
|
||||
* This file is part of Nokia H4P bluetooth driver
|
||||
*
|
||||
* Copyright (C) 2005-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#ifndef __DRIVERS_BLUETOOTH_HCI_H4P_H
|
||||
#define __DRIVERS_BLUETOOTH_HCI_H4P_H
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
#include <net/bluetooth/hci_core.h>
|
||||
#include <net/bluetooth/hci.h>
|
||||
|
||||
#define FW_NAME_TI1271_PRELE "ti1273_prele.bin"
|
||||
#define FW_NAME_TI1271_LE "ti1273_le.bin"
|
||||
#define FW_NAME_TI1271 "ti1273.bin"
|
||||
#define FW_NAME_BCM2048 "bcmfw.bin"
|
||||
#define FW_NAME_CSR "bc4fw.bin"
|
||||
|
||||
#define UART_SYSC_OMAP_RESET 0x03
|
||||
#define UART_SYSS_RESETDONE 0x01
|
||||
#define UART_OMAP_SCR_EMPTY_THR 0x08
|
||||
#define UART_OMAP_SCR_WAKEUP 0x10
|
||||
#define UART_OMAP_SSR_WAKEUP 0x02
|
||||
#define UART_OMAP_SSR_TXFULL 0x01
|
||||
|
||||
#define UART_OMAP_SYSC_IDLEMODE 0x03
|
||||
#define UART_OMAP_SYSC_IDLEMASK (3 << UART_OMAP_SYSC_IDLEMODE)
|
||||
|
||||
#define UART_OMAP_SYSC_FORCE_IDLE (0 << UART_OMAP_SYSC_IDLEMODE)
|
||||
#define UART_OMAP_SYSC_NO_IDLE (1 << UART_OMAP_SYSC_IDLEMODE)
|
||||
#define UART_OMAP_SYSC_SMART_IDLE (2 << UART_OMAP_SYSC_IDLEMODE)
|
||||
|
||||
#define H4P_TRANSFER_MODE 1
|
||||
#define H4P_SCHED_TRANSFER_MODE 2
|
||||
#define H4P_ACTIVE_MODE 3
|
||||
|
||||
struct hci_h4p_info {
|
||||
struct timer_list lazy_release;
|
||||
struct hci_dev *hdev;
|
||||
spinlock_t lock;
|
||||
|
||||
void __iomem *uart_base;
|
||||
unsigned long uart_phys_base;
|
||||
int irq;
|
||||
struct device *dev;
|
||||
u8 chip_type;
|
||||
u8 bt_wakeup_gpio;
|
||||
u8 host_wakeup_gpio;
|
||||
u8 reset_gpio;
|
||||
u8 reset_gpio_shared;
|
||||
u8 bt_sysclk;
|
||||
u8 man_id;
|
||||
u8 ver_id;
|
||||
|
||||
struct sk_buff_head fw_queue;
|
||||
struct sk_buff *alive_cmd_skb;
|
||||
struct completion init_completion;
|
||||
struct completion fw_completion;
|
||||
struct completion test_completion;
|
||||
int fw_error;
|
||||
int init_error;
|
||||
|
||||
struct sk_buff_head txq;
|
||||
|
||||
struct sk_buff *rx_skb;
|
||||
long rx_count;
|
||||
unsigned long rx_state;
|
||||
unsigned long garbage_bytes;
|
||||
|
||||
u8 bd_addr[6];
|
||||
struct sk_buff_head *fw_q;
|
||||
|
||||
int pm_enabled;
|
||||
int tx_enabled;
|
||||
int autorts;
|
||||
int rx_enabled;
|
||||
unsigned long pm_flags;
|
||||
|
||||
int tx_clocks_en;
|
||||
int rx_clocks_en;
|
||||
spinlock_t clocks_lock;
|
||||
struct clk *uart_iclk;
|
||||
struct clk *uart_fclk;
|
||||
atomic_t clk_users;
|
||||
u16 dll;
|
||||
u16 dlh;
|
||||
u16 ier;
|
||||
u16 mdr1;
|
||||
u16 efr;
|
||||
};
|
||||
|
||||
struct hci_h4p_radio_hdr {
|
||||
__u8 evt;
|
||||
__u8 dlen;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct hci_h4p_neg_hdr {
|
||||
__u8 dlen;
|
||||
} __attribute__ ((packed));
|
||||
#define H4P_NEG_HDR_SIZE 1
|
||||
|
||||
#define H4P_NEG_REQ 0x00
|
||||
#define H4P_NEG_ACK 0x20
|
||||
#define H4P_NEG_NAK 0x40
|
||||
|
||||
#define H4P_PROTO_PKT 0x44
|
||||
#define H4P_PROTO_BYTE 0x4c
|
||||
|
||||
#define H4P_ID_CSR 0x02
|
||||
#define H4P_ID_BCM2048 0x04
|
||||
#define H4P_ID_TI1271 0x31
|
||||
|
||||
struct hci_h4p_neg_cmd {
|
||||
__u8 ack;
|
||||
__u16 baud;
|
||||
__u16 unused1;
|
||||
__u8 proto;
|
||||
__u16 sys_clk;
|
||||
__u16 unused2;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
struct hci_h4p_neg_evt {
|
||||
__u8 ack;
|
||||
__u16 baud;
|
||||
__u16 unused1;
|
||||
__u8 proto;
|
||||
__u16 sys_clk;
|
||||
__u16 unused2;
|
||||
__u8 man_id;
|
||||
__u8 ver_id;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define H4P_ALIVE_REQ 0x55
|
||||
#define H4P_ALIVE_RESP 0xcc
|
||||
|
||||
struct hci_h4p_alive_hdr {
|
||||
__u8 dlen;
|
||||
} __attribute__ ((packed));
|
||||
#define H4P_ALIVE_HDR_SIZE 1
|
||||
|
||||
struct hci_h4p_alive_pkt {
|
||||
__u8 mid;
|
||||
__u8 unused;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
#define MAX_BAUD_RATE 921600
|
||||
#define BC4_MAX_BAUD_RATE 3692300
|
||||
#define UART_CLOCK 48000000
|
||||
#define BT_INIT_DIVIDER 320
|
||||
#define BT_BAUDRATE_DIVIDER 384000000
|
||||
#define BT_SYSCLK_DIV 1000
|
||||
#define INIT_SPEED 120000
|
||||
|
||||
#define H4_TYPE_SIZE 1
|
||||
#define H4_RADIO_HDR_SIZE 2
|
||||
|
||||
/* H4+ packet types */
|
||||
#define H4_CMD_PKT 0x01
|
||||
#define H4_ACL_PKT 0x02
|
||||
#define H4_SCO_PKT 0x03
|
||||
#define H4_EVT_PKT 0x04
|
||||
#define H4_NEG_PKT 0x06
|
||||
#define H4_ALIVE_PKT 0x07
|
||||
#define H4_RADIO_PKT 0x08
|
||||
|
||||
/* TX states */
|
||||
#define WAIT_FOR_PKT_TYPE 1
|
||||
#define WAIT_FOR_HEADER 2
|
||||
#define WAIT_FOR_DATA 3
|
||||
|
||||
struct hci_fw_event {
|
||||
struct hci_event_hdr hev;
|
||||
struct hci_ev_cmd_complete cmd;
|
||||
u8 status;
|
||||
} __attribute__ ((packed));
|
||||
|
||||
int hci_h4p_send_alive_packet(struct hci_h4p_info *info);
|
||||
|
||||
void hci_h4p_bcm_parse_fw_event(struct hci_h4p_info *info,
|
||||
struct sk_buff *skb);
|
||||
int hci_h4p_bcm_send_fw(struct hci_h4p_info *info,
|
||||
struct sk_buff_head *fw_queue);
|
||||
|
||||
void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info,
|
||||
struct sk_buff *skb);
|
||||
int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
|
||||
struct sk_buff_head *fw_queue);
|
||||
|
||||
void hci_h4p_ti1273_parse_fw_event(struct hci_h4p_info *info,
|
||||
struct sk_buff *skb);
|
||||
int hci_h4p_ti1273_send_fw(struct hci_h4p_info *info,
|
||||
struct sk_buff_head *fw_queue);
|
||||
|
||||
int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
|
||||
int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue);
|
||||
void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb);
|
||||
|
||||
void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val);
|
||||
u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset);
|
||||
void hci_h4p_set_rts(struct hci_h4p_info *info, int active);
|
||||
int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active, int timeout_ms);
|
||||
void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
|
||||
void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which);
|
||||
void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed);
|
||||
int hci_h4p_reset_uart(struct hci_h4p_info *info);
|
||||
void hci_h4p_init_uart(struct hci_h4p_info *info);
|
||||
void hci_h4p_enable_tx(struct hci_h4p_info *info);
|
||||
void hci_h4p_store_regs(struct hci_h4p_info *info);
|
||||
void hci_h4p_restore_regs(struct hci_h4p_info *info);
|
||||
void hci_h4p_smart_idle(struct hci_h4p_info *info, bool enable);
|
||||
|
||||
#endif /* __DRIVERS_BLUETOOTH_HCI_H4P_H */
|
Разница между файлами не показана из-за своего большого размера
Загрузить разницу
|
@ -0,0 +1,147 @@
|
|||
/*
|
||||
* This file is part of Nokia H4P bluetooth driver
|
||||
*
|
||||
* Copyright (C) 2005-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/serial_reg.h>
|
||||
|
||||
#include "hci_h4p.h"
|
||||
|
||||
static int hci_h4p_bcm_set_bdaddr(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||
{
|
||||
int i;
|
||||
static const u8 nokia_oui[3] = {0x00, 0x1f, 0xdf};
|
||||
int not_valid;
|
||||
|
||||
not_valid = 1;
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (info->bd_addr[i] != 0x00) {
|
||||
not_valid = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (not_valid) {
|
||||
dev_info(info->dev, "Valid bluetooth address not found, setting some random\n");
|
||||
/* When address is not valid, use some random but Nokia MAC */
|
||||
memcpy(info->bd_addr, nokia_oui, 3);
|
||||
get_random_bytes(info->bd_addr + 3, 3);
|
||||
}
|
||||
|
||||
for (i = 0; i < 6; i++)
|
||||
skb->data[9 - i] = info->bd_addr[i];
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hci_h4p_bcm_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *fw_skb;
|
||||
int err;
|
||||
unsigned long flags;
|
||||
|
||||
if (skb->data[5] != 0x00) {
|
||||
dev_err(info->dev, "Firmware sending command failed 0x%.2x\n",
|
||||
skb->data[5]);
|
||||
info->fw_error = -EPROTO;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
fw_skb = skb_dequeue(info->fw_q);
|
||||
if (fw_skb == NULL || info->fw_error) {
|
||||
complete(&info->fw_completion);
|
||||
return;
|
||||
}
|
||||
|
||||
if (fw_skb->data[1] == 0x01 && fw_skb->data[2] == 0xfc && fw_skb->len >= 10) {
|
||||
BT_DBG("Setting bluetooth address");
|
||||
err = hci_h4p_bcm_set_bdaddr(info, fw_skb);
|
||||
if (err < 0) {
|
||||
kfree_skb(fw_skb);
|
||||
info->fw_error = err;
|
||||
complete(&info->fw_completion);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
skb_queue_tail(&info->txq, fw_skb);
|
||||
spin_lock_irqsave(&info->lock, flags);
|
||||
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||
UART_IER_THRI);
|
||||
spin_unlock_irqrestore(&info->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
int hci_h4p_bcm_send_fw(struct hci_h4p_info *info,
|
||||
struct sk_buff_head *fw_queue)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags, time;
|
||||
|
||||
info->fw_error = 0;
|
||||
|
||||
BT_DBG("Sending firmware");
|
||||
|
||||
time = jiffies;
|
||||
|
||||
info->fw_q = fw_queue;
|
||||
skb = skb_dequeue(fw_queue);
|
||||
if (!skb)
|
||||
return -ENODATA;
|
||||
|
||||
BT_DBG("Sending commands");
|
||||
|
||||
/*
|
||||
* Disable smart-idle as UART TX interrupts
|
||||
* are not wake-up capable
|
||||
*/
|
||||
hci_h4p_smart_idle(info, 0);
|
||||
|
||||
/* Check if this is bd_address packet */
|
||||
init_completion(&info->fw_completion);
|
||||
skb_queue_tail(&info->txq, skb);
|
||||
spin_lock_irqsave(&info->lock, flags);
|
||||
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||
UART_IER_THRI);
|
||||
spin_unlock_irqrestore(&info->lock, flags);
|
||||
|
||||
if (!wait_for_completion_timeout(&info->fw_completion,
|
||||
msecs_to_jiffies(2000))) {
|
||||
dev_err(info->dev, "No reply to fw command\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (info->fw_error) {
|
||||
dev_err(info->dev, "FW error\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
BT_DBG("Firmware sent in %d msecs",
|
||||
jiffies_to_msecs(jiffies-time));
|
||||
|
||||
hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
|
||||
hci_h4p_set_rts(info, 0);
|
||||
hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
|
||||
hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,150 @@
|
|||
/*
|
||||
* This file is part of Nokia H4P bluetooth driver
|
||||
*
|
||||
* Copyright (C) 2005-2008 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/serial_reg.h>
|
||||
|
||||
#include "hci_h4p.h"
|
||||
|
||||
void hci_h4p_bc4_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||
{
|
||||
/* Check if this is fw packet */
|
||||
if (skb->data[0] != 0xff) {
|
||||
hci_recv_frame(info->hdev, skb);
|
||||
return;
|
||||
}
|
||||
|
||||
if (skb->data[11] || skb->data[12]) {
|
||||
dev_err(info->dev, "Firmware sending command failed\n");
|
||||
info->fw_error = -EPROTO;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
complete(&info->fw_completion);
|
||||
}
|
||||
|
||||
int hci_h4p_bc4_send_fw(struct hci_h4p_info *info,
|
||||
struct sk_buff_head *fw_queue)
|
||||
{
|
||||
static const u8 nokia_oui[3] = {0x00, 0x19, 0x4F};
|
||||
struct sk_buff *skb;
|
||||
unsigned int offset;
|
||||
int retries, count, i, not_valid;
|
||||
unsigned long flags;
|
||||
|
||||
info->fw_error = 0;
|
||||
|
||||
BT_DBG("Sending firmware");
|
||||
skb = skb_dequeue(fw_queue);
|
||||
|
||||
if (!skb)
|
||||
return -ENOMSG;
|
||||
|
||||
/* Check if this is bd_address packet */
|
||||
if (skb->data[15] == 0x01 && skb->data[16] == 0x00) {
|
||||
offset = 21;
|
||||
skb->data[offset + 1] = 0x00;
|
||||
skb->data[offset + 5] = 0x00;
|
||||
|
||||
not_valid = 1;
|
||||
for (i = 0; i < 6; i++) {
|
||||
if (info->bd_addr[i] != 0x00) {
|
||||
not_valid = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (not_valid) {
|
||||
dev_info(info->dev, "Valid bluetooth address not found,"
|
||||
" setting some random\n");
|
||||
/* When address is not valid, use some random */
|
||||
memcpy(info->bd_addr, nokia_oui, 3);
|
||||
get_random_bytes(info->bd_addr + 3, 3);
|
||||
}
|
||||
|
||||
skb->data[offset + 7] = info->bd_addr[0];
|
||||
skb->data[offset + 6] = info->bd_addr[1];
|
||||
skb->data[offset + 4] = info->bd_addr[2];
|
||||
skb->data[offset + 0] = info->bd_addr[3];
|
||||
skb->data[offset + 3] = info->bd_addr[4];
|
||||
skb->data[offset + 2] = info->bd_addr[5];
|
||||
}
|
||||
|
||||
for (count = 1; ; count++) {
|
||||
BT_DBG("Sending firmware command %d", count);
|
||||
init_completion(&info->fw_completion);
|
||||
skb_queue_tail(&info->txq, skb);
|
||||
spin_lock_irqsave(&info->lock, flags);
|
||||
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||
UART_IER_THRI);
|
||||
spin_unlock_irqrestore(&info->lock, flags);
|
||||
|
||||
skb = skb_dequeue(fw_queue);
|
||||
if (!skb)
|
||||
break;
|
||||
|
||||
if (!wait_for_completion_timeout(&info->fw_completion,
|
||||
msecs_to_jiffies(1000))) {
|
||||
dev_err(info->dev, "No reply to fw command\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (info->fw_error) {
|
||||
dev_err(info->dev, "FW error\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
};
|
||||
|
||||
/* Wait for chip warm reset */
|
||||
retries = 100;
|
||||
while ((!skb_queue_empty(&info->txq) ||
|
||||
!(hci_h4p_inb(info, UART_LSR) & UART_LSR_TEMT)) &&
|
||||
retries--) {
|
||||
msleep(10);
|
||||
}
|
||||
if (!retries) {
|
||||
dev_err(info->dev, "Transmitter not empty\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
|
||||
|
||||
if (hci_h4p_wait_for_cts(info, 1, 100)) {
|
||||
dev_err(info->dev, "cts didn't deassert after final speed\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
retries = 100;
|
||||
do {
|
||||
init_completion(&info->init_completion);
|
||||
hci_h4p_send_alive_packet(info);
|
||||
retries--;
|
||||
} while (!wait_for_completion_timeout(&info->init_completion, 100) &&
|
||||
retries > 0);
|
||||
|
||||
if (!retries) {
|
||||
dev_err(info->dev, "No alive reply after speed change\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,110 @@
|
|||
/*
|
||||
* This file is part of Nokia H4P bluetooth driver
|
||||
*
|
||||
* Copyright (C) 2009 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/serial_reg.h>
|
||||
|
||||
#include "hci_h4p.h"
|
||||
|
||||
static struct sk_buff_head *fw_q;
|
||||
|
||||
void hci_h4p_ti1273_parse_fw_event(struct hci_h4p_info *info,
|
||||
struct sk_buff *skb)
|
||||
{
|
||||
struct sk_buff *fw_skb;
|
||||
unsigned long flags;
|
||||
|
||||
if (skb->data[5] != 0x00) {
|
||||
dev_err(info->dev, "Firmware sending command failed 0x%.2x\n",
|
||||
skb->data[5]);
|
||||
info->fw_error = -EPROTO;
|
||||
}
|
||||
|
||||
kfree_skb(skb);
|
||||
|
||||
fw_skb = skb_dequeue(fw_q);
|
||||
if (fw_skb == NULL || info->fw_error) {
|
||||
complete(&info->fw_completion);
|
||||
return;
|
||||
}
|
||||
|
||||
skb_queue_tail(&info->txq, fw_skb);
|
||||
spin_lock_irqsave(&info->lock, flags);
|
||||
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||
UART_IER_THRI);
|
||||
spin_unlock_irqrestore(&info->lock, flags);
|
||||
}
|
||||
|
||||
|
||||
int hci_h4p_ti1273_send_fw(struct hci_h4p_info *info,
|
||||
struct sk_buff_head *fw_queue)
|
||||
{
|
||||
struct sk_buff *skb;
|
||||
unsigned long flags, time;
|
||||
|
||||
info->fw_error = 0;
|
||||
|
||||
BT_DBG("Sending firmware");
|
||||
|
||||
time = jiffies;
|
||||
|
||||
fw_q = fw_queue;
|
||||
skb = skb_dequeue(fw_queue);
|
||||
if (!skb)
|
||||
return -ENODATA;
|
||||
|
||||
BT_DBG("Sending commands");
|
||||
/* Check if this is bd_address packet */
|
||||
init_completion(&info->fw_completion);
|
||||
hci_h4p_smart_idle(info, 0);
|
||||
skb_queue_tail(&info->txq, skb);
|
||||
spin_lock_irqsave(&info->lock, flags);
|
||||
hci_h4p_outb(info, UART_IER, hci_h4p_inb(info, UART_IER) |
|
||||
UART_IER_THRI);
|
||||
spin_unlock_irqrestore(&info->lock, flags);
|
||||
|
||||
if (!wait_for_completion_timeout(&info->fw_completion,
|
||||
msecs_to_jiffies(2000))) {
|
||||
dev_err(info->dev, "No reply to fw command\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
|
||||
if (info->fw_error) {
|
||||
dev_err(info->dev, "FW error\n");
|
||||
return -EPROTO;
|
||||
}
|
||||
|
||||
BT_DBG("Firmware sent in %d msecs",
|
||||
jiffies_to_msecs(jiffies-time));
|
||||
|
||||
hci_h4p_set_auto_ctsrts(info, 0, UART_EFR_RTS);
|
||||
hci_h4p_set_rts(info, 0);
|
||||
hci_h4p_change_speed(info, BC4_MAX_BAUD_RATE);
|
||||
if (hci_h4p_wait_for_cts(info, 1, 100)) {
|
||||
dev_err(info->dev,
|
||||
"cts didn't go down after final speed change\n");
|
||||
return -ETIMEDOUT;
|
||||
}
|
||||
hci_h4p_set_auto_ctsrts(info, 1, UART_EFR_RTS);
|
||||
|
||||
return 0;
|
||||
}
|
|
@ -0,0 +1,195 @@
|
|||
/*
|
||||
* This file is part of hci_h4p bluetooth driver
|
||||
*
|
||||
* Copyright (C) 2005, 2006 Nokia Corporation.
|
||||
*
|
||||
* Contact: Ville Tervo <ville.tervo@nokia.com>
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/skbuff.h>
|
||||
#include <linux/firmware.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <net/bluetooth/bluetooth.h>
|
||||
|
||||
#include "hci_h4p.h"
|
||||
|
||||
static int fw_pos;
|
||||
|
||||
/* Firmware handling */
|
||||
static int hci_h4p_open_firmware(struct hci_h4p_info *info,
|
||||
const struct firmware **fw_entry)
|
||||
{
|
||||
int err;
|
||||
|
||||
fw_pos = 0;
|
||||
BT_DBG("Opening firmware man_id 0x%.2x ver_id 0x%.2x",
|
||||
info->man_id, info->ver_id);
|
||||
switch (info->man_id) {
|
||||
case H4P_ID_TI1271:
|
||||
switch (info->ver_id) {
|
||||
case 0xe1:
|
||||
err = request_firmware(fw_entry, FW_NAME_TI1271_PRELE,
|
||||
info->dev);
|
||||
break;
|
||||
case 0xd1:
|
||||
case 0xf1:
|
||||
err = request_firmware(fw_entry, FW_NAME_TI1271_LE,
|
||||
info->dev);
|
||||
break;
|
||||
default:
|
||||
err = request_firmware(fw_entry, FW_NAME_TI1271,
|
||||
info->dev);
|
||||
}
|
||||
break;
|
||||
case H4P_ID_CSR:
|
||||
err = request_firmware(fw_entry, FW_NAME_CSR, info->dev);
|
||||
break;
|
||||
case H4P_ID_BCM2048:
|
||||
err = request_firmware(fw_entry, FW_NAME_BCM2048, info->dev);
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "Invalid chip type\n");
|
||||
*fw_entry = NULL;
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
static void hci_h4p_close_firmware(const struct firmware *fw_entry)
|
||||
{
|
||||
release_firmware(fw_entry);
|
||||
}
|
||||
|
||||
/* Read fw. Return length of the command. If no more commands in
|
||||
* fw 0 is returned. In error case return value is negative.
|
||||
*/
|
||||
static int hci_h4p_read_fw_cmd(struct hci_h4p_info *info, struct sk_buff **skb,
|
||||
const struct firmware *fw_entry, gfp_t how)
|
||||
{
|
||||
unsigned int cmd_len;
|
||||
|
||||
if (fw_pos >= fw_entry->size)
|
||||
return 0;
|
||||
|
||||
if (fw_pos + 2 > fw_entry->size) {
|
||||
dev_err(info->dev, "Corrupted firmware image 1\n");
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
cmd_len = fw_entry->data[fw_pos++];
|
||||
cmd_len += fw_entry->data[fw_pos++] << 8;
|
||||
if (cmd_len == 0)
|
||||
return 0;
|
||||
|
||||
if (fw_pos + cmd_len > fw_entry->size) {
|
||||
dev_err(info->dev, "Corrupted firmware image 2\n");
|
||||
return -EMSGSIZE;
|
||||
}
|
||||
|
||||
*skb = bt_skb_alloc(cmd_len, how);
|
||||
if (!*skb) {
|
||||
dev_err(info->dev, "Cannot reserve memory for buffer\n");
|
||||
return -ENOMEM;
|
||||
}
|
||||
memcpy(skb_put(*skb, cmd_len), &fw_entry->data[fw_pos], cmd_len);
|
||||
|
||||
fw_pos += cmd_len;
|
||||
|
||||
return (*skb)->len;
|
||||
}
|
||||
|
||||
int hci_h4p_read_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
|
||||
{
|
||||
const struct firmware *fw_entry = NULL;
|
||||
struct sk_buff *skb = NULL;
|
||||
int err;
|
||||
|
||||
err = hci_h4p_open_firmware(info, &fw_entry);
|
||||
if (err < 0 || !fw_entry)
|
||||
goto err_clean;
|
||||
|
||||
while ((err = hci_h4p_read_fw_cmd(info, &skb, fw_entry, GFP_KERNEL))) {
|
||||
if (err < 0 || !skb)
|
||||
goto err_clean;
|
||||
|
||||
skb_queue_tail(fw_queue, skb);
|
||||
}
|
||||
|
||||
/* Chip detection code does neg and alive stuff
|
||||
* discard two first skbs */
|
||||
skb = skb_dequeue(fw_queue);
|
||||
if (!skb) {
|
||||
err = -EMSGSIZE;
|
||||
goto err_clean;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
skb = skb_dequeue(fw_queue);
|
||||
if (!skb) {
|
||||
err = -EMSGSIZE;
|
||||
goto err_clean;
|
||||
}
|
||||
kfree_skb(skb);
|
||||
|
||||
err_clean:
|
||||
hci_h4p_close_firmware(fw_entry);
|
||||
return err;
|
||||
}
|
||||
|
||||
int hci_h4p_send_fw(struct hci_h4p_info *info, struct sk_buff_head *fw_queue)
|
||||
{
|
||||
int err;
|
||||
|
||||
switch (info->man_id) {
|
||||
case H4P_ID_CSR:
|
||||
err = hci_h4p_bc4_send_fw(info, fw_queue);
|
||||
break;
|
||||
case H4P_ID_TI1271:
|
||||
err = hci_h4p_ti1273_send_fw(info, fw_queue);
|
||||
break;
|
||||
case H4P_ID_BCM2048:
|
||||
err = hci_h4p_bcm_send_fw(info, fw_queue);
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "Don't know how to send firmware\n");
|
||||
err = -EINVAL;
|
||||
}
|
||||
|
||||
return err;
|
||||
}
|
||||
|
||||
void hci_h4p_parse_fw_event(struct hci_h4p_info *info, struct sk_buff *skb)
|
||||
{
|
||||
switch (info->man_id) {
|
||||
case H4P_ID_CSR:
|
||||
hci_h4p_bc4_parse_fw_event(info, skb);
|
||||
break;
|
||||
case H4P_ID_TI1271:
|
||||
hci_h4p_ti1273_parse_fw_event(info, skb);
|
||||
break;
|
||||
case H4P_ID_BCM2048:
|
||||
hci_h4p_bcm_parse_fw_event(info, skb);
|
||||
break;
|
||||
default:
|
||||
dev_err(info->dev, "Don't know how to parse fw event\n");
|
||||
info->fw_error = -EINVAL;
|
||||
}
|
||||
|
||||
return;
|
||||
}
|
|
@ -0,0 +1,199 @@
|
|||
/*
|
||||
* This file is part of Nokia H4P bluetooth driver
|
||||
*
|
||||
* Copyright (C) 2005, 2006 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
#include <linux/serial_reg.h>
|
||||
#include <linux/delay.h>
|
||||
#include <linux/clk.h>
|
||||
|
||||
#include <linux/io.h>
|
||||
|
||||
#include "hci_h4p.h"
|
||||
|
||||
inline void hci_h4p_outb(struct hci_h4p_info *info, unsigned int offset, u8 val)
|
||||
{
|
||||
__raw_writeb(val, info->uart_base + (offset << 2));
|
||||
}
|
||||
|
||||
inline u8 hci_h4p_inb(struct hci_h4p_info *info, unsigned int offset)
|
||||
{
|
||||
return __raw_readb(info->uart_base + (offset << 2));
|
||||
}
|
||||
|
||||
void hci_h4p_set_rts(struct hci_h4p_info *info, int active)
|
||||
{
|
||||
u8 b;
|
||||
|
||||
b = hci_h4p_inb(info, UART_MCR);
|
||||
if (active)
|
||||
b |= UART_MCR_RTS;
|
||||
else
|
||||
b &= ~UART_MCR_RTS;
|
||||
hci_h4p_outb(info, UART_MCR, b);
|
||||
}
|
||||
|
||||
int hci_h4p_wait_for_cts(struct hci_h4p_info *info, int active,
|
||||
int timeout_ms)
|
||||
{
|
||||
unsigned long timeout;
|
||||
int state;
|
||||
|
||||
timeout = jiffies + msecs_to_jiffies(timeout_ms);
|
||||
for (;;) {
|
||||
state = hci_h4p_inb(info, UART_MSR) & UART_MSR_CTS;
|
||||
if (active) {
|
||||
if (state)
|
||||
return 0;
|
||||
} else {
|
||||
if (!state)
|
||||
return 0;
|
||||
}
|
||||
if (time_after(jiffies, timeout))
|
||||
return -ETIMEDOUT;
|
||||
msleep(1);
|
||||
}
|
||||
}
|
||||
|
||||
void __hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
|
||||
{
|
||||
u8 lcr, b;
|
||||
|
||||
lcr = hci_h4p_inb(info, UART_LCR);
|
||||
hci_h4p_outb(info, UART_LCR, 0xbf);
|
||||
b = hci_h4p_inb(info, UART_EFR);
|
||||
if (on)
|
||||
b |= which;
|
||||
else
|
||||
b &= ~which;
|
||||
hci_h4p_outb(info, UART_EFR, b);
|
||||
hci_h4p_outb(info, UART_LCR, lcr);
|
||||
}
|
||||
|
||||
void hci_h4p_set_auto_ctsrts(struct hci_h4p_info *info, int on, u8 which)
|
||||
{
|
||||
unsigned long flags;
|
||||
|
||||
spin_lock_irqsave(&info->lock, flags);
|
||||
__hci_h4p_set_auto_ctsrts(info, on, which);
|
||||
spin_unlock_irqrestore(&info->lock, flags);
|
||||
}
|
||||
|
||||
void hci_h4p_change_speed(struct hci_h4p_info *info, unsigned long speed)
|
||||
{
|
||||
unsigned int divisor;
|
||||
u8 lcr, mdr1;
|
||||
|
||||
BT_DBG("Setting speed %lu", speed);
|
||||
|
||||
if (speed >= 460800) {
|
||||
divisor = UART_CLOCK / 13 / speed;
|
||||
mdr1 = 3;
|
||||
} else {
|
||||
divisor = UART_CLOCK / 16 / speed;
|
||||
mdr1 = 0;
|
||||
}
|
||||
|
||||
/* Make sure UART mode is disabled */
|
||||
hci_h4p_outb(info, UART_OMAP_MDR1, 7);
|
||||
|
||||
lcr = hci_h4p_inb(info, UART_LCR);
|
||||
hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB); /* Set DLAB */
|
||||
hci_h4p_outb(info, UART_DLL, divisor & 0xff); /* Set speed */
|
||||
hci_h4p_outb(info, UART_DLM, divisor >> 8);
|
||||
hci_h4p_outb(info, UART_LCR, lcr);
|
||||
|
||||
/* Make sure UART mode is enabled */
|
||||
hci_h4p_outb(info, UART_OMAP_MDR1, mdr1);
|
||||
}
|
||||
|
||||
int hci_h4p_reset_uart(struct hci_h4p_info *info)
|
||||
{
|
||||
int count = 0;
|
||||
|
||||
/* Reset the UART */
|
||||
hci_h4p_outb(info, UART_OMAP_SYSC, UART_SYSC_OMAP_RESET);
|
||||
while (!(hci_h4p_inb(info, UART_OMAP_SYSS) & UART_SYSS_RESETDONE)) {
|
||||
if (count++ > 100) {
|
||||
dev_err(info->dev, "hci_h4p: UART reset timeout\n");
|
||||
return -ENODEV;
|
||||
}
|
||||
udelay(1);
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
void hci_h4p_store_regs(struct hci_h4p_info *info)
|
||||
{
|
||||
u16 lcr = 0;
|
||||
|
||||
lcr = hci_h4p_inb(info, UART_LCR);
|
||||
hci_h4p_outb(info, UART_LCR, 0xBF);
|
||||
info->dll = hci_h4p_inb(info, UART_DLL);
|
||||
info->dlh = hci_h4p_inb(info, UART_DLM);
|
||||
info->efr = hci_h4p_inb(info, UART_EFR);
|
||||
hci_h4p_outb(info, UART_LCR, lcr);
|
||||
info->mdr1 = hci_h4p_inb(info, UART_OMAP_MDR1);
|
||||
info->ier = hci_h4p_inb(info, UART_IER);
|
||||
}
|
||||
|
||||
void hci_h4p_restore_regs(struct hci_h4p_info *info)
|
||||
{
|
||||
u16 lcr = 0;
|
||||
|
||||
hci_h4p_init_uart(info);
|
||||
|
||||
hci_h4p_outb(info, UART_OMAP_MDR1, 7);
|
||||
lcr = hci_h4p_inb(info, UART_LCR);
|
||||
hci_h4p_outb(info, UART_LCR, 0xBF);
|
||||
hci_h4p_outb(info, UART_DLL, info->dll); /* Set speed */
|
||||
hci_h4p_outb(info, UART_DLM, info->dlh);
|
||||
hci_h4p_outb(info, UART_EFR, info->efr);
|
||||
hci_h4p_outb(info, UART_LCR, lcr);
|
||||
hci_h4p_outb(info, UART_OMAP_MDR1, info->mdr1);
|
||||
hci_h4p_outb(info, UART_IER, info->ier);
|
||||
}
|
||||
|
||||
void hci_h4p_init_uart(struct hci_h4p_info *info)
|
||||
{
|
||||
u8 mcr, efr;
|
||||
|
||||
/* Enable and setup FIFO */
|
||||
hci_h4p_outb(info, UART_OMAP_MDR1, 0x00);
|
||||
|
||||
hci_h4p_outb(info, UART_LCR, 0xbf);
|
||||
efr = hci_h4p_inb(info, UART_EFR);
|
||||
hci_h4p_outb(info, UART_EFR, UART_EFR_ECB);
|
||||
hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
|
||||
mcr = hci_h4p_inb(info, UART_MCR);
|
||||
hci_h4p_outb(info, UART_MCR, UART_MCR_TCRTLR);
|
||||
hci_h4p_outb(info, UART_FCR, UART_FCR_ENABLE_FIFO |
|
||||
UART_FCR_CLEAR_RCVR | UART_FCR_CLEAR_XMIT |
|
||||
(3 << 6) | (0 << 4));
|
||||
hci_h4p_outb(info, UART_LCR, 0xbf);
|
||||
hci_h4p_outb(info, UART_TI752_TLR, 0xed);
|
||||
hci_h4p_outb(info, UART_TI752_TCR, 0xef);
|
||||
hci_h4p_outb(info, UART_EFR, efr);
|
||||
hci_h4p_outb(info, UART_LCR, UART_LCR_DLAB);
|
||||
hci_h4p_outb(info, UART_MCR, 0x00);
|
||||
hci_h4p_outb(info, UART_LCR, UART_LCR_WLEN8);
|
||||
hci_h4p_outb(info, UART_IER, UART_IER_RDI);
|
||||
hci_h4p_outb(info, UART_OMAP_SYSC, (1 << 0) | (1 << 2) | (2 << 3));
|
||||
}
|
|
@ -0,0 +1,38 @@
|
|||
/*
|
||||
* This file is part of Nokia H4P bluetooth driver
|
||||
*
|
||||
* Copyright (C) 2010 Nokia Corporation.
|
||||
*
|
||||
* This program is free software; you can redistribute it and/or
|
||||
* modify it under the terms of the GNU General Public License
|
||||
* version 2 as published by the Free Software Foundation.
|
||||
*
|
||||
* 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., 51 Franklin St, Fifth Floor, Boston, MA
|
||||
* 02110-1301 USA
|
||||
*
|
||||
*/
|
||||
|
||||
|
||||
/**
|
||||
* struct hci_h4p_platform data - hci_h4p Platform data structure
|
||||
*/
|
||||
struct hci_h4p_platform_data {
|
||||
int chip_type;
|
||||
int bt_sysclk;
|
||||
unsigned int bt_wakeup_gpio;
|
||||
unsigned int host_wakeup_gpio;
|
||||
unsigned int reset_gpio;
|
||||
int reset_gpio_shared;
|
||||
unsigned int uart_irq;
|
||||
phys_addr_t uart_base;
|
||||
const char *uart_iclk;
|
||||
const char *uart_fclk;
|
||||
void (*set_pm_limits)(struct device *dev, bool set);
|
||||
};
|
Загрузка…
Ссылка в новой задаче