| .. | .. |
|---|
| 1 | +// SPDX-License-Identifier: GPL-2.0-or-later |
|---|
| 1 | 2 | /* |
|---|
| 2 | 3 | * |
|---|
| 3 | 4 | * Bluetooth HCI UART driver for marvell devices |
|---|
| 4 | 5 | * |
|---|
| 5 | 6 | * Copyright (C) 2016 Marvell International Ltd. |
|---|
| 6 | 7 | * Copyright (C) 2016 Intel Corporation |
|---|
| 7 | | - * |
|---|
| 8 | | - * This program is free software; you can redistribute it and/or modify |
|---|
| 9 | | - * it under the terms of the GNU General Public License as published by |
|---|
| 10 | | - * the Free Software Foundation; either version 2 of the License, or |
|---|
| 11 | | - * (at your option) any later version. |
|---|
| 12 | | - * |
|---|
| 13 | | - * This program is distributed in the hope that it will be useful, |
|---|
| 14 | | - * but WITHOUT ANY WARRANTY; without even the implied warranty of |
|---|
| 15 | | - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
|---|
| 16 | | - * GNU General Public License for more details. |
|---|
| 17 | | - * |
|---|
| 18 | | - * You should have received a copy of the GNU General Public License |
|---|
| 19 | | - * along with this program; if not, write to the Free Software |
|---|
| 20 | | - * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA |
|---|
| 21 | | - * |
|---|
| 22 | 8 | */ |
|---|
| 23 | 9 | |
|---|
| 24 | 10 | #include <linux/kernel.h> |
|---|
| .. | .. |
|---|
| 27 | 13 | #include <linux/firmware.h> |
|---|
| 28 | 14 | #include <linux/module.h> |
|---|
| 29 | 15 | #include <linux/tty.h> |
|---|
| 16 | +#include <linux/of.h> |
|---|
| 17 | +#include <linux/serdev.h> |
|---|
| 30 | 18 | |
|---|
| 31 | 19 | #include <net/bluetooth/bluetooth.h> |
|---|
| 32 | 20 | #include <net/bluetooth/hci_core.h> |
|---|
| .. | .. |
|---|
| 54 | 42 | u8 id, rev; |
|---|
| 55 | 43 | }; |
|---|
| 56 | 44 | |
|---|
| 45 | +struct mrvl_serdev { |
|---|
| 46 | + struct hci_uart hu; |
|---|
| 47 | +}; |
|---|
| 48 | + |
|---|
| 57 | 49 | struct hci_mrvl_pkt { |
|---|
| 58 | 50 | __le16 lhs; |
|---|
| 59 | 51 | __le16 rhs; |
|---|
| .. | .. |
|---|
| 63 | 55 | static int mrvl_open(struct hci_uart *hu) |
|---|
| 64 | 56 | { |
|---|
| 65 | 57 | struct mrvl_data *mrvl; |
|---|
| 58 | + int ret; |
|---|
| 66 | 59 | |
|---|
| 67 | 60 | BT_DBG("hu %p", hu); |
|---|
| 68 | 61 | |
|---|
| .. | .. |
|---|
| 79 | 72 | set_bit(STATE_CHIP_VER_PENDING, &mrvl->flags); |
|---|
| 80 | 73 | |
|---|
| 81 | 74 | hu->priv = mrvl; |
|---|
| 75 | + |
|---|
| 76 | + if (hu->serdev) { |
|---|
| 77 | + ret = serdev_device_open(hu->serdev); |
|---|
| 78 | + if (ret) |
|---|
| 79 | + goto err; |
|---|
| 80 | + } |
|---|
| 81 | + |
|---|
| 82 | 82 | return 0; |
|---|
| 83 | +err: |
|---|
| 84 | + kfree(mrvl); |
|---|
| 85 | + |
|---|
| 86 | + return ret; |
|---|
| 83 | 87 | } |
|---|
| 84 | 88 | |
|---|
| 85 | 89 | static int mrvl_close(struct hci_uart *hu) |
|---|
| .. | .. |
|---|
| 87 | 91 | struct mrvl_data *mrvl = hu->priv; |
|---|
| 88 | 92 | |
|---|
| 89 | 93 | BT_DBG("hu %p", hu); |
|---|
| 94 | + |
|---|
| 95 | + if (hu->serdev) |
|---|
| 96 | + serdev_device_close(hu->serdev); |
|---|
| 90 | 97 | |
|---|
| 91 | 98 | skb_queue_purge(&mrvl->txq); |
|---|
| 92 | 99 | skb_queue_purge(&mrvl->rawq); |
|---|
| .. | .. |
|---|
| 356 | 363 | return -EINVAL; |
|---|
| 357 | 364 | } |
|---|
| 358 | 365 | |
|---|
| 359 | | - hci_uart_set_baudrate(hu, 3000000); |
|---|
| 366 | + /* Let the final ack go out before switching the baudrate */ |
|---|
| 367 | + hci_uart_wait_until_sent(hu); |
|---|
| 368 | + |
|---|
| 369 | + if (hu->serdev) |
|---|
| 370 | + serdev_device_set_baudrate(hu->serdev, 3000000); |
|---|
| 371 | + else |
|---|
| 372 | + hci_uart_set_baudrate(hu, 3000000); |
|---|
| 373 | + |
|---|
| 360 | 374 | hci_uart_set_flow_control(hu, false); |
|---|
| 361 | 375 | |
|---|
| 362 | 376 | err = mrvl_load_firmware(hu->hdev, "mrvl/uart8897_bt.bin"); |
|---|
| .. | .. |
|---|
| 379 | 393 | .dequeue = mrvl_dequeue, |
|---|
| 380 | 394 | }; |
|---|
| 381 | 395 | |
|---|
| 396 | +static int mrvl_serdev_probe(struct serdev_device *serdev) |
|---|
| 397 | +{ |
|---|
| 398 | + struct mrvl_serdev *mrvldev; |
|---|
| 399 | + |
|---|
| 400 | + mrvldev = devm_kzalloc(&serdev->dev, sizeof(*mrvldev), GFP_KERNEL); |
|---|
| 401 | + if (!mrvldev) |
|---|
| 402 | + return -ENOMEM; |
|---|
| 403 | + |
|---|
| 404 | + mrvldev->hu.serdev = serdev; |
|---|
| 405 | + serdev_device_set_drvdata(serdev, mrvldev); |
|---|
| 406 | + |
|---|
| 407 | + return hci_uart_register_device(&mrvldev->hu, &mrvl_proto); |
|---|
| 408 | +} |
|---|
| 409 | + |
|---|
| 410 | +static void mrvl_serdev_remove(struct serdev_device *serdev) |
|---|
| 411 | +{ |
|---|
| 412 | + struct mrvl_serdev *mrvldev = serdev_device_get_drvdata(serdev); |
|---|
| 413 | + |
|---|
| 414 | + hci_uart_unregister_device(&mrvldev->hu); |
|---|
| 415 | +} |
|---|
| 416 | + |
|---|
| 417 | +#ifdef CONFIG_OF |
|---|
| 418 | +static const struct of_device_id mrvl_bluetooth_of_match[] = { |
|---|
| 419 | + { .compatible = "mrvl,88w8897" }, |
|---|
| 420 | + { }, |
|---|
| 421 | +}; |
|---|
| 422 | +MODULE_DEVICE_TABLE(of, mrvl_bluetooth_of_match); |
|---|
| 423 | +#endif |
|---|
| 424 | + |
|---|
| 425 | +static struct serdev_device_driver mrvl_serdev_driver = { |
|---|
| 426 | + .probe = mrvl_serdev_probe, |
|---|
| 427 | + .remove = mrvl_serdev_remove, |
|---|
| 428 | + .driver = { |
|---|
| 429 | + .name = "hci_uart_mrvl", |
|---|
| 430 | + .of_match_table = of_match_ptr(mrvl_bluetooth_of_match), |
|---|
| 431 | + }, |
|---|
| 432 | +}; |
|---|
| 433 | + |
|---|
| 382 | 434 | int __init mrvl_init(void) |
|---|
| 383 | 435 | { |
|---|
| 436 | + serdev_device_driver_register(&mrvl_serdev_driver); |
|---|
| 437 | + |
|---|
| 384 | 438 | return hci_uart_register_proto(&mrvl_proto); |
|---|
| 385 | 439 | } |
|---|
| 386 | 440 | |
|---|
| 387 | 441 | int __exit mrvl_deinit(void) |
|---|
| 388 | 442 | { |
|---|
| 443 | + serdev_device_driver_unregister(&mrvl_serdev_driver); |
|---|
| 444 | + |
|---|
| 389 | 445 | return hci_uart_unregister_proto(&mrvl_proto); |
|---|
| 390 | 446 | } |
|---|