18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Copyright (c) 2018-2020, The Linux Foundation. All rights reserved. 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci */ 68c2ecf20Sopenharmony_ci 78c2ecf20Sopenharmony_ci#include <linux/device.h> 88c2ecf20Sopenharmony_ci#include <linux/dma-direction.h> 98c2ecf20Sopenharmony_ci#include <linux/dma-mapping.h> 108c2ecf20Sopenharmony_ci#include <linux/interrupt.h> 118c2ecf20Sopenharmony_ci#include <linux/list.h> 128c2ecf20Sopenharmony_ci#include <linux/mhi.h> 138c2ecf20Sopenharmony_ci#include <linux/module.h> 148c2ecf20Sopenharmony_ci#include <linux/skbuff.h> 158c2ecf20Sopenharmony_ci#include <linux/slab.h> 168c2ecf20Sopenharmony_ci#include "internal.h" 178c2ecf20Sopenharmony_ci 188c2ecf20Sopenharmony_ciint __must_check mhi_read_reg(struct mhi_controller *mhi_cntrl, 198c2ecf20Sopenharmony_ci void __iomem *base, u32 offset, u32 *out) 208c2ecf20Sopenharmony_ci{ 218c2ecf20Sopenharmony_ci return mhi_cntrl->read_reg(mhi_cntrl, base + offset, out); 228c2ecf20Sopenharmony_ci} 238c2ecf20Sopenharmony_ci 248c2ecf20Sopenharmony_ciint __must_check mhi_read_reg_field(struct mhi_controller *mhi_cntrl, 258c2ecf20Sopenharmony_ci void __iomem *base, u32 offset, 268c2ecf20Sopenharmony_ci u32 mask, u32 shift, u32 *out) 278c2ecf20Sopenharmony_ci{ 288c2ecf20Sopenharmony_ci u32 tmp; 298c2ecf20Sopenharmony_ci int ret; 308c2ecf20Sopenharmony_ci 318c2ecf20Sopenharmony_ci ret = mhi_read_reg(mhi_cntrl, base, offset, &tmp); 328c2ecf20Sopenharmony_ci if (ret) 338c2ecf20Sopenharmony_ci return ret; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci *out = (tmp & mask) >> shift; 368c2ecf20Sopenharmony_ci 378c2ecf20Sopenharmony_ci return 0; 388c2ecf20Sopenharmony_ci} 398c2ecf20Sopenharmony_ci 408c2ecf20Sopenharmony_civoid mhi_write_reg(struct mhi_controller *mhi_cntrl, void __iomem *base, 418c2ecf20Sopenharmony_ci u32 offset, u32 val) 428c2ecf20Sopenharmony_ci{ 438c2ecf20Sopenharmony_ci mhi_cntrl->write_reg(mhi_cntrl, base + offset, val); 448c2ecf20Sopenharmony_ci} 458c2ecf20Sopenharmony_ci 468c2ecf20Sopenharmony_civoid mhi_write_reg_field(struct mhi_controller *mhi_cntrl, void __iomem *base, 478c2ecf20Sopenharmony_ci u32 offset, u32 mask, u32 shift, u32 val) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci int ret; 508c2ecf20Sopenharmony_ci u32 tmp; 518c2ecf20Sopenharmony_ci 528c2ecf20Sopenharmony_ci ret = mhi_read_reg(mhi_cntrl, base, offset, &tmp); 538c2ecf20Sopenharmony_ci if (ret) 548c2ecf20Sopenharmony_ci return; 558c2ecf20Sopenharmony_ci 568c2ecf20Sopenharmony_ci tmp &= ~mask; 578c2ecf20Sopenharmony_ci tmp |= (val << shift); 588c2ecf20Sopenharmony_ci mhi_write_reg(mhi_cntrl, base, offset, tmp); 598c2ecf20Sopenharmony_ci} 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_civoid mhi_write_db(struct mhi_controller *mhi_cntrl, void __iomem *db_addr, 628c2ecf20Sopenharmony_ci dma_addr_t db_val) 638c2ecf20Sopenharmony_ci{ 648c2ecf20Sopenharmony_ci mhi_write_reg(mhi_cntrl, db_addr, 4, upper_32_bits(db_val)); 658c2ecf20Sopenharmony_ci mhi_write_reg(mhi_cntrl, db_addr, 0, lower_32_bits(db_val)); 668c2ecf20Sopenharmony_ci} 678c2ecf20Sopenharmony_ci 688c2ecf20Sopenharmony_civoid mhi_db_brstmode(struct mhi_controller *mhi_cntrl, 698c2ecf20Sopenharmony_ci struct db_cfg *db_cfg, 708c2ecf20Sopenharmony_ci void __iomem *db_addr, 718c2ecf20Sopenharmony_ci dma_addr_t db_val) 728c2ecf20Sopenharmony_ci{ 738c2ecf20Sopenharmony_ci if (db_cfg->db_mode) { 748c2ecf20Sopenharmony_ci db_cfg->db_val = db_val; 758c2ecf20Sopenharmony_ci mhi_write_db(mhi_cntrl, db_addr, db_val); 768c2ecf20Sopenharmony_ci db_cfg->db_mode = 0; 778c2ecf20Sopenharmony_ci } 788c2ecf20Sopenharmony_ci} 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_civoid mhi_db_brstmode_disable(struct mhi_controller *mhi_cntrl, 818c2ecf20Sopenharmony_ci struct db_cfg *db_cfg, 828c2ecf20Sopenharmony_ci void __iomem *db_addr, 838c2ecf20Sopenharmony_ci dma_addr_t db_val) 848c2ecf20Sopenharmony_ci{ 858c2ecf20Sopenharmony_ci db_cfg->db_val = db_val; 868c2ecf20Sopenharmony_ci mhi_write_db(mhi_cntrl, db_addr, db_val); 878c2ecf20Sopenharmony_ci} 888c2ecf20Sopenharmony_ci 898c2ecf20Sopenharmony_civoid mhi_ring_er_db(struct mhi_event *mhi_event) 908c2ecf20Sopenharmony_ci{ 918c2ecf20Sopenharmony_ci struct mhi_ring *ring = &mhi_event->ring; 928c2ecf20Sopenharmony_ci 938c2ecf20Sopenharmony_ci mhi_event->db_cfg.process_db(mhi_event->mhi_cntrl, &mhi_event->db_cfg, 948c2ecf20Sopenharmony_ci ring->db_addr, *ring->ctxt_wp); 958c2ecf20Sopenharmony_ci} 968c2ecf20Sopenharmony_ci 978c2ecf20Sopenharmony_civoid mhi_ring_cmd_db(struct mhi_controller *mhi_cntrl, struct mhi_cmd *mhi_cmd) 988c2ecf20Sopenharmony_ci{ 998c2ecf20Sopenharmony_ci dma_addr_t db; 1008c2ecf20Sopenharmony_ci struct mhi_ring *ring = &mhi_cmd->ring; 1018c2ecf20Sopenharmony_ci 1028c2ecf20Sopenharmony_ci db = ring->iommu_base + (ring->wp - ring->base); 1038c2ecf20Sopenharmony_ci *ring->ctxt_wp = db; 1048c2ecf20Sopenharmony_ci mhi_write_db(mhi_cntrl, ring->db_addr, db); 1058c2ecf20Sopenharmony_ci} 1068c2ecf20Sopenharmony_ci 1078c2ecf20Sopenharmony_civoid mhi_ring_chan_db(struct mhi_controller *mhi_cntrl, 1088c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan) 1098c2ecf20Sopenharmony_ci{ 1108c2ecf20Sopenharmony_ci struct mhi_ring *ring = &mhi_chan->tre_ring; 1118c2ecf20Sopenharmony_ci dma_addr_t db; 1128c2ecf20Sopenharmony_ci 1138c2ecf20Sopenharmony_ci db = ring->iommu_base + (ring->wp - ring->base); 1148c2ecf20Sopenharmony_ci *ring->ctxt_wp = db; 1158c2ecf20Sopenharmony_ci mhi_chan->db_cfg.process_db(mhi_cntrl, &mhi_chan->db_cfg, 1168c2ecf20Sopenharmony_ci ring->db_addr, db); 1178c2ecf20Sopenharmony_ci} 1188c2ecf20Sopenharmony_ci 1198c2ecf20Sopenharmony_cienum mhi_ee_type mhi_get_exec_env(struct mhi_controller *mhi_cntrl) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci u32 exec; 1228c2ecf20Sopenharmony_ci int ret = mhi_read_reg(mhi_cntrl, mhi_cntrl->bhi, BHI_EXECENV, &exec); 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci return (ret) ? MHI_EE_MAX : exec; 1258c2ecf20Sopenharmony_ci} 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_cienum mhi_state mhi_get_mhi_state(struct mhi_controller *mhi_cntrl) 1288c2ecf20Sopenharmony_ci{ 1298c2ecf20Sopenharmony_ci u32 state; 1308c2ecf20Sopenharmony_ci int ret = mhi_read_reg_field(mhi_cntrl, mhi_cntrl->regs, MHISTATUS, 1318c2ecf20Sopenharmony_ci MHISTATUS_MHISTATE_MASK, 1328c2ecf20Sopenharmony_ci MHISTATUS_MHISTATE_SHIFT, &state); 1338c2ecf20Sopenharmony_ci return ret ? MHI_STATE_MAX : state; 1348c2ecf20Sopenharmony_ci} 1358c2ecf20Sopenharmony_ci 1368c2ecf20Sopenharmony_ciint mhi_map_single_no_bb(struct mhi_controller *mhi_cntrl, 1378c2ecf20Sopenharmony_ci struct mhi_buf_info *buf_info) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci buf_info->p_addr = dma_map_single(mhi_cntrl->cntrl_dev, 1408c2ecf20Sopenharmony_ci buf_info->v_addr, buf_info->len, 1418c2ecf20Sopenharmony_ci buf_info->dir); 1428c2ecf20Sopenharmony_ci if (dma_mapping_error(mhi_cntrl->cntrl_dev, buf_info->p_addr)) 1438c2ecf20Sopenharmony_ci return -ENOMEM; 1448c2ecf20Sopenharmony_ci 1458c2ecf20Sopenharmony_ci return 0; 1468c2ecf20Sopenharmony_ci} 1478c2ecf20Sopenharmony_ci 1488c2ecf20Sopenharmony_ciint mhi_map_single_use_bb(struct mhi_controller *mhi_cntrl, 1498c2ecf20Sopenharmony_ci struct mhi_buf_info *buf_info) 1508c2ecf20Sopenharmony_ci{ 1518c2ecf20Sopenharmony_ci void *buf = mhi_alloc_coherent(mhi_cntrl, buf_info->len, 1528c2ecf20Sopenharmony_ci &buf_info->p_addr, GFP_ATOMIC); 1538c2ecf20Sopenharmony_ci 1548c2ecf20Sopenharmony_ci if (!buf) 1558c2ecf20Sopenharmony_ci return -ENOMEM; 1568c2ecf20Sopenharmony_ci 1578c2ecf20Sopenharmony_ci if (buf_info->dir == DMA_TO_DEVICE) 1588c2ecf20Sopenharmony_ci memcpy(buf, buf_info->v_addr, buf_info->len); 1598c2ecf20Sopenharmony_ci 1608c2ecf20Sopenharmony_ci buf_info->bb_addr = buf; 1618c2ecf20Sopenharmony_ci 1628c2ecf20Sopenharmony_ci return 0; 1638c2ecf20Sopenharmony_ci} 1648c2ecf20Sopenharmony_ci 1658c2ecf20Sopenharmony_civoid mhi_unmap_single_no_bb(struct mhi_controller *mhi_cntrl, 1668c2ecf20Sopenharmony_ci struct mhi_buf_info *buf_info) 1678c2ecf20Sopenharmony_ci{ 1688c2ecf20Sopenharmony_ci dma_unmap_single(mhi_cntrl->cntrl_dev, buf_info->p_addr, buf_info->len, 1698c2ecf20Sopenharmony_ci buf_info->dir); 1708c2ecf20Sopenharmony_ci} 1718c2ecf20Sopenharmony_ci 1728c2ecf20Sopenharmony_civoid mhi_unmap_single_use_bb(struct mhi_controller *mhi_cntrl, 1738c2ecf20Sopenharmony_ci struct mhi_buf_info *buf_info) 1748c2ecf20Sopenharmony_ci{ 1758c2ecf20Sopenharmony_ci if (buf_info->dir == DMA_FROM_DEVICE) 1768c2ecf20Sopenharmony_ci memcpy(buf_info->v_addr, buf_info->bb_addr, buf_info->len); 1778c2ecf20Sopenharmony_ci 1788c2ecf20Sopenharmony_ci mhi_free_coherent(mhi_cntrl, buf_info->len, buf_info->bb_addr, 1798c2ecf20Sopenharmony_ci buf_info->p_addr); 1808c2ecf20Sopenharmony_ci} 1818c2ecf20Sopenharmony_ci 1828c2ecf20Sopenharmony_cistatic int get_nr_avail_ring_elements(struct mhi_controller *mhi_cntrl, 1838c2ecf20Sopenharmony_ci struct mhi_ring *ring) 1848c2ecf20Sopenharmony_ci{ 1858c2ecf20Sopenharmony_ci int nr_el; 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci if (ring->wp < ring->rp) { 1888c2ecf20Sopenharmony_ci nr_el = ((ring->rp - ring->wp) / ring->el_size) - 1; 1898c2ecf20Sopenharmony_ci } else { 1908c2ecf20Sopenharmony_ci nr_el = (ring->rp - ring->base) / ring->el_size; 1918c2ecf20Sopenharmony_ci nr_el += ((ring->base + ring->len - ring->wp) / 1928c2ecf20Sopenharmony_ci ring->el_size) - 1; 1938c2ecf20Sopenharmony_ci } 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return nr_el; 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_cistatic void *mhi_to_virtual(struct mhi_ring *ring, dma_addr_t addr) 1998c2ecf20Sopenharmony_ci{ 2008c2ecf20Sopenharmony_ci return (addr - ring->iommu_base) + ring->base; 2018c2ecf20Sopenharmony_ci} 2028c2ecf20Sopenharmony_ci 2038c2ecf20Sopenharmony_cistatic void mhi_add_ring_element(struct mhi_controller *mhi_cntrl, 2048c2ecf20Sopenharmony_ci struct mhi_ring *ring) 2058c2ecf20Sopenharmony_ci{ 2068c2ecf20Sopenharmony_ci ring->wp += ring->el_size; 2078c2ecf20Sopenharmony_ci if (ring->wp >= (ring->base + ring->len)) 2088c2ecf20Sopenharmony_ci ring->wp = ring->base; 2098c2ecf20Sopenharmony_ci /* smp update */ 2108c2ecf20Sopenharmony_ci smp_wmb(); 2118c2ecf20Sopenharmony_ci} 2128c2ecf20Sopenharmony_ci 2138c2ecf20Sopenharmony_cistatic void mhi_del_ring_element(struct mhi_controller *mhi_cntrl, 2148c2ecf20Sopenharmony_ci struct mhi_ring *ring) 2158c2ecf20Sopenharmony_ci{ 2168c2ecf20Sopenharmony_ci ring->rp += ring->el_size; 2178c2ecf20Sopenharmony_ci if (ring->rp >= (ring->base + ring->len)) 2188c2ecf20Sopenharmony_ci ring->rp = ring->base; 2198c2ecf20Sopenharmony_ci /* smp update */ 2208c2ecf20Sopenharmony_ci smp_wmb(); 2218c2ecf20Sopenharmony_ci} 2228c2ecf20Sopenharmony_ci 2238c2ecf20Sopenharmony_cistatic bool is_valid_ring_ptr(struct mhi_ring *ring, dma_addr_t addr) 2248c2ecf20Sopenharmony_ci{ 2258c2ecf20Sopenharmony_ci return addr >= ring->iommu_base && addr < ring->iommu_base + ring->len; 2268c2ecf20Sopenharmony_ci} 2278c2ecf20Sopenharmony_ci 2288c2ecf20Sopenharmony_ciint mhi_destroy_device(struct device *dev, void *data) 2298c2ecf20Sopenharmony_ci{ 2308c2ecf20Sopenharmony_ci struct mhi_chan *ul_chan, *dl_chan; 2318c2ecf20Sopenharmony_ci struct mhi_device *mhi_dev; 2328c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl; 2338c2ecf20Sopenharmony_ci enum mhi_ee_type ee = MHI_EE_MAX; 2348c2ecf20Sopenharmony_ci 2358c2ecf20Sopenharmony_ci if (dev->bus != &mhi_bus_type) 2368c2ecf20Sopenharmony_ci return 0; 2378c2ecf20Sopenharmony_ci 2388c2ecf20Sopenharmony_ci mhi_dev = to_mhi_device(dev); 2398c2ecf20Sopenharmony_ci mhi_cntrl = mhi_dev->mhi_cntrl; 2408c2ecf20Sopenharmony_ci 2418c2ecf20Sopenharmony_ci /* Only destroy virtual devices thats attached to bus */ 2428c2ecf20Sopenharmony_ci if (mhi_dev->dev_type == MHI_DEVICE_CONTROLLER) 2438c2ecf20Sopenharmony_ci return 0; 2448c2ecf20Sopenharmony_ci 2458c2ecf20Sopenharmony_ci ul_chan = mhi_dev->ul_chan; 2468c2ecf20Sopenharmony_ci dl_chan = mhi_dev->dl_chan; 2478c2ecf20Sopenharmony_ci 2488c2ecf20Sopenharmony_ci /* 2498c2ecf20Sopenharmony_ci * If execution environment is specified, remove only those devices that 2508c2ecf20Sopenharmony_ci * started in them based on ee_mask for the channels as we move on to a 2518c2ecf20Sopenharmony_ci * different execution environment 2528c2ecf20Sopenharmony_ci */ 2538c2ecf20Sopenharmony_ci if (data) 2548c2ecf20Sopenharmony_ci ee = *(enum mhi_ee_type *)data; 2558c2ecf20Sopenharmony_ci 2568c2ecf20Sopenharmony_ci /* 2578c2ecf20Sopenharmony_ci * For the suspend and resume case, this function will get called 2588c2ecf20Sopenharmony_ci * without mhi_unregister_controller(). Hence, we need to drop the 2598c2ecf20Sopenharmony_ci * references to mhi_dev created for ul and dl channels. We can 2608c2ecf20Sopenharmony_ci * be sure that there will be no instances of mhi_dev left after 2618c2ecf20Sopenharmony_ci * this. 2628c2ecf20Sopenharmony_ci */ 2638c2ecf20Sopenharmony_ci if (ul_chan) { 2648c2ecf20Sopenharmony_ci if (ee != MHI_EE_MAX && !(ul_chan->ee_mask & BIT(ee))) 2658c2ecf20Sopenharmony_ci return 0; 2668c2ecf20Sopenharmony_ci 2678c2ecf20Sopenharmony_ci put_device(&ul_chan->mhi_dev->dev); 2688c2ecf20Sopenharmony_ci } 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci if (dl_chan) { 2718c2ecf20Sopenharmony_ci if (ee != MHI_EE_MAX && !(dl_chan->ee_mask & BIT(ee))) 2728c2ecf20Sopenharmony_ci return 0; 2738c2ecf20Sopenharmony_ci 2748c2ecf20Sopenharmony_ci put_device(&dl_chan->mhi_dev->dev); 2758c2ecf20Sopenharmony_ci } 2768c2ecf20Sopenharmony_ci 2778c2ecf20Sopenharmony_ci dev_dbg(&mhi_cntrl->mhi_dev->dev, "destroy device for chan:%s\n", 2788c2ecf20Sopenharmony_ci mhi_dev->name); 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci /* Notify the client and remove the device from MHI bus */ 2818c2ecf20Sopenharmony_ci device_del(dev); 2828c2ecf20Sopenharmony_ci put_device(dev); 2838c2ecf20Sopenharmony_ci 2848c2ecf20Sopenharmony_ci return 0; 2858c2ecf20Sopenharmony_ci} 2868c2ecf20Sopenharmony_ci 2878c2ecf20Sopenharmony_civoid mhi_notify(struct mhi_device *mhi_dev, enum mhi_callback cb_reason) 2888c2ecf20Sopenharmony_ci{ 2898c2ecf20Sopenharmony_ci struct mhi_driver *mhi_drv; 2908c2ecf20Sopenharmony_ci 2918c2ecf20Sopenharmony_ci if (!mhi_dev->dev.driver) 2928c2ecf20Sopenharmony_ci return; 2938c2ecf20Sopenharmony_ci 2948c2ecf20Sopenharmony_ci mhi_drv = to_mhi_driver(mhi_dev->dev.driver); 2958c2ecf20Sopenharmony_ci 2968c2ecf20Sopenharmony_ci if (mhi_drv->status_cb) 2978c2ecf20Sopenharmony_ci mhi_drv->status_cb(mhi_dev, cb_reason); 2988c2ecf20Sopenharmony_ci} 2998c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_notify); 3008c2ecf20Sopenharmony_ci 3018c2ecf20Sopenharmony_ci/* Bind MHI channels to MHI devices */ 3028c2ecf20Sopenharmony_civoid mhi_create_devices(struct mhi_controller *mhi_cntrl) 3038c2ecf20Sopenharmony_ci{ 3048c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan; 3058c2ecf20Sopenharmony_ci struct mhi_device *mhi_dev; 3068c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 3078c2ecf20Sopenharmony_ci int i, ret; 3088c2ecf20Sopenharmony_ci 3098c2ecf20Sopenharmony_ci mhi_chan = mhi_cntrl->mhi_chan; 3108c2ecf20Sopenharmony_ci for (i = 0; i < mhi_cntrl->max_chan; i++, mhi_chan++) { 3118c2ecf20Sopenharmony_ci if (!mhi_chan->configured || mhi_chan->mhi_dev || 3128c2ecf20Sopenharmony_ci !(mhi_chan->ee_mask & BIT(mhi_cntrl->ee))) 3138c2ecf20Sopenharmony_ci continue; 3148c2ecf20Sopenharmony_ci mhi_dev = mhi_alloc_device(mhi_cntrl); 3158c2ecf20Sopenharmony_ci if (IS_ERR(mhi_dev)) 3168c2ecf20Sopenharmony_ci return; 3178c2ecf20Sopenharmony_ci 3188c2ecf20Sopenharmony_ci mhi_dev->dev_type = MHI_DEVICE_XFER; 3198c2ecf20Sopenharmony_ci switch (mhi_chan->dir) { 3208c2ecf20Sopenharmony_ci case DMA_TO_DEVICE: 3218c2ecf20Sopenharmony_ci mhi_dev->ul_chan = mhi_chan; 3228c2ecf20Sopenharmony_ci mhi_dev->ul_chan_id = mhi_chan->chan; 3238c2ecf20Sopenharmony_ci break; 3248c2ecf20Sopenharmony_ci case DMA_FROM_DEVICE: 3258c2ecf20Sopenharmony_ci /* We use dl_chan as offload channels */ 3268c2ecf20Sopenharmony_ci mhi_dev->dl_chan = mhi_chan; 3278c2ecf20Sopenharmony_ci mhi_dev->dl_chan_id = mhi_chan->chan; 3288c2ecf20Sopenharmony_ci break; 3298c2ecf20Sopenharmony_ci default: 3308c2ecf20Sopenharmony_ci dev_err(dev, "Direction not supported\n"); 3318c2ecf20Sopenharmony_ci put_device(&mhi_dev->dev); 3328c2ecf20Sopenharmony_ci return; 3338c2ecf20Sopenharmony_ci } 3348c2ecf20Sopenharmony_ci 3358c2ecf20Sopenharmony_ci get_device(&mhi_dev->dev); 3368c2ecf20Sopenharmony_ci mhi_chan->mhi_dev = mhi_dev; 3378c2ecf20Sopenharmony_ci 3388c2ecf20Sopenharmony_ci /* Check next channel if it matches */ 3398c2ecf20Sopenharmony_ci if ((i + 1) < mhi_cntrl->max_chan && mhi_chan[1].configured) { 3408c2ecf20Sopenharmony_ci if (!strcmp(mhi_chan[1].name, mhi_chan->name)) { 3418c2ecf20Sopenharmony_ci i++; 3428c2ecf20Sopenharmony_ci mhi_chan++; 3438c2ecf20Sopenharmony_ci if (mhi_chan->dir == DMA_TO_DEVICE) { 3448c2ecf20Sopenharmony_ci mhi_dev->ul_chan = mhi_chan; 3458c2ecf20Sopenharmony_ci mhi_dev->ul_chan_id = mhi_chan->chan; 3468c2ecf20Sopenharmony_ci } else { 3478c2ecf20Sopenharmony_ci mhi_dev->dl_chan = mhi_chan; 3488c2ecf20Sopenharmony_ci mhi_dev->dl_chan_id = mhi_chan->chan; 3498c2ecf20Sopenharmony_ci } 3508c2ecf20Sopenharmony_ci get_device(&mhi_dev->dev); 3518c2ecf20Sopenharmony_ci mhi_chan->mhi_dev = mhi_dev; 3528c2ecf20Sopenharmony_ci } 3538c2ecf20Sopenharmony_ci } 3548c2ecf20Sopenharmony_ci 3558c2ecf20Sopenharmony_ci /* Channel name is same for both UL and DL */ 3568c2ecf20Sopenharmony_ci mhi_dev->name = mhi_chan->name; 3578c2ecf20Sopenharmony_ci dev_set_name(&mhi_dev->dev, "%s_%s", 3588c2ecf20Sopenharmony_ci dev_name(mhi_cntrl->cntrl_dev), 3598c2ecf20Sopenharmony_ci mhi_dev->name); 3608c2ecf20Sopenharmony_ci 3618c2ecf20Sopenharmony_ci /* Init wakeup source if available */ 3628c2ecf20Sopenharmony_ci if (mhi_dev->dl_chan && mhi_dev->dl_chan->wake_capable) 3638c2ecf20Sopenharmony_ci device_init_wakeup(&mhi_dev->dev, true); 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ci ret = device_add(&mhi_dev->dev); 3668c2ecf20Sopenharmony_ci if (ret) 3678c2ecf20Sopenharmony_ci put_device(&mhi_dev->dev); 3688c2ecf20Sopenharmony_ci } 3698c2ecf20Sopenharmony_ci} 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ciirqreturn_t mhi_irq_handler(int irq_number, void *dev) 3728c2ecf20Sopenharmony_ci{ 3738c2ecf20Sopenharmony_ci struct mhi_event *mhi_event = dev; 3748c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl; 3758c2ecf20Sopenharmony_ci struct mhi_event_ctxt *er_ctxt = 3768c2ecf20Sopenharmony_ci &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; 3778c2ecf20Sopenharmony_ci struct mhi_ring *ev_ring = &mhi_event->ring; 3788c2ecf20Sopenharmony_ci dma_addr_t ptr = er_ctxt->rp; 3798c2ecf20Sopenharmony_ci void *dev_rp; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci if (!is_valid_ring_ptr(ev_ring, ptr)) { 3828c2ecf20Sopenharmony_ci dev_err(&mhi_cntrl->mhi_dev->dev, 3838c2ecf20Sopenharmony_ci "Event ring rp points outside of the event ring\n"); 3848c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3858c2ecf20Sopenharmony_ci } 3868c2ecf20Sopenharmony_ci 3878c2ecf20Sopenharmony_ci dev_rp = mhi_to_virtual(ev_ring, ptr); 3888c2ecf20Sopenharmony_ci 3898c2ecf20Sopenharmony_ci /* Only proceed if event ring has pending events */ 3908c2ecf20Sopenharmony_ci if (ev_ring->rp == dev_rp) 3918c2ecf20Sopenharmony_ci return IRQ_HANDLED; 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci /* For client managed event ring, notify pending data */ 3948c2ecf20Sopenharmony_ci if (mhi_event->cl_manage) { 3958c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan = mhi_event->mhi_chan; 3968c2ecf20Sopenharmony_ci struct mhi_device *mhi_dev = mhi_chan->mhi_dev; 3978c2ecf20Sopenharmony_ci 3988c2ecf20Sopenharmony_ci if (mhi_dev) 3998c2ecf20Sopenharmony_ci mhi_notify(mhi_dev, MHI_CB_PENDING_DATA); 4008c2ecf20Sopenharmony_ci } else { 4018c2ecf20Sopenharmony_ci tasklet_schedule(&mhi_event->task); 4028c2ecf20Sopenharmony_ci } 4038c2ecf20Sopenharmony_ci 4048c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4058c2ecf20Sopenharmony_ci} 4068c2ecf20Sopenharmony_ci 4078c2ecf20Sopenharmony_ciirqreturn_t mhi_intvec_threaded_handler(int irq_number, void *priv) 4088c2ecf20Sopenharmony_ci{ 4098c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = priv; 4108c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 4118c2ecf20Sopenharmony_ci enum mhi_state state = MHI_STATE_MAX; 4128c2ecf20Sopenharmony_ci enum mhi_pm_state pm_state = 0; 4138c2ecf20Sopenharmony_ci enum mhi_ee_type ee = 0; 4148c2ecf20Sopenharmony_ci 4158c2ecf20Sopenharmony_ci write_lock_irq(&mhi_cntrl->pm_lock); 4168c2ecf20Sopenharmony_ci if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { 4178c2ecf20Sopenharmony_ci write_unlock_irq(&mhi_cntrl->pm_lock); 4188c2ecf20Sopenharmony_ci goto exit_intvec; 4198c2ecf20Sopenharmony_ci } 4208c2ecf20Sopenharmony_ci 4218c2ecf20Sopenharmony_ci state = mhi_get_mhi_state(mhi_cntrl); 4228c2ecf20Sopenharmony_ci ee = mhi_cntrl->ee; 4238c2ecf20Sopenharmony_ci mhi_cntrl->ee = mhi_get_exec_env(mhi_cntrl); 4248c2ecf20Sopenharmony_ci dev_dbg(dev, "local ee:%s device ee:%s dev_state:%s\n", 4258c2ecf20Sopenharmony_ci TO_MHI_EXEC_STR(mhi_cntrl->ee), TO_MHI_EXEC_STR(ee), 4268c2ecf20Sopenharmony_ci TO_MHI_STATE_STR(state)); 4278c2ecf20Sopenharmony_ci 4288c2ecf20Sopenharmony_ci if (state == MHI_STATE_SYS_ERR) { 4298c2ecf20Sopenharmony_ci dev_dbg(dev, "System error detected\n"); 4308c2ecf20Sopenharmony_ci pm_state = mhi_tryset_pm_state(mhi_cntrl, 4318c2ecf20Sopenharmony_ci MHI_PM_SYS_ERR_DETECT); 4328c2ecf20Sopenharmony_ci } 4338c2ecf20Sopenharmony_ci write_unlock_irq(&mhi_cntrl->pm_lock); 4348c2ecf20Sopenharmony_ci 4358c2ecf20Sopenharmony_ci /* If device supports RDDM don't bother processing SYS error */ 4368c2ecf20Sopenharmony_ci if (mhi_cntrl->rddm_image) { 4378c2ecf20Sopenharmony_ci if (mhi_cntrl->ee == MHI_EE_RDDM && mhi_cntrl->ee != ee) { 4388c2ecf20Sopenharmony_ci mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM); 4398c2ecf20Sopenharmony_ci wake_up_all(&mhi_cntrl->state_event); 4408c2ecf20Sopenharmony_ci } 4418c2ecf20Sopenharmony_ci goto exit_intvec; 4428c2ecf20Sopenharmony_ci } 4438c2ecf20Sopenharmony_ci 4448c2ecf20Sopenharmony_ci if (pm_state == MHI_PM_SYS_ERR_DETECT) { 4458c2ecf20Sopenharmony_ci wake_up_all(&mhi_cntrl->state_event); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci /* For fatal errors, we let controller decide next step */ 4488c2ecf20Sopenharmony_ci if (MHI_IN_PBL(ee)) 4498c2ecf20Sopenharmony_ci mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_FATAL_ERROR); 4508c2ecf20Sopenharmony_ci else 4518c2ecf20Sopenharmony_ci mhi_pm_sys_err_handler(mhi_cntrl); 4528c2ecf20Sopenharmony_ci } 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ciexit_intvec: 4558c2ecf20Sopenharmony_ci 4568c2ecf20Sopenharmony_ci return IRQ_HANDLED; 4578c2ecf20Sopenharmony_ci} 4588c2ecf20Sopenharmony_ci 4598c2ecf20Sopenharmony_ciirqreturn_t mhi_intvec_handler(int irq_number, void *dev) 4608c2ecf20Sopenharmony_ci{ 4618c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = dev; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci /* Wake up events waiting for state change */ 4648c2ecf20Sopenharmony_ci wake_up_all(&mhi_cntrl->state_event); 4658c2ecf20Sopenharmony_ci 4668c2ecf20Sopenharmony_ci return IRQ_WAKE_THREAD; 4678c2ecf20Sopenharmony_ci} 4688c2ecf20Sopenharmony_ci 4698c2ecf20Sopenharmony_cistatic void mhi_recycle_ev_ring_element(struct mhi_controller *mhi_cntrl, 4708c2ecf20Sopenharmony_ci struct mhi_ring *ring) 4718c2ecf20Sopenharmony_ci{ 4728c2ecf20Sopenharmony_ci dma_addr_t ctxt_wp; 4738c2ecf20Sopenharmony_ci 4748c2ecf20Sopenharmony_ci /* Update the WP */ 4758c2ecf20Sopenharmony_ci ring->wp += ring->el_size; 4768c2ecf20Sopenharmony_ci ctxt_wp = *ring->ctxt_wp + ring->el_size; 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci if (ring->wp >= (ring->base + ring->len)) { 4798c2ecf20Sopenharmony_ci ring->wp = ring->base; 4808c2ecf20Sopenharmony_ci ctxt_wp = ring->iommu_base; 4818c2ecf20Sopenharmony_ci } 4828c2ecf20Sopenharmony_ci 4838c2ecf20Sopenharmony_ci *ring->ctxt_wp = ctxt_wp; 4848c2ecf20Sopenharmony_ci 4858c2ecf20Sopenharmony_ci /* Update the RP */ 4868c2ecf20Sopenharmony_ci ring->rp += ring->el_size; 4878c2ecf20Sopenharmony_ci if (ring->rp >= (ring->base + ring->len)) 4888c2ecf20Sopenharmony_ci ring->rp = ring->base; 4898c2ecf20Sopenharmony_ci 4908c2ecf20Sopenharmony_ci /* Update to all cores */ 4918c2ecf20Sopenharmony_ci smp_wmb(); 4928c2ecf20Sopenharmony_ci} 4938c2ecf20Sopenharmony_ci 4948c2ecf20Sopenharmony_cistatic int parse_xfer_event(struct mhi_controller *mhi_cntrl, 4958c2ecf20Sopenharmony_ci struct mhi_tre *event, 4968c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan) 4978c2ecf20Sopenharmony_ci{ 4988c2ecf20Sopenharmony_ci struct mhi_ring *buf_ring, *tre_ring; 4998c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 5008c2ecf20Sopenharmony_ci struct mhi_result result; 5018c2ecf20Sopenharmony_ci unsigned long flags = 0; 5028c2ecf20Sopenharmony_ci u32 ev_code; 5038c2ecf20Sopenharmony_ci 5048c2ecf20Sopenharmony_ci ev_code = MHI_TRE_GET_EV_CODE(event); 5058c2ecf20Sopenharmony_ci buf_ring = &mhi_chan->buf_ring; 5068c2ecf20Sopenharmony_ci tre_ring = &mhi_chan->tre_ring; 5078c2ecf20Sopenharmony_ci 5088c2ecf20Sopenharmony_ci result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ? 5098c2ecf20Sopenharmony_ci -EOVERFLOW : 0; 5108c2ecf20Sopenharmony_ci 5118c2ecf20Sopenharmony_ci /* 5128c2ecf20Sopenharmony_ci * If it's a DB Event then we need to grab the lock 5138c2ecf20Sopenharmony_ci * with preemption disabled and as a write because we 5148c2ecf20Sopenharmony_ci * have to update db register and there are chances that 5158c2ecf20Sopenharmony_ci * another thread could be doing the same. 5168c2ecf20Sopenharmony_ci */ 5178c2ecf20Sopenharmony_ci if (ev_code >= MHI_EV_CC_OOB) 5188c2ecf20Sopenharmony_ci write_lock_irqsave(&mhi_chan->lock, flags); 5198c2ecf20Sopenharmony_ci else 5208c2ecf20Sopenharmony_ci read_lock_bh(&mhi_chan->lock); 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) 5238c2ecf20Sopenharmony_ci goto end_process_tx_event; 5248c2ecf20Sopenharmony_ci 5258c2ecf20Sopenharmony_ci switch (ev_code) { 5268c2ecf20Sopenharmony_ci case MHI_EV_CC_OVERFLOW: 5278c2ecf20Sopenharmony_ci case MHI_EV_CC_EOB: 5288c2ecf20Sopenharmony_ci case MHI_EV_CC_EOT: 5298c2ecf20Sopenharmony_ci { 5308c2ecf20Sopenharmony_ci dma_addr_t ptr = MHI_TRE_GET_EV_PTR(event); 5318c2ecf20Sopenharmony_ci struct mhi_tre *local_rp, *ev_tre; 5328c2ecf20Sopenharmony_ci void *dev_rp; 5338c2ecf20Sopenharmony_ci struct mhi_buf_info *buf_info; 5348c2ecf20Sopenharmony_ci u16 xfer_len; 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci if (!is_valid_ring_ptr(tre_ring, ptr)) { 5378c2ecf20Sopenharmony_ci dev_err(&mhi_cntrl->mhi_dev->dev, 5388c2ecf20Sopenharmony_ci "Event element points outside of the tre ring\n"); 5398c2ecf20Sopenharmony_ci break; 5408c2ecf20Sopenharmony_ci } 5418c2ecf20Sopenharmony_ci /* Get the TRB this event points to */ 5428c2ecf20Sopenharmony_ci ev_tre = mhi_to_virtual(tre_ring, ptr); 5438c2ecf20Sopenharmony_ci 5448c2ecf20Sopenharmony_ci dev_rp = ev_tre + 1; 5458c2ecf20Sopenharmony_ci if (dev_rp >= (tre_ring->base + tre_ring->len)) 5468c2ecf20Sopenharmony_ci dev_rp = tre_ring->base; 5478c2ecf20Sopenharmony_ci 5488c2ecf20Sopenharmony_ci result.dir = mhi_chan->dir; 5498c2ecf20Sopenharmony_ci 5508c2ecf20Sopenharmony_ci local_rp = tre_ring->rp; 5518c2ecf20Sopenharmony_ci while (local_rp != dev_rp) { 5528c2ecf20Sopenharmony_ci buf_info = buf_ring->rp; 5538c2ecf20Sopenharmony_ci /* If it's the last TRE, get length from the event */ 5548c2ecf20Sopenharmony_ci if (local_rp == ev_tre) 5558c2ecf20Sopenharmony_ci xfer_len = MHI_TRE_GET_EV_LEN(event); 5568c2ecf20Sopenharmony_ci else 5578c2ecf20Sopenharmony_ci xfer_len = buf_info->len; 5588c2ecf20Sopenharmony_ci 5598c2ecf20Sopenharmony_ci /* Unmap if it's not pre-mapped by client */ 5608c2ecf20Sopenharmony_ci if (likely(!buf_info->pre_mapped)) 5618c2ecf20Sopenharmony_ci mhi_cntrl->unmap_single(mhi_cntrl, buf_info); 5628c2ecf20Sopenharmony_ci 5638c2ecf20Sopenharmony_ci result.buf_addr = buf_info->cb_buf; 5648c2ecf20Sopenharmony_ci 5658c2ecf20Sopenharmony_ci /* truncate to buf len if xfer_len is larger */ 5668c2ecf20Sopenharmony_ci result.bytes_xferd = 5678c2ecf20Sopenharmony_ci min_t(u16, xfer_len, buf_info->len); 5688c2ecf20Sopenharmony_ci mhi_del_ring_element(mhi_cntrl, buf_ring); 5698c2ecf20Sopenharmony_ci mhi_del_ring_element(mhi_cntrl, tre_ring); 5708c2ecf20Sopenharmony_ci local_rp = tre_ring->rp; 5718c2ecf20Sopenharmony_ci 5728c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_chan->lock); 5738c2ecf20Sopenharmony_ci 5748c2ecf20Sopenharmony_ci /* notify client */ 5758c2ecf20Sopenharmony_ci mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); 5768c2ecf20Sopenharmony_ci 5778c2ecf20Sopenharmony_ci if (mhi_chan->dir == DMA_TO_DEVICE) 5788c2ecf20Sopenharmony_ci atomic_dec(&mhi_cntrl->pending_pkts); 5798c2ecf20Sopenharmony_ci 5808c2ecf20Sopenharmony_ci /* 5818c2ecf20Sopenharmony_ci * Recycle the buffer if buffer is pre-allocated, 5828c2ecf20Sopenharmony_ci * if there is an error, not much we can do apart 5838c2ecf20Sopenharmony_ci * from dropping the packet 5848c2ecf20Sopenharmony_ci */ 5858c2ecf20Sopenharmony_ci if (mhi_chan->pre_alloc) { 5868c2ecf20Sopenharmony_ci if (mhi_queue_buf(mhi_chan->mhi_dev, 5878c2ecf20Sopenharmony_ci mhi_chan->dir, 5888c2ecf20Sopenharmony_ci buf_info->cb_buf, 5898c2ecf20Sopenharmony_ci buf_info->len, MHI_EOT)) { 5908c2ecf20Sopenharmony_ci dev_err(dev, 5918c2ecf20Sopenharmony_ci "Error recycling buffer for chan:%d\n", 5928c2ecf20Sopenharmony_ci mhi_chan->chan); 5938c2ecf20Sopenharmony_ci kfree(buf_info->cb_buf); 5948c2ecf20Sopenharmony_ci } 5958c2ecf20Sopenharmony_ci } 5968c2ecf20Sopenharmony_ci 5978c2ecf20Sopenharmony_ci read_lock_bh(&mhi_chan->lock); 5988c2ecf20Sopenharmony_ci } 5998c2ecf20Sopenharmony_ci break; 6008c2ecf20Sopenharmony_ci } /* CC_EOT */ 6018c2ecf20Sopenharmony_ci case MHI_EV_CC_OOB: 6028c2ecf20Sopenharmony_ci case MHI_EV_CC_DB_MODE: 6038c2ecf20Sopenharmony_ci { 6048c2ecf20Sopenharmony_ci unsigned long flags; 6058c2ecf20Sopenharmony_ci 6068c2ecf20Sopenharmony_ci mhi_chan->db_cfg.db_mode = 1; 6078c2ecf20Sopenharmony_ci read_lock_irqsave(&mhi_cntrl->pm_lock, flags); 6088c2ecf20Sopenharmony_ci if (tre_ring->wp != tre_ring->rp && 6098c2ecf20Sopenharmony_ci MHI_DB_ACCESS_VALID(mhi_cntrl)) { 6108c2ecf20Sopenharmony_ci mhi_ring_chan_db(mhi_cntrl, mhi_chan); 6118c2ecf20Sopenharmony_ci } 6128c2ecf20Sopenharmony_ci read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags); 6138c2ecf20Sopenharmony_ci break; 6148c2ecf20Sopenharmony_ci } 6158c2ecf20Sopenharmony_ci case MHI_EV_CC_BAD_TRE: 6168c2ecf20Sopenharmony_ci default: 6178c2ecf20Sopenharmony_ci dev_err(dev, "Unknown event 0x%x\n", ev_code); 6188c2ecf20Sopenharmony_ci break; 6198c2ecf20Sopenharmony_ci } /* switch(MHI_EV_READ_CODE(EV_TRB_CODE,event)) */ 6208c2ecf20Sopenharmony_ci 6218c2ecf20Sopenharmony_ciend_process_tx_event: 6228c2ecf20Sopenharmony_ci if (ev_code >= MHI_EV_CC_OOB) 6238c2ecf20Sopenharmony_ci write_unlock_irqrestore(&mhi_chan->lock, flags); 6248c2ecf20Sopenharmony_ci else 6258c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_chan->lock); 6268c2ecf20Sopenharmony_ci 6278c2ecf20Sopenharmony_ci return 0; 6288c2ecf20Sopenharmony_ci} 6298c2ecf20Sopenharmony_ci 6308c2ecf20Sopenharmony_cistatic int parse_rsc_event(struct mhi_controller *mhi_cntrl, 6318c2ecf20Sopenharmony_ci struct mhi_tre *event, 6328c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan) 6338c2ecf20Sopenharmony_ci{ 6348c2ecf20Sopenharmony_ci struct mhi_ring *buf_ring, *tre_ring; 6358c2ecf20Sopenharmony_ci struct mhi_buf_info *buf_info; 6368c2ecf20Sopenharmony_ci struct mhi_result result; 6378c2ecf20Sopenharmony_ci int ev_code; 6388c2ecf20Sopenharmony_ci u32 cookie; /* offset to local descriptor */ 6398c2ecf20Sopenharmony_ci u16 xfer_len; 6408c2ecf20Sopenharmony_ci 6418c2ecf20Sopenharmony_ci buf_ring = &mhi_chan->buf_ring; 6428c2ecf20Sopenharmony_ci tre_ring = &mhi_chan->tre_ring; 6438c2ecf20Sopenharmony_ci 6448c2ecf20Sopenharmony_ci ev_code = MHI_TRE_GET_EV_CODE(event); 6458c2ecf20Sopenharmony_ci cookie = MHI_TRE_GET_EV_COOKIE(event); 6468c2ecf20Sopenharmony_ci xfer_len = MHI_TRE_GET_EV_LEN(event); 6478c2ecf20Sopenharmony_ci 6488c2ecf20Sopenharmony_ci /* Received out of bound cookie */ 6498c2ecf20Sopenharmony_ci WARN_ON(cookie >= buf_ring->len); 6508c2ecf20Sopenharmony_ci 6518c2ecf20Sopenharmony_ci buf_info = buf_ring->base + cookie; 6528c2ecf20Sopenharmony_ci 6538c2ecf20Sopenharmony_ci result.transaction_status = (ev_code == MHI_EV_CC_OVERFLOW) ? 6548c2ecf20Sopenharmony_ci -EOVERFLOW : 0; 6558c2ecf20Sopenharmony_ci 6568c2ecf20Sopenharmony_ci /* truncate to buf len if xfer_len is larger */ 6578c2ecf20Sopenharmony_ci result.bytes_xferd = min_t(u16, xfer_len, buf_info->len); 6588c2ecf20Sopenharmony_ci result.buf_addr = buf_info->cb_buf; 6598c2ecf20Sopenharmony_ci result.dir = mhi_chan->dir; 6608c2ecf20Sopenharmony_ci 6618c2ecf20Sopenharmony_ci read_lock_bh(&mhi_chan->lock); 6628c2ecf20Sopenharmony_ci 6638c2ecf20Sopenharmony_ci if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) 6648c2ecf20Sopenharmony_ci goto end_process_rsc_event; 6658c2ecf20Sopenharmony_ci 6668c2ecf20Sopenharmony_ci WARN_ON(!buf_info->used); 6678c2ecf20Sopenharmony_ci 6688c2ecf20Sopenharmony_ci /* notify the client */ 6698c2ecf20Sopenharmony_ci mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); 6708c2ecf20Sopenharmony_ci 6718c2ecf20Sopenharmony_ci /* 6728c2ecf20Sopenharmony_ci * Note: We're arbitrarily incrementing RP even though, completion 6738c2ecf20Sopenharmony_ci * packet we processed might not be the same one, reason we can do this 6748c2ecf20Sopenharmony_ci * is because device guaranteed to cache descriptors in order it 6758c2ecf20Sopenharmony_ci * receive, so even though completion event is different we can re-use 6768c2ecf20Sopenharmony_ci * all descriptors in between. 6778c2ecf20Sopenharmony_ci * Example: 6788c2ecf20Sopenharmony_ci * Transfer Ring has descriptors: A, B, C, D 6798c2ecf20Sopenharmony_ci * Last descriptor host queue is D (WP) and first descriptor 6808c2ecf20Sopenharmony_ci * host queue is A (RP). 6818c2ecf20Sopenharmony_ci * The completion event we just serviced is descriptor C. 6828c2ecf20Sopenharmony_ci * Then we can safely queue descriptors to replace A, B, and C 6838c2ecf20Sopenharmony_ci * even though host did not receive any completions. 6848c2ecf20Sopenharmony_ci */ 6858c2ecf20Sopenharmony_ci mhi_del_ring_element(mhi_cntrl, tre_ring); 6868c2ecf20Sopenharmony_ci buf_info->used = false; 6878c2ecf20Sopenharmony_ci 6888c2ecf20Sopenharmony_ciend_process_rsc_event: 6898c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_chan->lock); 6908c2ecf20Sopenharmony_ci 6918c2ecf20Sopenharmony_ci return 0; 6928c2ecf20Sopenharmony_ci} 6938c2ecf20Sopenharmony_ci 6948c2ecf20Sopenharmony_cistatic void mhi_process_cmd_completion(struct mhi_controller *mhi_cntrl, 6958c2ecf20Sopenharmony_ci struct mhi_tre *tre) 6968c2ecf20Sopenharmony_ci{ 6978c2ecf20Sopenharmony_ci dma_addr_t ptr = MHI_TRE_GET_EV_PTR(tre); 6988c2ecf20Sopenharmony_ci struct mhi_cmd *cmd_ring = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; 6998c2ecf20Sopenharmony_ci struct mhi_ring *mhi_ring = &cmd_ring->ring; 7008c2ecf20Sopenharmony_ci struct mhi_tre *cmd_pkt; 7018c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan; 7028c2ecf20Sopenharmony_ci u32 chan; 7038c2ecf20Sopenharmony_ci 7048c2ecf20Sopenharmony_ci if (!is_valid_ring_ptr(mhi_ring, ptr)) { 7058c2ecf20Sopenharmony_ci dev_err(&mhi_cntrl->mhi_dev->dev, 7068c2ecf20Sopenharmony_ci "Event element points outside of the cmd ring\n"); 7078c2ecf20Sopenharmony_ci return; 7088c2ecf20Sopenharmony_ci } 7098c2ecf20Sopenharmony_ci 7108c2ecf20Sopenharmony_ci cmd_pkt = mhi_to_virtual(mhi_ring, ptr); 7118c2ecf20Sopenharmony_ci 7128c2ecf20Sopenharmony_ci chan = MHI_TRE_GET_CMD_CHID(cmd_pkt); 7138c2ecf20Sopenharmony_ci 7148c2ecf20Sopenharmony_ci if (chan < mhi_cntrl->max_chan && 7158c2ecf20Sopenharmony_ci mhi_cntrl->mhi_chan[chan].configured) { 7168c2ecf20Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[chan]; 7178c2ecf20Sopenharmony_ci write_lock_bh(&mhi_chan->lock); 7188c2ecf20Sopenharmony_ci mhi_chan->ccs = MHI_TRE_GET_EV_CODE(tre); 7198c2ecf20Sopenharmony_ci complete(&mhi_chan->completion); 7208c2ecf20Sopenharmony_ci write_unlock_bh(&mhi_chan->lock); 7218c2ecf20Sopenharmony_ci } else { 7228c2ecf20Sopenharmony_ci dev_err(&mhi_cntrl->mhi_dev->dev, 7238c2ecf20Sopenharmony_ci "Completion packet for invalid channel ID: %d\n", chan); 7248c2ecf20Sopenharmony_ci } 7258c2ecf20Sopenharmony_ci 7268c2ecf20Sopenharmony_ci mhi_del_ring_element(mhi_cntrl, mhi_ring); 7278c2ecf20Sopenharmony_ci} 7288c2ecf20Sopenharmony_ci 7298c2ecf20Sopenharmony_ciint mhi_process_ctrl_ev_ring(struct mhi_controller *mhi_cntrl, 7308c2ecf20Sopenharmony_ci struct mhi_event *mhi_event, 7318c2ecf20Sopenharmony_ci u32 event_quota) 7328c2ecf20Sopenharmony_ci{ 7338c2ecf20Sopenharmony_ci struct mhi_tre *dev_rp, *local_rp; 7348c2ecf20Sopenharmony_ci struct mhi_ring *ev_ring = &mhi_event->ring; 7358c2ecf20Sopenharmony_ci struct mhi_event_ctxt *er_ctxt = 7368c2ecf20Sopenharmony_ci &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; 7378c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan; 7388c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 7398c2ecf20Sopenharmony_ci u32 chan; 7408c2ecf20Sopenharmony_ci int count = 0; 7418c2ecf20Sopenharmony_ci dma_addr_t ptr = er_ctxt->rp; 7428c2ecf20Sopenharmony_ci 7438c2ecf20Sopenharmony_ci /* 7448c2ecf20Sopenharmony_ci * This is a quick check to avoid unnecessary event processing 7458c2ecf20Sopenharmony_ci * in case MHI is already in error state, but it's still possible 7468c2ecf20Sopenharmony_ci * to transition to error state while processing events 7478c2ecf20Sopenharmony_ci */ 7488c2ecf20Sopenharmony_ci if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) 7498c2ecf20Sopenharmony_ci return -EIO; 7508c2ecf20Sopenharmony_ci 7518c2ecf20Sopenharmony_ci if (!is_valid_ring_ptr(ev_ring, ptr)) { 7528c2ecf20Sopenharmony_ci dev_err(&mhi_cntrl->mhi_dev->dev, 7538c2ecf20Sopenharmony_ci "Event ring rp points outside of the event ring\n"); 7548c2ecf20Sopenharmony_ci return -EIO; 7558c2ecf20Sopenharmony_ci } 7568c2ecf20Sopenharmony_ci 7578c2ecf20Sopenharmony_ci dev_rp = mhi_to_virtual(ev_ring, ptr); 7588c2ecf20Sopenharmony_ci local_rp = ev_ring->rp; 7598c2ecf20Sopenharmony_ci 7608c2ecf20Sopenharmony_ci while (dev_rp != local_rp) { 7618c2ecf20Sopenharmony_ci enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp); 7628c2ecf20Sopenharmony_ci 7638c2ecf20Sopenharmony_ci switch (type) { 7648c2ecf20Sopenharmony_ci case MHI_PKT_TYPE_BW_REQ_EVENT: 7658c2ecf20Sopenharmony_ci { 7668c2ecf20Sopenharmony_ci struct mhi_link_info *link_info; 7678c2ecf20Sopenharmony_ci 7688c2ecf20Sopenharmony_ci link_info = &mhi_cntrl->mhi_link_info; 7698c2ecf20Sopenharmony_ci write_lock_irq(&mhi_cntrl->pm_lock); 7708c2ecf20Sopenharmony_ci link_info->target_link_speed = 7718c2ecf20Sopenharmony_ci MHI_TRE_GET_EV_LINKSPEED(local_rp); 7728c2ecf20Sopenharmony_ci link_info->target_link_width = 7738c2ecf20Sopenharmony_ci MHI_TRE_GET_EV_LINKWIDTH(local_rp); 7748c2ecf20Sopenharmony_ci write_unlock_irq(&mhi_cntrl->pm_lock); 7758c2ecf20Sopenharmony_ci dev_dbg(dev, "Received BW_REQ event\n"); 7768c2ecf20Sopenharmony_ci mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_BW_REQ); 7778c2ecf20Sopenharmony_ci break; 7788c2ecf20Sopenharmony_ci } 7798c2ecf20Sopenharmony_ci case MHI_PKT_TYPE_STATE_CHANGE_EVENT: 7808c2ecf20Sopenharmony_ci { 7818c2ecf20Sopenharmony_ci enum mhi_state new_state; 7828c2ecf20Sopenharmony_ci 7838c2ecf20Sopenharmony_ci new_state = MHI_TRE_GET_EV_STATE(local_rp); 7848c2ecf20Sopenharmony_ci 7858c2ecf20Sopenharmony_ci dev_dbg(dev, "State change event to state: %s\n", 7868c2ecf20Sopenharmony_ci TO_MHI_STATE_STR(new_state)); 7878c2ecf20Sopenharmony_ci 7888c2ecf20Sopenharmony_ci switch (new_state) { 7898c2ecf20Sopenharmony_ci case MHI_STATE_M0: 7908c2ecf20Sopenharmony_ci mhi_pm_m0_transition(mhi_cntrl); 7918c2ecf20Sopenharmony_ci break; 7928c2ecf20Sopenharmony_ci case MHI_STATE_M1: 7938c2ecf20Sopenharmony_ci mhi_pm_m1_transition(mhi_cntrl); 7948c2ecf20Sopenharmony_ci break; 7958c2ecf20Sopenharmony_ci case MHI_STATE_M3: 7968c2ecf20Sopenharmony_ci mhi_pm_m3_transition(mhi_cntrl); 7978c2ecf20Sopenharmony_ci break; 7988c2ecf20Sopenharmony_ci case MHI_STATE_SYS_ERR: 7998c2ecf20Sopenharmony_ci { 8008c2ecf20Sopenharmony_ci enum mhi_pm_state new_state; 8018c2ecf20Sopenharmony_ci 8028c2ecf20Sopenharmony_ci /* skip SYS_ERROR handling if RDDM supported */ 8038c2ecf20Sopenharmony_ci if (mhi_cntrl->ee == MHI_EE_RDDM || 8048c2ecf20Sopenharmony_ci mhi_cntrl->rddm_image) 8058c2ecf20Sopenharmony_ci break; 8068c2ecf20Sopenharmony_ci 8078c2ecf20Sopenharmony_ci dev_dbg(dev, "System error detected\n"); 8088c2ecf20Sopenharmony_ci write_lock_irq(&mhi_cntrl->pm_lock); 8098c2ecf20Sopenharmony_ci new_state = mhi_tryset_pm_state(mhi_cntrl, 8108c2ecf20Sopenharmony_ci MHI_PM_SYS_ERR_DETECT); 8118c2ecf20Sopenharmony_ci write_unlock_irq(&mhi_cntrl->pm_lock); 8128c2ecf20Sopenharmony_ci if (new_state == MHI_PM_SYS_ERR_DETECT) 8138c2ecf20Sopenharmony_ci mhi_pm_sys_err_handler(mhi_cntrl); 8148c2ecf20Sopenharmony_ci break; 8158c2ecf20Sopenharmony_ci } 8168c2ecf20Sopenharmony_ci default: 8178c2ecf20Sopenharmony_ci dev_err(dev, "Invalid state: %s\n", 8188c2ecf20Sopenharmony_ci TO_MHI_STATE_STR(new_state)); 8198c2ecf20Sopenharmony_ci } 8208c2ecf20Sopenharmony_ci 8218c2ecf20Sopenharmony_ci break; 8228c2ecf20Sopenharmony_ci } 8238c2ecf20Sopenharmony_ci case MHI_PKT_TYPE_CMD_COMPLETION_EVENT: 8248c2ecf20Sopenharmony_ci mhi_process_cmd_completion(mhi_cntrl, local_rp); 8258c2ecf20Sopenharmony_ci break; 8268c2ecf20Sopenharmony_ci case MHI_PKT_TYPE_EE_EVENT: 8278c2ecf20Sopenharmony_ci { 8288c2ecf20Sopenharmony_ci enum dev_st_transition st = DEV_ST_TRANSITION_MAX; 8298c2ecf20Sopenharmony_ci enum mhi_ee_type event = MHI_TRE_GET_EV_EXECENV(local_rp); 8308c2ecf20Sopenharmony_ci 8318c2ecf20Sopenharmony_ci dev_dbg(dev, "Received EE event: %s\n", 8328c2ecf20Sopenharmony_ci TO_MHI_EXEC_STR(event)); 8338c2ecf20Sopenharmony_ci switch (event) { 8348c2ecf20Sopenharmony_ci case MHI_EE_SBL: 8358c2ecf20Sopenharmony_ci st = DEV_ST_TRANSITION_SBL; 8368c2ecf20Sopenharmony_ci break; 8378c2ecf20Sopenharmony_ci case MHI_EE_WFW: 8388c2ecf20Sopenharmony_ci case MHI_EE_AMSS: 8398c2ecf20Sopenharmony_ci st = DEV_ST_TRANSITION_MISSION_MODE; 8408c2ecf20Sopenharmony_ci break; 8418c2ecf20Sopenharmony_ci case MHI_EE_RDDM: 8428c2ecf20Sopenharmony_ci mhi_cntrl->status_cb(mhi_cntrl, MHI_CB_EE_RDDM); 8438c2ecf20Sopenharmony_ci write_lock_irq(&mhi_cntrl->pm_lock); 8448c2ecf20Sopenharmony_ci mhi_cntrl->ee = event; 8458c2ecf20Sopenharmony_ci write_unlock_irq(&mhi_cntrl->pm_lock); 8468c2ecf20Sopenharmony_ci wake_up_all(&mhi_cntrl->state_event); 8478c2ecf20Sopenharmony_ci break; 8488c2ecf20Sopenharmony_ci default: 8498c2ecf20Sopenharmony_ci dev_err(dev, 8508c2ecf20Sopenharmony_ci "Unhandled EE event: 0x%x\n", type); 8518c2ecf20Sopenharmony_ci } 8528c2ecf20Sopenharmony_ci if (st != DEV_ST_TRANSITION_MAX) 8538c2ecf20Sopenharmony_ci mhi_queue_state_transition(mhi_cntrl, st); 8548c2ecf20Sopenharmony_ci 8558c2ecf20Sopenharmony_ci break; 8568c2ecf20Sopenharmony_ci } 8578c2ecf20Sopenharmony_ci case MHI_PKT_TYPE_TX_EVENT: 8588c2ecf20Sopenharmony_ci chan = MHI_TRE_GET_EV_CHID(local_rp); 8598c2ecf20Sopenharmony_ci 8608c2ecf20Sopenharmony_ci WARN_ON(chan >= mhi_cntrl->max_chan); 8618c2ecf20Sopenharmony_ci 8628c2ecf20Sopenharmony_ci /* 8638c2ecf20Sopenharmony_ci * Only process the event ring elements whose channel 8648c2ecf20Sopenharmony_ci * ID is within the maximum supported range. 8658c2ecf20Sopenharmony_ci */ 8668c2ecf20Sopenharmony_ci if (chan < mhi_cntrl->max_chan) { 8678c2ecf20Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[chan]; 8688c2ecf20Sopenharmony_ci if (!mhi_chan->configured) 8698c2ecf20Sopenharmony_ci break; 8708c2ecf20Sopenharmony_ci parse_xfer_event(mhi_cntrl, local_rp, mhi_chan); 8718c2ecf20Sopenharmony_ci event_quota--; 8728c2ecf20Sopenharmony_ci } 8738c2ecf20Sopenharmony_ci break; 8748c2ecf20Sopenharmony_ci default: 8758c2ecf20Sopenharmony_ci dev_err(dev, "Unhandled event type: %d\n", type); 8768c2ecf20Sopenharmony_ci break; 8778c2ecf20Sopenharmony_ci } 8788c2ecf20Sopenharmony_ci 8798c2ecf20Sopenharmony_ci mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); 8808c2ecf20Sopenharmony_ci local_rp = ev_ring->rp; 8818c2ecf20Sopenharmony_ci 8828c2ecf20Sopenharmony_ci ptr = er_ctxt->rp; 8838c2ecf20Sopenharmony_ci if (!is_valid_ring_ptr(ev_ring, ptr)) { 8848c2ecf20Sopenharmony_ci dev_err(&mhi_cntrl->mhi_dev->dev, 8858c2ecf20Sopenharmony_ci "Event ring rp points outside of the event ring\n"); 8868c2ecf20Sopenharmony_ci return -EIO; 8878c2ecf20Sopenharmony_ci } 8888c2ecf20Sopenharmony_ci 8898c2ecf20Sopenharmony_ci dev_rp = mhi_to_virtual(ev_ring, ptr); 8908c2ecf20Sopenharmony_ci count++; 8918c2ecf20Sopenharmony_ci } 8928c2ecf20Sopenharmony_ci 8938c2ecf20Sopenharmony_ci read_lock_bh(&mhi_cntrl->pm_lock); 8948c2ecf20Sopenharmony_ci if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) 8958c2ecf20Sopenharmony_ci mhi_ring_er_db(mhi_event); 8968c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 8978c2ecf20Sopenharmony_ci 8988c2ecf20Sopenharmony_ci return count; 8998c2ecf20Sopenharmony_ci} 9008c2ecf20Sopenharmony_ci 9018c2ecf20Sopenharmony_ciint mhi_process_data_event_ring(struct mhi_controller *mhi_cntrl, 9028c2ecf20Sopenharmony_ci struct mhi_event *mhi_event, 9038c2ecf20Sopenharmony_ci u32 event_quota) 9048c2ecf20Sopenharmony_ci{ 9058c2ecf20Sopenharmony_ci struct mhi_tre *dev_rp, *local_rp; 9068c2ecf20Sopenharmony_ci struct mhi_ring *ev_ring = &mhi_event->ring; 9078c2ecf20Sopenharmony_ci struct mhi_event_ctxt *er_ctxt = 9088c2ecf20Sopenharmony_ci &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_event->er_index]; 9098c2ecf20Sopenharmony_ci int count = 0; 9108c2ecf20Sopenharmony_ci u32 chan; 9118c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan; 9128c2ecf20Sopenharmony_ci dma_addr_t ptr = er_ctxt->rp; 9138c2ecf20Sopenharmony_ci 9148c2ecf20Sopenharmony_ci if (unlikely(MHI_EVENT_ACCESS_INVALID(mhi_cntrl->pm_state))) 9158c2ecf20Sopenharmony_ci return -EIO; 9168c2ecf20Sopenharmony_ci 9178c2ecf20Sopenharmony_ci if (!is_valid_ring_ptr(ev_ring, ptr)) { 9188c2ecf20Sopenharmony_ci dev_err(&mhi_cntrl->mhi_dev->dev, 9198c2ecf20Sopenharmony_ci "Event ring rp points outside of the event ring\n"); 9208c2ecf20Sopenharmony_ci return -EIO; 9218c2ecf20Sopenharmony_ci } 9228c2ecf20Sopenharmony_ci 9238c2ecf20Sopenharmony_ci dev_rp = mhi_to_virtual(ev_ring, ptr); 9248c2ecf20Sopenharmony_ci local_rp = ev_ring->rp; 9258c2ecf20Sopenharmony_ci 9268c2ecf20Sopenharmony_ci while (dev_rp != local_rp && event_quota > 0) { 9278c2ecf20Sopenharmony_ci enum mhi_pkt_type type = MHI_TRE_GET_EV_TYPE(local_rp); 9288c2ecf20Sopenharmony_ci 9298c2ecf20Sopenharmony_ci chan = MHI_TRE_GET_EV_CHID(local_rp); 9308c2ecf20Sopenharmony_ci 9318c2ecf20Sopenharmony_ci WARN_ON(chan >= mhi_cntrl->max_chan); 9328c2ecf20Sopenharmony_ci 9338c2ecf20Sopenharmony_ci /* 9348c2ecf20Sopenharmony_ci * Only process the event ring elements whose channel 9358c2ecf20Sopenharmony_ci * ID is within the maximum supported range. 9368c2ecf20Sopenharmony_ci */ 9378c2ecf20Sopenharmony_ci if (chan < mhi_cntrl->max_chan && 9388c2ecf20Sopenharmony_ci mhi_cntrl->mhi_chan[chan].configured) { 9398c2ecf20Sopenharmony_ci mhi_chan = &mhi_cntrl->mhi_chan[chan]; 9408c2ecf20Sopenharmony_ci 9418c2ecf20Sopenharmony_ci if (likely(type == MHI_PKT_TYPE_TX_EVENT)) { 9428c2ecf20Sopenharmony_ci parse_xfer_event(mhi_cntrl, local_rp, mhi_chan); 9438c2ecf20Sopenharmony_ci event_quota--; 9448c2ecf20Sopenharmony_ci } else if (type == MHI_PKT_TYPE_RSC_TX_EVENT) { 9458c2ecf20Sopenharmony_ci parse_rsc_event(mhi_cntrl, local_rp, mhi_chan); 9468c2ecf20Sopenharmony_ci event_quota--; 9478c2ecf20Sopenharmony_ci } 9488c2ecf20Sopenharmony_ci } 9498c2ecf20Sopenharmony_ci 9508c2ecf20Sopenharmony_ci mhi_recycle_ev_ring_element(mhi_cntrl, ev_ring); 9518c2ecf20Sopenharmony_ci local_rp = ev_ring->rp; 9528c2ecf20Sopenharmony_ci 9538c2ecf20Sopenharmony_ci ptr = er_ctxt->rp; 9548c2ecf20Sopenharmony_ci if (!is_valid_ring_ptr(ev_ring, ptr)) { 9558c2ecf20Sopenharmony_ci dev_err(&mhi_cntrl->mhi_dev->dev, 9568c2ecf20Sopenharmony_ci "Event ring rp points outside of the event ring\n"); 9578c2ecf20Sopenharmony_ci return -EIO; 9588c2ecf20Sopenharmony_ci } 9598c2ecf20Sopenharmony_ci 9608c2ecf20Sopenharmony_ci dev_rp = mhi_to_virtual(ev_ring, ptr); 9618c2ecf20Sopenharmony_ci count++; 9628c2ecf20Sopenharmony_ci } 9638c2ecf20Sopenharmony_ci read_lock_bh(&mhi_cntrl->pm_lock); 9648c2ecf20Sopenharmony_ci if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) 9658c2ecf20Sopenharmony_ci mhi_ring_er_db(mhi_event); 9668c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 9678c2ecf20Sopenharmony_ci 9688c2ecf20Sopenharmony_ci return count; 9698c2ecf20Sopenharmony_ci} 9708c2ecf20Sopenharmony_ci 9718c2ecf20Sopenharmony_civoid mhi_ev_task(unsigned long data) 9728c2ecf20Sopenharmony_ci{ 9738c2ecf20Sopenharmony_ci struct mhi_event *mhi_event = (struct mhi_event *)data; 9748c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl; 9758c2ecf20Sopenharmony_ci 9768c2ecf20Sopenharmony_ci /* process all pending events */ 9778c2ecf20Sopenharmony_ci spin_lock_bh(&mhi_event->lock); 9788c2ecf20Sopenharmony_ci mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX); 9798c2ecf20Sopenharmony_ci spin_unlock_bh(&mhi_event->lock); 9808c2ecf20Sopenharmony_ci} 9818c2ecf20Sopenharmony_ci 9828c2ecf20Sopenharmony_civoid mhi_ctrl_ev_task(unsigned long data) 9838c2ecf20Sopenharmony_ci{ 9848c2ecf20Sopenharmony_ci struct mhi_event *mhi_event = (struct mhi_event *)data; 9858c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = mhi_event->mhi_cntrl; 9868c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 9878c2ecf20Sopenharmony_ci enum mhi_state state; 9888c2ecf20Sopenharmony_ci enum mhi_pm_state pm_state = 0; 9898c2ecf20Sopenharmony_ci int ret; 9908c2ecf20Sopenharmony_ci 9918c2ecf20Sopenharmony_ci /* 9928c2ecf20Sopenharmony_ci * We can check PM state w/o a lock here because there is no way 9938c2ecf20Sopenharmony_ci * PM state can change from reg access valid to no access while this 9948c2ecf20Sopenharmony_ci * thread being executed. 9958c2ecf20Sopenharmony_ci */ 9968c2ecf20Sopenharmony_ci if (!MHI_REG_ACCESS_VALID(mhi_cntrl->pm_state)) { 9978c2ecf20Sopenharmony_ci /* 9988c2ecf20Sopenharmony_ci * We may have a pending event but not allowed to 9998c2ecf20Sopenharmony_ci * process it since we are probably in a suspended state, 10008c2ecf20Sopenharmony_ci * so trigger a resume. 10018c2ecf20Sopenharmony_ci */ 10028c2ecf20Sopenharmony_ci mhi_trigger_resume(mhi_cntrl); 10038c2ecf20Sopenharmony_ci 10048c2ecf20Sopenharmony_ci return; 10058c2ecf20Sopenharmony_ci } 10068c2ecf20Sopenharmony_ci 10078c2ecf20Sopenharmony_ci /* Process ctrl events events */ 10088c2ecf20Sopenharmony_ci ret = mhi_event->process_event(mhi_cntrl, mhi_event, U32_MAX); 10098c2ecf20Sopenharmony_ci 10108c2ecf20Sopenharmony_ci /* 10118c2ecf20Sopenharmony_ci * We received an IRQ but no events to process, maybe device went to 10128c2ecf20Sopenharmony_ci * SYS_ERR state? Check the state to confirm. 10138c2ecf20Sopenharmony_ci */ 10148c2ecf20Sopenharmony_ci if (!ret) { 10158c2ecf20Sopenharmony_ci write_lock_irq(&mhi_cntrl->pm_lock); 10168c2ecf20Sopenharmony_ci state = mhi_get_mhi_state(mhi_cntrl); 10178c2ecf20Sopenharmony_ci if (state == MHI_STATE_SYS_ERR) { 10188c2ecf20Sopenharmony_ci dev_dbg(dev, "System error detected\n"); 10198c2ecf20Sopenharmony_ci pm_state = mhi_tryset_pm_state(mhi_cntrl, 10208c2ecf20Sopenharmony_ci MHI_PM_SYS_ERR_DETECT); 10218c2ecf20Sopenharmony_ci } 10228c2ecf20Sopenharmony_ci write_unlock_irq(&mhi_cntrl->pm_lock); 10238c2ecf20Sopenharmony_ci if (pm_state == MHI_PM_SYS_ERR_DETECT) 10248c2ecf20Sopenharmony_ci mhi_pm_sys_err_handler(mhi_cntrl); 10258c2ecf20Sopenharmony_ci } 10268c2ecf20Sopenharmony_ci} 10278c2ecf20Sopenharmony_ci 10288c2ecf20Sopenharmony_cistatic bool mhi_is_ring_full(struct mhi_controller *mhi_cntrl, 10298c2ecf20Sopenharmony_ci struct mhi_ring *ring) 10308c2ecf20Sopenharmony_ci{ 10318c2ecf20Sopenharmony_ci void *tmp = ring->wp + ring->el_size; 10328c2ecf20Sopenharmony_ci 10338c2ecf20Sopenharmony_ci if (tmp >= (ring->base + ring->len)) 10348c2ecf20Sopenharmony_ci tmp = ring->base; 10358c2ecf20Sopenharmony_ci 10368c2ecf20Sopenharmony_ci return (tmp == ring->rp); 10378c2ecf20Sopenharmony_ci} 10388c2ecf20Sopenharmony_ci 10398c2ecf20Sopenharmony_ciint mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, 10408c2ecf20Sopenharmony_ci struct sk_buff *skb, size_t len, enum mhi_flags mflags) 10418c2ecf20Sopenharmony_ci{ 10428c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; 10438c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan : 10448c2ecf20Sopenharmony_ci mhi_dev->dl_chan; 10458c2ecf20Sopenharmony_ci struct mhi_ring *tre_ring = &mhi_chan->tre_ring; 10468c2ecf20Sopenharmony_ci struct mhi_buf_info buf_info = { }; 10478c2ecf20Sopenharmony_ci int ret; 10488c2ecf20Sopenharmony_ci 10498c2ecf20Sopenharmony_ci /* If MHI host pre-allocates buffers then client drivers cannot queue */ 10508c2ecf20Sopenharmony_ci if (mhi_chan->pre_alloc) 10518c2ecf20Sopenharmony_ci return -EINVAL; 10528c2ecf20Sopenharmony_ci 10538c2ecf20Sopenharmony_ci if (mhi_is_ring_full(mhi_cntrl, tre_ring)) 10548c2ecf20Sopenharmony_ci return -ENOMEM; 10558c2ecf20Sopenharmony_ci 10568c2ecf20Sopenharmony_ci read_lock_bh(&mhi_cntrl->pm_lock); 10578c2ecf20Sopenharmony_ci if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { 10588c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 10598c2ecf20Sopenharmony_ci return -EIO; 10608c2ecf20Sopenharmony_ci } 10618c2ecf20Sopenharmony_ci 10628c2ecf20Sopenharmony_ci /* we're in M3 or transitioning to M3 */ 10638c2ecf20Sopenharmony_ci if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) 10648c2ecf20Sopenharmony_ci mhi_trigger_resume(mhi_cntrl); 10658c2ecf20Sopenharmony_ci 10668c2ecf20Sopenharmony_ci /* Toggle wake to exit out of M2 */ 10678c2ecf20Sopenharmony_ci mhi_cntrl->wake_toggle(mhi_cntrl); 10688c2ecf20Sopenharmony_ci 10698c2ecf20Sopenharmony_ci buf_info.v_addr = skb->data; 10708c2ecf20Sopenharmony_ci buf_info.cb_buf = skb; 10718c2ecf20Sopenharmony_ci buf_info.len = len; 10728c2ecf20Sopenharmony_ci 10738c2ecf20Sopenharmony_ci ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags); 10748c2ecf20Sopenharmony_ci if (unlikely(ret)) { 10758c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 10768c2ecf20Sopenharmony_ci return ret; 10778c2ecf20Sopenharmony_ci } 10788c2ecf20Sopenharmony_ci 10798c2ecf20Sopenharmony_ci if (mhi_chan->dir == DMA_TO_DEVICE) 10808c2ecf20Sopenharmony_ci atomic_inc(&mhi_cntrl->pending_pkts); 10818c2ecf20Sopenharmony_ci 10828c2ecf20Sopenharmony_ci if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) { 10838c2ecf20Sopenharmony_ci read_lock_bh(&mhi_chan->lock); 10848c2ecf20Sopenharmony_ci mhi_ring_chan_db(mhi_cntrl, mhi_chan); 10858c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_chan->lock); 10868c2ecf20Sopenharmony_ci } 10878c2ecf20Sopenharmony_ci 10888c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 10898c2ecf20Sopenharmony_ci 10908c2ecf20Sopenharmony_ci return 0; 10918c2ecf20Sopenharmony_ci} 10928c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_skb); 10938c2ecf20Sopenharmony_ci 10948c2ecf20Sopenharmony_ciint mhi_queue_dma(struct mhi_device *mhi_dev, enum dma_data_direction dir, 10958c2ecf20Sopenharmony_ci struct mhi_buf *mhi_buf, size_t len, enum mhi_flags mflags) 10968c2ecf20Sopenharmony_ci{ 10978c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; 10988c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan : 10998c2ecf20Sopenharmony_ci mhi_dev->dl_chan; 11008c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 11018c2ecf20Sopenharmony_ci struct mhi_ring *tre_ring = &mhi_chan->tre_ring; 11028c2ecf20Sopenharmony_ci struct mhi_buf_info buf_info = { }; 11038c2ecf20Sopenharmony_ci int ret; 11048c2ecf20Sopenharmony_ci 11058c2ecf20Sopenharmony_ci /* If MHI host pre-allocates buffers then client drivers cannot queue */ 11068c2ecf20Sopenharmony_ci if (mhi_chan->pre_alloc) 11078c2ecf20Sopenharmony_ci return -EINVAL; 11088c2ecf20Sopenharmony_ci 11098c2ecf20Sopenharmony_ci if (mhi_is_ring_full(mhi_cntrl, tre_ring)) 11108c2ecf20Sopenharmony_ci return -ENOMEM; 11118c2ecf20Sopenharmony_ci 11128c2ecf20Sopenharmony_ci read_lock_bh(&mhi_cntrl->pm_lock); 11138c2ecf20Sopenharmony_ci if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) { 11148c2ecf20Sopenharmony_ci dev_err(dev, "MHI is not in activate state, PM state: %s\n", 11158c2ecf20Sopenharmony_ci to_mhi_pm_state_str(mhi_cntrl->pm_state)); 11168c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 11178c2ecf20Sopenharmony_ci 11188c2ecf20Sopenharmony_ci return -EIO; 11198c2ecf20Sopenharmony_ci } 11208c2ecf20Sopenharmony_ci 11218c2ecf20Sopenharmony_ci /* we're in M3 or transitioning to M3 */ 11228c2ecf20Sopenharmony_ci if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) 11238c2ecf20Sopenharmony_ci mhi_trigger_resume(mhi_cntrl); 11248c2ecf20Sopenharmony_ci 11258c2ecf20Sopenharmony_ci /* Toggle wake to exit out of M2 */ 11268c2ecf20Sopenharmony_ci mhi_cntrl->wake_toggle(mhi_cntrl); 11278c2ecf20Sopenharmony_ci 11288c2ecf20Sopenharmony_ci buf_info.p_addr = mhi_buf->dma_addr; 11298c2ecf20Sopenharmony_ci buf_info.cb_buf = mhi_buf; 11308c2ecf20Sopenharmony_ci buf_info.pre_mapped = true; 11318c2ecf20Sopenharmony_ci buf_info.len = len; 11328c2ecf20Sopenharmony_ci 11338c2ecf20Sopenharmony_ci ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags); 11348c2ecf20Sopenharmony_ci if (unlikely(ret)) { 11358c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 11368c2ecf20Sopenharmony_ci return ret; 11378c2ecf20Sopenharmony_ci } 11388c2ecf20Sopenharmony_ci 11398c2ecf20Sopenharmony_ci if (mhi_chan->dir == DMA_TO_DEVICE) 11408c2ecf20Sopenharmony_ci atomic_inc(&mhi_cntrl->pending_pkts); 11418c2ecf20Sopenharmony_ci 11428c2ecf20Sopenharmony_ci if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) { 11438c2ecf20Sopenharmony_ci read_lock_bh(&mhi_chan->lock); 11448c2ecf20Sopenharmony_ci mhi_ring_chan_db(mhi_cntrl, mhi_chan); 11458c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_chan->lock); 11468c2ecf20Sopenharmony_ci } 11478c2ecf20Sopenharmony_ci 11488c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 11498c2ecf20Sopenharmony_ci 11508c2ecf20Sopenharmony_ci return 0; 11518c2ecf20Sopenharmony_ci} 11528c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_dma); 11538c2ecf20Sopenharmony_ci 11548c2ecf20Sopenharmony_ciint mhi_gen_tre(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan, 11558c2ecf20Sopenharmony_ci struct mhi_buf_info *info, enum mhi_flags flags) 11568c2ecf20Sopenharmony_ci{ 11578c2ecf20Sopenharmony_ci struct mhi_ring *buf_ring, *tre_ring; 11588c2ecf20Sopenharmony_ci struct mhi_tre *mhi_tre; 11598c2ecf20Sopenharmony_ci struct mhi_buf_info *buf_info; 11608c2ecf20Sopenharmony_ci int eot, eob, chain, bei; 11618c2ecf20Sopenharmony_ci int ret; 11628c2ecf20Sopenharmony_ci 11638c2ecf20Sopenharmony_ci buf_ring = &mhi_chan->buf_ring; 11648c2ecf20Sopenharmony_ci tre_ring = &mhi_chan->tre_ring; 11658c2ecf20Sopenharmony_ci 11668c2ecf20Sopenharmony_ci buf_info = buf_ring->wp; 11678c2ecf20Sopenharmony_ci WARN_ON(buf_info->used); 11688c2ecf20Sopenharmony_ci buf_info->pre_mapped = info->pre_mapped; 11698c2ecf20Sopenharmony_ci if (info->pre_mapped) 11708c2ecf20Sopenharmony_ci buf_info->p_addr = info->p_addr; 11718c2ecf20Sopenharmony_ci else 11728c2ecf20Sopenharmony_ci buf_info->v_addr = info->v_addr; 11738c2ecf20Sopenharmony_ci buf_info->cb_buf = info->cb_buf; 11748c2ecf20Sopenharmony_ci buf_info->wp = tre_ring->wp; 11758c2ecf20Sopenharmony_ci buf_info->dir = mhi_chan->dir; 11768c2ecf20Sopenharmony_ci buf_info->len = info->len; 11778c2ecf20Sopenharmony_ci 11788c2ecf20Sopenharmony_ci if (!info->pre_mapped) { 11798c2ecf20Sopenharmony_ci ret = mhi_cntrl->map_single(mhi_cntrl, buf_info); 11808c2ecf20Sopenharmony_ci if (ret) 11818c2ecf20Sopenharmony_ci return ret; 11828c2ecf20Sopenharmony_ci } 11838c2ecf20Sopenharmony_ci 11848c2ecf20Sopenharmony_ci eob = !!(flags & MHI_EOB); 11858c2ecf20Sopenharmony_ci eot = !!(flags & MHI_EOT); 11868c2ecf20Sopenharmony_ci chain = !!(flags & MHI_CHAIN); 11878c2ecf20Sopenharmony_ci bei = !!(mhi_chan->intmod); 11888c2ecf20Sopenharmony_ci 11898c2ecf20Sopenharmony_ci mhi_tre = tre_ring->wp; 11908c2ecf20Sopenharmony_ci mhi_tre->ptr = MHI_TRE_DATA_PTR(buf_info->p_addr); 11918c2ecf20Sopenharmony_ci mhi_tre->dword[0] = MHI_TRE_DATA_DWORD0(info->len); 11928c2ecf20Sopenharmony_ci mhi_tre->dword[1] = MHI_TRE_DATA_DWORD1(bei, eot, eob, chain); 11938c2ecf20Sopenharmony_ci 11948c2ecf20Sopenharmony_ci /* increment WP */ 11958c2ecf20Sopenharmony_ci mhi_add_ring_element(mhi_cntrl, tre_ring); 11968c2ecf20Sopenharmony_ci mhi_add_ring_element(mhi_cntrl, buf_ring); 11978c2ecf20Sopenharmony_ci 11988c2ecf20Sopenharmony_ci return 0; 11998c2ecf20Sopenharmony_ci} 12008c2ecf20Sopenharmony_ci 12018c2ecf20Sopenharmony_ciint mhi_queue_buf(struct mhi_device *mhi_dev, enum dma_data_direction dir, 12028c2ecf20Sopenharmony_ci void *buf, size_t len, enum mhi_flags mflags) 12038c2ecf20Sopenharmony_ci{ 12048c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; 12058c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan : 12068c2ecf20Sopenharmony_ci mhi_dev->dl_chan; 12078c2ecf20Sopenharmony_ci struct mhi_ring *tre_ring; 12088c2ecf20Sopenharmony_ci struct mhi_buf_info buf_info = { }; 12098c2ecf20Sopenharmony_ci unsigned long flags; 12108c2ecf20Sopenharmony_ci int ret; 12118c2ecf20Sopenharmony_ci 12128c2ecf20Sopenharmony_ci /* 12138c2ecf20Sopenharmony_ci * this check here only as a guard, it's always 12148c2ecf20Sopenharmony_ci * possible mhi can enter error while executing rest of function, 12158c2ecf20Sopenharmony_ci * which is not fatal so we do not need to hold pm_lock 12168c2ecf20Sopenharmony_ci */ 12178c2ecf20Sopenharmony_ci if (unlikely(MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state))) 12188c2ecf20Sopenharmony_ci return -EIO; 12198c2ecf20Sopenharmony_ci 12208c2ecf20Sopenharmony_ci tre_ring = &mhi_chan->tre_ring; 12218c2ecf20Sopenharmony_ci if (mhi_is_ring_full(mhi_cntrl, tre_ring)) 12228c2ecf20Sopenharmony_ci return -ENOMEM; 12238c2ecf20Sopenharmony_ci 12248c2ecf20Sopenharmony_ci buf_info.v_addr = buf; 12258c2ecf20Sopenharmony_ci buf_info.cb_buf = buf; 12268c2ecf20Sopenharmony_ci buf_info.len = len; 12278c2ecf20Sopenharmony_ci 12288c2ecf20Sopenharmony_ci ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &buf_info, mflags); 12298c2ecf20Sopenharmony_ci if (unlikely(ret)) 12308c2ecf20Sopenharmony_ci return ret; 12318c2ecf20Sopenharmony_ci 12328c2ecf20Sopenharmony_ci read_lock_irqsave(&mhi_cntrl->pm_lock, flags); 12338c2ecf20Sopenharmony_ci 12348c2ecf20Sopenharmony_ci /* we're in M3 or transitioning to M3 */ 12358c2ecf20Sopenharmony_ci if (MHI_PM_IN_SUSPEND_STATE(mhi_cntrl->pm_state)) 12368c2ecf20Sopenharmony_ci mhi_trigger_resume(mhi_cntrl); 12378c2ecf20Sopenharmony_ci 12388c2ecf20Sopenharmony_ci /* Toggle wake to exit out of M2 */ 12398c2ecf20Sopenharmony_ci mhi_cntrl->wake_toggle(mhi_cntrl); 12408c2ecf20Sopenharmony_ci 12418c2ecf20Sopenharmony_ci if (mhi_chan->dir == DMA_TO_DEVICE) 12428c2ecf20Sopenharmony_ci atomic_inc(&mhi_cntrl->pending_pkts); 12438c2ecf20Sopenharmony_ci 12448c2ecf20Sopenharmony_ci if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) { 12458c2ecf20Sopenharmony_ci unsigned long flags; 12468c2ecf20Sopenharmony_ci 12478c2ecf20Sopenharmony_ci read_lock_irqsave(&mhi_chan->lock, flags); 12488c2ecf20Sopenharmony_ci mhi_ring_chan_db(mhi_cntrl, mhi_chan); 12498c2ecf20Sopenharmony_ci read_unlock_irqrestore(&mhi_chan->lock, flags); 12508c2ecf20Sopenharmony_ci } 12518c2ecf20Sopenharmony_ci 12528c2ecf20Sopenharmony_ci read_unlock_irqrestore(&mhi_cntrl->pm_lock, flags); 12538c2ecf20Sopenharmony_ci 12548c2ecf20Sopenharmony_ci return 0; 12558c2ecf20Sopenharmony_ci} 12568c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_queue_buf); 12578c2ecf20Sopenharmony_ci 12588c2ecf20Sopenharmony_ciint mhi_send_cmd(struct mhi_controller *mhi_cntrl, 12598c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan, 12608c2ecf20Sopenharmony_ci enum mhi_cmd_type cmd) 12618c2ecf20Sopenharmony_ci{ 12628c2ecf20Sopenharmony_ci struct mhi_tre *cmd_tre = NULL; 12638c2ecf20Sopenharmony_ci struct mhi_cmd *mhi_cmd = &mhi_cntrl->mhi_cmd[PRIMARY_CMD_RING]; 12648c2ecf20Sopenharmony_ci struct mhi_ring *ring = &mhi_cmd->ring; 12658c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 12668c2ecf20Sopenharmony_ci int chan = 0; 12678c2ecf20Sopenharmony_ci 12688c2ecf20Sopenharmony_ci if (mhi_chan) 12698c2ecf20Sopenharmony_ci chan = mhi_chan->chan; 12708c2ecf20Sopenharmony_ci 12718c2ecf20Sopenharmony_ci spin_lock_bh(&mhi_cmd->lock); 12728c2ecf20Sopenharmony_ci if (!get_nr_avail_ring_elements(mhi_cntrl, ring)) { 12738c2ecf20Sopenharmony_ci spin_unlock_bh(&mhi_cmd->lock); 12748c2ecf20Sopenharmony_ci return -ENOMEM; 12758c2ecf20Sopenharmony_ci } 12768c2ecf20Sopenharmony_ci 12778c2ecf20Sopenharmony_ci /* prepare the cmd tre */ 12788c2ecf20Sopenharmony_ci cmd_tre = ring->wp; 12798c2ecf20Sopenharmony_ci switch (cmd) { 12808c2ecf20Sopenharmony_ci case MHI_CMD_RESET_CHAN: 12818c2ecf20Sopenharmony_ci cmd_tre->ptr = MHI_TRE_CMD_RESET_PTR; 12828c2ecf20Sopenharmony_ci cmd_tre->dword[0] = MHI_TRE_CMD_RESET_DWORD0; 12838c2ecf20Sopenharmony_ci cmd_tre->dword[1] = MHI_TRE_CMD_RESET_DWORD1(chan); 12848c2ecf20Sopenharmony_ci break; 12858c2ecf20Sopenharmony_ci case MHI_CMD_START_CHAN: 12868c2ecf20Sopenharmony_ci cmd_tre->ptr = MHI_TRE_CMD_START_PTR; 12878c2ecf20Sopenharmony_ci cmd_tre->dword[0] = MHI_TRE_CMD_START_DWORD0; 12888c2ecf20Sopenharmony_ci cmd_tre->dword[1] = MHI_TRE_CMD_START_DWORD1(chan); 12898c2ecf20Sopenharmony_ci break; 12908c2ecf20Sopenharmony_ci default: 12918c2ecf20Sopenharmony_ci dev_err(dev, "Command not supported\n"); 12928c2ecf20Sopenharmony_ci break; 12938c2ecf20Sopenharmony_ci } 12948c2ecf20Sopenharmony_ci 12958c2ecf20Sopenharmony_ci /* queue to hardware */ 12968c2ecf20Sopenharmony_ci mhi_add_ring_element(mhi_cntrl, ring); 12978c2ecf20Sopenharmony_ci read_lock_bh(&mhi_cntrl->pm_lock); 12988c2ecf20Sopenharmony_ci if (likely(MHI_DB_ACCESS_VALID(mhi_cntrl))) 12998c2ecf20Sopenharmony_ci mhi_ring_cmd_db(mhi_cntrl, mhi_cmd); 13008c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 13018c2ecf20Sopenharmony_ci spin_unlock_bh(&mhi_cmd->lock); 13028c2ecf20Sopenharmony_ci 13038c2ecf20Sopenharmony_ci return 0; 13048c2ecf20Sopenharmony_ci} 13058c2ecf20Sopenharmony_ci 13068c2ecf20Sopenharmony_cistatic void __mhi_unprepare_channel(struct mhi_controller *mhi_cntrl, 13078c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan) 13088c2ecf20Sopenharmony_ci{ 13098c2ecf20Sopenharmony_ci int ret; 13108c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 13118c2ecf20Sopenharmony_ci 13128c2ecf20Sopenharmony_ci dev_dbg(dev, "Entered: unprepare channel:%d\n", mhi_chan->chan); 13138c2ecf20Sopenharmony_ci 13148c2ecf20Sopenharmony_ci /* no more processing events for this channel */ 13158c2ecf20Sopenharmony_ci mutex_lock(&mhi_chan->mutex); 13168c2ecf20Sopenharmony_ci write_lock_irq(&mhi_chan->lock); 13178c2ecf20Sopenharmony_ci if (mhi_chan->ch_state != MHI_CH_STATE_ENABLED) { 13188c2ecf20Sopenharmony_ci write_unlock_irq(&mhi_chan->lock); 13198c2ecf20Sopenharmony_ci mutex_unlock(&mhi_chan->mutex); 13208c2ecf20Sopenharmony_ci return; 13218c2ecf20Sopenharmony_ci } 13228c2ecf20Sopenharmony_ci 13238c2ecf20Sopenharmony_ci mhi_chan->ch_state = MHI_CH_STATE_DISABLED; 13248c2ecf20Sopenharmony_ci write_unlock_irq(&mhi_chan->lock); 13258c2ecf20Sopenharmony_ci 13268c2ecf20Sopenharmony_ci reinit_completion(&mhi_chan->completion); 13278c2ecf20Sopenharmony_ci read_lock_bh(&mhi_cntrl->pm_lock); 13288c2ecf20Sopenharmony_ci if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { 13298c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 13308c2ecf20Sopenharmony_ci goto error_invalid_state; 13318c2ecf20Sopenharmony_ci } 13328c2ecf20Sopenharmony_ci 13338c2ecf20Sopenharmony_ci mhi_cntrl->wake_toggle(mhi_cntrl); 13348c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 13358c2ecf20Sopenharmony_ci 13368c2ecf20Sopenharmony_ci mhi_cntrl->runtime_get(mhi_cntrl); 13378c2ecf20Sopenharmony_ci mhi_cntrl->runtime_put(mhi_cntrl); 13388c2ecf20Sopenharmony_ci ret = mhi_send_cmd(mhi_cntrl, mhi_chan, MHI_CMD_RESET_CHAN); 13398c2ecf20Sopenharmony_ci if (ret) 13408c2ecf20Sopenharmony_ci goto error_invalid_state; 13418c2ecf20Sopenharmony_ci 13428c2ecf20Sopenharmony_ci /* even if it fails we will still reset */ 13438c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&mhi_chan->completion, 13448c2ecf20Sopenharmony_ci msecs_to_jiffies(mhi_cntrl->timeout_ms)); 13458c2ecf20Sopenharmony_ci if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS) 13468c2ecf20Sopenharmony_ci dev_err(dev, 13478c2ecf20Sopenharmony_ci "Failed to receive cmd completion, still resetting\n"); 13488c2ecf20Sopenharmony_ci 13498c2ecf20Sopenharmony_cierror_invalid_state: 13508c2ecf20Sopenharmony_ci if (!mhi_chan->offload_ch) { 13518c2ecf20Sopenharmony_ci mhi_reset_chan(mhi_cntrl, mhi_chan); 13528c2ecf20Sopenharmony_ci mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); 13538c2ecf20Sopenharmony_ci } 13548c2ecf20Sopenharmony_ci dev_dbg(dev, "chan:%d successfully resetted\n", mhi_chan->chan); 13558c2ecf20Sopenharmony_ci mutex_unlock(&mhi_chan->mutex); 13568c2ecf20Sopenharmony_ci} 13578c2ecf20Sopenharmony_ci 13588c2ecf20Sopenharmony_ciint mhi_prepare_channel(struct mhi_controller *mhi_cntrl, 13598c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan) 13608c2ecf20Sopenharmony_ci{ 13618c2ecf20Sopenharmony_ci int ret = 0; 13628c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 13638c2ecf20Sopenharmony_ci 13648c2ecf20Sopenharmony_ci dev_dbg(dev, "Preparing channel: %d\n", mhi_chan->chan); 13658c2ecf20Sopenharmony_ci 13668c2ecf20Sopenharmony_ci if (!(BIT(mhi_cntrl->ee) & mhi_chan->ee_mask)) { 13678c2ecf20Sopenharmony_ci dev_err(dev, 13688c2ecf20Sopenharmony_ci "Current EE: %s Required EE Mask: 0x%x for chan: %s\n", 13698c2ecf20Sopenharmony_ci TO_MHI_EXEC_STR(mhi_cntrl->ee), mhi_chan->ee_mask, 13708c2ecf20Sopenharmony_ci mhi_chan->name); 13718c2ecf20Sopenharmony_ci return -ENOTCONN; 13728c2ecf20Sopenharmony_ci } 13738c2ecf20Sopenharmony_ci 13748c2ecf20Sopenharmony_ci mutex_lock(&mhi_chan->mutex); 13758c2ecf20Sopenharmony_ci 13768c2ecf20Sopenharmony_ci /* If channel is not in disable state, do not allow it to start */ 13778c2ecf20Sopenharmony_ci if (mhi_chan->ch_state != MHI_CH_STATE_DISABLED) { 13788c2ecf20Sopenharmony_ci ret = -EIO; 13798c2ecf20Sopenharmony_ci dev_dbg(dev, "channel: %d is not in disabled state\n", 13808c2ecf20Sopenharmony_ci mhi_chan->chan); 13818c2ecf20Sopenharmony_ci goto error_init_chan; 13828c2ecf20Sopenharmony_ci } 13838c2ecf20Sopenharmony_ci 13848c2ecf20Sopenharmony_ci /* Check of client manages channel context for offload channels */ 13858c2ecf20Sopenharmony_ci if (!mhi_chan->offload_ch) { 13868c2ecf20Sopenharmony_ci ret = mhi_init_chan_ctxt(mhi_cntrl, mhi_chan); 13878c2ecf20Sopenharmony_ci if (ret) 13888c2ecf20Sopenharmony_ci goto error_init_chan; 13898c2ecf20Sopenharmony_ci } 13908c2ecf20Sopenharmony_ci 13918c2ecf20Sopenharmony_ci reinit_completion(&mhi_chan->completion); 13928c2ecf20Sopenharmony_ci read_lock_bh(&mhi_cntrl->pm_lock); 13938c2ecf20Sopenharmony_ci if (MHI_PM_IN_ERROR_STATE(mhi_cntrl->pm_state)) { 13948c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 13958c2ecf20Sopenharmony_ci ret = -EIO; 13968c2ecf20Sopenharmony_ci goto error_pm_state; 13978c2ecf20Sopenharmony_ci } 13988c2ecf20Sopenharmony_ci 13998c2ecf20Sopenharmony_ci mhi_cntrl->wake_toggle(mhi_cntrl); 14008c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 14018c2ecf20Sopenharmony_ci mhi_cntrl->runtime_get(mhi_cntrl); 14028c2ecf20Sopenharmony_ci mhi_cntrl->runtime_put(mhi_cntrl); 14038c2ecf20Sopenharmony_ci 14048c2ecf20Sopenharmony_ci ret = mhi_send_cmd(mhi_cntrl, mhi_chan, MHI_CMD_START_CHAN); 14058c2ecf20Sopenharmony_ci if (ret) 14068c2ecf20Sopenharmony_ci goto error_pm_state; 14078c2ecf20Sopenharmony_ci 14088c2ecf20Sopenharmony_ci ret = wait_for_completion_timeout(&mhi_chan->completion, 14098c2ecf20Sopenharmony_ci msecs_to_jiffies(mhi_cntrl->timeout_ms)); 14108c2ecf20Sopenharmony_ci if (!ret || mhi_chan->ccs != MHI_EV_CC_SUCCESS) { 14118c2ecf20Sopenharmony_ci ret = -EIO; 14128c2ecf20Sopenharmony_ci goto error_pm_state; 14138c2ecf20Sopenharmony_ci } 14148c2ecf20Sopenharmony_ci 14158c2ecf20Sopenharmony_ci write_lock_irq(&mhi_chan->lock); 14168c2ecf20Sopenharmony_ci mhi_chan->ch_state = MHI_CH_STATE_ENABLED; 14178c2ecf20Sopenharmony_ci write_unlock_irq(&mhi_chan->lock); 14188c2ecf20Sopenharmony_ci 14198c2ecf20Sopenharmony_ci /* Pre-allocate buffer for xfer ring */ 14208c2ecf20Sopenharmony_ci if (mhi_chan->pre_alloc) { 14218c2ecf20Sopenharmony_ci int nr_el = get_nr_avail_ring_elements(mhi_cntrl, 14228c2ecf20Sopenharmony_ci &mhi_chan->tre_ring); 14238c2ecf20Sopenharmony_ci size_t len = mhi_cntrl->buffer_len; 14248c2ecf20Sopenharmony_ci 14258c2ecf20Sopenharmony_ci while (nr_el--) { 14268c2ecf20Sopenharmony_ci void *buf; 14278c2ecf20Sopenharmony_ci struct mhi_buf_info info = { }; 14288c2ecf20Sopenharmony_ci buf = kmalloc(len, GFP_KERNEL); 14298c2ecf20Sopenharmony_ci if (!buf) { 14308c2ecf20Sopenharmony_ci ret = -ENOMEM; 14318c2ecf20Sopenharmony_ci goto error_pre_alloc; 14328c2ecf20Sopenharmony_ci } 14338c2ecf20Sopenharmony_ci 14348c2ecf20Sopenharmony_ci /* Prepare transfer descriptors */ 14358c2ecf20Sopenharmony_ci info.v_addr = buf; 14368c2ecf20Sopenharmony_ci info.cb_buf = buf; 14378c2ecf20Sopenharmony_ci info.len = len; 14388c2ecf20Sopenharmony_ci ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &info, MHI_EOT); 14398c2ecf20Sopenharmony_ci if (ret) { 14408c2ecf20Sopenharmony_ci kfree(buf); 14418c2ecf20Sopenharmony_ci goto error_pre_alloc; 14428c2ecf20Sopenharmony_ci } 14438c2ecf20Sopenharmony_ci } 14448c2ecf20Sopenharmony_ci 14458c2ecf20Sopenharmony_ci read_lock_bh(&mhi_cntrl->pm_lock); 14468c2ecf20Sopenharmony_ci if (MHI_DB_ACCESS_VALID(mhi_cntrl)) { 14478c2ecf20Sopenharmony_ci read_lock_irq(&mhi_chan->lock); 14488c2ecf20Sopenharmony_ci mhi_ring_chan_db(mhi_cntrl, mhi_chan); 14498c2ecf20Sopenharmony_ci read_unlock_irq(&mhi_chan->lock); 14508c2ecf20Sopenharmony_ci } 14518c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 14528c2ecf20Sopenharmony_ci } 14538c2ecf20Sopenharmony_ci 14548c2ecf20Sopenharmony_ci mutex_unlock(&mhi_chan->mutex); 14558c2ecf20Sopenharmony_ci 14568c2ecf20Sopenharmony_ci dev_dbg(dev, "Chan: %d successfully moved to start state\n", 14578c2ecf20Sopenharmony_ci mhi_chan->chan); 14588c2ecf20Sopenharmony_ci 14598c2ecf20Sopenharmony_ci return 0; 14608c2ecf20Sopenharmony_ci 14618c2ecf20Sopenharmony_cierror_pm_state: 14628c2ecf20Sopenharmony_ci if (!mhi_chan->offload_ch) 14638c2ecf20Sopenharmony_ci mhi_deinit_chan_ctxt(mhi_cntrl, mhi_chan); 14648c2ecf20Sopenharmony_ci 14658c2ecf20Sopenharmony_cierror_init_chan: 14668c2ecf20Sopenharmony_ci mutex_unlock(&mhi_chan->mutex); 14678c2ecf20Sopenharmony_ci 14688c2ecf20Sopenharmony_ci return ret; 14698c2ecf20Sopenharmony_ci 14708c2ecf20Sopenharmony_cierror_pre_alloc: 14718c2ecf20Sopenharmony_ci mutex_unlock(&mhi_chan->mutex); 14728c2ecf20Sopenharmony_ci __mhi_unprepare_channel(mhi_cntrl, mhi_chan); 14738c2ecf20Sopenharmony_ci 14748c2ecf20Sopenharmony_ci return ret; 14758c2ecf20Sopenharmony_ci} 14768c2ecf20Sopenharmony_ci 14778c2ecf20Sopenharmony_cistatic void mhi_mark_stale_events(struct mhi_controller *mhi_cntrl, 14788c2ecf20Sopenharmony_ci struct mhi_event *mhi_event, 14798c2ecf20Sopenharmony_ci struct mhi_event_ctxt *er_ctxt, 14808c2ecf20Sopenharmony_ci int chan) 14818c2ecf20Sopenharmony_ci 14828c2ecf20Sopenharmony_ci{ 14838c2ecf20Sopenharmony_ci struct mhi_tre *dev_rp, *local_rp; 14848c2ecf20Sopenharmony_ci struct mhi_ring *ev_ring; 14858c2ecf20Sopenharmony_ci struct device *dev = &mhi_cntrl->mhi_dev->dev; 14868c2ecf20Sopenharmony_ci unsigned long flags; 14878c2ecf20Sopenharmony_ci dma_addr_t ptr; 14888c2ecf20Sopenharmony_ci 14898c2ecf20Sopenharmony_ci dev_dbg(dev, "Marking all events for chan: %d as stale\n", chan); 14908c2ecf20Sopenharmony_ci 14918c2ecf20Sopenharmony_ci ev_ring = &mhi_event->ring; 14928c2ecf20Sopenharmony_ci 14938c2ecf20Sopenharmony_ci /* mark all stale events related to channel as STALE event */ 14948c2ecf20Sopenharmony_ci spin_lock_irqsave(&mhi_event->lock, flags); 14958c2ecf20Sopenharmony_ci 14968c2ecf20Sopenharmony_ci ptr = er_ctxt->rp; 14978c2ecf20Sopenharmony_ci if (!is_valid_ring_ptr(ev_ring, ptr)) { 14988c2ecf20Sopenharmony_ci dev_err(&mhi_cntrl->mhi_dev->dev, 14998c2ecf20Sopenharmony_ci "Event ring rp points outside of the event ring\n"); 15008c2ecf20Sopenharmony_ci dev_rp = ev_ring->rp; 15018c2ecf20Sopenharmony_ci } else { 15028c2ecf20Sopenharmony_ci dev_rp = mhi_to_virtual(ev_ring, ptr); 15038c2ecf20Sopenharmony_ci } 15048c2ecf20Sopenharmony_ci 15058c2ecf20Sopenharmony_ci local_rp = ev_ring->rp; 15068c2ecf20Sopenharmony_ci while (dev_rp != local_rp) { 15078c2ecf20Sopenharmony_ci if (MHI_TRE_GET_EV_TYPE(local_rp) == MHI_PKT_TYPE_TX_EVENT && 15088c2ecf20Sopenharmony_ci chan == MHI_TRE_GET_EV_CHID(local_rp)) 15098c2ecf20Sopenharmony_ci local_rp->dword[1] = MHI_TRE_EV_DWORD1(chan, 15108c2ecf20Sopenharmony_ci MHI_PKT_TYPE_STALE_EVENT); 15118c2ecf20Sopenharmony_ci local_rp++; 15128c2ecf20Sopenharmony_ci if (local_rp == (ev_ring->base + ev_ring->len)) 15138c2ecf20Sopenharmony_ci local_rp = ev_ring->base; 15148c2ecf20Sopenharmony_ci } 15158c2ecf20Sopenharmony_ci 15168c2ecf20Sopenharmony_ci dev_dbg(dev, "Finished marking events as stale events\n"); 15178c2ecf20Sopenharmony_ci spin_unlock_irqrestore(&mhi_event->lock, flags); 15188c2ecf20Sopenharmony_ci} 15198c2ecf20Sopenharmony_ci 15208c2ecf20Sopenharmony_cistatic void mhi_reset_data_chan(struct mhi_controller *mhi_cntrl, 15218c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan) 15228c2ecf20Sopenharmony_ci{ 15238c2ecf20Sopenharmony_ci struct mhi_ring *buf_ring, *tre_ring; 15248c2ecf20Sopenharmony_ci struct mhi_result result; 15258c2ecf20Sopenharmony_ci 15268c2ecf20Sopenharmony_ci /* Reset any pending buffers */ 15278c2ecf20Sopenharmony_ci buf_ring = &mhi_chan->buf_ring; 15288c2ecf20Sopenharmony_ci tre_ring = &mhi_chan->tre_ring; 15298c2ecf20Sopenharmony_ci result.transaction_status = -ENOTCONN; 15308c2ecf20Sopenharmony_ci result.bytes_xferd = 0; 15318c2ecf20Sopenharmony_ci while (tre_ring->rp != tre_ring->wp) { 15328c2ecf20Sopenharmony_ci struct mhi_buf_info *buf_info = buf_ring->rp; 15338c2ecf20Sopenharmony_ci 15348c2ecf20Sopenharmony_ci if (mhi_chan->dir == DMA_TO_DEVICE) 15358c2ecf20Sopenharmony_ci atomic_dec(&mhi_cntrl->pending_pkts); 15368c2ecf20Sopenharmony_ci 15378c2ecf20Sopenharmony_ci if (!buf_info->pre_mapped) 15388c2ecf20Sopenharmony_ci mhi_cntrl->unmap_single(mhi_cntrl, buf_info); 15398c2ecf20Sopenharmony_ci 15408c2ecf20Sopenharmony_ci mhi_del_ring_element(mhi_cntrl, buf_ring); 15418c2ecf20Sopenharmony_ci mhi_del_ring_element(mhi_cntrl, tre_ring); 15428c2ecf20Sopenharmony_ci 15438c2ecf20Sopenharmony_ci if (mhi_chan->pre_alloc) { 15448c2ecf20Sopenharmony_ci kfree(buf_info->cb_buf); 15458c2ecf20Sopenharmony_ci } else { 15468c2ecf20Sopenharmony_ci result.buf_addr = buf_info->cb_buf; 15478c2ecf20Sopenharmony_ci mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); 15488c2ecf20Sopenharmony_ci } 15498c2ecf20Sopenharmony_ci } 15508c2ecf20Sopenharmony_ci} 15518c2ecf20Sopenharmony_ci 15528c2ecf20Sopenharmony_civoid mhi_reset_chan(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan) 15538c2ecf20Sopenharmony_ci{ 15548c2ecf20Sopenharmony_ci struct mhi_event *mhi_event; 15558c2ecf20Sopenharmony_ci struct mhi_event_ctxt *er_ctxt; 15568c2ecf20Sopenharmony_ci int chan = mhi_chan->chan; 15578c2ecf20Sopenharmony_ci 15588c2ecf20Sopenharmony_ci /* Nothing to reset, client doesn't queue buffers */ 15598c2ecf20Sopenharmony_ci if (mhi_chan->offload_ch) 15608c2ecf20Sopenharmony_ci return; 15618c2ecf20Sopenharmony_ci 15628c2ecf20Sopenharmony_ci read_lock_bh(&mhi_cntrl->pm_lock); 15638c2ecf20Sopenharmony_ci mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index]; 15648c2ecf20Sopenharmony_ci er_ctxt = &mhi_cntrl->mhi_ctxt->er_ctxt[mhi_chan->er_index]; 15658c2ecf20Sopenharmony_ci 15668c2ecf20Sopenharmony_ci mhi_mark_stale_events(mhi_cntrl, mhi_event, er_ctxt, chan); 15678c2ecf20Sopenharmony_ci 15688c2ecf20Sopenharmony_ci mhi_reset_data_chan(mhi_cntrl, mhi_chan); 15698c2ecf20Sopenharmony_ci 15708c2ecf20Sopenharmony_ci read_unlock_bh(&mhi_cntrl->pm_lock); 15718c2ecf20Sopenharmony_ci} 15728c2ecf20Sopenharmony_ci 15738c2ecf20Sopenharmony_ci/* Move channel to start state */ 15748c2ecf20Sopenharmony_ciint mhi_prepare_for_transfer(struct mhi_device *mhi_dev) 15758c2ecf20Sopenharmony_ci{ 15768c2ecf20Sopenharmony_ci int ret, dir; 15778c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; 15788c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan; 15798c2ecf20Sopenharmony_ci 15808c2ecf20Sopenharmony_ci for (dir = 0; dir < 2; dir++) { 15818c2ecf20Sopenharmony_ci mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan; 15828c2ecf20Sopenharmony_ci if (!mhi_chan) 15838c2ecf20Sopenharmony_ci continue; 15848c2ecf20Sopenharmony_ci 15858c2ecf20Sopenharmony_ci ret = mhi_prepare_channel(mhi_cntrl, mhi_chan); 15868c2ecf20Sopenharmony_ci if (ret) 15878c2ecf20Sopenharmony_ci goto error_open_chan; 15888c2ecf20Sopenharmony_ci } 15898c2ecf20Sopenharmony_ci 15908c2ecf20Sopenharmony_ci return 0; 15918c2ecf20Sopenharmony_ci 15928c2ecf20Sopenharmony_cierror_open_chan: 15938c2ecf20Sopenharmony_ci for (--dir; dir >= 0; dir--) { 15948c2ecf20Sopenharmony_ci mhi_chan = dir ? mhi_dev->dl_chan : mhi_dev->ul_chan; 15958c2ecf20Sopenharmony_ci if (!mhi_chan) 15968c2ecf20Sopenharmony_ci continue; 15978c2ecf20Sopenharmony_ci 15988c2ecf20Sopenharmony_ci __mhi_unprepare_channel(mhi_cntrl, mhi_chan); 15998c2ecf20Sopenharmony_ci } 16008c2ecf20Sopenharmony_ci 16018c2ecf20Sopenharmony_ci return ret; 16028c2ecf20Sopenharmony_ci} 16038c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_prepare_for_transfer); 16048c2ecf20Sopenharmony_ci 16058c2ecf20Sopenharmony_civoid mhi_unprepare_from_transfer(struct mhi_device *mhi_dev) 16068c2ecf20Sopenharmony_ci{ 16078c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; 16088c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan; 16098c2ecf20Sopenharmony_ci int dir; 16108c2ecf20Sopenharmony_ci 16118c2ecf20Sopenharmony_ci for (dir = 0; dir < 2; dir++) { 16128c2ecf20Sopenharmony_ci mhi_chan = dir ? mhi_dev->ul_chan : mhi_dev->dl_chan; 16138c2ecf20Sopenharmony_ci if (!mhi_chan) 16148c2ecf20Sopenharmony_ci continue; 16158c2ecf20Sopenharmony_ci 16168c2ecf20Sopenharmony_ci __mhi_unprepare_channel(mhi_cntrl, mhi_chan); 16178c2ecf20Sopenharmony_ci } 16188c2ecf20Sopenharmony_ci} 16198c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_unprepare_from_transfer); 16208c2ecf20Sopenharmony_ci 16218c2ecf20Sopenharmony_ciint mhi_poll(struct mhi_device *mhi_dev, u32 budget) 16228c2ecf20Sopenharmony_ci{ 16238c2ecf20Sopenharmony_ci struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; 16248c2ecf20Sopenharmony_ci struct mhi_chan *mhi_chan = mhi_dev->dl_chan; 16258c2ecf20Sopenharmony_ci struct mhi_event *mhi_event = &mhi_cntrl->mhi_event[mhi_chan->er_index]; 16268c2ecf20Sopenharmony_ci int ret; 16278c2ecf20Sopenharmony_ci 16288c2ecf20Sopenharmony_ci spin_lock_bh(&mhi_event->lock); 16298c2ecf20Sopenharmony_ci ret = mhi_event->process_event(mhi_cntrl, mhi_event, budget); 16308c2ecf20Sopenharmony_ci spin_unlock_bh(&mhi_event->lock); 16318c2ecf20Sopenharmony_ci 16328c2ecf20Sopenharmony_ci return ret; 16338c2ecf20Sopenharmony_ci} 16348c2ecf20Sopenharmony_ciEXPORT_SYMBOL_GPL(mhi_poll); 1635