diff --git a/drivers/bus/mhi/common.h b/drivers/bus/mhi/common.h index f794b9c8049e..da2f1214f2eb 100644 --- a/drivers/bus/mhi/common.h +++ b/drivers/bus/mhi/common.h @@ -250,6 +250,7 @@ enum mhi_cmd_type { MHI_CMD_RESET_CHAN = 16, MHI_CMD_STOP_CHAN = 17, MHI_CMD_START_CHAN = 18, + MHI_CMD_SFR_CFG = 73, }; #define EV_CTX_RESERVED_MASK GENMASK(7, 0) diff --git a/drivers/bus/mhi/host/boot.c b/drivers/bus/mhi/host/boot.c index c712a2654aec..14f771c1a3ec 100644 --- a/drivers/bus/mhi/host/boot.c +++ b/drivers/bus/mhi/host/boot.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. * */ @@ -35,7 +35,7 @@ int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, bhi_vec->size = mhi_buf->len; } - dev_dbg(dev, "BHIe programming for RDDM\n"); + MHI_VERB(dev, "BHIe programming for RDDM\n"); mhi_write_reg(mhi_cntrl, base, BHIE_RXVECADDR_HIGH_OFFS, upper_32_bits(mhi_buf->dma_addr)); @@ -49,30 +49,61 @@ int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, ret = mhi_write_reg_field(mhi_cntrl, base, BHIE_RXVECDB_OFFS, BHIE_RXVECDB_SEQNUM_BMSK, sequence_id); if (ret) { - dev_err(dev, "Failed to write sequence ID for BHIE_RXVECDB\n"); + MHI_ERR(dev, "Failed to write sequence ID for BHIE_RXVECDB\n"); return ret; } - dev_dbg(dev, "Address: %p and len: 0x%zx sequence: %u\n", + MHI_VERB(dev, "Address: %p and len: 0x%zx sequence: %u\n", &mhi_buf->dma_addr, mhi_buf->len, sequence_id); return 0; } +/* check RDDM image is downloaded */ +int mhi_rddm_download_status(struct mhi_controller *mhi_cntrl) +{ + u32 rx_status; + enum mhi_ee_type ee; + const u32 delayus = 5000; + void __iomem *base = mhi_cntrl->bhie; + u32 retry = (mhi_cntrl->timeout_ms * 1000) / delayus; + struct device *dev = &mhi_cntrl->mhi_dev->dev; + int ret = 0; + + while (retry--) { + ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, + BHIE_RXVECSTATUS_STATUS_BMSK, + &rx_status); + if (ret) + return -EIO; + + if (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) { + MHI_LOG(dev, "RDDM dumps collected successfully"); + return 0; + } + + udelay(delayus); + } + + ee = mhi_get_exec_env(mhi_cntrl); + ret = mhi_read_reg(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, &rx_status); + MHI_ERR(dev, "ret: %d, RXVEC_STATUS: 0x%x, EE:%s\n", ret, rx_status, + TO_MHI_EXEC_STR(ee)); + + return -EIO; +} + /* Collect RDDM buffer during kernel panic */ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl) { int ret; - u32 rx_status; enum mhi_ee_type ee; const u32 delayus = 2000; - u32 retry = (mhi_cntrl->timeout_ms * 1000) / delayus; const u32 rddm_timeout_us = 200000; int rddm_retry = rddm_timeout_us / delayus; - void __iomem *base = mhi_cntrl->bhie; struct device *dev = &mhi_cntrl->mhi_dev->dev; - dev_dbg(dev, "Entered with pm_state:%s dev_state:%s ee:%s\n", + MHI_VERB(dev, "Entered with pm_state:%s dev_state:%s ee:%s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), mhi_state_str(mhi_cntrl->dev_state), TO_MHI_EXEC_STR(mhi_cntrl->ee)); @@ -102,10 +133,10 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl) goto error_exit_rddm; if (ee != MHI_EE_RDDM) { - dev_dbg(dev, "Trigger device into RDDM mode using SYS ERR\n"); + MHI_VERB(dev, "Trigger device into RDDM mode using SYS ERR\n"); mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR); - dev_dbg(dev, "Waiting for device to enter RDDM\n"); + MHI_VERB(dev, "Waiting for device to enter RDDM\n"); while (rddm_retry--) { ee = mhi_get_exec_env(mhi_cntrl); if (ee == MHI_EE_RDDM) @@ -116,40 +147,27 @@ static int __mhi_download_rddm_in_panic(struct mhi_controller *mhi_cntrl) if (rddm_retry <= 0) { /* Hardware reset so force device to enter RDDM */ - dev_dbg(dev, + MHI_VERB(dev, "Did not enter RDDM, do a host req reset\n"); - mhi_write_reg(mhi_cntrl, mhi_cntrl->regs, - MHI_SOC_RESET_REQ_OFFSET, - MHI_SOC_RESET_REQ); + mhi_soc_reset(mhi_cntrl); udelay(delayus); } ee = mhi_get_exec_env(mhi_cntrl); } - dev_dbg(dev, + MHI_VERB(dev, "Waiting for RDDM image download via BHIe, current EE:%s\n", TO_MHI_EXEC_STR(ee)); - while (retry--) { - ret = mhi_read_reg_field(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, - BHIE_RXVECSTATUS_STATUS_BMSK, &rx_status); - if (ret) - return -EIO; - - if (rx_status == BHIE_RXVECSTATUS_STATUS_XFER_COMPL) - return 0; - - udelay(delayus); + ret = mhi_rddm_download_status(mhi_cntrl); + if (!ret) { + MHI_LOG(dev, "RDDM dumps collected successfully"); + return 0; } - ee = mhi_get_exec_env(mhi_cntrl); - ret = mhi_read_reg(mhi_cntrl, base, BHIE_RXVECSTATUS_OFFS, &rx_status); - - dev_err(dev, "RXVEC_STATUS: 0x%x\n", rx_status); - error_exit_rddm: - dev_err(dev, "RDDM transfer failed. Current EE: %s\n", + MHI_ERR(dev, "RDDM transfer failed. Current EE: %s\n", TO_MHI_EXEC_STR(ee)); return -EIO; @@ -165,7 +183,7 @@ int mhi_download_rddm_image(struct mhi_controller *mhi_cntrl, bool in_panic) if (in_panic) return __mhi_download_rddm_in_panic(mhi_cntrl); - dev_dbg(dev, "Waiting for RDDM image download via BHIe\n"); + MHI_VERB(dev, "Waiting for RDDM image download via BHIe\n"); /* Wait for the image download to complete */ wait_event_timeout(mhi_cntrl->state_event, @@ -185,7 +203,7 @@ static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl, void __iomem *base = mhi_cntrl->bhie; struct device *dev = &mhi_cntrl->mhi_dev->dev; rwlock_t *pm_lock = &mhi_cntrl->pm_lock; - u32 tx_status, sequence_id; + u32 tx_status; int ret; read_lock_bh(pm_lock); @@ -194,9 +212,9 @@ static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl, return -EIO; } - sequence_id = MHI_RANDOM_U32_NONZERO(BHIE_TXVECSTATUS_SEQNUM_BMSK); - dev_dbg(dev, "Starting image download via BHIe. Sequence ID: %u\n", - sequence_id); + mhi_cntrl->session_id = MHI_RANDOM_U32_NONZERO(BHIE_TXVECSTATUS_SEQNUM_BMSK); + MHI_VERB(dev, "Starting image download via BHIe. Sequence ID: %u\n", + mhi_cntrl->session_id); mhi_write_reg(mhi_cntrl, base, BHIE_TXVECADDR_HIGH_OFFS, upper_32_bits(mhi_buf->dma_addr)); @@ -206,7 +224,7 @@ static int mhi_fw_load_bhie(struct mhi_controller *mhi_cntrl, mhi_write_reg(mhi_cntrl, base, BHIE_TXVECSIZE_OFFS, mhi_buf->len); ret = mhi_write_reg_field(mhi_cntrl, base, BHIE_TXVECDB_OFFS, - BHIE_TXVECDB_SEQNUM_BMSK, sequence_id); + BHIE_TXVECDB_SEQNUM_BMSK, mhi_cntrl->session_id); read_unlock_bh(pm_lock); if (ret) @@ -254,7 +272,7 @@ static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl, } session_id = MHI_RANDOM_U32_NONZERO(BHI_TXDB_SEQNUM_BMSK); - dev_dbg(dev, "Starting image download via BHI. Session ID: %u\n", + MHI_VERB(dev, "Starting image download via BHI. Session ID: %u\n", session_id); mhi_write_reg(mhi_cntrl, base, BHI_STATUS, 0); mhi_write_reg(mhi_cntrl, base, BHI_IMGADDR_HIGH, @@ -275,7 +293,7 @@ static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl, goto invalid_pm_state; if (tx_status == BHI_STATUS_ERROR) { - dev_err(dev, "Image transfer failed\n"); + MHI_ERR(dev, "Image transfer failed\n"); read_lock_bh(pm_lock); if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { for (i = 0; error_reg[i].name; i++) { @@ -283,7 +301,7 @@ static int mhi_fw_load_bhi(struct mhi_controller *mhi_cntrl, error_reg[i].offset, &val); if (ret) break; - dev_err(dev, "Reg: %s value: 0x%x\n", + MHI_ERR(dev, "Reg: %s value: 0x%x\n", error_reg[i].name, val); } } @@ -408,7 +426,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) int i, ret; if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - dev_err(dev, "Device MHI is not in valid state\n"); + MHI_ERR(dev, "Device MHI is not in valid state\n"); return; } @@ -416,13 +434,13 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_SERIALNU, &mhi_cntrl->serial_number); if (ret) - dev_err(dev, "Could not capture serial number via BHI\n"); + MHI_ERR(dev, "Could not capture serial number via BHI\n"); for (i = 0; i < ARRAY_SIZE(mhi_cntrl->oem_pk_hash); i++) { ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_OEMPKHASH(i), &mhi_cntrl->oem_pk_hash[i]); if (ret) { - dev_err(dev, "Could not capture OEM PK HASH via BHI\n"); + MHI_ERR(dev, "Could not capture OEM PK HASH via BHI\n"); break; } } @@ -436,23 +454,23 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) if (!fw_name || (mhi_cntrl->fbc_download && (!mhi_cntrl->sbl_size || !mhi_cntrl->seg_len))) { - dev_err(dev, + MHI_ERR(dev, "No firmware image defined or !sbl_size || !seg_len\n"); goto error_fw_load; } - ret = request_firmware(&firmware, fw_name, dev); + ret = request_firmware(&firmware, fw_name, dev->parent); if (ret) { if (!mhi_cntrl->fallback_fw_image) { - dev_err(dev, "Error loading firmware: %d\n", ret); + MHI_ERR(dev, "Error loading firmware: %d\n", ret); goto error_fw_load; } ret = request_firmware(&firmware, mhi_cntrl->fallback_fw_image, - dev); + dev->parent); if (ret) { - dev_err(dev, "Error loading fallback firmware: %d\n", + MHI_ERR(dev, "Error loading fallback firmware: %d\n", ret); goto error_fw_load; } @@ -479,7 +497,7 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) /* Error or in EDL mode, we're done */ if (ret) { - dev_err(dev, "MHI did not load image over BHI, ret: %d\n", ret); + MHI_ERR(dev, "MHI did not load image over BHI, ret: %d\n", ret); release_firmware(firmware); goto error_fw_load; } @@ -516,11 +534,11 @@ void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl) /* Transitioning into MHI RESET->READY state */ ret = mhi_ready_state_transition(mhi_cntrl); if (ret) { - dev_err(dev, "MHI did not enter READY state\n"); + MHI_ERR(dev, "MHI did not enter READY state\n"); goto error_ready_state; } - dev_info(dev, "Wait for device to enter SBL or Mission mode\n"); + MHI_LOG(dev, "Wait for device to enter SBL or Mission mode\n"); return; error_ready_state: @@ -545,7 +563,7 @@ int mhi_download_amss_image(struct mhi_controller *mhi_cntrl) /* Vector table is the last entry */ &image_info->mhi_buf[image_info->entries - 1]); if (ret) { - dev_err(dev, "MHI did not load AMSS, ret:%d\n", ret); + MHI_ERR(dev, "MHI did not load AMSS, ret:%d\n", ret); mhi_cntrl->pm_state = MHI_PM_FW_DL_ERR; wake_up_all(&mhi_cntrl->state_event); } diff --git a/drivers/bus/mhi/host/debugfs.c b/drivers/bus/mhi/host/debugfs.c index cfec7811dfbb..b2cac7775d40 100644 --- a/drivers/bus/mhi/host/debugfs.c +++ b/drivers/bus/mhi/host/debugfs.c @@ -25,8 +25,8 @@ static int mhi_debugfs_states_show(struct seq_file *m, void *d) mhi_cntrl->wake_set ? "true" : "false"); /* counters */ - seq_printf(m, "M0: %u M2: %u M3: %u", mhi_cntrl->M0, mhi_cntrl->M2, - mhi_cntrl->M3); + seq_printf(m, "M0: %u M2: %u M3: %u, M3_fast: %u", mhi_cntrl->M0, + mhi_cntrl->M2, mhi_cntrl->M3, mhi_cntrl->M3_fast); seq_printf(m, " device wake: %u pending packets: %u\n", atomic_read(&mhi_cntrl->dev_wake), @@ -71,7 +71,8 @@ static int mhi_debugfs_events_show(struct seq_file *m, void *d) seq_printf(m, " rp: 0x%llx wp: 0x%llx", le64_to_cpu(er_ctxt->rp), le64_to_cpu(er_ctxt->wp)); - seq_printf(m, " local rp: 0x%pK db: 0x%pad\n", ring->rp, + seq_printf(m, " local rp: 0x%llx db: 0x%pad\n", + (u64)mhi_to_physical(ring, ring->rp), &mhi_event->db_cfg.db_val); } @@ -119,8 +120,9 @@ static int mhi_debugfs_channels_show(struct seq_file *m, void *d) le64_to_cpu(chan_ctxt->rbase), le64_to_cpu(chan_ctxt->rlen), le64_to_cpu(chan_ctxt->rp), le64_to_cpu(chan_ctxt->wp)); - seq_printf(m, " local rp: 0x%pK local wp: 0x%pK db: 0x%pad\n", - ring->rp, ring->wp, + seq_printf(m, " local rp: 0x%llx local wp: 0x%llx db: 0x%pad\n", + (u64)mhi_to_physical(ring, ring->rp), + (u64)mhi_to_physical(ring, ring->wp), &mhi_chan->db_cfg.db_val); } diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index cc31ed191943..3878d875bf5e 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -190,7 +190,7 @@ int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl) continue; if (mhi_event->irq >= mhi_cntrl->nr_irqs) { - dev_err(dev, "irq %d not available for event ring\n", + MHI_ERR(dev, "irq %d not available for event ring\n", mhi_event->irq); ret = -EINVAL; goto error_request; @@ -201,7 +201,7 @@ int mhi_init_irq_setup(struct mhi_controller *mhi_cntrl) irq_flags, "mhi", mhi_event); if (ret) { - dev_err(dev, "Error requesting irq:%d for ev:%d\n", + MHI_ERR(dev, "Error requesting irq:%d for ev:%d\n", mhi_cntrl->irq[mhi_event->irq], i); goto error_request; } @@ -507,12 +507,12 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) {0, 0} }; - dev_dbg(dev, "Initializing MHI registers\n"); + MHI_VERB(dev, "Initializing MHI registers\n"); /* Read channel db offset */ ret = mhi_read_reg(mhi_cntrl, base, CHDBOFF, &val); if (ret) { - dev_err(dev, "Unable to read CHDBOFF register\n"); + MHI_ERR(dev, "Unable to read CHDBOFF register\n"); return -EIO; } @@ -528,7 +528,7 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) /* Read event ring db offset */ ret = mhi_read_reg(mhi_cntrl, base, ERDBOFF, &val); if (ret) { - dev_err(dev, "Unable to read ERDBOFF register\n"); + MHI_ERR(dev, "Unable to read ERDBOFF register\n"); return -EIO; } @@ -552,14 +552,14 @@ int mhi_init_mmio(struct mhi_controller *mhi_cntrl) ret = mhi_write_reg_field(mhi_cntrl, base, MHICFG, MHICFG_NER_MASK, mhi_cntrl->total_ev_rings); if (ret) { - dev_err(dev, "Unable to write MHICFG register\n"); + MHI_ERR(dev, "Unable to write MHICFG register\n"); return ret; } ret = mhi_write_reg_field(mhi_cntrl, base, MHICFG, MHICFG_NHWER_MASK, mhi_cntrl->hw_ev_rings); if (ret) { - dev_err(dev, "Unable to write MHICFG register\n"); + MHI_ERR(dev, "Unable to write MHICFG register\n"); return ret; } @@ -680,7 +680,7 @@ static int parse_ev_cfg(struct mhi_controller *mhi_cntrl, /* This event ring has a dedicated channel */ mhi_event->chan = event_cfg->channel; if (mhi_event->chan >= mhi_cntrl->max_chan) { - dev_err(dev, + MHI_ERR(dev, "Event Ring channel not available\n"); goto error_ev_cfg; } @@ -712,8 +712,11 @@ static int parse_ev_cfg(struct mhi_controller *mhi_cntrl, case MHI_ER_BW_SCALE: mhi_event->process_event = mhi_process_misc_bw_ev_ring; break; + case MHI_ER_TIMESYNC: + mhi_event->process_event = mhi_process_misc_tsync_ev_ring; + break; default: - dev_err(dev, "Event Ring type not supported\n"); + MHI_ERR(dev, "Event Ring type not supported\n"); goto error_ev_cfg; } @@ -766,7 +769,7 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl, chan = ch_cfg->num; if (chan >= mhi_cntrl->max_chan) { - dev_err(dev, "Channel %d not available\n", chan); + MHI_ERR(dev, "Channel %d not available\n", chan); goto error_chan_cfg; } @@ -813,7 +816,7 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl, * should be DMA_FROM_DEVICE */ if (mhi_chan->pre_alloc && mhi_chan->dir != DMA_FROM_DEVICE) { - dev_err(dev, "Invalid channel configuration\n"); + MHI_ERR(dev, "Invalid channel configuration\n"); goto error_chan_cfg; } @@ -823,14 +826,14 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl, */ if ((mhi_chan->dir == DMA_BIDIRECTIONAL || mhi_chan->dir == DMA_NONE) && !mhi_chan->offload_ch) { - dev_err(dev, "Invalid channel configuration\n"); + MHI_ERR(dev, "Invalid channel configuration\n"); goto error_chan_cfg; } if (!mhi_chan->offload_ch) { mhi_chan->db_cfg.brstmode = ch_cfg->doorbell; if (MHI_INVALID_BRSTMODE(mhi_chan->db_cfg.brstmode)) { - dev_err(dev, "Invalid Door bell mode\n"); + MHI_ERR(dev, "Invalid Door bell mode\n"); goto error_chan_cfg; } } @@ -946,11 +949,13 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, mhi_event->mhi_cntrl = mhi_cntrl; spin_lock_init(&mhi_event->lock); - if (mhi_event->data_type == MHI_ER_CTRL) - tasklet_init(&mhi_event->task, mhi_ctrl_ev_task, - (ulong)mhi_event); + + if (mhi_event->priority == MHI_ER_PRIORITY_HI_SLEEP) + INIT_WORK(&mhi_event->work, mhi_process_ev_work); else - tasklet_init(&mhi_event->task, mhi_ev_task, + tasklet_init(&mhi_event->task, + (mhi_event->data_type == MHI_ER_CTRL) ? + mhi_ctrl_ev_task : mhi_ev_task, (ulong)mhi_event); } @@ -1016,6 +1021,14 @@ int mhi_register_controller(struct mhi_controller *mhi_cntrl, mhi_cntrl->mhi_dev = mhi_dev; + ret = mhi_misc_register_controller(mhi_cntrl); + if (ret) { + dev_err(mhi_cntrl->cntrl_dev, + "Could not enable miscellaneous features\n"); + mhi_cntrl->mhi_dev = NULL; + goto err_release_dev; + } + mhi_create_debugfs(mhi_cntrl); return 0; @@ -1045,6 +1058,8 @@ void mhi_unregister_controller(struct mhi_controller *mhi_cntrl) unsigned int i; mhi_deinit_free_irq(mhi_cntrl); + mhi_misc_unregister_controller(mhi_cntrl); + /* Free the memory controller wanted to preserve for BHIe images */ if (mhi_cntrl->img_pre_alloc) { mhi_cntrl->img_pre_alloc = false; @@ -1104,12 +1119,12 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl) ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, BHIOFF, &bhi_off); if (ret) { - dev_err(dev, "Error getting BHI offset\n"); + MHI_ERR(dev, "Error getting BHI offset\n"); goto error_reg_offset; } if (bhi_off >= mhi_cntrl->reg_len) { - dev_err(dev, "BHI offset: 0x%x is out of range: 0x%zx\n", + MHI_ERR(dev, "BHI offset: 0x%x is out of range: 0x%zx\n", bhi_off, mhi_cntrl->reg_len); ret = -EINVAL; goto error_reg_offset; @@ -1120,12 +1135,12 @@ int mhi_prepare_for_power_up(struct mhi_controller *mhi_cntrl) ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->regs, BHIEOFF, &bhie_off); if (ret) { - dev_err(dev, "Error getting BHIE offset\n"); + MHI_ERR(dev, "Error getting BHIE offset\n"); goto error_reg_offset; } if (bhie_off >= mhi_cntrl->reg_len) { - dev_err(dev, + MHI_ERR(dev, "BHIe offset: 0x%x is out of range: 0x%zx\n", bhie_off, mhi_cntrl->reg_len); ret = -EINVAL; @@ -1440,12 +1455,14 @@ struct bus_type mhi_bus_type = { static int __init mhi_init(void) { + mhi_misc_init(); mhi_debugfs_init(); return bus_register(&mhi_bus_type); } static void __exit mhi_exit(void) { + mhi_misc_exit(); mhi_debugfs_exit(); bus_unregister(&mhi_bus_type); } diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index 78c33c00393c..8db7f597c34f 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 */ /* - * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. * */ @@ -201,6 +201,7 @@ struct mhi_event { struct mhi_ring ring; struct db_cfg db_cfg; struct tasklet_struct task; + struct work_struct work; spinlock_t lock; int (*process_event)(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, @@ -295,6 +296,7 @@ int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl); int mhi_send_cmd(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, enum mhi_cmd_type cmd); int mhi_download_amss_image(struct mhi_controller *mhi_cntrl); +int mhi_rddm_download_status(struct mhi_controller *mhi_cntrl); static inline bool mhi_is_active(struct mhi_controller *mhi_cntrl) { return (mhi_cntrl->dev_state >= MHI_STATE_M0 && @@ -333,6 +335,7 @@ void mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr, void mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd); void mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan); +dma_addr_t mhi_to_physical(struct mhi_ring *ring, void *addr); /* Initialization methods */ int mhi_init_mmio(struct mhi_controller *mhi_cntrl); @@ -359,6 +362,8 @@ void mhi_reset_chan(struct mhi_controller *mhi_cntrl, /* Event processing methods */ void mhi_ctrl_ev_task(unsigned long data); void mhi_ev_task(unsigned long data); +void mhi_process_ev_work(struct work_struct *work); +void mhi_process_sleeping_events(struct mhi_controller *mhi_cntrl); int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, struct mhi_event *mhi_event, u32 event_quota); int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 116f86685851..c14dd41298ae 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. * */ @@ -246,6 +246,11 @@ static void *mhi_to_virtual(struct mhi_ring *ring, dma_addr_t addr) return (addr - ring->iommu_base) + ring->base; } +dma_addr_t mhi_to_physical(struct mhi_ring *ring, void *addr) +{ + return (addr - ring->base) + ring->iommu_base; +} + static void mhi_add_ring_element(struct mhi_controller *mhi_cntrl, struct mhi_ring *ring) { @@ -320,7 +325,7 @@ int mhi_destroy_device(struct device *dev, void *data) put_device(&dl_chan->mhi_dev->dev); } - dev_dbg(&mhi_cntrl->mhi_dev->dev, "destroy device for chan:%s\n", + MHI_VERB(dev, "destroy device for chan:%s\n", mhi_dev->name); /* Notify the client and remove the device from MHI bus */ @@ -378,14 +383,22 @@ void mhi_create_devices(struct mhi_controller *mhi_cntrl) case DMA_TO_DEVICE: mhi_dev->ul_chan = mhi_chan; mhi_dev->ul_chan_id = mhi_chan->chan; + mhi_dev->ul_event_id = mhi_chan->er_index; break; + case DMA_NONE: + __attribute__((__fallthrough__)); + case DMA_BIDIRECTIONAL: + mhi_dev->ul_chan_id = mhi_chan->chan; + mhi_dev->ul_event_id = mhi_chan->er_index; + __attribute__((__fallthrough__)); case DMA_FROM_DEVICE: /* We use dl_chan as offload channels */ mhi_dev->dl_chan = mhi_chan; mhi_dev->dl_chan_id = mhi_chan->chan; + mhi_dev->dl_event_id = mhi_chan->er_index; break; default: - dev_err(dev, "Direction not supported\n"); + MHI_ERR(dev, "Direction not supported\n"); put_device(&mhi_dev->dev); return; } @@ -401,9 +414,11 @@ void mhi_create_devices(struct mhi_controller *mhi_cntrl) if (mhi_chan->dir == DMA_TO_DEVICE) { mhi_dev->ul_chan = mhi_chan; mhi_dev->ul_chan_id = mhi_chan->chan; + mhi_dev->ul_event_id = mhi_chan->er_index; } else { mhi_dev->dl_chan = mhi_chan; mhi_dev->dl_chan_id = mhi_chan->chan; + mhi_dev->dl_event_id = mhi_chan->er_index; } get_device(&mhi_dev->dev); mhi_chan->mhi_dev = mhi_dev; @@ -426,6 +441,30 @@ void mhi_create_devices(struct mhi_controller *mhi_cntrl) } } +void mhi_process_sleeping_events(struct mhi_controller *mhi_cntrl) +{ + struct mhi_event *mhi_event; + struct mhi_event_ctxt *er_ctxt; + struct mhi_ring *ev_ring; + int i; + + mhi_event = mhi_cntrl->mhi_event; + for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { + if (mhi_event->offload_ev || mhi_event->priority != + MHI_ER_PRIORITY_HI_SLEEP) + continue; + + er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; + ev_ring = &mhi_event->ring; + + /* Only proceed if event ring has pending events */ + if (ev_ring->rp == mhi_to_virtual(ev_ring, er_ctxt->rp)) + continue; + + queue_work(mhi_cntrl->hiprio_wq, &mhi_event->work); + } +} + irqreturn_t mhi_irq_handler(int irq_number, void *priv) { struct mhi_event *mhi_event = priv; @@ -438,7 +477,7 @@ irqreturn_t mhi_irq_handler(int irq_number, void *priv) void *dev_rp; if (!is_valid_ring_ptr(ev_ring, ptr)) { - dev_err(&mhi_cntrl->mhi_dev->dev, + MHI_ERR(dev, "Event ring rp points outside of the event ring\n"); return IRQ_HANDLED; } @@ -467,8 +506,11 @@ irqreturn_t mhi_irq_handler(int irq_number, void *priv) case MHI_ER_PRIORITY_DEFAULT_NOSLEEP: tasklet_schedule(&mhi_event->task); break; + case MHI_ER_PRIORITY_HI_SLEEP: + queue_work(mhi_cntrl->hiprio_wq, &mhi_event->work); + break; default: - dev_dbg(dev, "skip unknown priority event\n"); + MHI_VERB(dev, "skip unknown priority event\n"); break; } @@ -491,13 +533,13 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv) state = mhi_get_mhi_state(mhi_cntrl); ee = mhi_get_exec_env(mhi_cntrl); - dev_dbg(dev, "local ee: %s state: %s device ee: %s state: %s\n", + MHI_VERB(dev, "local ee: %s state: %s device ee: %s state: %s\n", TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_state_str(mhi_cntrl->dev_state), TO_MHI_EXEC_STR(ee), mhi_state_str(state)); if (state == MHI_STATE_SYS_ERR) { - dev_dbg(dev, "System error detected\n"); + MHI_VERB(dev, "System error detected\n"); pm_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_SYS_ERR_DETECT); } @@ -510,6 +552,9 @@ irqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv) case MHI_EE_RDDM: /* proceed if power down is not already in progress */ if (mhi_cntrl->rddm_image && mhi_is_active(mhi_cntrl)) { + /* notify critical clients with early notifications */ + mhi_report_error(mhi_cntrl); + mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM); mhi_cntrl->ee = ee; wake_up_all(&mhi_cntrl->state_event); @@ -607,7 +652,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, u16 xfer_len; if (!is_valid_ring_ptr(tre_ring, ptr)) { - dev_err(&mhi_cntrl->mhi_dev->dev, + MHI_ERR(dev, "Event element points outside of the tre ring\n"); break; } @@ -642,14 +687,13 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, mhi_del_ring_element(mhi_cntrl, tre_ring); local_rp = tre_ring->rp; + read_unlock_bh(&mhi_chan->lock); + /* notify client */ mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); - if (mhi_chan->dir == DMA_TO_DEVICE) { + if (mhi_chan->dir == DMA_TO_DEVICE) atomic_dec(&mhi_cntrl->pending_pkts); - /* Release the reference got from mhi_queue() */ - mhi_cntrl->runtime_put(mhi_cntrl); - } /* * Recycle the buffer if buffer is pre-allocated, @@ -661,12 +705,14 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, mhi_chan->dir, buf_info->cb_buf, buf_info->len, MHI_EOT)) { - dev_err(dev, + MHI_ERR(dev, "Error recycling buffer for chan:%d\n", mhi_chan->chan); kfree(buf_info->cb_buf); } } + + read_lock_bh(&mhi_chan->lock); } break; } /* CC_EOT */ @@ -686,7 +732,7 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, } case MHI_EV_CC_BAD_TRE: default: - dev_err(dev, "Unknown event 0x%x\n", ev_code); + panic("Unknown event 0x%x\n", ev_code); break; } /* switch(MHI_EV_READ_CODE(EV_TRB_CODE,event)) */ @@ -767,6 +813,7 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, struct mhi_ring_element *tre) { dma_addr_t ptr = MHI_TRE_GET_EV_PTR(tre); + struct device *dev = &mhi_cntrl->mhi_dev->dev; struct mhi_cmd *cmd_ring = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; struct mhi_ring *mhi_ring = &cmd_ring->ring; struct mhi_ring_element *cmd_pkt; @@ -774,13 +821,23 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, u32 chan; if (!is_valid_ring_ptr(mhi_ring, ptr)) { - dev_err(&mhi_cntrl->mhi_dev->dev, + MHI_ERR(dev, "Event element points outside of the cmd ring\n"); return; } cmd_pkt = mhi_to_virtual(mhi_ring, ptr); + if (cmd_pkt != mhi_ring->rp) + panic("Out of order cmd completion: 0x%llx. Expected: 0x%llx\n", + cmd_pkt, mhi_ring->rp); + + if (MHI_TRE_GET_CMD_TYPE(cmd_pkt) == MHI_CMD_SFR_CFG) { + mhi_misc_cmd_completion(mhi_cntrl, MHI_CMD_SFR_CFG, + MHI_TRE_GET_EV_CODE(tre)); + goto exit_cmd_completion; + } + chan = MHI_TRE_GET_CMD_CHID(cmd_pkt); if (chan < mhi_cntrl->max_chan && @@ -791,10 +848,11 @@ static void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, complete(&mhi_chan->completion); write_unlock_bh(&mhi_chan->lock); } else { - dev_err(&mhi_cntrl->mhi_dev->dev, + MHI_ERR(dev, "Completion packet for invalid channel ID: %d\n", chan); } +exit_cmd_completion: mhi_del_ring_element(mhi_cntrl, mhi_ring); } @@ -821,7 +879,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, return -EIO; if (!is_valid_ring_ptr(ev_ring, ptr)) { - dev_err(&mhi_cntrl->mhi_dev->dev, + MHI_ERR(dev, "Event ring rp points outside of the event ring\n"); return -EIO; } @@ -832,6 +890,10 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, while (dev_rp != local_rp) { enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp); + MHI_VERB(dev, "RP:0x%llx Processing Event:0x%llx 0x%08x 0x%08x\n", + (u64)mhi_to_physical(ev_ring, local_rp), + local_rp->ptr, local_rp->dword[0], local_rp->dword[1]); + switch (type) { case MHI_PKT_TYPE_BW_REQ_EVENT: { @@ -844,7 +906,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, link_info->target_link_width = MHI_TRE_GET_EV_LINKWIDTH(local_rp); write_unlock_irq(&mhi_cntrl->pm_lock); - dev_dbg(dev, "Received BW_REQ event\n"); + MHI_VERB(dev, "Received BW_REQ event\n"); mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_BW_REQ); break; } @@ -854,7 +916,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, new_state = MHI_TRE_GET_EV_STATE(local_rp); - dev_dbg(dev, "State change event to state: %s\n", + MHI_VERB(dev, "State change event to state: %s\n", mhi_state_str(new_state)); switch (new_state) { @@ -871,7 +933,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, { enum mhi_pm_state pm_state; - dev_dbg(dev, "System error detected\n"); + MHI_VERB(dev, "System error detected\n"); write_lock_irq(&mhi_cntrl->pm_lock); pm_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_SYS_ERR_DETECT); @@ -881,7 +943,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, break; } default: - dev_err(dev, "Invalid state: %s\n", + MHI_ERR(dev, "Invalid state: %s\n", mhi_state_str(new_state)); } @@ -895,7 +957,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, enum dev_st_transition st = DEV_ST_TRANSITION_MAX; enum mhi_ee_type event = MHI_TRE_GET_EV_EXECENV(local_rp); - dev_dbg(dev, "Received EE event: %s\n", + MHI_VERB(dev, "Received EE event: %s\n", TO_MHI_EXEC_STR(event)); switch (event) { case MHI_EE_SBL: @@ -916,7 +978,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, wake_up_all(&mhi_cntrl->state_event); break; default: - dev_err(dev, + MHI_ERR(dev, "Unhandled EE event: 0x%x\n", type); } if (st != DEV_ST_TRANSITION_MAX) @@ -942,7 +1004,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, } break; default: - dev_err(dev, "Unhandled event type: %d\n", type); + MHI_ERR(dev, "Unhandled event type: %d\n", type); break; } @@ -951,7 +1013,7 @@ int mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, ptr = le64_to_cpu(er_ctxt->rp); if (!is_valid_ring_ptr(ev_ring, ptr)) { - dev_err(&mhi_cntrl->mhi_dev->dev, + MHI_ERR(dev, "Event ring rp points outside of the event ring\n"); return -EIO; } @@ -976,6 +1038,7 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, struct mhi_ring *ev_ring = &mhi_event->ring; struct mhi_event_ctxt *er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; + struct device *dev = &mhi_cntrl->mhi_dev->dev; int count = 0; u32 chan; struct mhi_chan *mhi_chan; @@ -985,7 +1048,7 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, return -EIO; if (!is_valid_ring_ptr(ev_ring, ptr)) { - dev_err(&mhi_cntrl->mhi_dev->dev, + MHI_ERR(dev, "Event ring rp points outside of the event ring\n"); return -EIO; } @@ -996,6 +1059,9 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, while (dev_rp != local_rp && event_quota > 0) { enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp); + MHI_VERB(dev, "Processing Event:0x%llx 0x%08x 0x%08x\n", + local_rp->ptr, local_rp->dword[0], local_rp->dword[1]); + chan = MHI_TRE_GET_EV_CHID(local_rp); WARN_ON(chan >= mhi_cntrl->max_chan); @@ -1022,7 +1088,7 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, ptr = le64_to_cpu(er_ctxt->rp); if (!is_valid_ring_ptr(ev_ring, ptr)) { - dev_err(&mhi_cntrl->mhi_dev->dev, + MHI_ERR(dev, "Event ring rp points outside of the event ring\n"); return -EIO; } @@ -1040,13 +1106,14 @@ int mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, void mhi_ev_task(unsigned long data) { + unsigned long flags; struct mhi_event *mhi_event = (struct mhi_event *)data; struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl; /* process all pending events */ - spin_lock_bh(&mhi_event->lock); + spin_lock_irqsave(&mhi_event->lock, flags); mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX); - spin_unlock_bh(&mhi_event->lock); + spin_unlock_irqrestore(&mhi_event->lock, flags); } void mhi_ctrl_ev_task(unsigned long data) @@ -1085,7 +1152,7 @@ void mhi_ctrl_ev_task(unsigned long data) write_lock_irq(&mhi_cntrl->pm_lock); state = mhi_get_mhi_state(mhi_cntrl); if (state == MHI_STATE_SYS_ERR) { - dev_dbg(dev, "System error detected\n"); + MHI_VERB(dev, "System error detected\n"); pm_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_SYS_ERR_DETECT); } @@ -1095,6 +1162,24 @@ void mhi_ctrl_ev_task(unsigned long data) } } +void mhi_process_ev_work(struct work_struct *work) +{ + struct mhi_event *mhi_event = container_of(work, struct mhi_event, + work); + struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl; + struct device *dev = mhi_cntrl->cntrl_dev; + + MHI_VERB(dev, "Enter with pm_state:%s MHI_STATE:%s ee:%s\n", + to_mhi_pm_state_str(mhi_cntrl->pm_state), + mhi_state_str(mhi_cntrl->dev_state), + TO_MHI_EXEC_STR(mhi_cntrl->ee)); + + if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) + return; + + mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX); +} + static bool mhi_is_ring_full(struct mhi_controller *mhi_cntrl, struct mhi_ring *ring) { @@ -1119,23 +1204,23 @@ static int mhi_queue(struct mhi_device *mhi_dev, struct mhi_buf_info *buf_info, if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) return -EIO; - read_lock_irqsave(&mhi_cntrl->pm_lock, flags); - ret = mhi_is_ring_full(mhi_cntrl, tre_ring); - if (unlikely(ret)) { - ret = -EAGAIN; - goto exit_unlock; - } + if (unlikely(ret)) + return -EAGAIN; ret = mhi_gen_tre(mhi_cntrl, mhi_chan, buf_info, mflags); if (unlikely(ret)) - goto exit_unlock; + return ret; - /* Packet is queued, take a usage ref to exit M3 if necessary - * for host->device buffer, balanced put is done on buffer completion - * for device->host buffer, balanced put is after ringing the DB - */ - mhi_cntrl->runtime_get(mhi_cntrl); + /* Let controller mark last busy for runtime PM framework if needed */ + if (mhi_cntrl->runtime_last_busy) + mhi_cntrl->runtime_last_busy(mhi_cntrl); + + read_lock_irqsave(&mhi_cntrl->pm_lock, flags); + + /* trigger M3 exit if necessary */ + if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) + mhi_trigger_resume(mhi_cntrl); /* Assert dev_wake (to exit/prevent M1/M2)*/ mhi_cntrl->wake_toggle(mhi_cntrl); @@ -1146,10 +1231,6 @@ static int mhi_queue(struct mhi_device *mhi_dev, struct mhi_buf_info *buf_info, if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) mhi_ring_chan_db(mhi_cntrl, mhi_chan); - if (dir == DMA_FROM_DEVICE) - mhi_cntrl->runtime_put(mhi_cntrl); - -exit_unlock: read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags); return ret; @@ -1195,12 +1276,16 @@ EXPORT_SYMBOL_GPL(mhi_queue_dma); int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, struct mhi_buf_info *info, enum mhi_flags flags) { + struct device *dev = &mhi_chan->mhi_dev->dev; struct mhi_ring *buf_ring, *tre_ring; struct mhi_ring_element *mhi_tre; struct mhi_buf_info *buf_info; int eot, eob, chain, bei; int ret; + /* Protect accesses for reading and incrementing WP */ + write_lock_bh(&mhi_chan->lock); + buf_ring = &mhi_chan->buf_ring; tre_ring = &mhi_chan->tre_ring; @@ -1218,8 +1303,10 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, if (!info->pre_mapped) { ret = mhi_cntrl->map_single(mhi_cntrl, buf_info); - if (ret) + if (ret) { + write_unlock_bh(&mhi_chan->lock); return ret; + } } eob = !!(flags & MHI_EOB); @@ -1232,10 +1319,16 @@ int mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len); mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain); + MHI_VERB(dev, "Chan: %d WP: 0x%llx TRE: 0x%llx 0x%08x 0x%08x\n", + mhi_chan->chan, (u64)mhi_to_physical(tre_ring, mhi_tre), + mhi_tre->ptr, mhi_tre->dword[0], mhi_tre->dword[1]); + /* increment WP */ mhi_add_ring_element(mhi_cntrl, tre_ring); mhi_add_ring_element(mhi_cntrl, buf_ring); + write_unlock_bh(&mhi_chan->lock); + return 0; } @@ -1300,8 +1393,13 @@ int mhi_send_cmd(struct mhi_controller *mhi_cntrl, cmd_tre->dword[0] = MHI_TRE_CMD_START_DWORD0; cmd_tre->dword[1] = MHI_TRE_CMD_START_DWORD1(chan); break; + case MHI_CMD_SFR_CFG: + mhi_misc_cmd_configure(mhi_cntrl, MHI_CMD_SFR_CFG, + &cmd_tre->ptr, &cmd_tre->dword[0], + &cmd_tre->dword[1]); + break; default: - dev_err(dev, "Command not supported\n"); + MHI_ERR(dev, "Command not supported\n"); break; } @@ -1324,7 +1422,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, enum mhi_cmd_type cmd = MHI_CMD_NOP; int ret; - dev_dbg(dev, "%d: Updating channel state to: %s\n", mhi_chan->chan, + MHI_VERB(dev, "%d: Updating channel state to: %s\n", mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state)); switch (to_state) { @@ -1355,7 +1453,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, cmd = MHI_CMD_START_CHAN; break; default: - dev_err(dev, "%d: Channel state update to %s not allowed\n", + MHI_ERR(dev, "%d: Channel state update to %s not allowed\n", mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state)); return -EINVAL; } @@ -1369,7 +1467,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, reinit_completion(&mhi_chan->completion); ret = mhi_send_cmd(mhi_cntrl, mhi_chan, cmd); if (ret) { - dev_err(dev, "%d: Failed to send %s channel command\n", + MHI_ERR(dev, "%d: Failed to send %s channel command\n", mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state)); goto exit_channel_update; } @@ -1377,7 +1475,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, ret = wait_for_completion_timeout(&mhi_chan->completion, msecs_to_jiffies(mhi_cntrl->timeout_ms)); if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS) { - dev_err(dev, + MHI_ERR(dev, "%d: Failed to receive %s channel command completion\n", mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state)); ret = -EIO; @@ -1393,7 +1491,7 @@ static int mhi_update_channel_state(struct mhi_controller *mhi_cntrl, write_unlock_irq(&mhi_chan->lock); } - dev_dbg(dev, "%d: Channel state change to %s successful\n", + MHI_VERB(dev, "%d: Channel state change to %s successful\n", mhi_chan->chan, TO_CH_STATE_TYPE_STR(to_state)); exit_channel_update: @@ -1412,7 +1510,7 @@ static void mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, mutex_lock(&mhi_chan->mutex); if (!(BIT(mhi_cntrl->ee) & mhi_chan->ee_mask)) { - dev_dbg(dev, "Current EE: %s Required EE Mask: 0x%x\n", + MHI_VERB(dev, "Current EE: %s Required EE Mask: 0x%x\n", TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_chan->ee_mask); goto exit_unprepare_channel; } @@ -1421,7 +1519,7 @@ static void mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, ret = mhi_update_channel_state(mhi_cntrl, mhi_chan, MHI_CH_STATE_TYPE_RESET); if (ret) - dev_err(dev, "%d: Failed to reset channel, still resetting\n", + MHI_ERR(dev, "%d: Failed to reset channel, still resetting\n", mhi_chan->chan); exit_unprepare_channel: @@ -1433,7 +1531,7 @@ static void mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, mhi_reset_chan(mhi_cntrl, mhi_chan); mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); } - dev_dbg(dev, "%d: successfully reset\n", mhi_chan->chan); + MHI_VERB(dev, "%d: successfully reset\n", mhi_chan->chan); mutex_unlock(&mhi_chan->mutex); } @@ -1445,7 +1543,7 @@ int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, struct device *dev = &mhi_chan->mhi_dev->dev; if (!(BIT(mhi_cntrl->ee) & mhi_chan->ee_mask)) { - dev_err(dev, "Current EE: %s Required EE Mask: 0x%x\n", + MHI_ERR(dev, "Current EE: %s Required EE Mask: 0x%x\n", TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_chan->ee_mask); return -ENOTCONN; } @@ -1535,7 +1633,7 @@ static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl, unsigned long flags; dma_addr_t ptr; - dev_dbg(dev, "Marking all events for chan: %d as stale\n", chan); + MHI_VERB(dev, "Marking all events for chan: %d as stale\n", chan); ev_ring = &mhi_event->ring; @@ -1544,7 +1642,7 @@ static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl, ptr = le64_to_cpu(er_ctxt->rp); if (!is_valid_ring_ptr(ev_ring, ptr)) { - dev_err(&mhi_cntrl->mhi_dev->dev, + MHI_ERR(dev, "Event ring rp points outside of the event ring\n"); dev_rp = ev_ring->rp; } else { @@ -1562,7 +1660,7 @@ static void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl, local_rp = ev_ring->base; } - dev_dbg(dev, "Finished marking events as stale events\n"); + MHI_VERB(dev, "Finished marking events as stale events\n"); spin_unlock_irqrestore(&mhi_event->lock, flags); } @@ -1580,11 +1678,8 @@ static void mhi_reset_data_chan(struct mhi_controller *mhi_cntrl, while (tre_ring->rp != tre_ring->wp) { struct mhi_buf_info *buf_info = buf_ring->rp; - if (mhi_chan->dir == DMA_TO_DEVICE) { + if (mhi_chan->dir == DMA_TO_DEVICE) atomic_dec(&mhi_cntrl->pending_pkts); - /* Release the reference got from mhi_queue() */ - mhi_cntrl->runtime_put(mhi_cntrl); - } if (!buf_info->pre_mapped) mhi_cntrl->unmap_single(mhi_cntrl, buf_info); @@ -1680,6 +1775,47 @@ void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev) } EXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer); +static int mhi_update_transfer_state(struct mhi_device *mhi_dev, + enum mhi_ch_state_type to_state) +{ + struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; + struct mhi_chan *mhi_chan; + int dir, ret; + + for (dir = 0; dir < 2; dir++) { + mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; + + if (!mhi_chan) + continue; + + /* + * Bail out if one of the channels fails as client will reset + * both upon failure + */ + mutex_lock(&mhi_chan->mutex); + ret = mhi_update_channel_state(mhi_cntrl, mhi_chan, to_state); + if (ret) { + mutex_unlock(&mhi_chan->mutex); + return ret; + } + mutex_unlock(&mhi_chan->mutex); + } + + return 0; +} + +int mhi_stop_transfer(struct mhi_device *mhi_dev) +{ + return mhi_update_transfer_state(mhi_dev, MHI_CH_STATE_TYPE_STOP); +} +EXPORT_SYMBOL(mhi_stop_transfer); + +int mhi_start_transfer(struct mhi_device *mhi_dev) +{ + return mhi_update_transfer_state(mhi_dev, MHI_CH_STATE_TYPE_START); +} +EXPORT_SYMBOL(mhi_start_transfer); + int mhi_poll(struct mhi_device *mhi_dev, u32 budget) { struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; diff --git a/drivers/bus/mhi/host/misc.c b/drivers/bus/mhi/host/misc.c index 511502d88f9f..82153ae6b89e 100644 --- a/drivers/bus/mhi/host/misc.c +++ b/drivers/bus/mhi/host/misc.c @@ -1364,7 +1364,7 @@ int mhi_process_misc_bw_ev_ring(struct mhi_controller *mhi_cntrl, struct mhi_link_info link_info, *cur_info = &mhi_cntrl->mhi_link_info; struct device *dev = &mhi_cntrl->mhi_dev->dev; struct mhi_private *mhi_priv = dev_get_drvdata(dev); - u32 result = MHI_BW_SCALE_NACK; + enum mhi_bw_scale_req_status result = MHI_BW_SCALE_NACK; int ret = -EINVAL; if (!MHI_IN_MISSION_MODE(mhi_cntrl->ee)) @@ -1428,7 +1428,9 @@ int mhi_process_misc_bw_ev_ring(struct mhi_controller *mhi_cntrl, ret = mhi_priv->bw_scale(mhi_cntrl, &link_info); if (!ret) { *cur_info = link_info; - result = 0; + result = MHI_BW_SCALE_SUCCESS; + } else if (ret == -EINVAL) { + result = MHI_BW_SCALE_INVALID; } write_lock_bh(&mhi_cntrl->pm_lock); @@ -1890,6 +1892,24 @@ int mhi_get_remote_time(struct mhi_device *mhi_dev, } EXPORT_SYMBOL(mhi_get_remote_time); +/* MHI host reset request*/ +int mhi_force_reset(struct mhi_controller *mhi_cntrl) +{ + struct device *dev = &mhi_cntrl->mhi_dev->dev; + + MHI_VERB(dev, "Entered with pm_state:%s dev_state:%s ee:%s\n", + to_mhi_pm_state_str(mhi_cntrl->pm_state), + mhi_state_str(mhi_cntrl->dev_state), + TO_MHI_EXEC_STR(mhi_cntrl->ee)); + + /* notify critical clients in absence of RDDM */ + mhi_report_error(mhi_cntrl); + + mhi_soc_reset(mhi_cntrl); + return mhi_rddm_download_status(mhi_cntrl); +} +EXPORT_SYMBOL(mhi_force_reset); + /* Get SoC info before registering mhi controller */ int mhi_get_soc_info(struct mhi_controller *mhi_cntrl) { diff --git a/drivers/bus/mhi/host/misc.h b/drivers/bus/mhi/host/misc.h index 59bda8be22bd..acc1e108419b 100644 --- a/drivers/bus/mhi/host/misc.h +++ b/drivers/bus/mhi/host/misc.h @@ -44,7 +44,12 @@ #define MHI_BW_SCALE_RESULT(status, seq) (((status) & 0xF) << 8 | \ ((seq) & 0xFF)) -#define MHI_BW_SCALE_NACK 0xF + +enum mhi_bw_scale_req_status { + MHI_BW_SCALE_SUCCESS = 0x0, + MHI_BW_SCALE_INVALID = 0x1, + MHI_BW_SCALE_NACK = 0xF, +}; /* subsystem failure reason cfg command */ #define MHI_TRE_CMD_SFR_CFG_PTR(ptr) (ptr) diff --git a/drivers/bus/mhi/host/pm.c b/drivers/bus/mhi/host/pm.c index cda374bfc9ef..85a464241968 100644 --- a/drivers/bus/mhi/host/pm.c +++ b/drivers/bus/mhi/host/pm.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0 /* - * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. + * Copyright (c) 2018-2021, The Linux Foundation. All rights reserved. * */ @@ -141,7 +141,7 @@ void mhi_set_mhi_state(struct mhi_controller *mhi_cntrl, enum mhi_state state) } if (ret) - dev_err(dev, "Failed to set MHI state to: %s\n", + MHI_ERR(dev, "Failed to set MHI state to: %s\n", mhi_state_str(state)); } @@ -205,7 +205,7 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl) /* Check if device entered error state */ if (MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) { - dev_err(dev, "Device link is not accessible\n"); + MHI_ERR(dev, "Device link is not accessible\n"); return -EIO; } @@ -213,25 +213,25 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl) ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, MHICTRL_RESET_MASK, 0, interval_us); if (ret) { - dev_err(dev, "Device failed to clear MHI Reset\n"); + MHI_ERR(dev, "Device failed to clear MHI Reset\n"); return ret; } ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS, MHISTATUS_READY_MASK, 1, interval_us); if (ret) { - dev_err(dev, "Device failed to enter MHI Ready\n"); + MHI_ERR(dev, "Device failed to enter MHI Ready\n"); return ret; } - dev_dbg(dev, "Device in READY State\n"); + MHI_VERB(dev, "Device in READY State\n"); write_lock_irq(&mhi_cntrl->pm_lock); cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_POR); mhi_cntrl->dev_state = MHI_STATE_READY; write_unlock_irq(&mhi_cntrl->pm_lock); if (cur_state != MHI_PM_POR) { - dev_err(dev, "Error moving to state %s from %s\n", + MHI_ERR(dev, "Error moving to state %s from %s\n", to_mhi_pm_state_str(MHI_PM_POR), to_mhi_pm_state_str(cur_state)); return -EIO; @@ -239,14 +239,14 @@ int mhi_ready_state_transition(struct mhi_controller *mhi_cntrl) read_lock_bh(&mhi_cntrl->pm_lock); if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { - dev_err(dev, "Device registers not accessible\n"); + MHI_ERR(dev, "Device registers not accessible\n"); goto error_mmio; } /* Configure MMIO registers */ ret = mhi_init_mmio(mhi_cntrl); if (ret) { - dev_err(dev, "Error configuring MMIO registers\n"); + MHI_ERR(dev, "Error configuring MMIO registers\n"); goto error_mmio; } @@ -277,7 +277,7 @@ int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl) cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M0); write_unlock_irq(&mhi_cntrl->pm_lock); if (unlikely(cur_state != MHI_PM_M0)) { - dev_err(dev, "Unable to transition to M0 state\n"); + MHI_ERR(dev, "Unable to transition to M0 state\n"); return -EIO; } mhi_cntrl->M0++; @@ -298,6 +298,8 @@ int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl) if (mhi_cmd->ring.rp != mhi_cmd->ring.wp) mhi_ring_cmd_db(mhi_cntrl, mhi_cmd); spin_unlock_irq(&mhi_cmd->lock); + /* ring misc doorbells for certain controllers */ + mhi_misc_dbs_pending(mhi_cntrl); } /* Ring channel DB registers */ @@ -314,7 +316,8 @@ int mhi_pm_m0_transition(struct mhi_controller *mhi_cntrl) read_lock_irq(&mhi_chan->lock); /* Only ring DB if ring is not empty */ - if (tre_ring->base && tre_ring->wp != tre_ring->rp) + if (tre_ring->base && tre_ring->wp != tre_ring->rp && + mhi_chan->ch_state == MHI_CH_STATE_ENABLED) mhi_ring_chan_db(mhi_cntrl, mhi_chan); read_unlock_irq(&mhi_chan->lock); } @@ -350,7 +353,7 @@ void mhi_pm_m1_transition(struct mhi_controller *mhi_cntrl) /* If there are any pending resources, exit M2 immediately */ if (unlikely(atomic_read(&mhi_cntrl->pending_pkts) || atomic_read(&mhi_cntrl->dev_wake))) { - dev_dbg(dev, + MHI_VERB(dev, "Exiting M2, pending_pkts: %d dev_wake: %d\n", atomic_read(&mhi_cntrl->pending_pkts), atomic_read(&mhi_cntrl->dev_wake)); @@ -377,7 +380,7 @@ int mhi_pm_m3_transition(struct mhi_controller *mhi_cntrl) state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3); write_unlock_irq(&mhi_cntrl->pm_lock); if (state != MHI_PM_M3) { - dev_err(dev, "Unable to transition to M3 state\n"); + MHI_ERR(dev, "Unable to transition to M3 state\n"); return -EIO; } @@ -394,7 +397,7 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) enum mhi_ee_type ee = MHI_EE_MAX, current_ee = mhi_cntrl->ee; int ret; - dev_dbg(dev, "Processing Mission Mode transition\n"); + MHI_VERB(dev, "Processing Mission Mode transition\n"); write_lock_irq(&mhi_cntrl->pm_lock); if (MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) @@ -411,6 +414,8 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) wake_up_all(&mhi_cntrl->state_event); + mhi_reset_reg_write_q(mhi_cntrl); + device_for_each_child(&mhi_cntrl->mhi_dev->dev, ¤t_ee, mhi_destroy_device); mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_MISSION_MODE); @@ -432,11 +437,14 @@ static int mhi_pm_mission_mode_transition(struct mhi_controller *mhi_cntrl) read_unlock_bh(&mhi_cntrl->pm_lock); + mhi_process_sleeping_events(mhi_cntrl); + /* * The MHI devices are only created when the client device switches its * Execution Environment (EE) to either SBL or AMSS states */ mhi_create_devices(mhi_cntrl); + mhi_misc_mission_mode(mhi_cntrl); read_lock_bh(&mhi_cntrl->pm_lock); @@ -458,21 +466,28 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) struct device *dev = &mhi_cntrl->mhi_dev->dev; int ret, i; - dev_dbg(dev, "Processing disable transition with PM state: %s\n", + MHI_VERB(dev, "Processing disable transition with PM state: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state)); + mhi_reset_reg_write_q(mhi_cntrl); + mutex_lock(&mhi_cntrl->pm_mutex); - /* Trigger MHI RESET so that the device will not access host memory */ - if (!MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) { - dev_dbg(dev, "Triggering MHI Reset in device\n"); + /* + * Trigger MHI RESET so that the device will not access host memory. + * skip MHI reset if device is in RDDM. + */ + if ((!MHI_PM_IN_FATAL_STATE(mhi_cntrl->pm_state)) && + (mhi_cntrl->rddm_image && + mhi_get_exec_env(mhi_cntrl) != MHI_EE_RDDM)) { + MHI_VERB(dev, "Triggering MHI Reset in device\n"); mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); /* Wait for the reset bit to be cleared by the device */ ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, MHICTRL_RESET_MASK, 0, 25000); if (ret) - dev_err(dev, "Device failed to clear MHI Reset\n"); + MHI_ERR(dev, "Device failed to clear MHI Reset\n"); /* * Device will clear BHI_INTVEC as a part of RESET processing, @@ -486,26 +501,30 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) MHISTATUS, MHISTATUS_READY_MASK, 1, 25000); if (ret) - dev_err(dev, "Device failed to enter READY state\n"); + MHI_ERR(dev, "Device failed to enter READY state\n"); } } - dev_dbg(dev, + MHI_VERB(dev, "Waiting for all pending event ring processing to complete\n"); mhi_event = mhi_cntrl->mhi_event; for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { if (mhi_event->offload_ev) continue; disable_irq(mhi_cntrl->irq[mhi_event->irq]); - tasklet_kill(&mhi_event->task); + if (mhi_event->priority == MHI_ER_PRIORITY_HI_SLEEP) + cancel_work_sync(&mhi_event->work); + else + tasklet_kill(&mhi_event->task); } /* Release lock and wait for all pending threads to complete */ mutex_unlock(&mhi_cntrl->pm_mutex); - dev_dbg(dev, "Waiting for all pending threads to complete\n"); + mhi_misc_disable(mhi_cntrl); + MHI_VERB(dev, "Waiting for all pending threads to complete\n"); wake_up_all(&mhi_cntrl->state_event); - dev_dbg(dev, "Reset all active channels and remove MHI devices\n"); + MHI_VERB(dev, "Reset all active channels and remove MHI devices\n"); device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device); mutex_lock(&mhi_cntrl->pm_mutex); @@ -514,7 +533,7 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) WARN_ON(atomic_read(&mhi_cntrl->pending_pkts)); /* Reset the ev rings and cmd rings */ - dev_dbg(dev, "Resetting EV CTXT and CMD CTXT\n"); + MHI_VERB(dev, "Resetting EV CTXT and CMD CTXT\n"); mhi_cmd = mhi_cntrl->mhi_cmd; cmd_ctxt = mhi_cntrl->mhi_ctxt->cmd_ctxt; for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) { @@ -547,11 +566,11 @@ static void mhi_pm_disable_transition(struct mhi_controller *mhi_cntrl) cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_DISABLE); write_unlock_irq(&mhi_cntrl->pm_lock); if (unlikely(cur_state != MHI_PM_DISABLE)) - dev_err(dev, "Error moving from PM state: %s to: %s\n", + MHI_ERR(dev, "Error moving from PM state: %s to: %s\n", to_mhi_pm_state_str(cur_state), to_mhi_pm_state_str(MHI_PM_DISABLE)); - dev_dbg(dev, "Exiting with PM state: %s, MHI state: %s\n", + MHI_VERB(dev, "Exiting with PM state: %s, MHI state: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), mhi_state_str(mhi_cntrl->dev_state)); @@ -570,7 +589,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) struct device *dev = &mhi_cntrl->mhi_dev->dev; int ret, i; - dev_dbg(dev, "Transitioning from PM state: %s to: %s\n", + MHI_VERB(dev, "Transitioning from PM state: %s to: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), to_mhi_pm_state_str(MHI_PM_SYS_ERR_PROCESS)); @@ -584,7 +603,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) write_unlock_irq(&mhi_cntrl->pm_lock); if (cur_state != MHI_PM_SYS_ERR_PROCESS) { - dev_err(dev, "Failed to transition from PM state: %s to: %s\n", + MHI_ERR(dev, "Failed to transition from PM state: %s to: %s\n", to_mhi_pm_state_str(cur_state), to_mhi_pm_state_str(MHI_PM_SYS_ERR_PROCESS)); goto exit_sys_error_transition; @@ -601,7 +620,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) u32 in_reset = -1; unsigned long timeout = msecs_to_jiffies(mhi_cntrl->timeout_ms); - dev_dbg(dev, "Triggering MHI Reset in device\n"); + MHI_VERB(dev, "Triggering MHI Reset in device\n"); mhi_set_mhi_state(mhi_cntrl, MHI_STATE_RESET); /* Wait for the reset bit to be cleared by the device */ @@ -613,7 +632,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) &in_reset) || !in_reset, timeout); if (!ret || in_reset) { - dev_err(dev, "Device failed to exit MHI Reset state\n"); + MHI_ERR(dev, "Device failed to exit MHI Reset state\n"); goto exit_sys_error_transition; } @@ -624,21 +643,25 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) mhi_write_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_INTVEC, 0); } - dev_dbg(dev, + MHI_VERB(dev, "Waiting for all pending event ring processing to complete\n"); mhi_event = mhi_cntrl->mhi_event; for (i = 0; i < mhi_cntrl->total_ev_rings; i++, mhi_event++) { if (mhi_event->offload_ev) continue; - tasklet_kill(&mhi_event->task); + if (mhi_event->priority == MHI_ER_PRIORITY_HI_SLEEP) + cancel_work_sync(&mhi_event->work); + else + tasklet_kill(&mhi_event->task); } /* Release lock and wait for all pending threads to complete */ mutex_unlock(&mhi_cntrl->pm_mutex); - dev_dbg(dev, "Waiting for all pending threads to complete\n"); + mhi_misc_disable(mhi_cntrl); + MHI_VERB(dev, "Waiting for all pending threads to complete\n"); wake_up_all(&mhi_cntrl->state_event); - dev_dbg(dev, "Reset all active channels and remove MHI devices\n"); + MHI_VERB(dev, "Reset all active channels and remove MHI devices\n"); device_for_each_child(&mhi_cntrl->mhi_dev->dev, NULL, mhi_destroy_device); mutex_lock(&mhi_cntrl->pm_mutex); @@ -647,7 +670,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) WARN_ON(atomic_read(&mhi_cntrl->pending_pkts)); /* Reset the ev rings and cmd rings */ - dev_dbg(dev, "Resetting EV CTXT and CMD CTXT\n"); + MHI_VERB(dev, "Resetting EV CTXT and CMD CTXT\n"); mhi_cmd = mhi_cntrl->mhi_cmd; cmd_ctxt = mhi_cntrl->mhi_ctxt->cmd_ctxt; for (i = 0; i < NR_OF_CMD_RINGS; i++, mhi_cmd++, cmd_ctxt++) { @@ -681,7 +704,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_POR); write_unlock_irq(&mhi_cntrl->pm_lock); if (cur_state != MHI_PM_POR) { - dev_err(dev, "Error moving to state %s from %s\n", + MHI_ERR(dev, "Error moving to state %s from %s\n", to_mhi_pm_state_str(MHI_PM_POR), to_mhi_pm_state_str(cur_state)); goto exit_sys_error_transition; @@ -694,7 +717,7 @@ static void mhi_pm_sys_error_transition(struct mhi_controller *mhi_cntrl) mhi_queue_state_transition(mhi_cntrl, next_state); exit_sys_error_transition: - dev_dbg(dev, "Exiting with PM state: %s, MHI state: %s\n", + MHI_VERB(dev, "Exiting with PM state: %s, MHI state: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), mhi_state_str(mhi_cntrl->dev_state)); @@ -728,7 +751,7 @@ void mhi_pm_sys_err_handler(struct mhi_controller *mhi_cntrl) /* skip if controller supports RDDM */ if (mhi_cntrl->rddm_image) { - dev_dbg(dev, "Controller supports RDDM, skip SYS_ERROR\n"); + MHI_VERB(dev, "Controller supports RDDM, skip SYS_ERROR\n"); return; } @@ -751,7 +774,7 @@ void mhi_pm_st_worker(struct work_struct *work) list_for_each_entry_safe(itr, tmp, &head, node) { list_del(&itr->node); - dev_dbg(dev, "Handling state transition: %s\n", + MHI_VERB(dev, "Handling state transition: %s\n", TO_DEV_STATE_TRANS_STR(itr->state)); switch (itr->state) { @@ -766,6 +789,9 @@ void mhi_pm_st_worker(struct work_struct *work) write_lock_irq(&mhi_cntrl->pm_lock); mhi_cntrl->ee = MHI_EE_SBL; write_unlock_irq(&mhi_cntrl->pm_lock); + + mhi_process_sleeping_events(mhi_cntrl); + /* * The MHI devices are only created when the client * device switches its Execution Environment (EE) to @@ -800,6 +826,28 @@ void mhi_pm_st_worker(struct work_struct *work) } } +static bool mhi_in_rddm(struct mhi_controller *mhi_cntrl) +{ + struct device *dev = &mhi_cntrl->mhi_dev->dev; + + if (mhi_cntrl->rddm_image && mhi_get_exec_env(mhi_cntrl) == MHI_EE_RDDM + && mhi_is_active(mhi_cntrl)) { + mhi_cntrl->ee = MHI_EE_RDDM; + + MHI_ERR(dev, "RDDM event occurred!\n"); + + /* notify critical clients with early notifications */ + mhi_report_error(mhi_cntrl); + + mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM); + wake_up_all(&mhi_cntrl->state_event); + + return true; + } + + return false; +} + int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) { struct mhi_chan *itr, *tmp; @@ -834,7 +882,7 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) read_unlock_bh(&mhi_cntrl->pm_lock); if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - dev_err(dev, + MHI_ERR(dev, "Could not enter M0/M1 state"); return -EIO; } @@ -847,11 +895,11 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) return -EBUSY; } - dev_dbg(dev, "Allowing M3 transition\n"); + MHI_VERB(dev, "Allowing M3 transition\n"); new_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3_ENTER); if (new_state != MHI_PM_M3_ENTER) { write_unlock_irq(&mhi_cntrl->pm_lock); - dev_err(dev, + MHI_ERR(dev, "Error setting to PM state: %s from: %s\n", to_mhi_pm_state_str(MHI_PM_M3_ENTER), to_mhi_pm_state_str(mhi_cntrl->pm_state)); @@ -861,15 +909,22 @@ int mhi_pm_suspend(struct mhi_controller *mhi_cntrl) /* Set MHI to M3 and wait for completion */ mhi_set_mhi_state(mhi_cntrl, MHI_STATE_M3); write_unlock_irq(&mhi_cntrl->pm_lock); - dev_dbg(dev, "Waiting for M3 completion\n"); + MHI_VERB(dev, "Waiting for M3 completion\n"); + + /* finish reg writes before D3 cold */ + mhi_force_reg_write(mhi_cntrl); ret = wait_event_timeout(mhi_cntrl->state_event, mhi_cntrl->dev_state == MHI_STATE_M3 || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), msecs_to_jiffies(mhi_cntrl->timeout_ms)); - if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - dev_err(dev, + if (!ret) { + mhi_debug_reg_dump(mhi_cntrl); + panic("Timedout waiting for M3 ACK"); + return -EIO; + } else if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { + MHI_ERR(dev, "Did not enter M3 state, MHI state: %s, PM state: %s\n", mhi_state_str(mhi_cntrl->dev_state), to_mhi_pm_state_str(mhi_cntrl->pm_state)); @@ -895,7 +950,7 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) enum mhi_pm_state cur_state; int ret; - dev_dbg(dev, "Entered with PM state: %s, MHI state: %s\n", + MHI_VERB(dev, "Entered with PM state: %s, MHI state: %s\n", to_mhi_pm_state_str(mhi_cntrl->pm_state), mhi_state_str(mhi_cntrl->dev_state)); @@ -905,12 +960,11 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) return -EIO; - if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) { - dev_warn(dev, "Resuming from non M3 state (%s)\n", - mhi_state_str(mhi_get_mhi_state(mhi_cntrl))); - if (!force) - return -EINVAL; - } + if (mhi_get_mhi_state(mhi_cntrl) != MHI_STATE_M3) + panic("mhi_pm_state != M3"); + + if (mhi_in_rddm(mhi_cntrl)) + return 0; /* Notify clients about exiting LPM */ list_for_each_entry_safe(itr, tmp, &mhi_cntrl->lpm_chans, node) { @@ -924,7 +978,7 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) cur_state = mhi_tryset_pm_state(mhi_cntrl, MHI_PM_M3_EXIT); if (cur_state != MHI_PM_M3_EXIT) { write_unlock_irq(&mhi_cntrl->pm_lock); - dev_info(dev, + MHI_LOG(dev, "Error setting to PM state: %s from: %s\n", to_mhi_pm_state_str(MHI_PM_M3_EXIT), to_mhi_pm_state_str(mhi_cntrl->pm_state)); @@ -942,7 +996,9 @@ static int __mhi_pm_resume(struct mhi_controller *mhi_cntrl, bool force) msecs_to_jiffies(mhi_cntrl->timeout_ms)); if (!ret || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { - dev_err(dev, + if (mhi_in_rddm(mhi_cntrl)) + return 0; + MHI_ERR(dev, "Did not enter M0 state, MHI state: %s, PM state: %s\n", mhi_state_str(mhi_cntrl->dev_state), to_mhi_pm_state_str(mhi_cntrl->pm_state)); @@ -979,6 +1035,8 @@ int __mhi_device_get_sync(struct mhi_controller *mhi_cntrl) mhi_trigger_resume(mhi_cntrl); read_unlock_bh(&mhi_cntrl->pm_lock); + mhi_force_reg_write(mhi_cntrl); + ret = wait_event_timeout(mhi_cntrl->state_event, mhi_cntrl->pm_state == MHI_PM_M0 || MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state), @@ -1008,7 +1066,10 @@ static void mhi_assert_dev_wake(struct mhi_controller *mhi_cntrl, bool force) atomic_inc(&mhi_cntrl->dev_wake); if (MHI_WAKE_DB_FORCE_SET_VALID(mhi_cntrl->pm_state) && !mhi_cntrl->wake_set) { - mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 1); + if (mhi_cntrl->db_access & MHI_PM_M2) + mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 1); + else + mhi_write_offload_wakedb(mhi_cntrl, 1); mhi_cntrl->wake_set = true; } spin_unlock_irqrestore(&mhi_cntrl->wlock, flags); @@ -1024,7 +1085,10 @@ static void mhi_assert_dev_wake(struct mhi_controller *mhi_cntrl, bool force) if ((atomic_inc_return(&mhi_cntrl->dev_wake) == 1) && MHI_WAKE_DB_SET_VALID(mhi_cntrl->pm_state) && !mhi_cntrl->wake_set) { - mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 1); + if (mhi_cntrl->db_access & MHI_PM_M2) + mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 1); + else + mhi_write_offload_wakedb(mhi_cntrl, 1); mhi_cntrl->wake_set = true; } spin_unlock_irqrestore(&mhi_cntrl->wlock, flags); @@ -1048,7 +1112,10 @@ static void mhi_deassert_dev_wake(struct mhi_controller *mhi_cntrl, if ((atomic_dec_return(&mhi_cntrl->dev_wake) == 0) && MHI_WAKE_DB_CLEAR_VALID(mhi_cntrl->pm_state) && !override && mhi_cntrl->wake_set) { - mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 0); + if (mhi_cntrl->db_access & MHI_PM_M2) + mhi_write_db(mhi_cntrl, mhi_cntrl->wake_db, 0); + else + mhi_write_offload_wakedb(mhi_cntrl, 0); mhi_cntrl->wake_set = false; } spin_unlock_irqrestore(&mhi_cntrl->wlock, flags); @@ -1064,7 +1131,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) u32 interval_us = 25000; /* poll register field every 25 milliseconds */ int ret, i; - dev_info(dev, "Requested to power ON\n"); + MHI_LOG(dev, "Requested to power ON\n"); /* Supply default wake routines if not provided by controller driver */ if (!mhi_cntrl->wake_get || !mhi_cntrl->wake_put || @@ -1088,14 +1155,14 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) /* Confirm that the device is in valid exec env */ if (!MHI_POWER_UP_CAPABLE(current_ee)) { - dev_err(dev, "%s is not a valid EE for power on\n", + MHI_ERR(dev, "%s is not a valid EE for power on\n", TO_MHI_EXEC_STR(current_ee)); ret = -EIO; goto error_exit; } state = mhi_get_mhi_state(mhi_cntrl); - dev_dbg(dev, "Attempting power on with EE: %s, state: %s\n", + MHI_VERB(dev, "Attempting power on with EE: %s, state: %s\n", TO_MHI_EXEC_STR(current_ee), mhi_state_str(state)); if (state == MHI_STATE_SYS_ERR) { @@ -1103,7 +1170,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) ret = mhi_poll_reg_field(mhi_cntrl, mhi_cntrl->regs, MHICTRL, MHICTRL_RESET_MASK, 0, interval_us); if (ret) { - dev_info(dev, "Failed to reset MHI due to syserr state\n"); + MHI_LOG(dev, "Failed to reset MHI due to syserr state\n"); goto error_exit; } @@ -1132,7 +1199,7 @@ int mhi_async_power_up(struct mhi_controller *mhi_cntrl) mutex_unlock(&mhi_cntrl->pm_mutex); - dev_info(dev, "Power on setup success\n"); + MHI_LOG(dev, "Power on setup success\n"); return 0; @@ -1164,7 +1231,7 @@ void mhi_power_down(struct mhi_controller *mhi_cntrl, bool graceful) cur_state = mhi_tryset_pm_state(mhi_cntrl, transition_state); if (cur_state != transition_state) { - dev_err(dev, "Failed to move to state: %s from: %s\n", + MHI_ERR(dev, "Failed to move to state: %s from: %s\n", to_mhi_pm_state_str(transition_state), to_mhi_pm_state_str(mhi_cntrl->pm_state)); /* Force link down or error fatal detected state */ @@ -1204,7 +1271,7 @@ int mhi_sync_power_up(struct mhi_controller *mhi_cntrl) msecs_to_jiffies(mhi_cntrl->timeout_ms)); ret = (MHI_IN_MISSION_MODE(mhi_cntrl->ee)) ? 0 : -ETIMEDOUT; - if (ret) + if (ret && !mhi_cntrl->rddm_image) mhi_power_down(mhi_cntrl, false); return ret; @@ -1220,7 +1287,7 @@ int mhi_force_rddm_mode(struct mhi_controller *mhi_cntrl) if (mhi_cntrl->ee == MHI_EE_RDDM) return 0; - dev_dbg(dev, "Triggering SYS_ERR to force RDDM state\n"); + MHI_VERB(dev, "Triggering SYS_ERR to force RDDM state\n"); mhi_set_mhi_state(mhi_cntrl, MHI_STATE_SYS_ERR); /* Wait for RDDM event */ diff --git a/include/linux/mhi.h b/include/linux/mhi.h index 7c182fda4d7c..b5ac410138d8 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -38,6 +38,8 @@ struct mhi_buf_info; * @MHI_CB_FATAL_ERROR: MHI device entered fatal error state * @MHI_CB_BW_REQ: Received a bandwidth switch request from device * @MHI_CB_FALLBACK_IMG: MHI device was loaded with the provided fallback image + * @MHI_CB_DTR_SIGNAL: DTR signaling update + * @MHI_CB_DTR_START_CHANNELS: DTR signal for client driver to start channels */ enum mhi_callback { MHI_CB_IDLE, @@ -50,6 +52,8 @@ enum mhi_callback { MHI_CB_FATAL_ERROR, MHI_CB_BW_REQ, MHI_CB_FALLBACK_IMG, + MHI_CB_DTR_SIGNAL, + MHI_CB_DTR_START_CHANNELS, }; /** @@ -191,21 +195,25 @@ enum mhi_ch_ee_mask { * @MHI_ER_DATA: Only client data over this ring * @MHI_ER_CTRL: MHI control data and client data * @MHI_ER_BW_SCALE: MHI controller bandwidth scale functionality + * @MHI_ER_TIMESYNC: MHI controller time synchronization DB mode functionality */ enum mhi_er_data_type { MHI_ER_DATA, MHI_ER_CTRL, MHI_ER_BW_SCALE, + MHI_ER_TIMESYNC, }; /** * enum mhi_er_priority - Event ring processing priority * @MHI_ER_PRIORITY_DEFAULT_NOSLEEP: processed by tasklet * @MHI_ER_PRIORITY_HI_NOSLEEP: processed by hi-priority tasklet + * @MHI_ER_PRIORITY_HI_SLEEP: processed by hi-priority wq */ enum mhi_er_priority { MHI_ER_PRIORITY_DEFAULT_NOSLEEP, MHI_ER_PRIORITY_HI_NOSLEEP, + MHI_ER_PRIORITY_HI_SLEEP, }; /** @@ -352,7 +360,7 @@ struct mhi_controller_config { * @dev_state: MHI device state * @dev_wake: Device wakeup count * @pending_pkts: Pending packets for the controller - * @M0, M2, M3: Counters to track number of device MHI state changes + * @M0, M2, M3, M3_fast: Counters to track number of device MHI state changes * @transition_list: List of MHI state transitions * @transition_lock: Lock for protecting MHI state transition list * @wlock: Lock for protecting device wakeup @@ -366,6 +374,7 @@ struct mhi_controller_config { * @wake_toggle: CB function to assert and de-assert device wake (optional) * @runtime_get: CB function to controller runtime resume (required) * @runtime_put: CB function to decrement pm usage (required) + * @runtime_last_busy: CB function for controller to mark last busy (optional) * @map_single: CB function to create TRE buffer * @unmap_single: CB function to destroy TRE buffer * @read_reg: Read a MHI register via the physical link (required) @@ -427,6 +436,7 @@ struct mhi_controller { u32 minor_version; u32 serial_number; u32 oem_pk_hash[MHI_MAX_OEM_PK_HASH_SEGMENTS]; + u32 session_id; struct mhi_event *mhi_event; struct mhi_cmd *mhi_cmd; @@ -441,7 +451,7 @@ struct mhi_controller { enum mhi_state dev_state; atomic_t dev_wake; atomic_t pending_pkts; - u32 M0, M2, M3; + u32 M0, M2, M3, M3_fast; struct list_head transition_list; spinlock_t transition_lock; spinlock_t wlock; @@ -457,6 +467,7 @@ struct mhi_controller { void (*wake_toggle)(struct mhi_controller *mhi_cntrl); int (*runtime_get)(struct mhi_controller *mhi_cntrl); void (*runtime_put)(struct mhi_controller *mhi_cntrl); + void (*runtime_last_busy)(struct mhi_controller *mhi_cntrl); int (*map_single)(struct mhi_controller *mhi_cntrl, struct mhi_buf_info *buf); void (*unmap_single)(struct mhi_controller *mhi_cntrl, @@ -488,8 +499,11 @@ struct mhi_controller { * @dev: Driver model device node for the MHI device * @dev_type: MHI device type * @ul_chan_id: MHI channel id for UL transfer + * @ul_event_id: MHI event ring id for UL transfer * @dl_chan_id: MHI channel id for DL transfer + * @ul_event_id: MHI event ring id for DL transfer * @dev_wake: Device wakeup counter + * @tiocm: Device current terminal settings */ struct mhi_device { const struct mhi_device_id *id; @@ -500,8 +514,11 @@ struct mhi_device { struct device dev; enum mhi_device_type dev_type; int ul_chan_id; + int ul_event_id; int dl_chan_id; + int dl_event_id; u32 dev_wake; + u32 tiocm; }; /**