All of lore.kernel.org
 help / color / mirror / Atom feed
From: Matthias Kaehlcke <mka@chromium.org>
To: Balakrishna Godavarthi <bgodavar@codeaurora.org>
Cc: marcel@holtmann.org, johan.hedberg@gmail.com,
	linux-bluetooth@vger.kernel.org, rtatiya@codeaurora.org,
	hemantg@codeaurora.org, linux-arm-msm@vger.kernel.org
Subject: Re: [PATCH v6 5/5] Bluetooth: hci_qca: Add support for Qualcomm Bluetooth chip wcn3990.
Date: Tue, 29 May 2018 10:41:30 -0700	[thread overview]
Message-ID: <20180529174130.GD168650@google.com> (raw)
In-Reply-To: <20180524160051.29966-6-bgodavar@codeaurora.org>

On Thu, May 24, 2018 at 09:30:51PM +0530, Balakrishna Godavarthi wrote:
> Add support to set voltage/current of various regulators
> to power up/down Bluetooth chip wcn3990.
> 
> Signed-off-by: Balakrishna Godavarthi <bgodavar@codeaurora.org>
> ---
> 
> Changes in v6:
> 	* Hooked up qca_power to qca_serdev.	
> 	* renamed all the naming inconsistency functions with qca_*
> 	* leveraged common code of ROME for wcn3990.
> 	* created wrapper functions for re-usable blocks.
> 	* updated function of _*regulator_enable and _*regualtor_disable.  
> 	* removed redundant comments and functions.
> 	* addressed review comments.
> 
> Changes in v5:
> 	* updated regulator vddpa min_uV to 1304000.
>   	* addressed review comments.
>  
> Changes in v4:
> 	* Segregated the changes of btqca from hci_qca
> 	* rebased all changes on top of bluetooth-next.
> 	* addressed review comments.
> ---
>
> ...
>
> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> index cb1034998040..e235be0e5202 100644
> --- a/drivers/bluetooth/hci_qca.c
> +++ b/drivers/bluetooth/hci_qca.c
> ...
> +static void qca_set_init_speed(struct hci_uart *hu)
> +{
> +	unsigned int speed = 0;
>  
> -	/* Setup initial baudrate */
> -	speed = 0;
>  	if (hu->init_speed)
>  		speed = hu->init_speed;
>  	else if (hu->proto->init_speed)
> @@ -946,29 +1015,136 @@ static int qca_setup(struct hci_uart *hu)
>  
>  	if (speed)
>  		host_set_baudrate(hu, speed);
> +}
> +
> +static int qca_set_operating_speed(struct hci_uart *hu, u32 *qca_baudrate)
> +{

This is a bit convoluted, with the function setting the speed and
returning it. I would suggest a qca_get_oper_speed() and
qca_get_init_speed(), and just have them return the value instead of
passing it through a pointer. You could then have a qca_set_speed()
which converts the baudrate to the value the chip understands, calls
qca_set_baudrate() and host_set_baudrate()

> +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:
> +		bt_dev_dbg(hdev, "setting up wcn3990");
> +		/* Patch downloading has to be done without IBS mode */
> +		clear_bit(STATE_IN_BAND_SLEEP_ENABLED, &qca->flags);
> +		qca_set_init_speed(hu);
> +		hci_uart_set_flow_control(hu, true);
> +		ret = qca_send_vendor_cmd(hdev, CHEROKEE_POWERON_PULSE);
> +		if (ret) {
> +			bt_dev_err(hdev, "failed to send power on command");
> +			return ret;
> +		}
> +
> +		/* Close and re-open the port */

As mentioned earlier, this comment doesn't provide any useful
information, it's evident from the code. Rather explain *why* the
close/open is needed.

> +		serdev_device_close(hu->serdev);
> +		ret = serdev_device_open(hu->serdev);
> +		if (ret) {
> +			bt_dev_err(hdev, "failed to open port");
> +			return ret;
> +		}
> +
> +		qca_set_init_speed(hu);
> +		hci_uart_set_flow_control(hu, false);
> +		msleep(100);

Which step makes the delay necessary? I guess it's not enabling flow
control but probably sending the power on command. If that is correct
the delay should be done after the corresponding command.

> +		ret = qca_patch_ver_req(hdev, &soc_ver);
> +		if (ret < 0 || soc_ver == 0) {
> +			bt_dev_err(hdev, "Failed to get version 0x%x", ret);

Probably better: "Failed to get version: %d" since ret contains an
errno and the problem wasn't that we couldn't get "version 0x<errno>".

> +			return ret;
> +		}
> +
> +		bt_dev_info(hdev, "wcn3990 controller version 0x%08x", soc_ver);
> +		hci_uart_set_flow_control(hu, true);

Repeatedly switching on and off of flow control is a bit noisy, I
wonder if it could be hidden in a wrapper. From the code it seems that
flow control is disabled (true) for init speed and and enabled (false)
for operating speed. If this is correct the hci_uart_set_flow_control()
calls could be moved inside qca_set_oper/init_speed(). struct qca_serdev
could have a flag indicating if flow control is supported at all to
skip the hci_uart_set_flow_control() calls for Rome.

Just an idea, no objections if you prefer to leave it as is and Marcel
is ok with it.

> +		ret = qca_set_operating_speed(hu, &qca_baudrate);
> +		if (ret)
> +			return ret;
> +		hci_uart_set_flow_control(hu, false);
> +		/* Setup patch and NVM configurations */
> +		ret = qca_uart_setup_cherokee(hdev, qca_baudrate, &soc_ver);
> +
> +		break;
> +
> +	default:
> +		bt_dev_info(hdev, "ROME setup");
> +
> +		/* 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 you introduce helpers to determine the init/oper speed these should
also be used here. Probably best to do this in a separate patch.

> +
> +		if (speed)
> +			host_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;

ditto

> +static void qca_disable_regulator(struct qca_vreg vregs,
> +				  struct regulator *regulator)
> +{
> +	/* Disable the regulator if requested by user
> +	 * or when fault to enable any regulator.
> +	 */

This comment is not useful. The following code simply disables the
regulator, why this is done is a question of the caller.

> +	regulator_disable(regulator);
> +	regulator_set_voltage(regulator, 0, vregs.max_uV);
> +	if (vregs.load_uA)
> +		regulator_set_load(regulator, 0);
> +
> +}
> +
> +int qca_btsoc_power_setup(struct hci_uart *hu, bool on)
> +{
> +	struct qca_vreg *vregs;
> +	struct regulator_bulk_data *vreg_bulk;
> +	struct qca_serdev *qcadev;
> +	int i, num_vregs, ret = 0;
> +
> +	qcadev = serdev_device_get_drvdata(hu->serdev);
> +	if (!qcadev || !qcadev->bt_power || !qcadev->bt_power->vreg_data ||
> +	    !qcadev->bt_power->vreg_bulk)
> +		return -EINVAL;
> +
> +	vregs = qcadev->bt_power->vreg_data->vregs;
> +	vreg_bulk = qcadev->bt_power->vreg_bulk;
> +	num_vregs = qcadev->bt_power->vreg_data->num_vregs;
> +	BT_DBG("on: %d", on);
> +	if (on  && !qcadev->bt_power->vregs_on) {
> +		for (i = 0; i < num_vregs; i++) {
> +			ret = qca_enable_regulator(vregs[i],
> +						   vreg_bulk[i].consumer);
> +			if (ret)
> +				break;
> +		}
> +		/* regulators failed */

Comment is not useful, BT_ERR below leaves things clear.

> +		if (ret) {
> +			BT_ERR("failed to enable regulator:%s", vregs[i].name);
> +			/* turn off regulators which are enabled */
> +			for (i = i - 1; i >= 0; i--)
> +				qca_disable_regulator(vregs[i],
> +						      vreg_bulk[i].consumer);
> +		} else {
> +			qcadev->bt_power->vregs_on = true;
> +		}
> +	} else if (!on && qcadev->bt_power->vregs_on) {
> +		/* turn off regulator in reverse order */
> +		i = qcadev->bt_power->vreg_data->num_vregs - 1;
> +		for ( ; i >= 0; i--)
> +			qca_disable_regulator(vregs[i], vreg_bulk[i].consumer);
> +		qcadev->bt_power->vregs_on = false;
> +	}
> +
> +	return ret;
> +}
> +
> +static int qca_init_regulators(struct qca_power *qca,
> +			       const struct qca_vreg *vregs, size_t num_vregs)
> +{
> +	int i;
> +
> +	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 qca_serdev_probe(struct serdev_device *serdev)
>  {
>  	struct qca_serdev *qcadev;
> +	const struct qca_vreg_data *data;
>  	int err;
>  
>  	qcadev = devm_kzalloc(&serdev->dev, sizeof(*qcadev), GFP_KERNEL);
> @@ -1014,34 +1301,72 @@ static int qca_serdev_probe(struct serdev_device *serdev)
>  		return -ENOMEM;
>  
>  	qcadev->serdev_hu.serdev = serdev;
> +	data = of_device_get_match_data(&serdev->dev);
> +	if (data && data->soc_type == BTQCA_CHEROKEE)
> +		qcadev->btsoc_type = BTQCA_CHEROKEE;
> +	else
> +		qcadev->btsoc_type = BTQCA_ROME;
> +
>  	serdev_device_set_drvdata(serdev, qcadev);
> +	if (qcadev->btsoc_type == BTQCA_CHEROKEE) {
> +		qcadev->bt_power = devm_kzalloc(&serdev->dev,
> +						sizeof(struct qca_power),
> +						GFP_KERNEL);
> +		if (!qcadev->bt_power)
> +			return -ENOMEM;
> +
> +		qcadev->bt_power->dev = &serdev->dev;
> +		qcadev->bt_power->vreg_data = data;
> +		err = qca_init_regulators(qcadev->bt_power, data->vregs,
> +					  data->num_vregs);
> +		if (err) {
> +			BT_ERR("Failed to init regulators:%d", err);
> +			devm_kfree(&serdev->dev, qcadev->bt_power->vreg_bulk);
> +			devm_kfree(&serdev->dev, qcadev->bt_power);

Not necessary, the memory is allocated with devm_kzalloc(&serdev->dev,
...), therefore it is freed if probe() fails.

> +			goto out;
> +		}
>  
> -	qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable",
> -				       GPIOD_OUT_LOW);
> -	if (IS_ERR(qcadev->bt_en)) {
> -		dev_err(&serdev->dev, "failed to acquire enable gpio\n");
> -		return PTR_ERR(qcadev->bt_en);
> -	}
> +		qcadev->bt_power->vregs_on = false;
> +		device_property_read_u32(&serdev->dev, "max-speed",
> +					 &qcadev->oper_speed);
> +		if (!qcadev->oper_speed)
> +			BT_INFO("UART will pick default operating speed");
> +		err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
> +		if (err) {
> +			BT_ERR("wcn3990 serdev registration failed");
> +			devm_kfree(&serdev->dev, qcadev->bt_power->vreg_bulk);
> +			devm_kfree(&serdev->dev, qcadev->bt_power);

no need to free memory.

> +			goto out;
> +		}
> +	} else {
> +		qcadev->bt_en = devm_gpiod_get(&serdev->dev, "enable",
> +					       GPIOD_OUT_LOW);
> +		if (IS_ERR(qcadev->bt_en)) {
> +			dev_err(&serdev->dev, "failed to acquire enable gpio\n");
> +			return PTR_ERR(qcadev->bt_en);
> +		}
>  
> -	qcadev->susclk = devm_clk_get(&serdev->dev, NULL);
> -	if (IS_ERR(qcadev->susclk)) {
> -		dev_err(&serdev->dev, "failed to acquire clk\n");
> -		return PTR_ERR(qcadev->susclk);
> -	}
> +		qcadev->susclk = devm_clk_get(&serdev->dev, NULL);
> +		if (IS_ERR(qcadev->susclk)) {
> +			dev_err(&serdev->dev, "failed to acquire clk\n");
> +			return PTR_ERR(qcadev->susclk);
> +		}
>  
> -	err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
> -	if (err)
> -		return err;
> +		err = clk_set_rate(qcadev->susclk, SUSCLK_RATE_32KHZ);
> +		if (err)
> +			return err;
>  
> -	err = clk_prepare_enable(qcadev->susclk);
> -	if (err)
> -		return err;
> +		err = clk_prepare_enable(qcadev->susclk);
> +		if (err)
> +			return err;
>  
> -	err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
> -	if (err)
> -		clk_disable_unprepare(qcadev->susclk);
> +		err = hci_uart_register_device(&qcadev->serdev_hu, &qca_proto);
> +		if (err)
> +			clk_disable_unprepare(qcadev->susclk);
> +	}
> +
> +out:	return err;
>  
> -	return err;
>  }
>  
>  static void qca_serdev_remove(struct serdev_device *serdev)
> @@ -1050,11 +1375,17 @@ static void qca_serdev_remove(struct serdev_device *serdev)
>  
>  	hci_uart_unregister_device(&qcadev->serdev_hu);
>  
> -	clk_disable_unprepare(qcadev->susclk);
> +	if (qcadev->btsoc_type == BTQCA_CHEROKEE) {
> +		devm_kfree(&serdev->dev, qcadev->bt_power->vreg_bulk);
> +		devm_kfree(&serdev->dev, qcadev->bt_power);

no need to free memory

  parent reply	other threads:[~2018-05-29 17:41 UTC|newest]

Thread overview: 14+ messages / expand[flat|nested]  mbox.gz  Atom feed  top
2018-05-24 16:00 [PATCH v6 0/5] Enable Bluetooth functionality for WCN3990 Balakrishna Godavarthi
2018-05-24 16:00 ` [PATCH v6 1/5] dt-bindings: net: bluetooth: Add device tree bindings for QTI chip wcn3990 Balakrishna Godavarthi
2018-05-24 16:00 ` [PATCH v6 2/5] Bluetooth: btqca: Rename ROME related functions to Generic functions Balakrishna Godavarthi
2018-05-25 21:57   ` Matthias Kaehlcke
2018-05-28 11:04     ` Balakrishna Godavarthi
2018-05-29 17:50       ` Matthias Kaehlcke
2018-05-24 16:00 ` [PATCH v6 3/5] Bluetooth: hci_qca: Enable 3.2 Mbps operating speed Balakrishna Godavarthi
2018-05-25 22:03   ` Matthias Kaehlcke
2018-05-24 16:00 ` [PATCH v6 4/5] Bluetooth: btqca: Add wcn3990 firmware download support Balakrishna Godavarthi
2018-05-24 16:00 ` [PATCH v6 5/5] Bluetooth: hci_qca: Add support for Qualcomm Bluetooth chip wcn3990 Balakrishna Godavarthi
2018-05-27  6:54   ` kbuild test robot
2018-05-27  8:52   ` kbuild test robot
2018-05-29 17:41   ` Matthias Kaehlcke [this message]
2018-06-04 15:14     ` Balakrishna Godavarthi

Reply instructions:

You may reply publicly to this message via plain-text email
using any one of the following methods:

* Save the following mbox file, import it into your mail client,
  and reply-to-all from there: mbox

  Avoid top-posting and favor interleaved quoting:
  https://en.wikipedia.org/wiki/Posting_style#Interleaved_style

* Reply using the --to, --cc, and --in-reply-to
  switches of git-send-email(1):

  git send-email \
    --in-reply-to=20180529174130.GD168650@google.com \
    --to=mka@chromium.org \
    --cc=bgodavar@codeaurora.org \
    --cc=hemantg@codeaurora.org \
    --cc=johan.hedberg@gmail.com \
    --cc=linux-arm-msm@vger.kernel.org \
    --cc=linux-bluetooth@vger.kernel.org \
    --cc=marcel@holtmann.org \
    --cc=rtatiya@codeaurora.org \
    /path/to/YOUR_REPLY

  https://kernel.org/pub/software/scm/git/docs/git-send-email.html

* If your mail client supports setting the In-Reply-To header
  via mailto: links, try the mailto: link
Be sure your reply has a Subject: header at the top and a blank line before the message body.
This is an external index of several public inboxes,
see mirroring instructions on how to clone and mirror
all data and code used by this external index.