#define BTNXPUART_FW_DOWNLOADING       2
 #define BTNXPUART_CHECK_BOOT_SIGNATURE 3
 #define BTNXPUART_SERDEV_OPEN          4
+#define BTNXPUART_IR_IN_PROGRESS       5
+
+/* NXP HW err codes */
+#define BTNXPUART_IR_HW_ERR            0xb0
 
 #define FIRMWARE_W8987         "nxp/uartuart8987_bt.bin"
 #define FIRMWARE_W8997         "nxp/uartuart8997_bt_v4.bin"
        }
 }
 
-static int ps_init_work(struct hci_dev *hdev)
+static void ps_setup(struct hci_dev *hdev)
 {
        struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
        struct ps_data *psdata = &nxpdev->psdata;
 
-       psdata->h2c_ps_interval = PS_DEFAULT_TIMEOUT_PERIOD_MS;
-       psdata->ps_state = PS_STATE_AWAKE;
-       psdata->target_ps_mode = DEFAULT_PS_MODE;
        psdata->hdev = hdev;
-       psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_NONE;
-       psdata->c2h_wakeup_gpio = 0xff;
-
-       switch (DEFAULT_H2C_WAKEUP_MODE) {
-       case WAKEUP_METHOD_DTR:
-               psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
-               break;
-       case WAKEUP_METHOD_BREAK:
-       default:
-               psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK;
-               break;
-       }
-       psdata->cur_psmode = PS_MODE_DISABLE;
-       psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID;
        INIT_WORK(&psdata->work, ps_work_func);
-
-       return 0;
-}
-
-static void ps_init_timer(struct hci_dev *hdev)
-{
-       struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
-       struct ps_data *psdata = &nxpdev->psdata;
-
        timer_setup(&psdata->ps_timer, ps_timeout_func, 0);
 }
 
        serdev_device_set_tiocm(nxpdev->serdev, TIOCM_RTS, 0);
        usleep_range(5000, 10000);
 
-       switch (psdata->h2c_wakeupmode) {
+       psdata->ps_state = PS_STATE_AWAKE;
+       psdata->c2h_wakeupmode = BT_HOST_WAKEUP_METHOD_NONE;
+       psdata->c2h_wakeup_gpio = 0xff;
+
+       psdata->cur_h2c_wakeupmode = WAKEUP_METHOD_INVALID;
+       psdata->h2c_ps_interval = PS_DEFAULT_TIMEOUT_PERIOD_MS;
+       switch (DEFAULT_H2C_WAKEUP_MODE) {
        case WAKEUP_METHOD_DTR:
+               psdata->h2c_wakeupmode = WAKEUP_METHOD_DTR;
                serdev_device_set_tiocm(nxpdev->serdev, 0, TIOCM_DTR);
                serdev_device_set_tiocm(nxpdev->serdev, TIOCM_DTR, 0);
                break;
        case WAKEUP_METHOD_BREAK:
        default:
+               psdata->h2c_wakeupmode = WAKEUP_METHOD_BREAK;
                serdev_device_break_ctl(nxpdev->serdev, -1);
                usleep_range(5000, 10000);
                serdev_device_break_ctl(nxpdev->serdev, 0);
                usleep_range(5000, 10000);
                break;
        }
+
+       psdata->cur_psmode = PS_MODE_DISABLE;
+       psdata->target_ps_mode = DEFAULT_PS_MODE;
+
        if (psdata->cur_h2c_wakeupmode != psdata->h2c_wakeupmode)
                hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL);
        if (psdata->cur_psmode != psdata->target_ps_mode)
                goto free_skb;
 
        chip_id = le16_to_cpu(req->chip_id ^ req->chip_id_comp);
-       if (chip_id == 0xffff) {
+       if (chip_id == 0xffff && nxpdev->fw_dnld_v1_offset) {
                nxpdev->fw_dnld_v1_offset = 0;
                nxpdev->fw_v1_sent_bytes = 0;
                nxpdev->fw_v1_expected_len = HDR_LEN;
        return 0;
 }
 
-static int nxp_set_ind_reset(struct hci_dev *hdev, void *data)
-{
-       struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
-       struct sk_buff *skb;
-       u8 *status;
-       u8 pcmd = 0;
-       int err = 0;
-
-       skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd);
-       if (IS_ERR(skb))
-               return PTR_ERR(skb);
-
-       status = skb_pull_data(skb, 1);
-       if (!status || *status)
-               goto free_skb;
-
-       set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
-       err = nxp_download_firmware(hdev);
-       if (err < 0)
-               goto free_skb;
-       serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate);
-       nxpdev->current_baudrate = nxpdev->fw_init_baudrate;
-       if (nxpdev->current_baudrate != HCI_NXP_SEC_BAUDRATE) {
-               nxpdev->new_baudrate = HCI_NXP_SEC_BAUDRATE;
-               nxp_set_baudrate_cmd(hdev, NULL);
-       }
-       hci_cmd_sync_queue(hdev, send_wakeup_method_cmd, NULL, NULL);
-       hci_cmd_sync_queue(hdev, send_ps_cmd, NULL, NULL);
-
-free_skb:
-       kfree_skb(skb);
-       return err;
-}
-
-/* NXP protocol */
 static int nxp_check_boot_sign(struct btnxpuart_dev *nxpdev)
 {
        serdev_device_set_baudrate(nxpdev->serdev, HCI_NXP_PRI_BAUDRATE);
-       serdev_device_set_flow_control(nxpdev->serdev, true);
+       if (test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state))
+               serdev_device_set_flow_control(nxpdev->serdev, false);
+       else
+               serdev_device_set_flow_control(nxpdev->serdev, true);
        set_bit(BTNXPUART_CHECK_BOOT_SIGNATURE, &nxpdev->tx_state);
 
        return wait_event_interruptible_timeout(nxpdev->check_boot_sign_wait_q,
                                               msecs_to_jiffies(1000));
 }
 
+static int nxp_set_ind_reset(struct hci_dev *hdev, void *data)
+{
+       static const u8 ir_hw_err[] = { HCI_EV_HARDWARE_ERROR,
+                                       0x01, BTNXPUART_IR_HW_ERR };
+       struct sk_buff *skb;
+
+       skb = bt_skb_alloc(3, GFP_ATOMIC);
+       if (!skb)
+               return -ENOMEM;
+
+       hci_skb_pkt_type(skb) = HCI_EVENT_PKT;
+       skb_put_data(skb, ir_hw_err, 3);
+
+       /* Inject Hardware Error to upper stack */
+       return hci_recv_frame(hdev, skb);
+}
+
+/* NXP protocol */
 static int nxp_setup(struct hci_dev *hdev)
 {
        struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
        int err = 0;
 
-       set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
-       init_waitqueue_head(&nxpdev->fw_dnld_done_wait_q);
-       init_waitqueue_head(&nxpdev->check_boot_sign_wait_q);
-
        if (nxp_check_boot_sign(nxpdev)) {
                bt_dev_dbg(hdev, "Need FW Download.");
                err = nxp_download_firmware(hdev);
                clear_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
        }
 
-       device_property_read_u32(&nxpdev->serdev->dev, "fw-init-baudrate",
-                                &nxpdev->fw_init_baudrate);
-       if (!nxpdev->fw_init_baudrate)
-               nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE;
        serdev_device_set_baudrate(nxpdev->serdev, nxpdev->fw_init_baudrate);
        nxpdev->current_baudrate = nxpdev->fw_init_baudrate;
 
 
        ps_init(hdev);
 
+       if (test_and_clear_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state))
+               hci_dev_clear_flag(hdev, HCI_SETUP);
+
+       return 0;
+}
+
+static void nxp_hw_err(struct hci_dev *hdev, u8 code)
+{
+       struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+
+       switch (code) {
+       case BTNXPUART_IR_HW_ERR:
+               set_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state);
+               hci_dev_set_flag(hdev, HCI_SETUP);
+               break;
+       default:
+               break;
+       }
+}
+
+static int nxp_shutdown(struct hci_dev *hdev)
+{
+       struct btnxpuart_dev *nxpdev = hci_get_drvdata(hdev);
+       struct sk_buff *skb;
+       u8 *status;
+       u8 pcmd = 0;
+
+       if (test_bit(BTNXPUART_IR_IN_PROGRESS, &nxpdev->tx_state)) {
+               skb = nxp_drv_send_cmd(hdev, HCI_NXP_IND_RESET, 1, &pcmd);
+               if (IS_ERR(skb))
+                       return PTR_ERR(skb);
+
+               status = skb_pull_data(skb, 1);
+               if (status) {
+                       serdev_device_set_flow_control(nxpdev->serdev, false);
+                       set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+               }
+               kfree_skb(skb);
+       }
+
        return 0;
 }
 
                nxpdev->rx_skb = NULL;
                return err;
        }
-       nxpdev->hdev->stat.byte_rx += count;
+       if (!is_fw_downloading(nxpdev))
+               nxpdev->hdev->stat.byte_rx += count;
        return count;
 }
 
        INIT_WORK(&nxpdev->tx_work, btnxpuart_tx_work);
        skb_queue_head_init(&nxpdev->txq);
 
+       init_waitqueue_head(&nxpdev->fw_dnld_done_wait_q);
+       init_waitqueue_head(&nxpdev->check_boot_sign_wait_q);
+
+       device_property_read_u32(&nxpdev->serdev->dev, "fw-init-baudrate",
+                                &nxpdev->fw_init_baudrate);
+       if (!nxpdev->fw_init_baudrate)
+               nxpdev->fw_init_baudrate = FW_INIT_BAUDRATE;
+
+       set_bit(BTNXPUART_FW_DOWNLOADING, &nxpdev->tx_state);
+
        crc8_populate_msb(crc8_table, POLYNOMIAL8);
 
        /* Initialize and register HCI device */
        hdev->flush = btnxpuart_flush;
        hdev->setup = nxp_setup;
        hdev->send  = nxp_enqueue;
+       hdev->hw_error = nxp_hw_err;
+       hdev->shutdown = nxp_shutdown;
        SET_HCIDEV_DEV(hdev, &serdev->dev);
 
        if (hci_register_dev(hdev) < 0) {
                return -ENODEV;
        }
 
-       ps_init_work(hdev);
-       ps_init_timer(hdev);
+       ps_setup(hdev);
 
        return 0;
 }