Re: [PATCH v5 3/3] Bluetooth: hci_qca: Add support for Qualcomm Bluetooth chip wcn3990.

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Hi Bala,

On Fri, May 11, 2018 at 05:51:03PM +0530, Balakrishna Godavarthi wrote:
> Add support to set voltage/current of various regulators
> to power up/down Bluetooth chip wcn3990.
> Add support to read baudrate from dts.
> 
> Signed-off-by: Balakrishna Godavarthi <bgodavar@xxxxxxxxxxxxxx>
> ---

Please include a change log also in the individual patches, not just
in the cover letter. In a revision after many comments it may be
sufficient to say "addressed <reviewers> comments", if the number if
changes is limited it is preferable to briefly list the individual
changes.

>  drivers/bluetooth/btqca.h   |   6 +
>  drivers/bluetooth/hci_qca.c | 547 ++++++++++++++++++++++++++++++++++++++------
>  2 files changed, 480 insertions(+), 73 deletions(-)
> 
> diff --git a/drivers/bluetooth/btqca.h b/drivers/bluetooth/btqca.h
> index 2a7366b..8e2142a 100644
> --- a/drivers/bluetooth/btqca.h
> +++ b/drivers/bluetooth/btqca.h
> @@ -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)

The parentheses are not needed around a literal. That they are used
for the other values is IMO no good reason to introduce more of them.
You might want to squeeze in a clean up patch that removes them for
the other defines.

> +int btqca_power_setup(bool on);
> +int qca_btsoc_cleanup(struct hci_dev *hdev);

nit: inconsistent naming.

If maintainers and authors have no objections you might want to
squeeze in a patch that renames everything to btqca. Just a suggestion
though.

> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> index f05382b..71f9591 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...
> @@ -35,6 +35,11 @@
>  #include <linux/mod_devicetable.h>
>  #include <linux/module.h>
>  #include <linux/serdev.h>
> +#include <linux/delay.h>
> +#include <linux/platform_device.h>
> +#include <linux/regulator/consumer.h>
> +#include <linux/of_device.h>
> +#include <linux/device.h>

As I mentioned on v4, the includes should be in alphabetical order.

> +enum btqca_soc_t {

nit: Again, inconsistent naming, some functions/structs are called
qca_... other btqca_... Personally I prefer btqca_ to avoid clashes.

> +/*
> + * voltage regulator information required for configuring the
> + * QCA bluetooth chipset

nit: Voltage, Bluetooth

> + */
> +struct btqca_vreg {
> +	const char *name;
> +	unsigned int min_v;
> +	unsigned int max_v;
> +	unsigned int load_ua;
> +};

nit: consider min_uV, max_uV, load_uA (as in the regulator framework).

> +
> +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.

nit: Bluetooth

> +static int qca_send_vendor_cmd(struct hci_dev *hdev, u8 cmd)
> +{
> +	struct hci_uart *hu = hci_get_drvdata(hdev);
> +	struct qca_data *qca = hu->priv;
> +	struct sk_buff *skb;
> +
> +	BT_DBG("%s:sending command %02x to SoC", hdev->name, cmd);

bt_dev_dbg()

> +
> +	skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
> +	if (!skb) {
> +		BT_ERR("Failed to allocate memory for skb  packet");

bt_dev_err()

> +	/* Wait for 100 us for soc to settle down */

nit: uS, SoC

> +static int qca_serdev_open(struct hci_uart *hu)
> +{
> +	int ret = 0;
> +
> +	if (hu->serdev) {
> +		serdev_device_open(hu->serdev);
> +	} else {
> +		bt_dev_err(hu->hdev, "open operation not supported");
> +		ret = 1;
> +	}
> +
> +	return ret;
> +}

>From v4:

> > Check return value.
> [Bala]: as we are not checking in qca_open, the return in this
> function is to know the serdev function availability.

Sorry, your comment didn't really clarify things for me. The function
is called from qca_setup() apparently with the intention to open the
serial port, not to check if the function is available. If the serial
port can't be opened for whatever reason the function should return an
error (typically a negative value).

Also the error message is misleading, the underlying problem is not
that the open operations is not supported, but that no serdev device
is associated with the hci_uart. Actually it seems this could never
happen:

In qca_serdev_probe():

  qcadev->serdev_hu.serdev = serdev;

And qca_serdev_open() is only called from qca_setup(). Basically the
first thing qca_setup() does is:

  qcadev = serdev_device_get_drvdata(hu->serdev);

Thus if hu->serdev is NULL qca_setup() will crash shortly after:

  switch (qcadev->btsoc_type) {

I think we can get rid of qca_serdev_open/close() and just call
directly serdev_device_open().

> +int qca_btsoc_cleanup(struct hci_dev *hdev)
> +{
> +	struct hci_uart *hu = hci_get_drvdata(hdev);
> +
> +	/* change host baud rate before sending power off command */
> +	host_set_baudrate(hu, 2400);
> +	/* send 0xC0 command to btsoc before turning off regulators */
> +	qca_send_vendor_cmd(hdev, CHEROKEE_POWEROFF_PULSE);

The comment doesn't provide any useful information. From the code it's
evident that a CHEROKEE_POWEROFF_PULSE is sent, if anybody is
interested in the hex code they can look up the definition of
CHEROKEE_POWEROFF_PULSE.

> +	/* turn off btsoc */
> +	return btqca_power_setup(false);

This comment laso doesn't provide any value.

> +static int qca_serdev_close(struct hci_uart *hu)
> +{
> +	int ret = 0;
> +
> +	if (hu->serdev) {
> +		serdev_device_close(hu->serdev);
> +	} else {
> +		bt_dev_err(hu->hdev, "close operation not supported");
> +		ret = 1;
> +	}
> +
> +	return ret;
> +}

Same commants as for open().

Preferably keep open() and close() together, without squeezing the
cleanup function in between.

>  static int qca_setup(struct hci_uart *hu)
>  {
>  	struct hci_dev *hdev = hu->hdev;
>  	struct qca_data *qca = hu->priv;
> +	struct qca_serdev *qcadev;
>  	unsigned int speed, qca_baudrate = QCA_BAUDRATE_115200;
>  	int ret;
> +	int soc_ver;
> +
> +	qcadev = serdev_device_get_drvdata(hu->serdev);
> +
> +	switch (qcadev->btsoc_type) {
> +	case BTQCA_CHEROKEE:

I still thinks this is has a lot of common code with the default/Rome
case, but let's first clarify and possibly improve a few things.

> +		bt_dev_info(hdev, "setting up wcn3990");
> +		/* 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;

This pattern is repeated a several times for initial and operating
speeds. Helper functions like btqca_get_init_speed() and
btqca_get_oper_speed() would make the code more readable and compact.

> +
> +		if (speed) {
> +			host_set_baudrate(hu, speed);
> +		} else {
> +			bt_dev_err(hdev, "initial speed %u", speed);
> +			return -1;
> +		}

Note: Up to here Rome and Cherokee do exactly the same.

> +		/* disable flow control, as chip is still not turned on */
> +		hci_uart_set_flow_control(hu, true);
> +		/* send 0xFC  command to btsoc */
> +		ret = qca_send_vendor_cmd(hdev, CHEROKEE_POWERON_PULSE);

Delete useless comment.

> +		if (ret) {
> +			bt_dev_err(hdev, "Failed to send power on command");
> +			return ret;
> +		}
> +
> +		/* close serial port */
> +		ret = qca_serdev_close(hu);
> +		if (ret)
> +			return ret;
> +		/* open serial port */
> +		ret = qca_serdev_open(hu);
> +		if (ret)
> +			return ret;

The comments are pointless, it is evident from the code that the
serial port is opened/closed. Much more interesting would be to
explain why it is necessary to close and re-open the port.

> -	bt_dev_info(hdev, "ROME setup");
> +		/* 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) {
> +			host_set_baudrate(hu, speed);
> +		} else {
> +			BT_ERR("%s:initial speed %u", hdev->name, speed);

bt_dev_err()

> +			return -1;
> +		}

The initial baudrate has already been set above, is it necessary to
configure it again because the port was closed?

This baudrate code is extremely noisy. Besides introducing the helper
functions mentioned above you could log the error message in
host_set_baudrate() or even have an addtional wrapper that determines
and sets the baudrate. The latter would reduce the above to:

if (xyz_set_baudrate(hu, INIT_RATE))
	return -1;

And that multiple times.

> +		/* clear flow control */
> +		hci_uart_set_flow_control(hu, true);
> +		/* set operating speed */

Note: Starting from here the code for Cherokee and Rome is essentially
the same, except for enabling flow control.

If you prefer keep the somewhat redundant code paths in the next
revision, but please consider the improvements I suggested. With less
noisy code it will be easier to determine if it is reasonable to join
the two code paths.

> +static int btqca_enable_regulators(int i, struct btqca_vreg *vregs)

The common convention is to pass first the structure a function acts
on, then other parameters.  Use a more expressive name for the
number of regulators, like 'num_regs' or 'nregs'.


> +{
> +	int ret = 0;

Initialization is not necessary, ret is assigned in the next line.

> +static void btqca_disable_regulators(int reg_nu, struct btqca_vreg *vregs)
> +{

Same as for the enable function. Please use the same name for the
argument with the number of regulators on both functions, personally I
don't think 'reg_nu' is a great choice, but that's just my opinion ;-)

> +int btqca_power_setup(bool 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);
> +	/* turn on if regualtors are off */

regulators

Consider dropping the comment, IMO the code speaks for itself.

> +	if (on == true && qca->vregs_on == false) {

if (on && !qca->vreg_on) {

> +		qca->vregs_on = true;

Typically you'd change the variable after having performed the
operation with success.

> +		for (i = 0; i < qca->vreg_data->num_vregs; i++) {
> +			ret = btqca_enable_regulators(i, vregs);
> +			if (ret)
> +				break;
> +		}
> +	} else if (on == false && qca->vregs_on == true) {

if (!on && qca->vreg_on) {

> +		qca->vregs_on = false;

Better done after having disabled the regulators.

> +		/* turn off regulator in reverse order */
> +		btqca_disable_regulators(qca->vreg_data->num_vregs, vregs);
> +	}
> +
> +	/* regulators failed */
> +	if (ret) {
> +		qca->vregs_on = false;
> +		BT_ERR("failed to enable regulator:%s", vregs[i].name);
> +		/* turn off regulators which are enabled */
> +		btqca_disable_regulators(i, vregs);
> +	}

Why not do this directly when the problem is detected? The code also
relies on 'ret' only being set in the 'on' path. Which isn't intuitive
for the reader and *might* change in the future.

> +
> +	return ret;
> +}
> +
> +static int init_regulators(struct btqca_power *qca,

Preferably keep use the same prefix (btqca_) consistently, unless
names become awfully long or otherwise unreadable.

>  static int qca_serdev_probe(struct serdev_device *serdev)
>  {
>	struct qca_serdev *qcadev;
>	const struct btqca_vreg_data *data;
> 	int err = 0;

unnecessary initialization

>		/* set voltage regulator status as false */
>		qca->vregs_on = false;

delete pointless comment

>		/* get operating speed */
>		device_property_read_u32(&serdev->dev, "oper-speed",

comment doesn't add much value

> +		err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
> +		if (err) {
> +			BT_ERR("wcn3990 serdev registration failed");
> +			kfree(qca);
> +			goto out;

disable regulators

Thanks

Matthias
--
To unsubscribe from this list: send the line "unsubscribe linux-bluetooth" in
the body of a message to majordomo@xxxxxxxxxxxxxxx
More majordomo info at  http://vger.kernel.org/majordomo-info.html



[Index of Archives]     [Bluez Devel]     [Linux Wireless Networking]     [Linux Wireless Personal Area Networking]     [Linux ATH6KL]     [Linux USB Devel]     [Linux Media Drivers]     [Linux Audio Users]     [Linux Kernel]     [Linux SCSI]     [Big List of Linux Books]

  Powered by Linux