2018-04-06 11:52:23

by Balakrishna Godavarthi

[permalink] [raw]
Subject: [PATCH 0/3] Enable Bluetooth functionality for WCN3990

These patches enable Bluetooth functionality for WCN3990 by
- adding support for voting of voltage regulators for WCN3990 attached to MSM
- Bluetooth firmware download support for WCN3990
- description of device tree bindings

Balakrishna Godavarthi (1):
Bluetooth: Added support for wcn3990 soc.

Rupesh Tatiya (2):
Bluetooth: Add device tree bindings for Atheros chips
Add support to power Bluetooth QCA chips attached to MSM

.../devicetree/bindings/bluetooth/btqca_power.txt | 30 ++
drivers/bluetooth/Makefile | 5 +-
drivers/bluetooth/btqca.c | 393 ++++++++++++++++-----
drivers/bluetooth/btqca.h | 46 ++-
drivers/bluetooth/btqca_power.c | 177 ++++++++++
drivers/bluetooth/btqca_power.h | 71 ++++
drivers/bluetooth/hci_qca.c | 324 ++++++++++++++---
include/net/bluetooth/hci_core.h | 3 +-
net/bluetooth/hci_core.c | 2 +-
net/bluetooth/hci_request.c | 23 ++
net/bluetooth/hci_request.h | 2 +
11 files changed, 942 insertions(+), 134 deletions(-)
create mode 100644 Documentation/devicetree/bindings/bluetooth/btqca_power.txt
create mode 100644 drivers/bluetooth/btqca_power.c
create mode 100644 drivers/bluetooth/btqca_power.h

--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project


2018-04-12 14:52:18

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 2/3] Add support to power Bluetooth QCA chips attached to MSM

Hi Rupesh,

>>> Add support to set voltage/current of various regulators to power up/down
>>> BT QCA chips attached to MSM.
>>> Change-Id: I3600dd7bc97c753bc9cf7f8ac39d7b90bc21c67d
>>> Signed-off-by: Rupesh Tatiya <[email protected]>
>>> ---
>>> drivers/bluetooth/Makefile | 5 +-
>>> drivers/bluetooth/btqca_power.c | 177 ++++++++++++++++++++++++++++++++++++++++
>>> drivers/bluetooth/btqca_power.h | 71 ++++++++++++++++
>>> 3 files changed, 251 insertions(+), 2 deletions(-)
>>> create mode 100644 drivers/bluetooth/btqca_power.c
>>> create mode 100644 drivers/bluetooth/btqca_power.h
>>> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
>>> index 4e4e44d..f963909 100644
>>> --- a/drivers/bluetooth/Makefile
>>> +++ b/drivers/bluetooth/Makefile
>>> @@ -24,8 +24,9 @@ obj-$(CONFIG_BT_WILINK) += btwilink.o
>>> obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
>>> obj-$(CONFIG_BT_BCM) += btbcm.o
>>> obj-$(CONFIG_BT_RTL) += btrtl.o
>>> -obj-$(CONFIG_BT_QCA) += btqca.o
>>> -
>>> +obj-$(CONFIG_BT_QCA) += btqca_uart.o
>>> +btqca_uart-$(CONFIG_BT_QCA) += btqca.o
>>> +btqca_uart-$(CONFIG_BT_QCA) += btqca_power.o
>>> obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
>>> btmrvl-y := btmrvl_main.o
>>> diff --git a/drivers/bluetooth/btqca_power.c b/drivers/bluetooth/btqca_power.c
>>> new file mode 100644
>>> index 0000000..a189ff1
>>> --- /dev/null
>>> +++ b/drivers/bluetooth/btqca_power.c
>>> @@ -0,0 +1,177 @@
>>> +/* Copyright (c) 2009-2010, 2013-2018 The Linux Foundation.
>>> + * All rights reserved.
>>> + *
>>> + * This program is free software; you can redistribute it and/or modify
>>> + * it under the terms of the GNU General Public License version 2 and
>>> + * only 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.
>>> + */
>>> +/*
>>> + * Bluetooth Power Switch Module
>>> + * controls power to external Bluetooth device
>>> + * with interface to power management device
>>> + */
>>> +
>>> +#include <linux/init.h>
>>> +#include <linux/module.h>
>>> +#include <linux/kernel.h>
>>> +#include <linux/platform_device.h>
>>> +#include <linux/delay.h>
>>> +#include <linux/slab.h>
>>> +#include <linux/regulator/consumer.h>
>>> +#include <linux/of_device.h>
>>> +#include <net/bluetooth/bluetooth.h>
>>> +
>>> +#include "btqca_power.h"
>>> +
>>> +static struct btqca_power *qca;
>>> +
>>> +static const struct btqca_vreg_data cherokee_data = {
>>> + .soc_type = BTQCA_CHEROKEE,
>>> + .vregs = (struct btqca_vreg []) {
>>> + { "vddio", 1352000, 1352000, 0 },
>>> + { "vddxtal", 1904000, 2040000, 0 },
>>> + { "vddcore", 1800000, 1800000, 1 },
>>> + { "vddpa", 130400, 1304000, 1 },
>>> + { "vddldo", 3000000, 3312000, 1 },
>>> + { "vddpwd", 3312000, 3600000, 0 },
>>> + },
>>> + .num_vregs = 6,
>>> +};
>>> +
>>> +static const struct of_device_id btqca_power_match_table[] = {
>>> + { .compatible = "qca,wcn3990", .data = &cherokee_data},
>>> + {}
>>> +};
>>> +
>>> +int btqca_get_soc_type(enum btqca_soc_t *type)
>>> +{
>>> + if (!qca || !qca->vreg_data)
>>> + return -EINVAL;
>>> +
>>> + *type = qca->vreg_data->soc_type;
>>> + return 0;
>>> +}
>>> +EXPORT_SYMBOL_GPL(btqca_get_soc_type);
>>> +
>>> +int btqca_power_setup(int on)
>>> +{
>>> + int ret = 0;
>>> + int i;
>>> + struct btqca_vreg *vregs;
>>> +
>>> + if (!qca || !qca->vreg_data || !qca->vreg_bulk)
>>> + return -EINVAL;
>>> + vregs = qca->vreg_data->vregs;
>>> +
>>> + BT_DBG("on: %d", on);
>>> +
>>> + if (on) {
>>> + for (i = 0; i < qca->vreg_data->num_vregs; i++) {
>>> + regulator_set_voltage(qca->vreg_bulk[i].consumer,
>>> + vregs[i].min_v,
>>> + vregs[i].max_v);
>>> +
>>> + if (vregs[i].load_ua)
>>> + regulator_set_load(qca->vreg_bulk[i].consumer,
>>> + vregs[i].load_ua);
>>> +
>>> + regulator_enable(qca->vreg_bulk[i].consumer);
>>> + }
>>> + } else {
>>> + for (i = 0; i < qca->vreg_data->num_vregs; i++) {
>>> + regulator_disable(qca->vreg_bulk[i].consumer);
>>> +
>>> + regulator_set_voltage(qca->vreg_bulk[i].consumer,
>>> + 0, vregs[i].max_v);
>>> +
>>> + regulator_set_load(qca->vreg_bulk[i].consumer, 0);
>>> + }
>>> + }
>>> +
>>> + return ret;
>>> +}
>>> +EXPORT_SYMBOL_GPL(btqca_power_setup);
>> I do not get why this is a separate module and totally disconnected
>> from the Bluetooth driver. We have pending patches for using serdev
>> for the hci_qca.c and it seems that should be all build together. I am
>> not really keen on adding platform device and platform data here. So
>> frankly I am missing the big picture on how this is suppose to work.
>> We are driving towards serdev and thus a clean support for UART based
>> devices. This seems to be doing something else.
>> Regards
>> Marcel
>
> These patches are for older design based on line discipline and
> hciattach/btattach. I was unaware of the serdev side of changes. We will
> rewrite these changes on top of serdev patches for hci_qca.c
>
> In the bigger picture, this change contains code to power on/off WCN3990.
> Across different host MSMs and PMICs, the regulators that need to be voted are
> different. So we add these changes into device tree. The on/off API is called
> from hci_qca.c. (HCIDEVUP -> qca_setup -> btacq_power_setup). In serdev design,
> we will read these regulators as a part of probe & still vote them in
> context of HCIDEVUP/HCIDEVDOWN.

the power control really needs to happen in open/close callbacks. Doing them in setup is a bit too late. We expect the transport to be ready since setup is allowed to execute HCI commands.

> One minor issue I have with serdev is, it seems to be aligned with BlueZ
> architecture. There are other protocol stacks which are completely written
> in userspace & do not have any protocol layers (and line discipline in old
> design) in kernel (e.g. Fluoride in Android). Even for such cases, the sequence
> to turn WCN3990 on/off does not change. In such case, we expose a char device or
> sysfs entry to userspace & perform voting through ioctl calls. This code can not
> be reused in such cases. Hypothetically, voting code can be added to something
> like tty_serdev (parallel to n_tty line discipline) for non-BlueZ cases.

Just use HCI_CHANNEL_USER. It will give you raw HCI access. There is a driver for that in Android as well. It will then work with any hardware and the power control is correctly done by the kernel.

> But serdev seems to link voting code to tty clients (hci_serdev or tty_serdev) when
> client really does not have any relation to it (at least in case of WCN3990).
> I would be very happy to get my understanding corrected if there are any
> mistakes and hear your thoughts on this.

Don’t get this. If you run Bluetooth, that means any opening of the TTY is done to run Bluetooth.

Regards

Marcel


2018-04-12 13:23:59

by Rupesh Tatiya

[permalink] [raw]
Subject: Re: [PATCH 2/3] Add support to power Bluetooth QCA chips attached to MSM

Hi Marcel,

On 2018-04-06 22:17, Marcel Holtmann wrote:
> Hi Balakrishna,
>
>> Add support to set voltage/current of various regulators to power
>> up/down
>> BT QCA chips attached to MSM.
>>
>> Change-Id: I3600dd7bc97c753bc9cf7f8ac39d7b90bc21c67d
>> Signed-off-by: Rupesh Tatiya <[email protected]>
>> ---
>> drivers/bluetooth/Makefile | 5 +-
>> drivers/bluetooth/btqca_power.c | 177
>> ++++++++++++++++++++++++++++++++++++++++
>> drivers/bluetooth/btqca_power.h | 71 ++++++++++++++++
>> 3 files changed, 251 insertions(+), 2 deletions(-)
>> create mode 100644 drivers/bluetooth/btqca_power.c
>> create mode 100644 drivers/bluetooth/btqca_power.h
>>
>> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
>> index 4e4e44d..f963909 100644
>> --- a/drivers/bluetooth/Makefile
>> +++ b/drivers/bluetooth/Makefile
>> @@ -24,8 +24,9 @@ obj-$(CONFIG_BT_WILINK) += btwilink.o
>> obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
>> obj-$(CONFIG_BT_BCM) += btbcm.o
>> obj-$(CONFIG_BT_RTL) += btrtl.o
>> -obj-$(CONFIG_BT_QCA) += btqca.o
>> -
>> +obj-$(CONFIG_BT_QCA) += btqca_uart.o
>> +btqca_uart-$(CONFIG_BT_QCA) += btqca.o
>> +btqca_uart-$(CONFIG_BT_QCA) += btqca_power.o
>> obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
>>
>> btmrvl-y := btmrvl_main.o
>> diff --git a/drivers/bluetooth/btqca_power.c
>> b/drivers/bluetooth/btqca_power.c
>> new file mode 100644
>> index 0000000..a189ff1
>> --- /dev/null
>> +++ b/drivers/bluetooth/btqca_power.c
>> @@ -0,0 +1,177 @@
>> +/* Copyright (c) 2009-2010, 2013-2018 The Linux Foundation.
>> + * All rights reserved.
>> + *
>> + * This program is free software; you can redistribute it and/or
>> modify
>> + * it under the terms of the GNU General Public License version 2 and
>> + * only 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.
>> + */
>> +/*
>> + * Bluetooth Power Switch Module
>> + * controls power to external Bluetooth device
>> + * with interface to power management device
>> + */
>> +
>> +#include <linux/init.h>
>> +#include <linux/module.h>
>> +#include <linux/kernel.h>
>> +#include <linux/platform_device.h>
>> +#include <linux/delay.h>
>> +#include <linux/slab.h>
>> +#include <linux/regulator/consumer.h>
>> +#include <linux/of_device.h>
>> +#include <net/bluetooth/bluetooth.h>
>> +
>> +#include "btqca_power.h"
>> +
>> +static struct btqca_power *qca;
>> +
>> +static const struct btqca_vreg_data cherokee_data = {
>> + .soc_type = BTQCA_CHEROKEE,
>> + .vregs = (struct btqca_vreg []) {
>> + { "vddio", 1352000, 1352000, 0 },
>> + { "vddxtal", 1904000, 2040000, 0 },
>> + { "vddcore", 1800000, 1800000, 1 },
>> + { "vddpa", 130400, 1304000, 1 },
>> + { "vddldo", 3000000, 3312000, 1 },
>> + { "vddpwd", 3312000, 3600000, 0 },
>> + },
>> + .num_vregs = 6,
>> +};
>> +
>> +static const struct of_device_id btqca_power_match_table[] = {
>> + { .compatible = "qca,wcn3990", .data = &cherokee_data},
>> + {}
>> +};
>> +
>> +int btqca_get_soc_type(enum btqca_soc_t *type)
>> +{
>> + if (!qca || !qca->vreg_data)
>> + return -EINVAL;
>> +
>> + *type = qca->vreg_data->soc_type;
>> + return 0;
>> +}
>> +EXPORT_SYMBOL_GPL(btqca_get_soc_type);
>> +
>> +int btqca_power_setup(int on)
>> +{
>> + int ret = 0;
>> + int i;
>> + struct btqca_vreg *vregs;
>> +
>> + if (!qca || !qca->vreg_data || !qca->vreg_bulk)
>> + return -EINVAL;
>> + vregs = qca->vreg_data->vregs;
>> +
>> + BT_DBG("on: %d", on);
>> +
>> + if (on) {
>> + for (i = 0; i < qca->vreg_data->num_vregs; i++) {
>> + regulator_set_voltage(qca->vreg_bulk[i].consumer,
>> + vregs[i].min_v,
>> + vregs[i].max_v);
>> +
>> + if (vregs[i].load_ua)
>> + regulator_set_load(qca->vreg_bulk[i].consumer,
>> + vregs[i].load_ua);
>> +
>> + regulator_enable(qca->vreg_bulk[i].consumer);
>> + }
>> + } else {
>> + for (i = 0; i < qca->vreg_data->num_vregs; i++) {
>> + regulator_disable(qca->vreg_bulk[i].consumer);
>> +
>> + regulator_set_voltage(qca->vreg_bulk[i].consumer,
>> + 0, vregs[i].max_v);
>> +
>> + regulator_set_load(qca->vreg_bulk[i].consumer, 0);
>> + }
>> + }
>> +
>> + return ret;
>> +}
>> +EXPORT_SYMBOL_GPL(btqca_power_setup);
>
> I do not get why this is a separate module and totally disconnected
> from the Bluetooth driver. We have pending patches for using serdev
> for the hci_qca.c and it seems that should be all build together. I am
> not really keen on adding platform device and platform data here. So
> frankly I am missing the big picture on how this is suppose to work.
> We are driving towards serdev and thus a clean support for UART based
> devices. This seems to be doing something else.
>
> Regards
>
> Marcel

These patches are for older design based on line discipline and
hciattach/btattach. I was unaware of the serdev side of changes. We will
rewrite these changes on top of serdev patches for hci_qca.c

In the bigger picture, this change contains code to power on/off
WCN3990.
Across different host MSMs and PMICs, the regulators that need to be
voted are
different. So we add these changes into device tree. The on/off API is
called
from hci_qca.c. (HCIDEVUP -> qca_setup -> btacq_power_setup). In serdev
design,
we will read these regulators as a part of probe & still vote them in
context of HCIDEVUP/HCIDEVDOWN.

One minor issue I have with serdev is, it seems to be aligned with BlueZ
architecture. There are other protocol stacks which are completely
written
in userspace & do not have any protocol layers (and line discipline in
old
design) in kernel (e.g. Fluoride in Android). Even for such cases, the
sequence
to turn WCN3990 on/off does not change. In such case, we expose a char
device or
sysfs entry to userspace & perform voting through ioctl calls. This code
can not
be reused in such cases. Hypothetically, voting code can be added to
something
like tty_serdev (parallel to n_tty line discipline) for non-BlueZ cases.

But serdev seems to link voting code to tty clients (hci_serdev or
tty_serdev) when
client really does not have any relation to it (at least in case of
WCN3990).
I would be very happy to get my understanding corrected if there are any
mistakes and hear your thoughts on this.

Best Regards,
Rupesh

2018-04-06 16:49:01

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 1/3] Bluetooth: Add device tree bindings for Atheros chips

Hi Balakrishan,

> Change-Id: Ie62e3b90b6226019142206c8d44712a6c8550ef2
> Signed-off-by: Rupesh Tatiya <[email protected]>
> ---
> .../devicetree/bindings/bluetooth/btqca_power.txt | 30 ++++++++++++++++++++++
> 1 file changed, 30 insertions(+)
> create mode 100644 Documentation/devicetree/bindings/bluetooth/btqca_power.txt
>
> diff --git a/Documentation/devicetree/bindings/bluetooth/btqca_power.txt b/Documentation/devicetree/bindings/bluetooth/btqca_power.txt
> new file mode 100644
> index 0000000..f0f593c
> --- /dev/null
> +++ b/Documentation/devicetree/bindings/bluetooth/btqca_power.txt
> @@ -0,0 +1,30 @@
> +* Bluetooth Controller
> +Bluetooth controller communicates with the Bluetooth Host using HCI Transport layer.
> +HCI Transport layer can be based on UART or USB serial communication protocol.
> +
> +Required properties:
> + - compatible: Should be set to one of the following:
> + qca,ar3002
> + qca,qca6174
> + qca,wcn3990
> +
> +Optional properties:
> + - vddpa-supply: Bluetooth VDD PA regulator handle
> + - vddio-supply: Bluetooth VDD IO regulator handle
> + - vddldo-supply: Bluetooth VDD LDO regulator handle. Kept under optional parameters
> + as some of the chipsets doesn't require ldo or it may use from same vddio.
> + - vddxtal-supply: Bluetooth VDD XTAL regulator handle
> + - vddcore-supply: Bluetooth VDD CORE regulator handle
> + - vddpwd-supply: Chip power down gpio is required when bluetooth module
> + and other modules like wifi co-exist in a singe chip and shares a
> + common gpio to bring chip out of reset.
> +
> +Example:
> + bt-ar3002 {
> + compatible = "qca,ar3002";
> + vddio-supply = <&pm8941_s3>;
> + vddpa-supply = <&pm8941_l19>;
> + vddxtal-supply = <&pm8994_l30>;
> + vddcore-supply = <&pm8994_s3>;
> + vddpwd-supply = <&ath_chip_pwd_l>;
> +};

so I do not get this. I assumed we want to tie these to the UART and the Bluetooth serdev slave on top of that UART. Doing them as random platform device is pointless to me.

Regards

Marcel


2018-04-06 16:47:38

by Marcel Holtmann

[permalink] [raw]
Subject: Re: [PATCH 2/3] Add support to power Bluetooth QCA chips attached to MSM

Hi Balakrishna,

> Add support to set voltage/current of various regulators to power up/down
> BT QCA chips attached to MSM.
>
> Change-Id: I3600dd7bc97c753bc9cf7f8ac39d7b90bc21c67d
> Signed-off-by: Rupesh Tatiya <[email protected]>
> ---
> drivers/bluetooth/Makefile | 5 +-
> drivers/bluetooth/btqca_power.c | 177 ++++++++++++++++++++++++++++++++++++++++
> drivers/bluetooth/btqca_power.h | 71 ++++++++++++++++
> 3 files changed, 251 insertions(+), 2 deletions(-)
> create mode 100644 drivers/bluetooth/btqca_power.c
> create mode 100644 drivers/bluetooth/btqca_power.h
>
> diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
> index 4e4e44d..f963909 100644
> --- a/drivers/bluetooth/Makefile
> +++ b/drivers/bluetooth/Makefile
> @@ -24,8 +24,9 @@ obj-$(CONFIG_BT_WILINK) += btwilink.o
> obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
> obj-$(CONFIG_BT_BCM) += btbcm.o
> obj-$(CONFIG_BT_RTL) += btrtl.o
> -obj-$(CONFIG_BT_QCA) += btqca.o
> -
> +obj-$(CONFIG_BT_QCA) += btqca_uart.o
> +btqca_uart-$(CONFIG_BT_QCA) += btqca.o
> +btqca_uart-$(CONFIG_BT_QCA) += btqca_power.o
> obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o
>
> btmrvl-y := btmrvl_main.o
> diff --git a/drivers/bluetooth/btqca_power.c b/drivers/bluetooth/btqca_power.c
> new file mode 100644
> index 0000000..a189ff1
> --- /dev/null
> +++ b/drivers/bluetooth/btqca_power.c
> @@ -0,0 +1,177 @@
> +/* Copyright (c) 2009-2010, 2013-2018 The Linux Foundation.
> + * All rights reserved.
> + *
> + * This program is free software; you can redistribute it and/or modify
> + * it under the terms of the GNU General Public License version 2 and
> + * only 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.
> + */
> +/*
> + * Bluetooth Power Switch Module
> + * controls power to external Bluetooth device
> + * with interface to power management device
> + */
> +
> +#include <linux/init.h>
> +#include <linux/module.h>
> +#include <linux/kernel.h>
> +#include <linux/platform_device.h>
> +#include <linux/delay.h>
> +#include <linux/slab.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/of_device.h>
> +#include <net/bluetooth/bluetooth.h>
> +
> +#include "btqca_power.h"
> +
> +static struct btqca_power *qca;
> +
> +static const struct btqca_vreg_data cherokee_data = {
> + .soc_type = BTQCA_CHEROKEE,
> + .vregs = (struct btqca_vreg []) {
> + { "vddio", 1352000, 1352000, 0 },
> + { "vddxtal", 1904000, 2040000, 0 },
> + { "vddcore", 1800000, 1800000, 1 },
> + { "vddpa", 130400, 1304000, 1 },
> + { "vddldo", 3000000, 3312000, 1 },
> + { "vddpwd", 3312000, 3600000, 0 },
> + },
> + .num_vregs = 6,
> +};
> +
> +static const struct of_device_id btqca_power_match_table[] = {
> + { .compatible = "qca,wcn3990", .data = &cherokee_data},
> + {}
> +};
> +
> +int btqca_get_soc_type(enum btqca_soc_t *type)
> +{
> + if (!qca || !qca->vreg_data)
> + return -EINVAL;
> +
> + *type = qca->vreg_data->soc_type;
> + return 0;
> +}
> +EXPORT_SYMBOL_GPL(btqca_get_soc_type);
> +
> +int btqca_power_setup(int on)
> +{
> + int ret = 0;
> + int i;
> + struct btqca_vreg *vregs;
> +
> + if (!qca || !qca->vreg_data || !qca->vreg_bulk)
> + return -EINVAL;
> + vregs = qca->vreg_data->vregs;
> +
> + BT_DBG("on: %d", on);
> +
> + if (on) {
> + for (i = 0; i < qca->vreg_data->num_vregs; i++) {
> + regulator_set_voltage(qca->vreg_bulk[i].consumer,
> + vregs[i].min_v,
> + vregs[i].max_v);
> +
> + if (vregs[i].load_ua)
> + regulator_set_load(qca->vreg_bulk[i].consumer,
> + vregs[i].load_ua);
> +
> + regulator_enable(qca->vreg_bulk[i].consumer);
> + }
> + } else {
> + for (i = 0; i < qca->vreg_data->num_vregs; i++) {
> + regulator_disable(qca->vreg_bulk[i].consumer);
> +
> + regulator_set_voltage(qca->vreg_bulk[i].consumer,
> + 0, vregs[i].max_v);
> +
> + regulator_set_load(qca->vreg_bulk[i].consumer, 0);
> + }
> + }
> +
> + return ret;
> +}
> +EXPORT_SYMBOL_GPL(btqca_power_setup);

I do not get why this is a separate module and totally disconnected from the Bluetooth driver. We have pending patches for using serdev for the hci_qca.c and it seems that should be all build together. I am not really keen on adding platform device and platform data here. So frankly I am missing the big picture on how this is suppose to work. We are driving towards serdev and thus a clean support for UART based devices. This seems to be doing something else.

Regards

Marcel


2018-04-06 11:52:26

by Balakrishna Godavarthi

[permalink] [raw]
Subject: [PATCH 3/3] Bluetooth: Added support for wcn3990 soc.

Added firmware download support for QCA chip wcn3990.

Change-Id: Ic34668e0e2c0f452f0b11720d6d39ebae4997af9
Signed-off-by: Balakrishna Godavarthi <[email protected]>
---
drivers/bluetooth/btqca.c | 393 +++++++++++++++++++++++++++++++--------
drivers/bluetooth/btqca.h | 46 ++++-
drivers/bluetooth/hci_qca.c | 324 +++++++++++++++++++++++++++-----
include/net/bluetooth/hci_core.h | 3 +-
net/bluetooth/hci_core.c | 2 +-
net/bluetooth/hci_request.c | 23 +++
net/bluetooth/hci_request.h | 2 +
7 files changed, 661 insertions(+), 132 deletions(-)

diff --git a/drivers/bluetooth/btqca.c b/drivers/bluetooth/btqca.c
index 0bbdfce..5b47df8 100644
--- a/drivers/bluetooth/btqca.c
+++ b/drivers/bluetooth/btqca.c
@@ -1,7 +1,7 @@
/*
* Bluetooth supports for Qualcomm Atheros chips
*
- * Copyright (c) 2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -19,82 +19,18 @@
*/
#include <linux/module.h>
#include <linux/firmware.h>
-
#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-
#include "btqca.h"

#define VERSION "0.1"

-static int rome_patch_ver_req(struct hci_dev *hdev, u32 *rome_version)
-{
- struct sk_buff *skb;
- struct edl_event_hdr *edl;
- struct rome_version *ver;
- char cmd;
- int err = 0;
-
- BT_DBG("%s: ROME Patch Version Request", hdev->name);
-
- cmd = EDL_PATCH_VER_REQ_CMD;
- skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
- &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
- if (IS_ERR(skb)) {
- err = PTR_ERR(skb);
- BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name,
- err);
- return err;
- }
-
- if (skb->len != sizeof(*edl) + sizeof(*ver)) {
- BT_ERR("%s: Version size mismatch len %d", hdev->name,
- skb->len);
- err = -EILSEQ;
- goto out;
- }
-
- edl = (struct edl_event_hdr *)(skb->data);
- if (!edl) {
- BT_ERR("%s: TLV with no header", hdev->name);
- err = -EILSEQ;
- goto out;
- }
-
- if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
- edl->rtype != EDL_APP_VER_RES_EVT) {
- BT_ERR("%s: Wrong packet received %d %d", hdev->name,
- edl->cresp, edl->rtype);
- err = -EIO;
- goto out;
- }
-
- ver = (struct rome_version *)(edl->data);
-
- BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id));
- BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver));
- BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->rome_ver));
- BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id));
-
- /* ROME chipset version can be decided by patch and SoC
- * version, combination with upper 2 bytes from SoC
- * and lower 2 bytes from patch will be used.
- */
- *rome_version = (le32_to_cpu(ver->soc_id) << 16) |
- (le16_to_cpu(ver->rome_ver) & 0x0000ffff);
-
-out:
- kfree_skb(skb);
-
- return err;
-}
-
-static int rome_reset(struct hci_dev *hdev)
+static int qca_btsoc_reset(struct hci_dev *hdev)
{
struct sk_buff *skb;
int err;

- BT_DBG("%s: ROME HCI_RESET", hdev->name);
+ BT_DBG("%s: ROME/wcn3990 HCI_RESET", hdev->name);

skb = __hci_cmd_sync(hdev, HCI_OP_RESET, 0, NULL, HCI_INIT_TIMEOUT);
if (IS_ERR(skb)) {
@@ -108,7 +44,7 @@ static int rome_reset(struct hci_dev *hdev)
return 0;
}

-static void rome_tlv_check_data(struct rome_config *config,
+static void rome_tlv_check_data(struct qca_config *config,
const struct firmware *fw)
{
const u8 *data;
@@ -194,8 +130,121 @@ static void rome_tlv_check_data(struct rome_config *config,
}
}

-static int rome_tlv_send_segment(struct hci_dev *hdev, int idx, int seg_size,
- const u8 *data)
+static void cherokee_tlv_check_data(struct qca_config *config,
+ const struct firmware *fw, bool *dwnd_flag)
+{
+ const u8 *data;
+ u32 type_len;
+ u16 tag_id, tag_len;
+ int idx, length;
+ struct tlv_type_hdr *tlv;
+ struct cherokee_tlv_type_patch *tlv_patch;
+ struct tlv_type_nvm *tlv_nvm;
+
+ tlv = (struct tlv_type_hdr *)fw->data;
+
+ type_len = le32_to_cpu(tlv->type_len);
+ length = (type_len >> 8) & 0x00ffffff;
+
+ BT_DBG("TLV Type\t\t : 0x%x", type_len & 0x000000ff);
+ BT_DBG("Length\t\t : %d bytes", length);
+
+ switch (config->type) {
+ case TLV_TYPE_PATCH:
+ tlv_patch = (struct tlv_type_patch *)tlv->data;
+ BT_DBG("Total Length\t\t : %d bytes",
+ le32_to_cpu(tlv_patch->total_size));
+ BT_DBG("Patch Data Length\t : %d bytes",
+ le32_to_cpu(tlv_patch->data_length));
+ BT_DBG("Signing Format Version : 0x%x",
+ tlv_patch->format_version);
+ BT_DBG("Signature Algorithm\t : 0x%x",
+ tlv_patch->signature);
+ BT_DBG("Download flag\t : 0x%x",
+ tlv_patch->dwnd_cfg);
+ BT_DBG("Reserved\t\t : 0x%x",
+ le16_to_cpu(tlv_patch->reserved1));
+ BT_DBG("Product ID\t\t : 0x%04x",
+ le16_to_cpu(tlv_patch->product_id));
+ BT_DBG("Rom Build Version\t : 0x%04x",
+ le16_to_cpu(tlv_patch->rom_build));
+ BT_DBG("Patch Version\t\t : 0x%04x",
+ le16_to_cpu(tlv_patch->patch_version));
+ BT_DBG("Reserved\t\t : 0x%x",
+ le16_to_cpu(tlv_patch->reserved2));
+ BT_DBG("Patch Entry Address\t : 0x%x",
+ le32_to_cpu(tlv_patch->entry));
+ /* If the download flag is 0x03, don't wait
+ * for response from soc,i.e. 1 to n-1
+ * segment download.
+ */
+ if (tlv_patch->dwnd_cfg == 0x03)
+ *dwnd_flag = false;
+ else
+ *dwnd_flag = true;
+ break;
+
+ case TLV_TYPE_NVM:
+ idx = 0;
+ data = tlv->data;
+ while (idx < length) {
+ tlv_nvm = (struct tlv_type_nvm *)(data + idx);
+ tag_id = le16_to_cpu(tlv_nvm->tag_id);
+ tag_len = le16_to_cpu(tlv_nvm->tag_len);
+
+ /* Update NVM tags as needed */
+ switch (tag_id) {
+ case EDL_TAG_ID_HCI:
+ /* HCI transport layer parameters
+ * enabling software inband sleep
+ * onto controller side.
+ */
+ tlv_nvm->data[0] |= 0x80;
+
+ /* UART Baud Rate */
+ tlv_nvm->data[2] = config->user_baud_rate;
+ break;
+ case EDL_TAG_ID_DEEP_SLEEP:
+ /* Sleep enable mask
+ * enabling deep sleep feature on controller.
+ */
+ tlv_nvm->data[0] |= 0x01;
+ break;
+ }
+
+ idx += (sizeof(u16) + sizeof(u16) + 8 + tag_len);
+ }
+ break;
+ default:
+ BT_ERR("Unknown TLV type %d", config->type);
+ break;
+ }
+}
+
+static int cherokee_tlv_send_segment(struct hci_dev *hdev, int idx,
+ int seg_size, const u8 *data)
+{
+ u8 cmd[MAX_SIZE_PER_TLV_SEGMENT + 2];
+ int err = 0;
+
+ BT_DBG("%s: Download segment #%d size %d", hdev->name, idx, seg_size);
+
+ cmd[0] = EDL_PATCH_TLV_REQ_CMD;
+ cmd[1] = seg_size;
+ memcpy(cmd + 2, data, seg_size);
+
+ err = __hci_cmd_no_event(hdev, EDL_PATCH_CMD_OPCODE, seg_size + 2,
+ cmd);
+ if (err < 0) {
+ BT_ERR("%s: Failed to send TLV segment (%d)", hdev->name, err);
+ return err;
+ }
+
+ return err;
+}
+
+static int qca_btsoc_tlv_send_segment(struct hci_dev *hdev, int idx,
+ int seg_size, const u8 *data)
{
struct sk_buff *skb;
struct edl_event_hdr *edl;
@@ -264,16 +313,56 @@ static int rome_tlv_download_request(struct hci_dev *hdev,
data = fw->data;
for (i = 0; i < total_segment; i++) {
buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT;
- ret = rome_tlv_send_segment(hdev, i, MAX_SIZE_PER_TLV_SEGMENT,
- buffer);
+ ret = qca_btsoc_tlv_send_segment(hdev, i,
+ MAX_SIZE_PER_TLV_SEGMENT, buffer);
if (ret < 0)
return -EIO;
}

if (remain_size) {
buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT;
- ret = rome_tlv_send_segment(hdev, total_segment, remain_size,
- buffer);
+ ret = qca_btsoc_tlv_send_segment(hdev, total_segment,
+ remain_size, buffer);
+ if (ret < 0)
+ return -EIO;
+ }
+
+ return 0;
+}
+static int cherokee_tlv_download_request(struct hci_dev *hdev,
+ const struct firmware *fw)
+{
+ const u8 *buffer, *data;
+ int total_segment, remain_size;
+ int ret, i;
+
+ if (!fw || !fw->data)
+ return -EINVAL;
+
+ total_segment = fw->size / MAX_SIZE_PER_TLV_SEGMENT;
+ remain_size = fw->size % MAX_SIZE_PER_TLV_SEGMENT;
+
+ BT_DBG("%s: Total segment num %d remain size %d total size %zu",
+ hdev->name, total_segment, remain_size, fw->size);
+
+ data = fw->data;
+ for (i = 0; i < total_segment; i++) {
+ buffer = data + i * MAX_SIZE_PER_TLV_SEGMENT;
+ /* Read response from soc for last segment sent */
+ if (!remain_size && ((i+1) == total_segment))
+ ret = qca_btsoc_tlv_send_segment(hdev, i,
+ MAX_SIZE_PER_TLV_SEGMENT, buffer);
+ else
+ ret = cherokee_tlv_send_segment(hdev, i,
+ MAX_SIZE_PER_TLV_SEGMENT, buffer);
+ if (ret < 0)
+ return -EIO;
+ }
+
+ if (remain_size) {
+ buffer = data + total_segment * MAX_SIZE_PER_TLV_SEGMENT;
+ ret = qca_btsoc_tlv_send_segment(hdev, total_segment,
+ remain_size, buffer);
if (ret < 0)
return -EIO;
}
@@ -282,7 +371,7 @@ static int rome_tlv_download_request(struct hci_dev *hdev,
}

static int rome_download_firmware(struct hci_dev *hdev,
- struct rome_config *config)
+ struct qca_config *config)
{
const struct firmware *fw;
int ret;
@@ -309,7 +398,46 @@ static int rome_download_firmware(struct hci_dev *hdev,
return ret;
}

-int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+static int cherokee_download_firmware(struct hci_dev *hdev,
+ struct qca_config *config)
+{
+ const struct firmware *fw;
+ bool dwnd_flag = true;
+ int ret;
+
+ BT_INFO("%s: wcn3990 Downloading %s", hdev->name, config->fwname);
+
+ ret = request_firmware(&fw, config->fwname, &hdev->dev);
+ if (ret) {
+ BT_ERR("%s: Failed to request file: %s (%d)", hdev->name,
+ config->fwname, ret);
+ return ret;
+ }
+
+ cherokee_tlv_check_data(config, fw, &dwnd_flag);
+ /* check whether the download flag is set.if bit is enabled
+ * terminate the ram patch download. As we are not supporting,
+ * receiving of response from soc for every segment sent.
+ * We look for response from soc for last segment.
+ */
+ if (dwnd_flag == true && config->type == TLV_TYPE_PATCH) {
+ BT_ERR("%s: btsoc download flag enabled", hdev->name);
+ return -EOPNOTSUPP;
+ }
+
+ ret = cherokee_tlv_download_request(hdev, fw);
+ if (ret) {
+ BT_ERR("%s: Failed to download file: %s (%d)", hdev->name,
+ config->fwname, ret);
+ }
+
+ release_firmware(fw);
+
+ return ret;
+}
+
+
+int qca_btsoc_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr)
{
struct sk_buff *skb;
u8 cmd[9];
@@ -332,12 +460,12 @@ int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)

return 0;
}
-EXPORT_SYMBOL_GPL(qca_set_bdaddr_rome);
+EXPORT_SYMBOL_GPL(qca_btsoc_set_bdaddr);

int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate)
{
u32 rome_ver = 0;
- struct rome_config config;
+ struct qca_config config;
int err;

BT_DBG("%s: ROME setup on UART", hdev->name);
@@ -345,7 +473,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate)
config.user_baud_rate = baudrate;

/* Get ROME version information */
- err = rome_patch_ver_req(hdev, &rome_ver);
+ err = qca_btsoc_patch_ver_req(hdev, &rome_ver);
if (err < 0 || rome_ver == 0) {
BT_ERR("%s: Failed to get version 0x%x", hdev->name, err);
return err;
@@ -374,7 +502,7 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate)
}

/* Perform HCI reset */
- err = rome_reset(hdev);
+ err = qca_btsoc_reset(hdev);
if (err < 0) {
BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err);
return err;
@@ -386,6 +514,111 @@ int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate)
}
EXPORT_SYMBOL_GPL(qca_uart_setup_rome);

+int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *soc_version)
+{
+ struct sk_buff *skb;
+ struct edl_event_hdr *edl;
+ struct qca_btsoc_version *ver;
+ char cmd;
+ int err = 0;
+
+ BT_DBG("%s: BTSOC Patch Version Request", hdev->name);
+
+ cmd = EDL_PATCH_VER_REQ_CMD;
+ skb = __hci_cmd_sync_ev(hdev, EDL_PATCH_CMD_OPCODE, EDL_PATCH_CMD_LEN,
+ &cmd, HCI_VENDOR_PKT, HCI_INIT_TIMEOUT);
+ if (IS_ERR(skb)) {
+ err = PTR_ERR(skb);
+ BT_ERR("%s: Failed to read version of ROME (%d)", hdev->name,
+ err);
+ return err;
+ }
+
+ if (skb->len != sizeof(*edl) + sizeof(*ver)) {
+ BT_ERR("%s: Version size mismatch len %d", hdev->name,
+ skb->len);
+ err = -EILSEQ;
+ goto out;
+ }
+
+ edl = (struct edl_event_hdr *)(skb->data);
+ if (!edl) {
+ BT_ERR("%s: TLV with no header", hdev->name);
+ err = -EILSEQ;
+ goto out;
+ }
+
+ if (edl->cresp != EDL_CMD_REQ_RES_EVT ||
+ edl->rtype != EDL_APP_VER_RES_EVT) {
+ BT_ERR("%s: Wrong packet received %d %d", hdev->name,
+ edl->cresp, edl->rtype);
+ err = -EIO;
+ goto out;
+ }
+
+ ver = (struct qca_btsoc_version *)(edl->data);
+
+ BT_DBG("%s: Product:0x%08x", hdev->name, le32_to_cpu(ver->product_id));
+ BT_DBG("%s: Patch :0x%08x", hdev->name, le16_to_cpu(ver->patch_ver));
+ BT_DBG("%s: ROM :0x%08x", hdev->name, le16_to_cpu(ver->btsoc_ver));
+ BT_DBG("%s: SOC :0x%08x", hdev->name, le32_to_cpu(ver->soc_id));
+
+ /* BTSOC chipset version can be decided by patch and SoC
+ * version, combination with upper 2 bytes from SoC
+ * and lower 2 bytes from patch will be used.
+ */
+ *soc_version = (le32_to_cpu(ver->soc_id) << 16) |
+ (le16_to_cpu(ver->btsoc_ver) & 0x0000ffff);
+
+out:
+ kfree_skb(skb);
+
+ return err;
+}
+EXPORT_SYMBOL_GPL(qca_btsoc_patch_ver_req);
+
+int qca_uart_setup_cherokee(struct hci_dev *hdev, uint8_t baudrate,
+ u32 *cherokee_ver)
+{
+ struct qca_config config;
+ int err;
+
+ BT_DBG("%s: wcn3990 setup on UART", hdev->name);
+ config.user_baud_rate = baudrate;
+
+ /* Download rampatch file */
+ config.type = TLV_TYPE_PATCH;
+ snprintf(config.fwname, sizeof(config.fwname), "qca/rampatch_%08x.tlv",
+ *cherokee_ver);
+ err = cherokee_download_firmware(hdev, &config);
+ if (err < 0) {
+ BT_ERR("%s: Failed to download patch (%d)", hdev->name, err);
+ return err;
+ }
+
+ /* Download NVM configuration */
+ config.type = TLV_TYPE_NVM;
+ snprintf(config.fwname, sizeof(config.fwname), "qca/nvm_%08x.bin",
+ *cherokee_ver);
+ err = cherokee_download_firmware(hdev, &config);
+ if (err < 0) {
+ BT_ERR("%s: Failed to download NVM (%d)", hdev->name, err);
+ return err;
+ }
+
+ /* Perform HCI reset */
+ err = qca_btsoc_reset(hdev);
+ if (err < 0) {
+ BT_ERR("%s: Failed to run HCI_RESET (%d)", hdev->name, err);
+ return err;
+ }
+
+ BT_INFO("%s: wcn3990 setup on UART is completed", hdev->name);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(qca_uart_setup_cherokee);
+
MODULE_AUTHOR("Ben Young Tae Kim <[email protected]>");
MODULE_DESCRIPTION("Bluetooth support for Qualcomm Atheros family ver " VERSION);
MODULE_VERSION(VERSION);
diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
index 65e994b..677b266 100644
--- a/drivers/bluetooth/btqca.h
+++ b/drivers/bluetooth/btqca.h
@@ -1,7 +1,7 @@
/*
* Bluetooth supports for Qualcomm Atheros ROME chips
*
- * Copyright (c) 2015 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2015, 2018 The Linux Foundation. All rights reserved.
*
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2
@@ -37,6 +37,9 @@
#define EDL_TAG_ID_HCI (17)
#define EDL_TAG_ID_DEEP_SLEEP (27)

+#define CHEROKEE_POWERON_PULSE (0xFC)
+#define CHEROKEE_POWEROFF_PULSE (0xC0)
+
enum qca_bardrate {
QCA_BAUDRATE_115200 = 0,
QCA_BAUDRATE_57600,
@@ -66,7 +69,7 @@ enum rome_tlv_type {
TLV_TYPE_NVM
};

-struct rome_config {
+struct qca_config {
u8 type;
char fwname[64];
uint8_t user_baud_rate;
@@ -78,13 +81,14 @@ struct edl_event_hdr {
__u8 data[0];
} __packed;

-struct rome_version {
+struct qca_btsoc_version {
__le32 product_id;
__le16 patch_ver;
- __le16 rome_ver;
+ __le16 btsoc_ver;
__le32 soc_id;
} __packed;

+
struct tlv_seg_resp {
__u8 result;
} __packed;
@@ -102,6 +106,21 @@ struct tlv_type_patch {
__le32 entry;
} __packed;

+struct cherokee_tlv_type_patch {
+ __le32 total_size;
+ __le32 data_length;
+ __u8 format_version;
+ __u8 signature;
+ __u8 dwnd_cfg;
+ __le16 reserved1;
+ __le16 product_id;
+ __le16 rom_build;
+ __le16 patch_version;
+ __le16 reserved2;
+ __le32 entry;
+} __packed;
+
+
struct tlv_type_nvm {
__le16 tag_id;
__le16 tag_len;
@@ -117,12 +136,16 @@ struct tlv_type_hdr {

#if IS_ENABLED(CONFIG_BT_QCA)

-int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr);
+int qca_btsoc_set_bdaddr(struct hci_dev *hdev, const bdaddr_t *bdaddr);
int qca_uart_setup_rome(struct hci_dev *hdev, uint8_t baudrate);
+int qca_uart_setup_cherokee(struct hci_dev *hdev, uint8_t baudrate,
+ u32 *cherokee_ver);
+int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *soc_version);

#else

-static inline int qca_set_bdaddr_rome(struct hci_dev *hdev, const bdaddr_t *bdaddr)
+static inline int qca_btsoc_set_bdaddr(struct hci_dev *hdev,
+ const bdaddr_t *bdaddr)
{
return -EOPNOTSUPP;
}
@@ -132,4 +155,15 @@ static inline int qca_uart_setup_rome(struct hci_dev *hdev, int speed)
return -EOPNOTSUPP;
}

+static inline int qca_uart_setup_cherokee(struct hci_dev *hdev,
+ uint8_t baudrate, u32 *cherokee_ver)
+{
+ return -EOPNOTSUPP;
+}
+
+static int qca_btsoc_patch_ver_req(struct hci_dev *hdev, u32 *cherokee_version)
+{
+ return -EOPNOTSUPP;
+}
+
#endif
diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index 392f412..47212d1 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -5,7 +5,7 @@
* protocol extension to H4.
*
* Copyright (C) 2007 Texas Instruments, Inc.
- * Copyright (c) 2010, 2012 The Linux Foundation. All rights reserved.
+ * Copyright (c) 2010, 2012, 2018 The Linux Foundation. All rights reserved.
*
* Acknowledgements:
* This file is based on hci_ll.c, which was...
@@ -33,9 +33,11 @@

#include <net/bluetooth/bluetooth.h>
#include <net/bluetooth/hci_core.h>
-
+#include <asm-generic/delay.h>
+#include <linux/tty.h>
#include "hci_uart.h"
#include "btqca.h"
+#include "btqca_power.h"

/* HCI_IBS protocol messages */
#define HCI_IBS_SLEEP_IND 0xFE
@@ -76,12 +78,12 @@ struct qca_data {
struct hci_uart *hu;
struct sk_buff *rx_skb;
struct sk_buff_head txq;
- struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */
- spinlock_t hci_ibs_lock; /* HCI_IBS state lock */
- u8 tx_ibs_state; /* HCI_IBS transmit side power state*/
- u8 rx_ibs_state; /* HCI_IBS receive side power state */
- bool tx_vote; /* Clock must be on for TX */
- bool rx_vote; /* Clock must be on for RX */
+ struct sk_buff_head tx_wait_q; /* HCI_IBS wait queue */
+ spinlock_t hci_ibs_lock; /* HCI_IBS state lock */
+ u8 tx_ibs_state; /* HCI_IBS transmit side power state*/
+ u8 rx_ibs_state; /* HCI_IBS receive side power state */
+ bool tx_vote; /* Clock must be on for TX */
+ bool rx_vote; /* Clock must be on for RX */
struct timer_list tx_idle_timer;
u32 tx_idle_delay;
struct timer_list wake_retrans_timer;
@@ -843,6 +845,8 @@ static uint8_t qca_get_baudrate_value(int speed)
return QCA_BAUDRATE_2000000;
case 3000000:
return QCA_BAUDRATE_3000000;
+ case 3200000:
+ return QCA_BAUDRATE_3200000;
case 3500000:
return QCA_BAUDRATE_3500000;
default:
@@ -857,7 +861,7 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
struct sk_buff *skb;
u8 cmd[] = { 0x01, 0x48, 0xFC, 0x01, 0x00 };

- if (baudrate > QCA_BAUDRATE_3000000)
+ if (baudrate > QCA_BAUDRATE_3200000)
return -EINVAL;

cmd[4] = baudrate;
@@ -886,59 +890,291 @@ static int qca_set_baudrate(struct hci_dev *hdev, uint8_t baudrate)
return 0;
}

+static int qca_send_poweron_cmd(struct hci_dev *hdev)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct qca_data *qca = hu->priv;
+ struct sk_buff *skb;
+ u8 cmd;
+
+ BT_DBG("%s sending power on command to btsoc", hdev->name);
+ /* By sending 0xFC host is trying to power up the soc */
+ cmd = CHEROKEE_POWERON_PULSE;
+ skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("Failed to allocate memory for skb packet");
+ return -ENOMEM;
+ }
+
+ skb_put_data(skb, &cmd, sizeof(cmd));
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
+
+ skb_queue_tail(&qca->txq, skb);
+ hci_uart_tx_wakeup(hu);
+
+ /* Wait for 100 us for soc to settle down */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(usecs_to_jiffies(100));
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ return 0;
+}
+
+static int qca_send_poweroff_cmd(struct hci_dev *hdev)
+{
+ struct hci_uart *hu = hci_get_drvdata(hdev);
+ struct qca_data *qca = hu->priv;
+ struct sk_buff *skb;
+ u8 cmd;
+
+ BT_DBG("%s sending power off command to btsoc", hdev->name);
+ /* By sending 0xC0 host is trying to power off the soc */
+ cmd = CHEROKEE_POWEROFF_PULSE;
+ skb = bt_skb_alloc(sizeof(cmd), GFP_ATOMIC);
+ if (!skb) {
+ BT_ERR("Failed to allocate memory for skb packet");
+ return -ENOMEM;
+ }
+
+ skb_put_data(skb, &cmd, sizeof(cmd));
+ hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
+
+ skb_queue_tail(&qca->txq, skb);
+ hci_uart_tx_wakeup(hu);
+
+ /* Wait for 100 us for soc to settle down */
+ set_current_state(TASK_UNINTERRUPTIBLE);
+ schedule_timeout(usecs_to_jiffies(100));
+ set_current_state(TASK_INTERRUPTIBLE);
+
+ return 0;
+}
+
+static int qca_tty_open(struct hci_uart *hu)
+{
+
+ int ret;
+
+ if (!hu->tty->ops->open) {
+ BT_ERR("%s:open operation not supported", hu->hdev->name);
+ ret = -1;
+ } else {
+ ret = hu->tty->ops->open(hu->tty, NULL);
+ if (ret < 0) {
+ BT_ERR("unable to open port:%s", hu->hdev->name);
+ } else {
+ BT_INFO("%s:open success", hu->hdev->name);
+ ret = 0;
+ }
+ }
+ return ret;
+}
+
+static void qca_btsoc_cleanup(struct hci_dev *hdev)
+{
+ /* send 0xC0 command to btsoc before turning off regulators */
+ qca_send_poweroff_cmd(hdev);
+ /* turn off btsoc */
+ btqca_power_setup(false);
+
+}
+
+static int qca_tty_close(struct hci_uart *hu)
+{
+ int ret;
+
+ if (!hu->tty->ops->close) {
+ BT_ERR("%s:close operation not supported", hu->hdev->name);
+ ret = -1;
+ } else {
+ hu->tty->ops->close(hu->tty, NULL);
+ BT_INFO("%s:close success", hu->hdev->name);
+ ret = 0;
+ }
+ return ret;
+}
+
static int qca_setup(struct hci_uart *hu)
{
struct hci_dev *hdev = hu->hdev;
struct qca_data *qca = hu->priv;
unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200;
int ret;
+ unsigned int soc_ver;
+ enum btqca_soc_t type;

- BT_INFO("%s: ROME setup", hdev->name);
-
- /* Patch downloading has to be done without IBS mode */
- clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
+ ret = btqca_get_soc_type(&type);
+ if (ret == -EINVAL) {
+ BT_ERR("%s:invalid btsoc detected", hdev->name);
+ return ret;
+ }
+ /* Turn on btsoc */
+ ret = btqca_power_setup(true);
+ if (ret < 0) {
+ BT_ERR("%s:unable to turn on wcn3990", hdev->name);
+ return ret;
+ }

- /* Setup initial baudrate */
- speed = 0;
- if (hu->init_speed)
- speed = hu->init_speed;
- else if (hu->proto->init_speed)
- speed = hu->proto->init_speed;
+ switch (type) {
+ case BTQCA_CHEROKEE:

- if (speed)
- hci_uart_set_baudrate(hu, speed);
+ BT_INFO("%s:setting up wcn3990", hdev->name);
+ /* Patch downloading has to be done without IBS mode */
+ clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
+ /* close tty device if open */
+ ret = qca_tty_close(hu);
+ if (ret)
+ return ret;

- /* Setup user speed if needed */
- speed = 0;
- if (hu->oper_speed)
- speed = hu->oper_speed;
- else if (hu->proto->oper_speed)
- speed = hu->proto->oper_speed;
+ /* open tty device */
+ ret = qca_tty_open(hu);
+ if (ret)
+ return ret;

- if (speed) {
- qca_baudrate = qca_get_baudrate_value(speed);
+ /* Setup initial baudrate */
+ speed = 0;
+ if (hu->init_speed)
+ speed = hu->init_speed;
+ else if (hu->proto->init_speed)
+ speed = hu->proto->init_speed;
+
+ if (speed)
+ hci_uart_set_baudrate(hu, speed);
+ else {
+ BT_ERR("%s:initial speed %u", hdev->name, speed);
+ return -1;
+ }

- BT_INFO("%s: Set UART speed to %d", hdev->name, speed);
- ret = qca_set_baudrate(hdev, qca_baudrate);
+ /* clear flow control- for sync cmd*/
+ hci_uart_set_flow_control(hu, true);
+ /* send poweron command to btsoc */
+ ret = qca_send_poweron_cmd(hdev);
if (ret) {
- BT_ERR("%s: Failed to change the baud rate (%d)",
- hdev->name, ret);
+ BT_ERR("%s:sending sync command failed", hdev->name);
return ret;
}
- hci_uart_set_baudrate(hu, speed);
- }

- /* Setup patch / NVM configurations */
- ret = qca_uart_setup_rome(hdev, qca_baudrate);
- if (!ret) {
- set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
- qca_debugfs_init(hdev);
- }
+ /* close tty Device */
+ ret = qca_tty_close(hu);
+ if (ret)
+ return ret;
+
+ /* open tty device */
+ ret = qca_tty_open(hu);
+ if (ret)
+ return ret;

- /* Setup bdaddr */
- hu->hdev->set_bdaddr = qca_set_bdaddr_rome;
+ /* Setup initial baudrate */
+ speed = 0;
+ if (hu->init_speed)
+ speed = hu->init_speed;
+ else if (hu->proto->init_speed)
+ speed = hu->proto->init_speed;
+ if (speed)
+ hci_uart_set_baudrate(hu, speed);
+ else {
+ BT_ERR("%s:initial speed %u", hdev->name, speed);
+ return -1;
+ }

- return ret;
+ /* Enable flow control */
+ hci_uart_set_flow_control(hu, false);
+ /* wait until flow control settled */
+ mdelay(100);
+
+ ret = qca_btsoc_patch_ver_req(hdev, &soc_ver);
+ if (ret < 0 || soc_ver == 0) {
+ BT_ERR("%s: Failed to get version 0x%x", hdev->name,
+ ret);
+ qca_btsoc_cleanup(hdev);
+ return ret;
+ }
+
+ BT_INFO("%s:wcn3990 controller version 0x%08x", hdev->name,
+ soc_ver);
+
+ /* clear flow control */
+ hci_uart_set_flow_control(hu, true);
+ /* set operating speed */
+ speed = hu->proto->oper_speed;
+ if (speed) {
+ qca_baudrate = qca_get_baudrate_value(speed);
+ BT_INFO("%s: Set UART speed to %d", hdev->name, speed);
+ ret = qca_set_baudrate(hdev, qca_baudrate);
+ if (ret) {
+ BT_ERR("%s:Failed to change the baud rate(%d)",
+ hdev->name, ret);
+ qca_btsoc_cleanup(hdev);
+ return ret;
+ }
+ if (speed)
+ hci_uart_set_baudrate(hu, speed);
+ else {
+ BT_ERR("%s:Error in setting operator speed:%u",
+ hdev->name, speed);
+ return -1;
+ }
+ }
+ /* Set flow control */
+ hci_uart_set_flow_control(hu, false);
+
+ /*Setup patch and NVM configurations */
+ ret = qca_uart_setup_cherokee(hdev, qca_baudrate, &soc_ver);
+ if (!ret) {
+ set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
+ qca_debugfs_init(hdev);
+ }
+
+ /* Setup wcn3990 bdaddr */
+ hu->hdev->set_bdaddr = qca_btsoc_set_bdaddr;
+
+ return ret;
+
+ default:
+ BT_INFO("%s: ROME setup", hdev->name);
+ /* Patch downloading has to be done without IBS mode */
+ clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
+ /* Setup initial baudrate */
+ speed = 0;
+ if (hu->init_speed)
+ speed = hu->init_speed;
+ else if (hu->proto->init_speed)
+ speed = hu->proto->init_speed;
+ if (speed)
+ hci_uart_set_baudrate(hu, speed);
+
+ /* Setup user speed if needed */
+ speed = 0;
+ if (hu->oper_speed)
+ speed = hu->oper_speed;
+ else if (hu->proto->oper_speed)
+ speed = hu->proto->oper_speed;
+
+ if (speed) {
+ qca_baudrate = qca_get_baudrate_value(speed);
+
+ BT_INFO("%s: Set UART speed to %d", hdev->name, speed);
+ ret = qca_set_baudrate(hdev, qca_baudrate);
+ if (ret) {
+ BT_ERR("%s:Failed to change the baud rate(%d)",
+ hdev->name, ret);
+ return ret;
+ }
+ hci_uart_set_baudrate(hu, speed);
+ }
+
+ /* Setup patch / NVM configurations */
+ ret = qca_uart_setup_rome(hdev, qca_baudrate);
+ if (!ret) {
+ set_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
+ qca_debugfs_init(hdev);
+ }
+
+ /* Setup bdaddr */
+ hu->hdev->set_bdaddr = qca_btsoc_set_bdaddr;
+
+ return ret;
+ }
}

static struct hci_uart_proto qca_proto = {
diff --git a/include/net/bluetooth/hci_core.h b/include/net/bluetooth/hci_core.h
index 4385600..b8330fd 100644
--- a/include/net/bluetooth/hci_core.h
+++ b/include/net/bluetooth/hci_core.h
@@ -1418,7 +1418,8 @@ struct sk_buff *__hci_cmd_sync(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param, u32 timeout);
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param, u8 event, u32 timeout);
-
+int __hci_cmd_no_event(struct hci_dev *hdev, u16 opcode, u32 plen,
+ const void *param);
int hci_send_cmd(struct hci_dev *hdev, __u16 opcode, __u32 plen,
const void *param);
void hci_send_acl(struct hci_chan *chan, struct sk_buff *skb, __u16 flags);
diff --git a/net/bluetooth/hci_core.c b/net/bluetooth/hci_core.c
index 97ef85e..8addd97 100644
--- a/net/bluetooth/hci_core.c
+++ b/net/bluetooth/hci_core.c
@@ -3370,7 +3370,7 @@ int hci_unregister_cb(struct hci_cb *cb)
}
EXPORT_SYMBOL(hci_unregister_cb);

-static void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
+void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb)
{
int err;

diff --git a/net/bluetooth/hci_request.c b/net/bluetooth/hci_request.c
index f7d6ba6..1ffee59a 100644
--- a/net/bluetooth/hci_request.c
+++ b/net/bluetooth/hci_request.c
@@ -114,6 +114,29 @@ void hci_req_sync_cancel(struct hci_dev *hdev, int err)
}
}

+/* Queue a frame to an asynchronous transfer to btdevice,
+ * with out any event from btdevice.
+ */
+int __hci_cmd_no_event(struct hci_dev *hdev, u16 opcode, u32 plen,
+ const void *param)
+{
+ struct sk_buff *skb;
+
+ BT_DBG("%s opcode 0x%4.4x plen %d", hdev->name, opcode, plen);
+
+ skb = hci_prepare_cmd(hdev, opcode, plen, param);
+ if (!skb) {
+ BT_ERR("%s no memory for command (opcode 0x%4.4x)",
+ hdev->name, opcode);
+ return -ENOMEM;
+ }
+
+ hci_send_frame(hdev, skb);
+
+ return 0;
+}
+EXPORT_SYMBOL(__hci_cmd_no_event);
+
struct sk_buff *__hci_cmd_sync_ev(struct hci_dev *hdev, u16 opcode, u32 plen,
const void *param, u8 event, u32 timeout)
{
diff --git a/net/bluetooth/hci_request.h b/net/bluetooth/hci_request.h
index dde77bd..a855edd 100644
--- a/net/bluetooth/hci_request.h
+++ b/net/bluetooth/hci_request.h
@@ -128,3 +128,5 @@ static inline u16 eir_append_le16(u8 *eir, u16 eir_len, u8 type, u16 data)

return eir_len;
}
+
+void hci_send_frame(struct hci_dev *hdev, struct sk_buff *skb);
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2018-04-06 11:52:25

by Balakrishna Godavarthi

[permalink] [raw]
Subject: [PATCH 2/3] Add support to power Bluetooth QCA chips attached to MSM

From: Rupesh Tatiya <[email protected]>

Add support to set voltage/current of various regulators to power up/down
BT QCA chips attached to MSM.

Change-Id: I3600dd7bc97c753bc9cf7f8ac39d7b90bc21c67d
Signed-off-by: Rupesh Tatiya <[email protected]>
---
drivers/bluetooth/Makefile | 5 +-
drivers/bluetooth/btqca_power.c | 177 ++++++++++++++++++++++++++++++++++++++++
drivers/bluetooth/btqca_power.h | 71 ++++++++++++++++
3 files changed, 251 insertions(+), 2 deletions(-)
create mode 100644 drivers/bluetooth/btqca_power.c
create mode 100644 drivers/bluetooth/btqca_power.h

diff --git a/drivers/bluetooth/Makefile b/drivers/bluetooth/Makefile
index 4e4e44d..f963909 100644
--- a/drivers/bluetooth/Makefile
+++ b/drivers/bluetooth/Makefile
@@ -24,8 +24,9 @@ obj-$(CONFIG_BT_WILINK) += btwilink.o
obj-$(CONFIG_BT_QCOMSMD) += btqcomsmd.o
obj-$(CONFIG_BT_BCM) += btbcm.o
obj-$(CONFIG_BT_RTL) += btrtl.o
-obj-$(CONFIG_BT_QCA) += btqca.o
-
+obj-$(CONFIG_BT_QCA) += btqca_uart.o
+btqca_uart-$(CONFIG_BT_QCA) += btqca.o
+btqca_uart-$(CONFIG_BT_QCA) += btqca_power.o
obj-$(CONFIG_BT_HCIUART_NOKIA) += hci_nokia.o

btmrvl-y := btmrvl_main.o
diff --git a/drivers/bluetooth/btqca_power.c b/drivers/bluetooth/btqca_power.c
new file mode 100644
index 0000000..a189ff1
--- /dev/null
+++ b/drivers/bluetooth/btqca_power.c
@@ -0,0 +1,177 @@
+/* Copyright (c) 2009-2010, 2013-2018 The Linux Foundation.
+ * All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ */
+/*
+ * Bluetooth Power Switch Module
+ * controls power to external Bluetooth device
+ * with interface to power management device
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/kernel.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/regulator/consumer.h>
+#include <linux/of_device.h>
+#include <net/bluetooth/bluetooth.h>
+
+#include "btqca_power.h"
+
+static struct btqca_power *qca;
+
+static const struct btqca_vreg_data cherokee_data = {
+ .soc_type = BTQCA_CHEROKEE,
+ .vregs = (struct btqca_vreg []) {
+ { "vddio", 1352000, 1352000, 0 },
+ { "vddxtal", 1904000, 2040000, 0 },
+ { "vddcore", 1800000, 1800000, 1 },
+ { "vddpa", 130400, 1304000, 1 },
+ { "vddldo", 3000000, 3312000, 1 },
+ { "vddpwd", 3312000, 3600000, 0 },
+ },
+ .num_vregs = 6,
+};
+
+static const struct of_device_id btqca_power_match_table[] = {
+ { .compatible = "qca,wcn3990", .data = &cherokee_data},
+ {}
+};
+
+int btqca_get_soc_type(enum btqca_soc_t *type)
+{
+ if (!qca || !qca->vreg_data)
+ return -EINVAL;
+
+ *type = qca->vreg_data->soc_type;
+ return 0;
+}
+EXPORT_SYMBOL_GPL(btqca_get_soc_type);
+
+int btqca_power_setup(int on)
+{
+ int ret = 0;
+ int i;
+ struct btqca_vreg *vregs;
+
+ if (!qca || !qca->vreg_data || !qca->vreg_bulk)
+ return -EINVAL;
+ vregs = qca->vreg_data->vregs;
+
+ BT_DBG("on: %d", on);
+
+ if (on) {
+ for (i = 0; i < qca->vreg_data->num_vregs; i++) {
+ regulator_set_voltage(qca->vreg_bulk[i].consumer,
+ vregs[i].min_v,
+ vregs[i].max_v);
+
+ if (vregs[i].load_ua)
+ regulator_set_load(qca->vreg_bulk[i].consumer,
+ vregs[i].load_ua);
+
+ regulator_enable(qca->vreg_bulk[i].consumer);
+ }
+ } else {
+ for (i = 0; i < qca->vreg_data->num_vregs; i++) {
+ regulator_disable(qca->vreg_bulk[i].consumer);
+
+ regulator_set_voltage(qca->vreg_bulk[i].consumer,
+ 0, vregs[i].max_v);
+
+ regulator_set_load(qca->vreg_bulk[i].consumer, 0);
+ }
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(btqca_power_setup);
+
+static int init_regulators(struct btqca_power *qca,
+ const struct btqca_vreg *vregs,
+ size_t num_vregs)
+{
+ int i, ret;
+
+ qca->vreg_bulk = devm_kzalloc(qca->dev, num_vregs *
+ sizeof(struct regulator_bulk_data),
+ GFP_KERNEL);
+ if (!qca->vreg_bulk)
+ return -ENOMEM;
+
+ for (i = 0; i < num_vregs; i++)
+ qca->vreg_bulk[i].supply = vregs[i].name;
+
+ return devm_regulator_bulk_get(qca->dev, num_vregs, qca->vreg_bulk);
+}
+
+static int btqca_power_probe(struct platform_device *pdev)
+{
+ int ret = 0;
+ const struct btqca_vreg_data *data;
+
+ BT_DBG("");
+
+ qca = kzalloc(sizeof(struct btqca_power), GFP_KERNEL);
+ if (!qca)
+ return -ENOMEM;
+ qca->pdev = pdev;
+ qca->dev = &pdev->dev;
+
+ data = of_device_get_match_data(&pdev->dev);
+ qca->vreg_data = data;
+ ret = init_regulators(qca, data->vregs, data->num_vregs);
+ if (ret)
+ goto out;
+
+ return 0;
+
+out:
+ kfree(qca);
+ return ret;
+}
+
+static int btqca_power_remove(struct platform_device *pdev)
+{
+ BT_DBG("");
+
+ btqca_power_setup(0);
+ kfree(qca);
+
+ return 0;
+}
+
+static struct platform_driver btqca_power_driver = {
+ .probe = btqca_power_probe,
+ .remove = btqca_power_remove,
+ .driver = {
+ .name = "btqca_power",
+ .owner = THIS_MODULE,
+ .of_match_table = btqca_power_match_table,
+ },
+};
+
+static int __init btqca_power_init(void)
+{
+ return platform_driver_register(&btqca_power_driver);
+}
+
+static void __exit btqca_power_exit(void)
+{
+ platform_driver_unregister(&btqca_power_driver);
+}
+
+module_init(btqca_power_init);
+module_exit(btqca_power_exit);
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("MSM Bluetooth power control driver for Atheros family");
diff --git a/drivers/bluetooth/btqca_power.h b/drivers/bluetooth/btqca_power.h
new file mode 100644
index 0000000..0569760
--- /dev/null
+++ b/drivers/bluetooth/btqca_power.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (c) 2013-2018 The Linux Foundation. All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only 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.
+ *
+ */
+
+#ifndef __BTQCA_POWER_H
+#define __BTQCA_POWER_H
+
+enum btqca_soc_t {
+ BTQCA_INVALID = -1,
+ BTQCA_AR3002,
+ BTQCA_ROME,
+ BTQCA_CHEROKEE
+};
+
+/*
+ * voltage regulator information required for configuring the
+ * QCA bluetooth chipset
+ */
+struct btqca_vreg {
+ const char *name;
+ unsigned int min_v;
+ unsigned int max_v;
+ unsigned int load_ua;
+};
+
+struct btqca_vreg_data {
+ enum btqca_soc_t soc_type;
+ struct btqca_vreg *vregs;
+ size_t num_vregs;
+};
+
+/*
+ * Platform data for the QCA bluetooth power driver.
+ */
+struct btqca_power {
+ struct platform_device *pdev;
+ struct device *dev;
+ struct btqca_vreg_data *vreg_data;
+ struct regulator_bulk_data *vreg_bulk;
+};
+
+#if IS_ENABLED(CONFIG_BT_QCA)
+
+int btqca_power_setup(int on);
+int btqca_get_soc_type(enum btqca_soc_t *type);
+
+#else
+
+static inline int btqca_power_setup(int on)
+{
+ return -EOPNOTSUPP;
+}
+
+static inline int btqca_get_soc_type(enum btqca_soc_t *type)
+{
+ return -EOPNOTSUPP;
+}
+
+#endif
+
+#endif /* __BTQCA_POWER_H */
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project

2018-04-06 11:52:24

by Balakrishna Godavarthi

[permalink] [raw]
Subject: [PATCH 1/3] Bluetooth: Add device tree bindings for Atheros chips

From: Rupesh Tatiya <[email protected]>

Change-Id: Ie62e3b90b6226019142206c8d44712a6c8550ef2
Signed-off-by: Rupesh Tatiya <[email protected]>
---
.../devicetree/bindings/bluetooth/btqca_power.txt | 30 ++++++++++++++++++++++
1 file changed, 30 insertions(+)
create mode 100644 Documentation/devicetree/bindings/bluetooth/btqca_power.txt

diff --git a/Documentation/devicetree/bindings/bluetooth/btqca_power.txt b/Documentation/devicetree/bindings/bluetooth/btqca_power.txt
new file mode 100644
index 0000000..f0f593c
--- /dev/null
+++ b/Documentation/devicetree/bindings/bluetooth/btqca_power.txt
@@ -0,0 +1,30 @@
+* Bluetooth Controller
+Bluetooth controller communicates with the Bluetooth Host using HCI Transport layer.
+HCI Transport layer can be based on UART or USB serial communication protocol.
+
+Required properties:
+ - compatible: Should be set to one of the following:
+ qca,ar3002
+ qca,qca6174
+ qca,wcn3990
+
+Optional properties:
+ - vddpa-supply: Bluetooth VDD PA regulator handle
+ - vddio-supply: Bluetooth VDD IO regulator handle
+ - vddldo-supply: Bluetooth VDD LDO regulator handle. Kept under optional parameters
+ as some of the chipsets doesn't require ldo or it may use from same vddio.
+ - vddxtal-supply: Bluetooth VDD XTAL regulator handle
+ - vddcore-supply: Bluetooth VDD CORE regulator handle
+ - vddpwd-supply: Chip power down gpio is required when bluetooth module
+ and other modules like wifi co-exist in a singe chip and shares a
+ common gpio to bring chip out of reset.
+
+Example:
+ bt-ar3002 {
+ compatible = "qca,ar3002";
+ vddio-supply = <&pm8941_s3>;
+ vddpa-supply = <&pm8941_l19>;
+ vddxtal-supply = <&pm8994_l30>;
+ vddcore-supply = <&pm8994_s3>;
+ vddpwd-supply = <&ath_chip_pwd_l>;
+};
--
The Qualcomm Innovation Center, Inc. is a member of the Code Aurora Forum,
a Linux Foundation Collaborative Project