diff mbox series

[v3,1/4] Bluetooth: hci_qca: use wait_until_sent() for power pulses

Message ID 20181130150247.26294-2-bgodavar@codeaurora.org (mailing list archive)
State Not Applicable, archived
Headers show
Series Bug fixes for Qualcomm BT chip wcn3990 | expand

Commit Message

Balakrishna Godavarthi Nov. 30, 2018, 3:02 p.m. UTC
wcn3990 requires a power pulse to turn ON/OFF along with
regulators. Sometimes we are observing the power pulses are sent
out with some time delay, due to queuing these commands. This is
causing synchronization issues with chip, which intern delay the
chip setup or may end up with communication issues.

Signed-off-by: Balakrishna Godavarthi <bgodavar@codeaurora.org>
---
v3:
  * no change.
v2: 
  * Updated function qca_send_power_pulse()
  * addressed reviewer comments.

v1:
 * initial patch
---
 drivers/bluetooth/hci_qca.c | 37 +++++++++++++------------------------
 1 file changed, 13 insertions(+), 24 deletions(-)

Comments

Johan Hovold Dec. 5, 2018, 6:25 a.m. UTC | #1
On Fri, Nov 30, 2018 at 08:32:44PM +0530, Balakrishna Godavarthi wrote:
> wcn3990 requires a power pulse to turn ON/OFF along with
> regulators. Sometimes we are observing the power pulses are sent
> out with some time delay, due to queuing these commands. This is
> causing synchronization issues with chip, which intern delay the
> chip setup or may end up with communication issues.
> 
> Signed-off-by: Balakrishna Godavarthi <bgodavar@codeaurora.org>
> ---
> v3:
>   * no change.
> v2: 
>   * Updated function qca_send_power_pulse()
>   * addressed reviewer comments.

Please make sure to include reviewers on CC when resending, and as
someone else already mentioned, be a bit more specific about what
changes you actually made in response to the review feedback you
received.

> v1:
>  * initial patch
> ---
>  drivers/bluetooth/hci_qca.c | 37 +++++++++++++------------------------
>  1 file changed, 13 insertions(+), 24 deletions(-)
> 
> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
> index f036c8f98ea3..f5dd323c1967 100644
> --- a/drivers/bluetooth/hci_qca.c
> +++ b/drivers/bluetooth/hci_qca.c
> @@ -1013,11 +1013,9 @@ static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
>  		hci_uart_set_baudrate(hu, speed);
>  }
>  
> -static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd)
> +static int qca_send_power_pulse(struct hci_uart *hu, u8 cmd)
>  {
> -	struct hci_uart *hu = hci_get_drvdata(hdev);
> -	struct qca_data *qca = hu->priv;
> -	struct sk_buff *skb;
> +	int ret;
>  
>  	/* These power pulses are single byte command which are sent
>  	 * at required baudrate to wcn3990. On wcn3990, we have an external
> @@ -1029,19 +1027,16 @@ static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd)
>  	 * save power. Disabling hardware flow control is mandatory while
>  	 * sending power pulses to SoC.
>  	 */
> -	bt_dev_dbg(hdev, "sending power pulse %02x to SoC", cmd);
> -
> -	skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
> -	if (!skb)
> -		return -ENOMEM;
> -
> +	bt_dev_dbg(hu->hdev, "sending power pulse %02x to SoC", cmd);
>  	hci_uart_set_flow_control(hu, true);
> +	ret = serdev_device_write(hu->serdev, &cmd, sizeof(cmd), 0);

You're still using 0 as a timeout here which is broken, as I already
told you.

From 4.21 this will result in an indefinite timeout, but currently
implies not to wait for a full write buffer to drain at all.

As I also mentioned, you need to to make sure to call
serdev_device_write_wakeup() in the write_wakup() path if you are going
to use serdev_device_write() at all.

Johan
Balakrishna Godavarthi Dec. 6, 2018, 10:40 a.m. UTC | #2
Hi Johan,

On 2018-12-05 11:55, Johan Hovold wrote:
> On Fri, Nov 30, 2018 at 08:32:44PM +0530, Balakrishna Godavarthi wrote:
>> wcn3990 requires a power pulse to turn ON/OFF along with
>> regulators. Sometimes we are observing the power pulses are sent
>> out with some time delay, due to queuing these commands. This is
>> causing synchronization issues with chip, which intern delay the
>> chip setup or may end up with communication issues.
>> 
>> Signed-off-by: Balakrishna Godavarthi <bgodavar@codeaurora.org>
>> ---
>> v3:
>>   * no change.
>> v2:
>>   * Updated function qca_send_power_pulse()
>>   * addressed reviewer comments.
> 
> Please make sure to include reviewers on CC when resending, and as
> someone else already mentioned, be a bit more specific about what
> changes you actually made in response to the review feedback you
> received.
> 

[Bala]: sure will add and provide more info in version change history.

>> v1:
>>  * initial patch
>> ---
>>  drivers/bluetooth/hci_qca.c | 37 
>> +++++++++++++------------------------
>>  1 file changed, 13 insertions(+), 24 deletions(-)
>> 
>> diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
>> index f036c8f98ea3..f5dd323c1967 100644
>> --- a/drivers/bluetooth/hci_qca.c
>> +++ b/drivers/bluetooth/hci_qca.c
>> @@ -1013,11 +1013,9 @@ static inline void host_set_baudrate(struct 
>> hci_uart *hu, unsigned int speed)
>>  		hci_uart_set_baudrate(hu, speed);
>>  }
>> 
>> -static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd)
>> +static int qca_send_power_pulse(struct hci_uart *hu, u8 cmd)
>>  {
>> -	struct hci_uart *hu = hci_get_drvdata(hdev);
>> -	struct qca_data *qca = hu->priv;
>> -	struct sk_buff *skb;
>> +	int ret;
>> 
>>  	/* These power pulses are single byte command which are sent
>>  	 * at required baudrate to wcn3990. On wcn3990, we have an external
>> @@ -1029,19 +1027,16 @@ static int qca_send_power_pulse(struct hci_dev 
>> *hdev, u8 cmd)
>>  	 * save power. Disabling hardware flow control is mandatory while
>>  	 * sending power pulses to SoC.
>>  	 */
>> -	bt_dev_dbg(hdev, "sending power pulse %02x to SoC", cmd);
>> -
>> -	skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
>> -	if (!skb)
>> -		return -ENOMEM;
>> -
>> +	bt_dev_dbg(hu->hdev, "sending power pulse %02x to SoC", cmd);
>>  	hci_uart_set_flow_control(hu, true);
>> +	ret = serdev_device_write(hu->serdev, &cmd, sizeof(cmd), 0);
> 
> You're still using 0 as a timeout here which is broken, as I already
> told you.
> 

[Bala]: got the change now will update to timeout to non zero value.

> From 4.21 this will result in an indefinite timeout, but currently
> implies not to wait for a full write buffer to drain at all.
> 
> As I also mentioned, you need to to make sure to call
> serdev_device_write_wakeup() in the write_wakup() path if you are going
> to use serdev_device_write() at all.
> 

[Bala]: this where i am confused.
         calling serdev_device_write is calling an wakeup internally. 
below is the flow

         ttyport_write_buf:
               * calling serdev_device_write() will call write_buf() in 
this call we are enabling bit "TTY_DO_WRITE_WAKEUP" and calling write()
                 i.e. uart_write() where we call in start_tx. this will 
go to the vendor specific write where in isr we call uart_write_wakeup()
                 
https://elixir.bootlin.com/linux/latest/source/drivers/tty/serial/qcom_geni_serial.c#L756

         
uart_write_wakeup()->ttyport_write_wakeup()->serdev_controller_write_wakeup()->hci_uart_write_wakeup()->hci_uart_tx_wakeup()

         the above is flow when serdev_device_write() is called, it is 
indirectly calling serdev_write_wakeup().

         Why actual we need to call an serdev_write_wakeup() is this 
wakeup related to the UART port or for the BT chip.

> Johan
Balakrishna Godavarthi Dec. 11, 2018, 3:42 p.m. UTC | #3
Hi Johan,

On 2018-12-06 16:10, Balakrishna Godavarthi wrote:
> Hi Johan,
> 
> On 2018-12-05 11:55, Johan Hovold wrote:
>> On Fri, Nov 30, 2018 at 08:32:44PM +0530, Balakrishna Godavarthi 
>> wrote:
>>> wcn3990 requires a power pulse to turn ON/OFF along with
>>> regulators. Sometimes we are observing the power pulses are sent
>>> out with some time delay, due to queuing these commands. This is
>>> causing synchronization issues with chip, which intern delay the
>>> chip setup or may end up with communication issues.
>>> 
>>> Signed-off-by: Balakrishna Godavarthi <bgodavar@codeaurora.org>
>>> ---
>>> v3:
>>>   * no change.
>>> v2:
>>>   * Updated function qca_send_power_pulse()
>>>   * addressed reviewer comments.
>> 
>> Please make sure to include reviewers on CC when resending, and as
>> someone else already mentioned, be a bit more specific about what
>> changes you actually made in response to the review feedback you
>> received.
>> 
> 
> [Bala]: sure will add and provide more info in version change history.
> 
>>> v1:
>>>  * initial patch
>>> ---
>>>  drivers/bluetooth/hci_qca.c | 37 
>>> +++++++++++++------------------------
>>>  1 file changed, 13 insertions(+), 24 deletions(-)
>>> 
>>> diff --git a/drivers/bluetooth/hci_qca.c 
>>> b/drivers/bluetooth/hci_qca.c
>>> index f036c8f98ea3..f5dd323c1967 100644
>>> --- a/drivers/bluetooth/hci_qca.c
>>> +++ b/drivers/bluetooth/hci_qca.c
>>> @@ -1013,11 +1013,9 @@ static inline void host_set_baudrate(struct 
>>> hci_uart *hu, unsigned int speed)
>>>  		hci_uart_set_baudrate(hu, speed);
>>>  }
>>> 
>>> -static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd)
>>> +static int qca_send_power_pulse(struct hci_uart *hu, u8 cmd)
>>>  {
>>> -	struct hci_uart *hu = hci_get_drvdata(hdev);
>>> -	struct qca_data *qca = hu->priv;
>>> -	struct sk_buff *skb;
>>> +	int ret;
>>> 
>>>  	/* These power pulses are single byte command which are sent
>>>  	 * at required baudrate to wcn3990. On wcn3990, we have an external
>>> @@ -1029,19 +1027,16 @@ static int qca_send_power_pulse(struct 
>>> hci_dev *hdev, u8 cmd)
>>>  	 * save power. Disabling hardware flow control is mandatory while
>>>  	 * sending power pulses to SoC.
>>>  	 */
>>> -	bt_dev_dbg(hdev, "sending power pulse %02x to SoC", cmd);
>>> -
>>> -	skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
>>> -	if (!skb)
>>> -		return -ENOMEM;
>>> -
>>> +	bt_dev_dbg(hu->hdev, "sending power pulse %02x to SoC", cmd);
>>>  	hci_uart_set_flow_control(hu, true);
>>> +	ret = serdev_device_write(hu->serdev, &cmd, sizeof(cmd), 0);
>> 
>> You're still using 0 as a timeout here which is broken, as I already
>> told you.
>> 
> 
> [Bala]: got the change now will update to timeout to non zero value.
> 
>> From 4.21 this will result in an indefinite timeout, but currently
>> implies not to wait for a full write buffer to drain at all.
>> 
>> As I also mentioned, you need to to make sure to call
>> serdev_device_write_wakeup() in the write_wakup() path if you are 
>> going
>> to use serdev_device_write() at all.
>> 
> 
> [Bala]: this where i am confused.
>         calling serdev_device_write is calling an wakeup internally.
> below is the flow
> 
>         ttyport_write_buf:
>               * calling serdev_device_write() will call write_buf() in
> this call we are enabling bit "TTY_DO_WRITE_WAKEUP" and calling
> write()
>                 i.e. uart_write() where we call in start_tx. this will
> go to the vendor specific write where in isr we call
> uart_write_wakeup()
> 
> https://elixir.bootlin.com/linux/latest/source/drivers/tty/serial/qcom_geni_serial.c#L756
> 
> 
> uart_write_wakeup()->ttyport_write_wakeup()->serdev_controller_write_wakeup()->hci_uart_write_wakeup()->hci_uart_tx_wakeup()
> 
>         the above is flow when serdev_device_write() is called, it is
> indirectly calling serdev_write_wakeup().
> 
>         Why actual we need to call an serdev_write_wakeup() is this
> wakeup related to the UART port or for the BT chip.
> 
>> Johan

Can you help me to understand, whether my understating is correct wrt 
serdev_wakeup().
Johan Hovold Dec. 12, 2018, 4:42 p.m. UTC | #4
On Thu, Dec 06, 2018 at 04:10:07PM +0530, Balakrishna Godavarthi wrote:
> Hi Johan,
> 
> On 2018-12-05 11:55, Johan Hovold wrote:
> > On Fri, Nov 30, 2018 at 08:32:44PM +0530, Balakrishna Godavarthi wrote:

> >> +	ret = serdev_device_write(hu->serdev, &cmd, sizeof(cmd), 0);
> > 
> > You're still using 0 as a timeout here which is broken, as I already
> > told you.
> 
> [Bala]: got the change now will update to timeout to non zero value.
> 
> > From 4.21 this will result in an indefinite timeout, but currently
> > implies not to wait for a full write buffer to drain at all.
> > 
> > As I also mentioned, you need to to make sure to call
> > serdev_device_write_wakeup() in the write_wakup() path if you are going
> > to use serdev_device_write() at all.
> 
> [Bala]: this where i am confused.
>          calling serdev_device_write is calling an wakeup internally. 
> below is the flow
> 
>          ttyport_write_buf:
>                * calling serdev_device_write() will call write_buf() in 
> this call we are enabling bit "TTY_DO_WRITE_WAKEUP" and calling write()
>                  i.e. uart_write() where we call in start_tx. this will 
> go to the vendor specific write where in isr we call uart_write_wakeup()
>                  
> https://elixir.bootlin.com/linux/latest/source/drivers/tty/serial/qcom_geni_serial.c#L756
> 
>          
> uart_write_wakeup()->ttyport_write_wakeup()->serdev_controller_write_wakeup()->hci_uart_write_wakeup()->hci_uart_tx_wakeup()
> 
>          the above is flow when serdev_device_write() is called, it is 
> indirectly calling serdev_write_wakeup().

No, serdev_device_write_wakeup() is currently not called in this path,
which means you cannot use serdev_device_write().

>          Why actual we need to call an serdev_write_wakeup() is this 
> wakeup related to the UART port or for the BT chip.

serdev_device_write_wakeup() is where a writer blocked on a full write
buffer in serdev_device_write() is woken up. 

Johan
Balakrishna Godavarthi Dec. 14, 2018, 12:32 p.m. UTC | #5
Hi Johan,

On 2018-12-12 22:12, Johan Hovold wrote:
> On Thu, Dec 06, 2018 at 04:10:07PM +0530, Balakrishna Godavarthi wrote:
>> Hi Johan,
>> 
>> On 2018-12-05 11:55, Johan Hovold wrote:
>> > On Fri, Nov 30, 2018 at 08:32:44PM +0530, Balakrishna Godavarthi wrote:
> 
>> >> +	ret = serdev_device_write(hu->serdev, &cmd, sizeof(cmd), 0);
>> >
>> > You're still using 0 as a timeout here which is broken, as I already
>> > told you.
>> 
>> [Bala]: got the change now will update to timeout to non zero value.
>> 
>> > From 4.21 this will result in an indefinite timeout, but currently
>> > implies not to wait for a full write buffer to drain at all.
>> >
>> > As I also mentioned, you need to to make sure to call
>> > serdev_device_write_wakeup() in the write_wakup() path if you are going
>> > to use serdev_device_write() at all.
>> 
>> [Bala]: this where i am confused.
>>          calling serdev_device_write is calling an wakeup internally.
>> below is the flow
>> 
>>          ttyport_write_buf:
>>                * calling serdev_device_write() will call write_buf() 
>> in
>> this call we are enabling bit "TTY_DO_WRITE_WAKEUP" and calling 
>> write()
>>                  i.e. uart_write() where we call in start_tx. this 
>> will
>> go to the vendor specific write where in isr we call 
>> uart_write_wakeup()
>> 
>> https://elixir.bootlin.com/linux/latest/source/drivers/tty/serial/qcom_geni_serial.c#L756
>> 
>> 
>> uart_write_wakeup()->ttyport_write_wakeup()->serdev_controller_write_wakeup()->hci_uart_write_wakeup()->hci_uart_tx_wakeup()
>> 
>>          the above is flow when serdev_device_write() is called, it is
>> indirectly calling serdev_write_wakeup().
> 
> No, serdev_device_write_wakeup() is currently not called in this path,
> which means you cannot use serdev_device_write().
> 
>>          Why actual we need to call an serdev_write_wakeup() is this
>> wakeup related to the UART port or for the BT chip.
> 
> serdev_device_write_wakeup() is where a writer blocked on a full write
> buffer in serdev_device_write() is woken up.
> 
> Johan

Is it preferred to use and serdev_device_write_buf() followed by 
serdev_device_wait_until_sent()
or do we required an write_wakeup() called before writing into 
serdev_device_write_buf()
Johan Hovold Dec. 14, 2018, 1:16 p.m. UTC | #6
On Fri, Dec 14, 2018 at 06:02:40PM +0530, Balakrishna Godavarthi wrote:
> Hi Johan,
> 
> On 2018-12-12 22:12, Johan Hovold wrote:
> > On Thu, Dec 06, 2018 at 04:10:07PM +0530, Balakrishna Godavarthi wrote:

> >> uart_write_wakeup()->ttyport_write_wakeup()->serdev_controller_write_wakeup()->hci_uart_write_wakeup()->hci_uart_tx_wakeup()
> >> 
> >>          the above is flow when serdev_device_write() is called, it is
> >> indirectly calling serdev_write_wakeup().
> > 
> > No, serdev_device_write_wakeup() is currently not called in this path,
> > which means you cannot use serdev_device_write().
> > 
> >>          Why actual we need to call an serdev_write_wakeup() is this
> >> wakeup related to the UART port or for the BT chip.
> > 
> > serdev_device_write_wakeup() is where a writer blocked on a full write
> > buffer in serdev_device_write() is woken up.
> > 
> > Johan
> 
> Is it preferred to use and serdev_device_write_buf() followed by 
> serdev_device_wait_until_sent()

That's up to the driver author and depends on what you want to achieve.

If you want to do something synchronously, you always have to call
serdev_device_wait_until_sent() after, as both serdev_device_write_buf()
and serdev_device_write() only buffer the data.

If you use serdev_device_write_buf() you also have to make sure that you
can handle incomplete buffering yourself (e.g. when the underlying tty
driver buffer is getting full).

serdev_device_write() was added as a convenience helper for this, but
depends on serdev_device_write_wakeup() being called in the write_wakeup
path to make forward progress.

> or do we required an write_wakeup() called before writing into 
> serdev_device_write_buf()

No, that doesn't make any sense. serdev_device_write_wakeup() needs to
be called in the write-wakeup path, but only if you use
serdev_device_write().

Please see the documentation of these function that I added to
linux-next (and that I linked to earlier).

Johan
Balakrishna Godavarthi Dec. 14, 2018, 1:41 p.m. UTC | #7
Hi Johan,

On 2018-12-14 18:46, Johan Hovold wrote:
> On Fri, Dec 14, 2018 at 06:02:40PM +0530, Balakrishna Godavarthi wrote:
>> Hi Johan,
>> 
>> On 2018-12-12 22:12, Johan Hovold wrote:
>> > On Thu, Dec 06, 2018 at 04:10:07PM +0530, Balakrishna Godavarthi wrote:
> 
>> >> uart_write_wakeup()->ttyport_write_wakeup()->serdev_controller_write_wakeup()->hci_uart_write_wakeup()->hci_uart_tx_wakeup()
>> >>
>> >>          the above is flow when serdev_device_write() is called, it is
>> >> indirectly calling serdev_write_wakeup().
>> >
>> > No, serdev_device_write_wakeup() is currently not called in this path,
>> > which means you cannot use serdev_device_write().
>> >
>> >>          Why actual we need to call an serdev_write_wakeup() is this
>> >> wakeup related to the UART port or for the BT chip.
>> >
>> > serdev_device_write_wakeup() is where a writer blocked on a full write
>> > buffer in serdev_device_write() is woken up.
>> >
>> > Johan
>> 
>> Is it preferred to use and serdev_device_write_buf() followed by
>> serdev_device_wait_until_sent()
> 
> That's up to the driver author and depends on what you want to achieve.
> 

[Bala]: During the BT on & of, we need to send the commands to the 
controller
at a specific baudrate. as the data is getting queued in the buffer we 
see some times
the data is transmitting on the newer baudrate instead of the actual 
baudrate.
we want to achieve an synchronous data transfer for some commands to 
over come byte corruptions.

> If you want to do something synchronously, you always have to call
> serdev_device_wait_until_sent() after, as both 
> serdev_device_write_buf()
> and serdev_device_write() only buffer the data.
> 
> If you use serdev_device_write_buf() you also have to make sure that 
> you
> can handle incomplete buffering yourself (e.g. when the underlying tty
> driver buffer is getting full).
> 
> serdev_device_write() was added as a convenience helper for this, but
> depends on serdev_device_write_wakeup() being called in the 
> write_wakeup
> path to make forward progress.
> 

[Bala]: Thanks for the info, now i understood the usage of the both 
write() and write_buf().

>> or do we required an write_wakeup() called before writing into
>> serdev_device_write_buf()
> 
> No, that doesn't make any sense. serdev_device_write_wakeup() needs to
> be called in the write-wakeup path, but only if you use
> serdev_device_write().
> 
> Please see the documentation of these function that I added to
> linux-next (and that I linked to earlier).
> 

> Johan

Thanks Johan for the detailed info.
diff mbox series

Patch

diff --git a/drivers/bluetooth/hci_qca.c b/drivers/bluetooth/hci_qca.c
index f036c8f98ea3..f5dd323c1967 100644
--- a/drivers/bluetooth/hci_qca.c
+++ b/drivers/bluetooth/hci_qca.c
@@ -1013,11 +1013,9 @@  static inline void host_set_baudrate(struct hci_uart *hu, unsigned int speed)
 		hci_uart_set_baudrate(hu, speed);
 }
 
-static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd)
+static int qca_send_power_pulse(struct hci_uart *hu, u8 cmd)
 {
-	struct hci_uart *hu = hci_get_drvdata(hdev);
-	struct qca_data *qca = hu->priv;
-	struct sk_buff *skb;
+	int ret;
 
 	/* These power pulses are single byte command which are sent
 	 * at required baudrate to wcn3990. On wcn3990, we have an external
@@ -1029,19 +1027,16 @@  static int qca_send_power_pulse(struct hci_dev *hdev, u8 cmd)
 	 * save power. Disabling hardware flow control is mandatory while
 	 * sending power pulses to SoC.
 	 */
-	bt_dev_dbg(hdev, "sending power pulse %02x to SoC", cmd);
-
-	skb = bt_skb_alloc(sizeof(cmd), GFP_KERNEL);
-	if (!skb)
-		return -ENOMEM;
-
+	bt_dev_dbg(hu->hdev, "sending power pulse %02x to SoC", cmd);
 	hci_uart_set_flow_control(hu, true);
+	ret = serdev_device_write(hu->serdev, &cmd, sizeof(cmd), 0);
+	if (ret < 0) {
+		bt_dev_err(hu->hdev, "failed to send power pulse %02x to SoC",
+			   cmd);
+		return ret;
+	}
 
-	skb_put_u8(skb, cmd);
-	hci_skb_pkt_type(skb) = HCI_COMMAND_PKT;
-
-	skb_queue_tail(&qca->txq, skb);
-	hci_uart_tx_wakeup(hu);
+	serdev_device_wait_until_sent(hu->serdev, 0);
 
 	/* Wait for 100 uS for SoC to settle down */
 	usleep_range(100, 200);
@@ -1116,7 +1111,6 @@  static int qca_set_speed(struct hci_uart *hu, enum qca_speed_type speed_type)
 
 static int qca_wcn3990_init(struct hci_uart *hu)
 {
-	struct hci_dev *hdev = hu->hdev;
 	struct qca_serdev *qcadev;
 	int ret;
 
@@ -1139,12 +1133,12 @@  static int qca_wcn3990_init(struct hci_uart *hu)
 
 	/* Forcefully enable wcn3990 to enter in to boot mode. */
 	host_set_baudrate(hu, 2400);
-	ret = qca_send_power_pulse(hdev, QCA_WCN3990_POWEROFF_PULSE);
+	ret = qca_send_power_pulse(hu, QCA_WCN3990_POWEROFF_PULSE);
 	if (ret)
 		return ret;
 
 	qca_set_speed(hu, QCA_INIT_SPEED);
-	ret = qca_send_power_pulse(hdev, QCA_WCN3990_POWERON_PULSE);
+	ret = qca_send_power_pulse(hu, QCA_WCN3990_POWERON_PULSE);
 	if (ret)
 		return ret;
 
@@ -1274,13 +1268,8 @@  static const struct qca_vreg_data qca_soc_data = {
 
 static void qca_power_shutdown(struct hci_uart *hu)
 {
-	struct serdev_device *serdev = hu->serdev;
-	unsigned char cmd = QCA_WCN3990_POWEROFF_PULSE;
-
 	host_set_baudrate(hu, 2400);
-	hci_uart_set_flow_control(hu, true);
-	serdev_device_write_buf(serdev, &cmd, sizeof(cmd));
-	hci_uart_set_flow_control(hu, false);
+	qca_send_power_pulse(hu, QCA_WCN3990_POWEROFF_PULSE);
 	qca_power_setup(hu, false);
 }