in mhi/core/init.c [722:838]
static int parse_ch_cfg(struct mhi_controller *mhi_cntrl,
const struct mhi_controller_config *config)
{
const struct mhi_channel_config *ch_cfg;
struct device *dev = mhi_cntrl->cntrl_dev;
int i;
u32 chan;
mhi_cntrl->max_chan = config->max_channels;
/*
* The allocation of MHI channels can exceed 32KB in some scenarios,
* so to avoid any memory possible allocation failures, vzalloc is
* used here
*/
mhi_cntrl->mhi_chan = vzalloc(mhi_cntrl->max_chan *
sizeof(*mhi_cntrl->mhi_chan));
if (!mhi_cntrl->mhi_chan)
return -ENOMEM;
INIT_LIST_HEAD(&mhi_cntrl->lpm_chans);
/* Populate channel configurations */
for (i = 0; i < config->num_channels; i++) {
struct mhi_chan *mhi_chan;
ch_cfg = &config->ch_cfg[i];
chan = ch_cfg->num;
if (chan >= mhi_cntrl->max_chan) {
dev_err(dev, "Channel %d not available\n", chan);
goto error_chan_cfg;
}
mhi_chan = &mhi_cntrl->mhi_chan[chan];
mhi_chan->name = ch_cfg->name;
mhi_chan->chan = chan;
mhi_chan->tre_ring.elements = ch_cfg->num_elements;
if (!mhi_chan->tre_ring.elements)
goto error_chan_cfg;
/*
* For some channels, local ring length should be bigger than
* the transfer ring length due to internal logical channels
* in device. So host can queue much more buffers than transfer
* ring length. Example, RSC channels should have a larger local
* channel length than transfer ring length.
*/
mhi_chan->buf_ring.elements = ch_cfg->local_elements;
if (!mhi_chan->buf_ring.elements)
mhi_chan->buf_ring.elements = mhi_chan->tre_ring.elements;
mhi_chan->er_index = ch_cfg->event_ring;
mhi_chan->dir = ch_cfg->dir;
/*
* For most channels, chtype is identical to channel directions.
* So, if it is not defined then assign channel direction to
* chtype
*/
mhi_chan->type = ch_cfg->type;
if (!mhi_chan->type)
mhi_chan->type = (enum mhi_ch_type)mhi_chan->dir;
mhi_chan->ee_mask = ch_cfg->ee_mask;
mhi_chan->db_cfg.pollcfg = ch_cfg->pollcfg;
mhi_chan->lpm_notify = ch_cfg->lpm_notify;
mhi_chan->offload_ch = ch_cfg->offload_channel;
mhi_chan->db_cfg.reset_req = ch_cfg->doorbell_mode_switch;
mhi_chan->pre_alloc = ch_cfg->auto_queue;
mhi_chan->wake_capable = ch_cfg->wake_capable;
/*
* If MHI host allocates buffers, then the channel direction
* should be DMA_FROM_DEVICE
*/
if (mhi_chan->pre_alloc && mhi_chan->dir != DMA_FROM_DEVICE) {
dev_err(dev, "Invalid channel configuration\n");
goto error_chan_cfg;
}
/*
* Bi-directional and direction less channel must be an
* offload channel
*/
if ((mhi_chan->dir == DMA_BIDIRECTIONAL ||
mhi_chan->dir == DMA_NONE) && !mhi_chan->offload_ch) {
dev_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");
goto error_chan_cfg;
}
}
if (mhi_chan->db_cfg.brstmode == MHI_DB_BRST_ENABLE)
mhi_chan->db_cfg.process_db = mhi_db_brstmode;
else
mhi_chan->db_cfg.process_db = mhi_db_brstmode_disable;
mhi_chan->configured = true;
if (mhi_chan->lpm_notify)
list_add_tail(&mhi_chan->node, &mhi_cntrl->lpm_chans);
}
return 0;
error_chan_cfg:
vfree(mhi_cntrl->mhi_chan);
return -EINVAL;
}