mirror of
https://github.com/torvalds/linux.git
synced 2026-07-30 11:11:26 +02:00
Merge "bus: mhi: host: Enable and update IPC logging usage"
This commit is contained in:
commit
4cab18afee
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
}
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
{
|
||||
|
|
|
|||
|
|
@ -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)
|
||||
|
|
|
|||
|
|
@ -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 */
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
|
|
|||
Loading…
Reference in New Issue
Block a user