Merge "bus: mhi: host: Enable and update IPC logging usage"

This commit is contained in:
qctecmdr 2022-10-15 21:06:26 -07:00 committed by Gerrit - the friendly Code Review server
commit 4cab18afee
10 changed files with 502 additions and 214 deletions

View File

@ -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)

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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);
}

View File

@ -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,

View File

@ -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;

View File

@ -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)
{

View File

@ -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)

View File

@ -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, &current_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 */

View File

@ -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;
};
/**