1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2019, The Linux Foundation. All rights reserved.
- */
- #include <linux/dma-mapping.h>
- #include <linux/list.h>
- #include <linux/slab.h>
- #include <linux/device.h>
- #include <linux/module.h>
- #include <linux/mhi.h>
- #include <linux/msm_gsi.h>
- #include <linux/delay.h>
- #include <linux/log2.h>
- #include <linux/gfp.h>
- #include "../ipa_common_i.h"
- #include "ipa_i.h"
- #define IPA_MPM_DRV_NAME "ipa_mpm"
- #define IPA_MPM_DBG(fmt, args...) \
- do { \
- pr_debug(IPA_MPM_DRV_NAME " %s:%d " fmt, \
- __func__, __LINE__, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
- IPA_MPM_DRV_NAME " %s:%d " fmt, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
- IPA_MPM_DRV_NAME " %s:%d " fmt, ## args); \
- } while (0)
- #define IPA_MPM_DBG_LOW(fmt, args...) \
- do { \
- pr_debug(IPA_MPM_DRV_NAME " %s:%d " fmt, \
- __func__, __LINE__, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
- IPA_MPM_DRV_NAME " %s:%d " fmt, ## args); \
- } while (0)
- #define IPA_MPM_ERR(fmt, args...) \
- do { \
- pr_err(IPA_MPM_DRV_NAME " %s:%d " fmt, \
- __func__, __LINE__, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf(), \
- IPA_MPM_DRV_NAME " %s:%d " fmt, ## args); \
- IPA_IPC_LOGGING(ipa_get_ipc_logbuf_low(), \
- IPA_MPM_DRV_NAME " %s:%d " fmt, ## args); \
- } while (0)
- #define IPA_MPM_FUNC_ENTRY() \
- IPA_MPM_DBG("ENTRY\n")
- #define IPA_MPM_FUNC_EXIT() \
- IPA_MPM_DBG("EXIT\n")
- #define IPA_MPM_MAX_MHIP_CHAN 3
- #define IPA_MPM_NUM_RING_DESC 6
- #define IPA_MPM_RING_LEN IPA_MPM_NUM_RING_DESC
- #define IPA_MPM_MHI_HOST_UL_CHANNEL 4
- #define IPA_MPM_MHI_HOST_DL_CHANNEL 5
- #define TETH_AGGR_TIME_LIMIT 10000 /* 10ms */
- #define TETH_AGGR_BYTE_LIMIT 24
- #define TETH_AGGR_DL_BYTE_LIMIT 16
- #define TRE_BUFF_SIZE 32768
- #define IPA_HOLB_TMR_EN 0x1
- #define IPA_HOLB_TMR_DIS 0x0
- #define RNDIS_IPA_DFLT_RT_HDL 0
- #define IPA_POLL_FOR_EMPTINESS_NUM 50
- #define IPA_POLL_FOR_EMPTINESS_SLEEP_USEC 20
- #define IPA_CHANNEL_STOP_IN_PROC_TO_MSEC 5
- #define IPA_CHANNEL_STOP_IN_PROC_SLEEP_USEC 200
- #define IPA_MHIP_HOLB_TMO 31 /* value to match granularity on ipa HW 4.5 */
- #define IPA_MPM_FLOW_CTRL_ADD 1
- #define IPA_MPM_FLOW_CTRL_DELETE 0
- enum mhip_re_type {
- MHIP_RE_XFER = 0x2,
- MHIP_RE_NOP = 0x4,
- };
- enum ipa_mpm_mhi_ch_id_type {
- IPA_MPM_MHIP_CH_ID_0,
- IPA_MPM_MHIP_CH_ID_1,
- IPA_MPM_MHIP_CH_ID_2,
- IPA_MPM_MHIP_CH_ID_MAX,
- };
- enum ipa_mpm_dma_data_direction {
- DMA_HIPA_BIDIRECTIONAL = 0,
- DMA_TO_HIPA = 1,
- DMA_FROM_HIPA = 2,
- DMA_HIPA_NONE = 3,
- };
- enum ipa_mpm_ipa_teth_client_type {
- IPA_MPM_MHIP_USB,
- IPA_MPM_MHIP_WIFI,
- };
- enum ipa_mpm_mhip_client_type {
- IPA_MPM_MHIP_INIT,
- /* USB RMNET CLIENT */
- IPA_MPM_MHIP_USB_RMNET,
- /* USB RNDIS / WIFI CLIENT */
- IPA_MPM_MHIP_TETH,
- /* USB DPL CLIENT */
- IPA_MPM_MHIP_USB_DPL,
- IPA_MPM_MHIP_NONE,
- };
- enum ipa_mpm_clk_vote_type {
- CLK_ON,
- CLK_OFF,
- };
- enum mhip_status_type {
- MHIP_STATUS_SUCCESS,
- MHIP_STATUS_NO_OP,
- MHIP_STATUS_FAIL,
- MHIP_STATUS_BAD_STATE,
- MHIP_STATUS_EP_NOT_FOUND,
- MHIP_STATUS_EP_NOT_READY,
- };
- enum mhip_smmu_domain_type {
- MHIP_SMMU_DOMAIN_IPA,
- MHIP_SMMU_DOMAIN_PCIE,
- MHIP_SMMU_DOMAIN_NONE,
- };
- enum ipa_mpm_start_stop_type {
- MPM_MHIP_STOP,
- MPM_MHIP_START,
- };
- /* each pair of UL/DL channels are defined below */
- static const struct mhi_device_id mhi_driver_match_table[] = {
- { .chan = "IP_HW_MHIP_0" }, /* for rndis/Wifi teth pipes */
- { .chan = "IP_HW_MHIP_1" }, /* for MHIP rmnet */
- { .chan = "IP_HW_ADPL" }, /* ADPL/ODL DL pipe */
- };
- static const char *ipa_mpm_mhip_chan_str[IPA_MPM_MHIP_CH_ID_MAX] = {
- __stringify(IPA_MPM_MHIP_TETH),
- __stringify(IPA_MPM_MHIP_USB_RMNET),
- __stringify(IPA_MPM_MHIP_USB_DPL),
- };
- /*
- * MHI PRIME GSI Descriptor format that Host IPA uses.
- */
- struct __packed mhi_p_desc {
- uint64_t buffer_ptr;
- uint16_t buff_len;
- uint16_t resvd1;
- uint16_t chain : 1;
- uint16_t resvd4 : 7;
- uint16_t ieob : 1;
- uint16_t ieot : 1;
- uint16_t bei : 1;
- uint16_t sct : 1;
- uint16_t resvd3 : 4;
- uint8_t re_type;
- uint8_t resvd2;
- };
- /*
- * MHI PRIME Channel Context and Event Context Array
- * Information that is sent to Device IPA.
- */
- struct ipa_mpm_channel_context_type {
- u32 chstate : 8;
- u32 reserved1 : 24;
- u32 chtype;
- u32 erindex;
- u64 rbase;
- u64 rlen;
- u64 reserved2;
- u64 reserved3;
- } __packed;
- struct ipa_mpm_event_context_type {
- u32 reserved1 : 8;
- u32 update_rp_modc : 8;
- u32 update_rp_intmodt : 16;
- u32 ertype;
- u32 update_rp_addr;
- u64 rbase;
- u64 rlen;
- u32 buff_size : 16;
- u32 reserved2 : 16;
- u32 reserved3;
- u64 reserved4;
- } __packed;
- struct ipa_mpm_pipes_info_type {
- enum ipa_client_type ipa_client;
- struct ipa_ep_cfg ep_cfg;
- };
- struct ipa_mpm_channel_type {
- struct ipa_mpm_pipes_info_type dl_cons;
- struct ipa_mpm_pipes_info_type ul_prod;
- enum ipa_mpm_mhip_client_type mhip_client;
- };
- static struct ipa_mpm_channel_type ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_MAX];
- /* For configuring IPA_CLIENT_MHI_PRIME_TETH_CONS */
- static struct ipa_ep_cfg mhip_dl_teth_ep_cfg = {
- .mode = {
- .mode = IPA_BASIC,
- .dst = IPA_CLIENT_MHI_PRIME_TETH_CONS,
- },
- .hdr = {
- .hdr_len = 4,
- .hdr_ofst_metadata_valid = 1,
- .hdr_ofst_metadata = 1,
- .hdr_ofst_pkt_size_valid = 1,
- .hdr_ofst_pkt_size = 2,
- },
- .hdr_ext = {
- .hdr_total_len_or_pad_valid = true,
- .hdr_payload_len_inc_padding = true,
- },
- .aggr = {
- .aggr_en = IPA_ENABLE_DEAGGR,
- .aggr = IPA_QCMAP,
- .aggr_byte_limit = TETH_AGGR_DL_BYTE_LIMIT,
- .aggr_time_limit = TETH_AGGR_TIME_LIMIT,
- },
- };
- static struct ipa_ep_cfg mhip_ul_teth_ep_cfg = {
- .mode = {
- .mode = IPA_BASIC,
- .dst = IPA_CLIENT_MHI_PRIME_TETH_PROD,
- },
- .hdr = {
- .hdr_len = 4,
- .hdr_ofst_metadata_valid = 1,
- .hdr_ofst_metadata = 0,
- .hdr_ofst_pkt_size_valid = 1,
- .hdr_ofst_pkt_size = 2,
- },
- .hdr_ext = {
- .hdr_total_len_or_pad_valid = true,
- .hdr_payload_len_inc_padding = true,
- },
- .aggr = {
- .aggr_en = IPA_ENABLE_AGGR,
- .aggr = IPA_QCMAP,
- .aggr_byte_limit = TETH_AGGR_BYTE_LIMIT,
- .aggr_time_limit = TETH_AGGR_TIME_LIMIT,
- },
- };
- /* WARNING!! Temporary for rndis intgration only */
- /* For configuring IPA_CLIENT_MHIP_RMNET_PROD */
- static struct ipa_ep_cfg mhip_dl_rmnet_ep_cfg = {
- .mode = {
- .mode = IPA_DMA,
- .dst = IPA_CLIENT_USB_CONS,
- },
- };
- /* For configuring IPA_CLIENT_MHIP_RMNET_CONS */
- static struct ipa_ep_cfg mhip_ul_rmnet_ep_cfg = {
- .mode = {
- .mode = IPA_DMA,
- .dst = IPA_CLIENT_USB_CONS,
- },
- };
- /* For configuring IPA_CLIENT_MHIP_DPL_PROD using USB*/
- static struct ipa_ep_cfg mhip_dl_dpl_ep_cfg = {
- .mode = {
- .mode = IPA_DMA,
- .dst = IPA_CLIENT_USB_DPL_CONS,
- },
- };
- struct ipa_mpm_iova_addr {
- dma_addr_t base;
- unsigned int size;
- };
- struct ipa_mpm_dev_info {
- struct platform_device *pdev;
- struct device *dev;
- bool ipa_smmu_enabled;
- bool pcie_smmu_enabled;
- struct ipa_mpm_iova_addr ctrl;
- struct ipa_mpm_iova_addr data;
- u32 chdb_base;
- u32 erdb_base;
- bool is_cache_coherent;
- };
- struct ipa_mpm_event_props {
- u16 id;
- phys_addr_t device_db;
- struct ipa_mpm_event_context_type ev_ctx;
- };
- struct ipa_mpm_channel_props {
- u16 id;
- phys_addr_t device_db;
- struct ipa_mpm_channel_context_type ch_ctx;
- };
- enum ipa_mpm_gsi_state {
- GSI_ERR,
- GSI_INIT,
- GSI_ALLOCATED,
- GSI_STARTED,
- GSI_STOPPED,
- };
- enum ipa_mpm_remote_state {
- MPM_MHIP_REMOTE_STOP,
- MPM_MHIP_REMOTE_START,
- MPM_MHIP_REMOTE_ERR,
- };
- struct ipa_mpm_channel {
- struct ipa_mpm_channel_props chan_props;
- struct ipa_mpm_event_props evt_props;
- enum ipa_mpm_gsi_state gsi_state;
- dma_addr_t db_host_iova;
- dma_addr_t db_device_iova;
- };
- enum ipa_mpm_teth_state {
- IPA_MPM_TETH_INIT = 0,
- IPA_MPM_TETH_INPROGRESS,
- IPA_MPM_TETH_CONNECTED,
- };
- enum ipa_mpm_mhip_chan {
- IPA_MPM_MHIP_CHAN_UL,
- IPA_MPM_MHIP_CHAN_DL,
- IPA_MPM_MHIP_CHAN_BOTH,
- };
- struct ipa_mpm_clk_cnt_type {
- atomic_t pcie_clk_cnt;
- atomic_t ipa_clk_cnt;
- };
- struct producer_rings {
- struct mhi_p_desc *tr_va;
- struct mhi_p_desc *er_va;
- void *tr_buff_va[IPA_MPM_RING_LEN];
- dma_addr_t tr_pa;
- dma_addr_t er_pa;
- dma_addr_t tr_buff_c_iova[IPA_MPM_RING_LEN];
- /*
- * The iova generated for AP CB,
- * used only for dma_map_single to flush the cache.
- */
- dma_addr_t ap_iova_er;
- dma_addr_t ap_iova_tr;
- dma_addr_t ap_iova_buff[IPA_MPM_RING_LEN];
- };
- struct ipa_mpm_mhi_driver {
- struct mhi_device *mhi_dev;
- struct producer_rings ul_prod_ring;
- struct producer_rings dl_prod_ring;
- struct ipa_mpm_channel ul_prod;
- struct ipa_mpm_channel dl_cons;
- enum ipa_mpm_mhip_client_type mhip_client;
- enum ipa_mpm_teth_state teth_state;
- bool init_complete;
- /* General MPM mutex to protect concurrent update of MPM GSI states */
- struct mutex mutex;
- /*
- * Mutex to protect mhi_dev update/ access, for concurrency such as
- * 5G SSR and USB disconnect/connect.
- */
- struct mutex mhi_mutex;
- bool in_lpm;
- struct ipa_mpm_clk_cnt_type clk_cnt;
- enum ipa_mpm_remote_state remote_state;
- };
- struct ipa_mpm_context {
- struct ipa_mpm_dev_info dev_info;
- struct ipa_mpm_mhi_driver md[IPA_MPM_MAX_MHIP_CHAN];
- struct mutex mutex;
- atomic_t probe_cnt;
- atomic_t pcie_clk_total_cnt;
- atomic_t ipa_clk_total_cnt;
- atomic_t flow_ctrl_mask;
- atomic_t adpl_over_usb_available;
- struct device *parent_pdev;
- struct ipa_smmu_cb_ctx carved_smmu_cb;
- struct device *mhi_parent_dev;
- };
- #define IPA_MPM_DESC_SIZE (sizeof(struct mhi_p_desc))
- #define IPA_MPM_RING_TOTAL_SIZE (IPA_MPM_RING_LEN * IPA_MPM_DESC_SIZE)
- /* WA: Make the IPA_MPM_PAGE_SIZE from 16k (next power of ring size) to
- * 32k. This is to make sure IOMMU map happens for the same size
- * for all TR/ER and doorbells.
- */
- #define IPA_MPM_PAGE_SIZE TRE_BUFF_SIZE
- static struct ipa_mpm_context *ipa_mpm_ctx;
- static struct platform_device *m_pdev;
- static int ipa_mpm_mhi_probe_cb(struct mhi_device *,
- const struct mhi_device_id *);
- static void ipa_mpm_mhi_remove_cb(struct mhi_device *);
- static void ipa_mpm_mhi_status_cb(struct mhi_device *, enum MHI_CB);
- static void ipa_mpm_change_teth_state(int probe_id,
- enum ipa_mpm_teth_state ip_state);
- static void ipa_mpm_change_gsi_state(int probe_id,
- enum ipa_mpm_mhip_chan mhip_chan,
- enum ipa_mpm_gsi_state next_state);
- static int ipa_mpm_probe(struct platform_device *pdev);
- static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote,
- int probe_id, bool is_force, bool *is_acted);
- static void ipa_mpm_vote_unvote_ipa_clk(enum ipa_mpm_clk_vote_type vote,
- int probe_id);
- static enum mhip_status_type ipa_mpm_start_stop_mhip_chan(
- enum ipa_mpm_mhip_chan mhip_chan,
- int probe_id,
- enum ipa_mpm_start_stop_type start_stop);
- static int ipa_mpm_start_mhip_holb_tmo(u32 clnt_hdl);
- static struct mhi_driver mhi_driver = {
- .id_table = mhi_driver_match_table,
- .probe = ipa_mpm_mhi_probe_cb,
- .remove = ipa_mpm_mhi_remove_cb,
- .status_cb = ipa_mpm_mhi_status_cb,
- .driver = {
- .name = IPA_MPM_DRV_NAME,
- .owner = THIS_MODULE,
- },
- };
- static void ipa_mpm_ipa3_delayed_probe(struct work_struct *work)
- {
- (void)ipa_mpm_probe(m_pdev);
- }
- static DECLARE_WORK(ipa_mpm_ipa3_scheduled_probe, ipa_mpm_ipa3_delayed_probe);
- static void ipa_mpm_ipa3_ready_cb(void *user_data)
- {
- struct platform_device *pdev = (struct platform_device *)(user_data);
- m_pdev = pdev;
- IPA_MPM_DBG("IPA ready callback has been triggered\n");
- schedule_work(&ipa_mpm_ipa3_scheduled_probe);
- }
- static void ipa_mpm_gsi_evt_ring_err_cb(struct gsi_evt_err_notify *err_data)
- {
- IPA_MPM_ERR("GSI EVT RING ERROR, not expected..\n");
- ipa_assert();
- }
- static void ipa_mpm_gsi_chan_err_cb(struct gsi_chan_err_notify *err_data)
- {
- IPA_MPM_ERR("GSI CHAN ERROR, not expected..\n");
- ipa_assert();
- }
- static int ipa_mpm_set_dma_mode(enum ipa_client_type src_pipe,
- enum ipa_client_type dst_pipe, bool reset)
- {
- int result = 0;
- struct ipa_ep_cfg ep_cfg = { { 0 } };
- IPA_MPM_FUNC_ENTRY();
- IPA_MPM_DBG("DMA from %d to %d reset=%d\n", src_pipe, dst_pipe, reset);
- /* Reset to basic if reset = 1, otherwise set to DMA */
- if (reset)
- ep_cfg.mode.mode = IPA_BASIC;
- else
- ep_cfg.mode.mode = IPA_DMA;
- ep_cfg.mode.dst = dst_pipe;
- ep_cfg.seq.set_dynamic = true;
- result = ipa_cfg_ep(ipa_get_ep_mapping(src_pipe), &ep_cfg);
- IPA_MPM_FUNC_EXIT();
- return result;
- }
- static int ipa_mpm_start_mhip_holb_tmo(u32 clnt_hdl)
- {
- struct ipa_ep_cfg_holb holb_cfg;
- memset(&holb_cfg, 0, sizeof(holb_cfg));
- holb_cfg.en = IPA_HOLB_TMR_EN;
- /* 31 ms timer, which is less than tag timeout */
- holb_cfg.tmr_val = IPA_MHIP_HOLB_TMO;
- return ipa3_cfg_ep_holb(clnt_hdl, &holb_cfg);
- }
- /**
- * ipa_mpm_smmu_map() - SMMU maps ring and the buffer pointer.
- * @va_addr: virtual address that needs to be mapped
- * @sz: size of the address to be mapped
- * @dir: ipa_mpm_dma_data_direction
- * @ap_cb_iova: iova for AP context bank
- *
- * This function SMMU maps both ring and the buffer pointer.
- * The ring pointers will be aligned to ring size and
- * the buffer pointers should be aligned to buffer size.
- *
- * Returns: iova of the mapped address
- */
- static dma_addr_t ipa_mpm_smmu_map(void *va_addr,
- int sz,
- int dir,
- dma_addr_t *ap_cb_iova)
- {
- struct iommu_domain *ipa_smmu_domain, *pcie_smmu_domain;
- phys_addr_t phys_addr;
- dma_addr_t iova;
- int smmu_enabled;
- unsigned long iova_p;
- phys_addr_t pa_p;
- u32 size_p;
- int prot = IOMMU_READ | IOMMU_WRITE;
- struct ipa_smmu_cb_ctx *cb = &ipa_mpm_ctx->carved_smmu_cb;
- unsigned long carved_iova = roundup(cb->next_addr, IPA_MPM_PAGE_SIZE);
- int ret = 0;
- /* check cache coherent */
- if (ipa_mpm_ctx->dev_info.is_cache_coherent) {
- IPA_MPM_DBG_LOW("enable cache coherent\n");
- prot |= IOMMU_CACHE;
- }
- if (carved_iova >= cb->va_end) {
- IPA_MPM_ERR("running out of carved_iova %lx\n", carved_iova);
- ipa_assert();
- }
- /*
- * Both Host IPA and PCIE SMMU should be enabled or disabled
- * for proceed.
- * If SMMU Enabled => iova == pa
- * If SMMU Disabled => iova == iommu mapped iova
- * dma_map_single ensures cache is flushed and the memory is not
- * touched again until dma_unmap_single() is called
- */
- smmu_enabled = (ipa_mpm_ctx->dev_info.ipa_smmu_enabled &&
- ipa_mpm_ctx->dev_info.pcie_smmu_enabled) ? 1 : 0;
- if (smmu_enabled) {
- /* Map the phys addr to both PCIE and IPA AP CB
- * from the carved out common iova range.
- */
- ipa_smmu_domain = ipa3_get_smmu_domain();
- if (!ipa_smmu_domain) {
- IPA_MPM_ERR("invalid IPA smmu domain\n");
- ipa_assert();
- }
- if (!ipa_mpm_ctx->mhi_parent_dev) {
- IPA_MPM_ERR("invalid PCIE SMMU domain\n");
- ipa_assert();
- }
- phys_addr = virt_to_phys((void *) va_addr);
- IPA_SMMU_ROUND_TO_PAGE(carved_iova, phys_addr, sz,
- iova_p, pa_p, size_p);
- /* Flush the cache with dma_map_single for IPA AP CB */
- *ap_cb_iova = dma_map_single(ipa3_ctx->pdev, va_addr,
- size_p, dir);
- if (dma_mapping_error(ipa3_ctx->pdev, *ap_cb_iova)) {
- IPA_MPM_ERR("dma_map_single failure for entry\n");
- goto fail_dma_mapping;
- }
- ret = ipa3_iommu_map(ipa_smmu_domain, iova_p,
- pa_p, size_p, prot);
- if (ret) {
- IPA_MPM_ERR("IPA IOMMU returned failure, ret = %d\n",
- ret);
- ipa_assert();
- }
- pcie_smmu_domain = iommu_get_domain_for_dev(
- ipa_mpm_ctx->mhi_parent_dev);
- if (!pcie_smmu_domain) {
- IPA_MPM_ERR("invalid pcie smmu domain\n");
- ipa_assert();
- }
- ret = iommu_map(pcie_smmu_domain, iova_p, pa_p, size_p, prot);
- if (ret) {
- IPA_MPM_ERR("PCIe IOMMU returned failure, ret = %d\n",
- ret);
- ipa_assert();
- }
- cb->next_addr = iova_p + size_p;
- iova = iova_p;
- } else {
- iova = dma_map_single(ipa3_ctx->pdev, va_addr,
- IPA_MPM_RING_TOTAL_SIZE, dir);
- if (dma_mapping_error(ipa3_ctx->pdev, iova)) {
- IPA_MPM_ERR("dma_map_single failure for entry\n");
- goto fail_dma_mapping;
- }
- *ap_cb_iova = iova;
- }
- return iova;
- fail_dma_mapping:
- iova = 0;
- ipa_assert();
- return iova;
- }
- /**
- * ipa_mpm_smmu_unmap() - SMMU unmaps ring and the buffer pointer.
- * @va_addr: virtual address that needs to be mapped
- * @sz: size of the address to be mapped
- * @dir: ipa_mpm_dma_data_direction
- * @ap_cb_iova: iova for AP context bank
- *
- * This function SMMU unmaps both ring and the buffer pointer.
- * The ring pointers will be aligned to ring size and
- * the buffer pointers should be aligned to buffer size.
- *
- * Return: none
- */
- static void ipa_mpm_smmu_unmap(dma_addr_t carved_iova, int sz, int dir,
- dma_addr_t ap_cb_iova)
- {
- unsigned long iova_p;
- unsigned long pa_p;
- u32 size_p = 0;
- struct iommu_domain *ipa_smmu_domain, *pcie_smmu_domain;
- struct ipa_smmu_cb_ctx *cb = &ipa_mpm_ctx->carved_smmu_cb;
- int smmu_enabled = (ipa_mpm_ctx->dev_info.ipa_smmu_enabled &&
- ipa_mpm_ctx->dev_info.pcie_smmu_enabled) ? 1 : 0;
- if (carved_iova <= 0) {
- IPA_MPM_ERR("carved_iova is zero/negative\n");
- WARN_ON(1);
- return;
- }
- if (smmu_enabled) {
- ipa_smmu_domain = ipa3_get_smmu_domain();
- if (!ipa_smmu_domain) {
- IPA_MPM_ERR("invalid IPA smmu domain\n");
- ipa_assert();
- }
- if (!ipa_mpm_ctx->mhi_parent_dev) {
- IPA_MPM_ERR("invalid PCIE SMMU domain\n");
- ipa_assert();
- }
- IPA_SMMU_ROUND_TO_PAGE(carved_iova, carved_iova, sz,
- iova_p, pa_p, size_p);
- pcie_smmu_domain = iommu_get_domain_for_dev(
- ipa_mpm_ctx->mhi_parent_dev);
- if (pcie_smmu_domain) {
- iommu_unmap(pcie_smmu_domain, iova_p, size_p);
- } else {
- IPA_MPM_ERR("invalid PCIE SMMU domain\n");
- ipa_assert();
- }
- iommu_unmap(ipa_smmu_domain, iova_p, size_p);
- cb->next_addr -= size_p;
- dma_unmap_single(ipa3_ctx->pdev, ap_cb_iova,
- IPA_MPM_RING_TOTAL_SIZE, dir);
- } else {
- dma_unmap_single(ipa3_ctx->pdev, ap_cb_iova,
- IPA_MPM_RING_TOTAL_SIZE, dir);
- }
- }
- static u32 ipa_mpm_smmu_map_doorbell(enum mhip_smmu_domain_type smmu_domain,
- u32 pa_addr)
- {
- /*
- * Doorbells are already in PA, map these to
- * PCIE/IPA doman if SMMUs are enabled.
- */
- struct iommu_domain *ipa_smmu_domain, *pcie_smmu_domain;
- int smmu_enabled;
- unsigned long iova_p;
- phys_addr_t pa_p;
- u32 size_p;
- int ret = 0;
- int prot = IOMMU_READ | IOMMU_WRITE;
- struct ipa_smmu_cb_ctx *cb = &ipa_mpm_ctx->carved_smmu_cb;
- unsigned long carved_iova = roundup(cb->next_addr, IPA_MPM_PAGE_SIZE);
- u32 iova = 0;
- u64 offset = 0;
- /* check cache coherent */
- if (ipa_mpm_ctx->dev_info.is_cache_coherent) {
- IPA_MPM_DBG(" enable cache coherent\n");
- prot |= IOMMU_CACHE;
- }
- if (carved_iova >= cb->va_end) {
- IPA_MPM_ERR("running out of carved_iova %lx\n", carved_iova);
- ipa_assert();
- }
- smmu_enabled = (ipa_mpm_ctx->dev_info.ipa_smmu_enabled &&
- ipa_mpm_ctx->dev_info.pcie_smmu_enabled) ? 1 : 0;
- if (smmu_enabled) {
- IPA_SMMU_ROUND_TO_PAGE(carved_iova, pa_addr, IPA_MPM_PAGE_SIZE,
- iova_p, pa_p, size_p);
- if (smmu_domain == MHIP_SMMU_DOMAIN_IPA) {
- ipa_smmu_domain = ipa3_get_smmu_domain();
- if (!ipa_smmu_domain) {
- IPA_MPM_ERR("invalid IPA smmu domain\n");
- ipa_assert();
- }
- ret = ipa3_iommu_map(ipa_smmu_domain,
- iova_p, pa_p, size_p, prot);
- if (ret) {
- IPA_MPM_ERR("IPA doorbell mapping failed\n");
- ipa_assert();
- }
- offset = pa_addr - pa_p;
- } else if (smmu_domain == MHIP_SMMU_DOMAIN_PCIE) {
- pcie_smmu_domain = iommu_get_domain_for_dev(
- ipa_mpm_ctx->mhi_parent_dev);
- if (!pcie_smmu_domain) {
- IPA_MPM_ERR("invalid IPA smmu domain\n");
- ipa_assert();
- }
- ret = iommu_map(pcie_smmu_domain,
- iova_p, pa_p, size_p, prot);
- if (ret) {
- IPA_MPM_ERR("PCIe doorbell mapping failed\n");
- ipa_assert();
- }
- offset = pa_addr - pa_p;
- }
- iova = iova_p + offset;
- cb->next_addr = iova_p + IPA_MPM_PAGE_SIZE;
- } else {
- iova = pa_addr;
- }
- return iova;
- }
- static void ipa_mpm_smmu_unmap_doorbell(enum mhip_smmu_domain_type smmu_domain,
- dma_addr_t iova)
- {
- /*
- * Doorbells are already in PA, map these to
- * PCIE/IPA doman if SMMUs are enabled.
- */
- struct iommu_domain *ipa_smmu_domain, *pcie_smmu_domain;
- int smmu_enabled;
- unsigned long iova_p;
- phys_addr_t pa_p;
- u32 size_p;
- struct ipa_smmu_cb_ctx *cb = &ipa_mpm_ctx->carved_smmu_cb;
- smmu_enabled = (ipa_mpm_ctx->dev_info.ipa_smmu_enabled &&
- ipa_mpm_ctx->dev_info.pcie_smmu_enabled) ? 1 : 0;
- if (smmu_enabled) {
- IPA_SMMU_ROUND_TO_PAGE(iova, iova, IPA_MPM_PAGE_SIZE,
- iova_p, pa_p, size_p);
- if (smmu_domain == MHIP_SMMU_DOMAIN_IPA) {
- ipa_smmu_domain = ipa3_get_smmu_domain();
- if (ipa_smmu_domain) {
- iommu_unmap(ipa_smmu_domain, iova_p, size_p);
- } else {
- IPA_MPM_ERR("invalid IPA smmu domain\n");
- ipa_assert();
- }
- } else if (smmu_domain == MHIP_SMMU_DOMAIN_PCIE) {
- pcie_smmu_domain = iommu_get_domain_for_dev(
- ipa_mpm_ctx->mhi_parent_dev);
- if (pcie_smmu_domain) {
- iommu_unmap(pcie_smmu_domain, iova_p, size_p);
- } else {
- IPA_MPM_ERR("invalid PCIE smmu domain\n");
- ipa_assert();
- }
- cb->next_addr -= IPA_MPM_PAGE_SIZE;
- }
- }
- }
- static int get_idx_from_id(const struct mhi_device_id *id)
- {
- return (id - mhi_driver_match_table);
- }
- static void get_ipa3_client(int id,
- enum ipa_client_type *ul_prod,
- enum ipa_client_type *dl_cons)
- {
- IPA_MPM_FUNC_ENTRY();
- if (id >= IPA_MPM_MHIP_CH_ID_MAX) {
- *ul_prod = IPA_CLIENT_MAX;
- *dl_cons = IPA_CLIENT_MAX;
- } else {
- *ul_prod = ipa_mpm_pipes[id].ul_prod.ipa_client;
- *dl_cons = ipa_mpm_pipes[id].dl_cons.ipa_client;
- }
- IPA_MPM_FUNC_EXIT();
- }
- static int ipa_mpm_connect_mhip_gsi_pipe(enum ipa_client_type mhip_client,
- int mhi_idx, struct ipa_req_chan_out_params *out_params)
- {
- int ipa_ep_idx;
- int res;
- struct mhi_p_desc *er_ring_va, *tr_ring_va;
- void *buff_va;
- dma_addr_t er_carved_iova, tr_carved_iova;
- dma_addr_t ap_cb_tr_iova, ap_cb_er_iova, ap_cb_buff_iova;
- struct ipa_request_gsi_channel_params gsi_params;
- int dir;
- int i, k;
- int result;
- struct ipa3_ep_context *ep;
- if (mhip_client == IPA_CLIENT_MAX)
- goto fail_gen;
- if ((mhi_idx < IPA_MPM_MHIP_CH_ID_0) ||
- (mhi_idx >= IPA_MPM_MHIP_CH_ID_MAX))
- goto fail_gen;
- ipa_ep_idx = ipa3_get_ep_mapping(mhip_client);
- if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED) {
- IPA_MPM_ERR("fail to find channel EP.\n");
- goto fail_gen;
- }
- ep = &ipa3_ctx->ep[ipa_ep_idx];
- if (ep->valid == 1) {
- IPAERR("EP %d already allocated.\n", ipa_ep_idx);
- return 0;
- }
- IPA_MPM_DBG("connecting client %d (ep: %d)\n", mhip_client, ipa_ep_idx);
- IPA_MPM_FUNC_ENTRY();
- if (IPA_MPM_RING_TOTAL_SIZE > PAGE_SIZE) {
- IPA_MPM_ERR("Ring Size / allocation mismatch\n");
- ipa_assert();
- }
- /* Only ring need alignment, separate from buffer */
- er_ring_va = (struct mhi_p_desc *) get_zeroed_page(GFP_KERNEL);
- if (!er_ring_va)
- goto fail_evt_alloc;
- tr_ring_va = (struct mhi_p_desc *) get_zeroed_page(GFP_KERNEL);
- if (!tr_ring_va)
- goto fail_tr_alloc;
- tr_ring_va[0].re_type = MHIP_RE_NOP;
- dir = IPA_CLIENT_IS_PROD(mhip_client) ?
- DMA_TO_HIPA : DMA_FROM_HIPA;
- /* allocate transfer ring elements */
- for (i = 1, k = 1; i < IPA_MPM_RING_LEN; i++, k++) {
- buff_va = kzalloc(TRE_BUFF_SIZE, GFP_KERNEL);
- if (!buff_va)
- goto fail_buff_alloc;
- tr_ring_va[i].buffer_ptr =
- ipa_mpm_smmu_map(buff_va, TRE_BUFF_SIZE, dir,
- &ap_cb_buff_iova);
- if (!tr_ring_va[i].buffer_ptr)
- goto fail_smmu_map_ring;
- tr_ring_va[i].buff_len = TRE_BUFF_SIZE;
- tr_ring_va[i].chain = 0;
- tr_ring_va[i].ieob = 0;
- tr_ring_va[i].ieot = 0;
- tr_ring_va[i].bei = 0;
- tr_ring_va[i].sct = 0;
- tr_ring_va[i].re_type = MHIP_RE_XFER;
- if (IPA_CLIENT_IS_PROD(mhip_client)) {
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_buff_va[k] =
- buff_va;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_buff_c_iova[k]
- = tr_ring_va[i].buffer_ptr;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_buff[k] =
- ap_cb_buff_iova;
- } else {
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_buff_va[k] =
- buff_va;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_buff_c_iova[k]
- = tr_ring_va[i].buffer_ptr;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.ap_iova_buff[k] =
- ap_cb_buff_iova;
- }
- }
- tr_carved_iova = ipa_mpm_smmu_map(tr_ring_va, PAGE_SIZE, dir,
- &ap_cb_tr_iova);
- if (!tr_carved_iova)
- goto fail_smmu_map_ring;
- er_carved_iova = ipa_mpm_smmu_map(er_ring_va, PAGE_SIZE, dir,
- &ap_cb_er_iova);
- if (!er_carved_iova)
- goto fail_smmu_map_ring;
- /* Store Producer channel rings */
- if (IPA_CLIENT_IS_PROD(mhip_client)) {
- /* Device UL */
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.er_va = er_ring_va;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_va = tr_ring_va;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.er_pa = er_carved_iova;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_pa = tr_carved_iova;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_tr =
- ap_cb_tr_iova;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_er =
- ap_cb_er_iova;
- } else {
- /* Host UL */
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.er_va = er_ring_va;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_va = tr_ring_va;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.er_pa = er_carved_iova;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_pa = tr_carved_iova;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.ap_iova_tr =
- ap_cb_tr_iova;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.ap_iova_er =
- ap_cb_er_iova;
- }
- memset(&gsi_params, 0, sizeof(struct ipa_request_gsi_channel_params));
- if (IPA_CLIENT_IS_PROD(mhip_client))
- gsi_params.ipa_ep_cfg =
- ipa_mpm_pipes[mhi_idx].dl_cons.ep_cfg;
- else
- gsi_params.ipa_ep_cfg =
- ipa_mpm_pipes[mhi_idx].ul_prod.ep_cfg;
- gsi_params.client = mhip_client;
- gsi_params.skip_ep_cfg = false;
- /*
- * RP update address = Device channel DB address
- * CLIENT_PROD -> Host DL
- * CLIENT_CONS -> Host UL
- */
- if (IPA_CLIENT_IS_PROD(mhip_client)) {
- gsi_params.evt_ring_params.rp_update_addr =
- ipa_mpm_smmu_map_doorbell(
- MHIP_SMMU_DOMAIN_IPA,
- ipa_mpm_ctx->md[mhi_idx].dl_cons.chan_props.device_db);
- if (gsi_params.evt_ring_params.rp_update_addr == 0)
- goto fail_smmu_map_db;
- ipa_mpm_ctx->md[mhi_idx].dl_cons.db_host_iova =
- gsi_params.evt_ring_params.rp_update_addr;
- gsi_params.evt_ring_params.ring_base_addr =
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_pa;
- gsi_params.chan_params.ring_base_addr =
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.er_pa;
- } else {
- gsi_params.evt_ring_params.rp_update_addr =
- ipa_mpm_smmu_map_doorbell(
- MHIP_SMMU_DOMAIN_IPA,
- ipa_mpm_ctx->md[mhi_idx].ul_prod.chan_props.device_db);
- if (gsi_params.evt_ring_params.rp_update_addr == 0)
- goto fail_smmu_map_db;
- ipa_mpm_ctx->md[mhi_idx].ul_prod.db_host_iova =
- gsi_params.evt_ring_params.rp_update_addr;
- gsi_params.evt_ring_params.ring_base_addr =
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.er_pa;
- gsi_params.chan_params.ring_base_addr =
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_pa;
- }
- /* Fill Event ring params */
- gsi_params.evt_ring_params.intf = GSI_EVT_CHTYPE_MHIP_EV;
- gsi_params.evt_ring_params.intr = GSI_INTR_MSI;
- gsi_params.evt_ring_params.re_size = GSI_EVT_RING_RE_SIZE_16B;
- gsi_params.evt_ring_params.ring_len =
- (IPA_MPM_RING_LEN) * GSI_EVT_RING_RE_SIZE_16B;
- gsi_params.evt_ring_params.ring_base_vaddr = NULL;
- gsi_params.evt_ring_params.int_modt = 0;
- gsi_params.evt_ring_params.int_modc = 0;
- gsi_params.evt_ring_params.intvec = 0;
- gsi_params.evt_ring_params.msi_addr = 0;
- gsi_params.evt_ring_params.exclusive = true;
- gsi_params.evt_ring_params.err_cb = ipa_mpm_gsi_evt_ring_err_cb;
- gsi_params.evt_ring_params.user_data = NULL;
- /* Evt Scratch Params */
- /* Disable the Moderation for ringing doorbells */
- gsi_params.evt_scratch.mhip.rp_mod_threshold = 1;
- gsi_params.evt_scratch.mhip.rp_mod_timer = 0;
- gsi_params.evt_scratch.mhip.rp_mod_counter = 0;
- gsi_params.evt_scratch.mhip.rp_mod_timer_id = 0;
- gsi_params.evt_scratch.mhip.rp_mod_timer_running = 0;
- gsi_params.evt_scratch.mhip.fixed_buffer_sz = TRE_BUFF_SIZE;
- if (IPA_CLIENT_IS_PROD(mhip_client))
- gsi_params.evt_scratch.mhip.rp_mod_threshold = 4;
- /* Channel Params */
- gsi_params.chan_params.prot = GSI_CHAN_PROT_MHIP;
- gsi_params.chan_params.dir = IPA_CLIENT_IS_PROD(mhip_client) ?
- GSI_CHAN_DIR_TO_GSI : GSI_CHAN_DIR_FROM_GSI;
- /* chan_id is set in ipa3_request_gsi_channel() */
- gsi_params.chan_params.re_size = GSI_CHAN_RE_SIZE_16B;
- gsi_params.chan_params.ring_len =
- (IPA_MPM_RING_LEN) * GSI_EVT_RING_RE_SIZE_16B;
- gsi_params.chan_params.ring_base_vaddr = NULL;
- gsi_params.chan_params.use_db_eng = GSI_CHAN_DIRECT_MODE;
- gsi_params.chan_params.max_prefetch = GSI_ONE_PREFETCH_SEG;
- gsi_params.chan_params.low_weight = 1;
- gsi_params.chan_params.xfer_cb = NULL;
- gsi_params.chan_params.err_cb = ipa_mpm_gsi_chan_err_cb;
- gsi_params.chan_params.chan_user_data = NULL;
- /* Channel scratch */
- gsi_params.chan_scratch.mhip.assert_bit_40 = 0;
- gsi_params.chan_scratch.mhip.host_channel = 1;
- res = ipa3_request_gsi_channel(&gsi_params, out_params);
- if (res) {
- IPA_MPM_ERR("failed to allocate GSI channel res=%d\n", res);
- goto fail_alloc_channel;
- }
- if (IPA_CLIENT_IS_CONS(mhip_client)) {
- /*
- * Enable HOLB timer one time after bootup/SSR.
- * The HOLB timeout drops the packets on MHIP if
- * there is a stall on MHIP TX pipe greater than
- * configured timeout.
- */
- result = ipa_mpm_start_mhip_holb_tmo(ipa_ep_idx);
- if (result) {
- IPA_MPM_ERR("HOLB config failed for %d, fail = %d\n",
- ipa_ep_idx, result);
- goto fail_alloc_channel;
- }
- }
- if (IPA_CLIENT_IS_PROD(mhip_client))
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_DL,
- GSI_ALLOCATED);
- else
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_UL,
- GSI_ALLOCATED);
- result = ipa3_start_gsi_channel(ipa_ep_idx);
- if (result) {
- IPA_MPM_ERR("start MHIP channel %d failed\n", mhip_client);
- if (IPA_CLIENT_IS_PROD(mhip_client))
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_DL, GSI_ERR);
- else
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_UL, GSI_ERR);
- goto fail_start_channel;
- }
- if (IPA_CLIENT_IS_PROD(mhip_client))
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_DL, GSI_STARTED);
- else
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_UL, GSI_STARTED);
- /* Fill in the Device Context params */
- if (IPA_CLIENT_IS_PROD(mhip_client)) {
- /* This is the DL channel :: Device -> Host */
- ipa_mpm_ctx->md[mhi_idx].dl_cons.evt_props.ev_ctx.rbase =
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.er_pa;
- ipa_mpm_ctx->md[mhi_idx].dl_cons.chan_props.ch_ctx.rbase =
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_pa;
- } else {
- ipa_mpm_ctx->md[mhi_idx].ul_prod.evt_props.ev_ctx.rbase =
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_pa;
- ipa_mpm_ctx->md[mhi_idx].ul_prod.chan_props.ch_ctx.rbase =
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.er_pa;
- }
- IPA_MPM_FUNC_EXIT();
- return 0;
- fail_start_channel:
- ipa3_disable_data_path(ipa_ep_idx);
- ipa3_stop_gsi_channel(ipa_ep_idx);
- fail_alloc_channel:
- ipa3_release_gsi_channel(ipa_ep_idx);
- fail_smmu_map_db:
- fail_smmu_map_ring:
- fail_tr_alloc:
- fail_evt_alloc:
- fail_buff_alloc:
- ipa_assert();
- fail_gen:
- return -EFAULT;
- }
- static void ipa_mpm_clean_mhip_chan(int mhi_idx,
- enum ipa_client_type mhip_client)
- {
- int dir;
- int i;
- int ipa_ep_idx;
- int result;
- IPA_MPM_FUNC_ENTRY();
- if (mhip_client == IPA_CLIENT_MAX)
- return;
- if ((mhi_idx < IPA_MPM_MHIP_CH_ID_0) ||
- (mhi_idx >= IPA_MPM_MHIP_CH_ID_MAX))
- return;
- dir = IPA_CLIENT_IS_PROD(mhip_client) ?
- DMA_TO_HIPA : DMA_FROM_HIPA;
- ipa_ep_idx = ipa3_get_ep_mapping(mhip_client);
- if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED) {
- IPA_MPM_ERR("fail to find channel EP.\n");
- return;
- }
- /* For the uplink channels, enable HOLB. */
- if (IPA_CLIENT_IS_CONS(mhip_client))
- ipa3_disable_data_path(ipa_ep_idx);
- /* Release channel */
- result = ipa3_stop_gsi_channel(ipa_ep_idx);
- if (result) {
- IPA_MPM_ERR("Stop channel for MHIP_Client = %d failed\n",
- mhip_client);
- goto fail_chan;
- }
- result = ipa3_reset_gsi_channel(ipa_ep_idx);
- if (result) {
- IPA_MPM_ERR("Reset channel for MHIP_Client = %d failed\n",
- mhip_client);
- goto fail_chan;
- }
- result = ipa3_reset_gsi_event_ring(ipa_ep_idx);
- if (result) {
- IPA_MPM_ERR("Reset ev ring for MHIP_Client = %d failed\n",
- mhip_client);
- goto fail_chan;
- }
- result = ipa3_release_gsi_channel(ipa_ep_idx);
- if (result) {
- IPA_MPM_ERR("Release tr ring for MHIP_Client = %d failed\n",
- mhip_client);
- if (IPA_CLIENT_IS_PROD(mhip_client))
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_DL, GSI_ERR);
- else
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_UL, GSI_ERR);
- goto fail_chan;
- }
- if (IPA_CLIENT_IS_PROD(mhip_client))
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_DL, GSI_INIT);
- else
- ipa_mpm_change_gsi_state(mhi_idx,
- IPA_MPM_MHIP_CHAN_UL, GSI_INIT);
- memset(&ipa3_ctx->ep[ipa_ep_idx], 0, sizeof(struct ipa3_ep_context));
- /* Unmap Doorbells */
- if (IPA_CLIENT_IS_PROD(mhip_client)) {
- ipa_mpm_smmu_unmap_doorbell(MHIP_SMMU_DOMAIN_PCIE,
- ipa_mpm_ctx->md[mhi_idx].dl_cons.db_device_iova);
- ipa_mpm_smmu_unmap_doorbell(MHIP_SMMU_DOMAIN_IPA,
- ipa_mpm_ctx->md[mhi_idx].dl_cons.db_host_iova);
- ipa_mpm_ctx->md[mhi_idx].dl_cons.db_host_iova = 0;
- ipa_mpm_ctx->md[mhi_idx].dl_cons.db_device_iova = 0;
- } else {
- ipa_mpm_smmu_unmap_doorbell(MHIP_SMMU_DOMAIN_PCIE,
- ipa_mpm_ctx->md[mhi_idx].ul_prod.db_device_iova);
- ipa_mpm_smmu_unmap_doorbell(MHIP_SMMU_DOMAIN_IPA,
- ipa_mpm_ctx->md[mhi_idx].ul_prod.db_host_iova);
- ipa_mpm_ctx->md[mhi_idx].ul_prod.db_host_iova = 0;
- ipa_mpm_ctx->md[mhi_idx].ul_prod.db_device_iova = 0;
- }
- /* deallocate/Unmap transfer ring buffers */
- for (i = 1; i < IPA_MPM_RING_LEN; i++) {
- if (IPA_CLIENT_IS_PROD(mhip_client)) {
- ipa_mpm_smmu_unmap(
- (dma_addr_t)
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_buff_c_iova[i],
- TRE_BUFF_SIZE, dir,
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_buff[i]);
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_buff_c_iova[i]
- = 0;
- kfree(
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_buff_va[i]);
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_buff_va[i]
- = NULL;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_buff[i]
- = 0;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_buff_c_iova[i]
- = 0;
- } else {
- ipa_mpm_smmu_unmap(
- (dma_addr_t)
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_buff_c_iova[i],
- TRE_BUFF_SIZE, dir,
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.ap_iova_buff[i]
- );
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_buff_c_iova[i]
- = 0;
- kfree(
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_buff_va[i]);
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_buff_va[i]
- = NULL;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.ap_iova_buff[i]
- = 0;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_buff_c_iova[i]
- = 0;
- }
- }
- /* deallocate/Unmap rings */
- if (IPA_CLIENT_IS_PROD(mhip_client)) {
- ipa_mpm_smmu_unmap(
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.er_pa,
- IPA_MPM_PAGE_SIZE, dir,
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_er);
- ipa_mpm_smmu_unmap(
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_pa,
- IPA_MPM_PAGE_SIZE, dir,
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_tr);
- if (ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.er_va) {
- free_page((unsigned long)
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.er_va);
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.er_va = NULL;
- }
- if (ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_va) {
- free_page((unsigned long)
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_va);
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.tr_va = NULL;
- }
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_er = 0;
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_tr = 0;
- } else {
- ipa_mpm_smmu_unmap(
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_pa,
- IPA_MPM_PAGE_SIZE, dir,
- ipa_mpm_ctx->md[mhi_idx].dl_prod_ring.ap_iova_tr);
- ipa_mpm_smmu_unmap(
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.er_pa,
- IPA_MPM_PAGE_SIZE, dir,
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.ap_iova_er);
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_pa = 0;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.er_pa = 0;
- if (ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.er_va) {
- free_page((unsigned long)
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.er_va);
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.er_va = NULL;
- }
- if (ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_va) {
- free_page((unsigned long)
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_va);
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.tr_va = NULL;
- }
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.ap_iova_er = 0;
- ipa_mpm_ctx->md[mhi_idx].ul_prod_ring.ap_iova_tr = 0;
- }
- IPA_MPM_FUNC_EXIT();
- return;
- fail_chan:
- ipa_assert();
- }
- /* round addresses for closest page per SMMU requirements */
- static inline void ipa_mpm_smmu_round_to_page(uint64_t iova, uint64_t pa,
- uint64_t size, unsigned long *iova_p, phys_addr_t *pa_p, u32 *size_p)
- {
- *iova_p = rounddown(iova, PAGE_SIZE);
- *pa_p = rounddown(pa, PAGE_SIZE);
- *size_p = roundup(size + pa - *pa_p, PAGE_SIZE);
- }
- static int __ipa_mpm_configure_mhi_device(struct ipa_mpm_channel *ch,
- int mhi_idx, int dir)
- {
- struct mhi_buf ch_config[2];
- int ret;
- IPA_MPM_FUNC_ENTRY();
- if (ch == NULL) {
- IPA_MPM_ERR("ch config is NULL\n");
- return -EINVAL;
- }
- /* Populate CCA */
- ch_config[0].buf = &ch->chan_props.ch_ctx;
- ch_config[0].len = sizeof(ch->chan_props.ch_ctx);
- ch_config[0].name = "CCA";
- /* populate ECA */
- ch_config[1].buf = &ch->evt_props.ev_ctx;
- ch_config[1].len = sizeof(ch->evt_props.ev_ctx);
- ch_config[1].name = "ECA";
- IPA_MPM_DBG("Configuring MHI PRIME device for mhi_idx %d\n", mhi_idx);
- ret = mhi_device_configure(ipa_mpm_ctx->md[mhi_idx].mhi_dev, dir,
- ch_config, 2);
- if (ret) {
- IPA_MPM_ERR("mhi_device_configure failed\n");
- return -EINVAL;
- }
- IPA_MPM_FUNC_EXIT();
- return 0;
- }
- static void ipa_mpm_mhip_shutdown(int mhip_idx)
- {
- enum ipa_client_type ul_prod_chan, dl_cons_chan;
- IPA_MPM_FUNC_ENTRY();
- get_ipa3_client(mhip_idx, &ul_prod_chan, &dl_cons_chan);
- if (mhip_idx != IPA_MPM_MHIP_CH_ID_2)
- /* For DPL, stop only DL channel */
- ipa_mpm_clean_mhip_chan(mhip_idx, ul_prod_chan);
- ipa_mpm_clean_mhip_chan(mhip_idx, dl_cons_chan);
- if (!ipa_mpm_ctx->md[mhip_idx].in_lpm) {
- ipa_mpm_vote_unvote_ipa_clk(CLK_OFF, mhip_idx);
- /* while in modem shutdown scenarios such as SSR, no explicit
- * PCIe vote is needed.
- */
- ipa_mpm_ctx->md[mhip_idx].in_lpm = true;
- }
- mutex_lock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex);
- ipa_mpm_ctx->md[mhip_idx].mhi_dev = NULL;
- mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex);
- IPA_MPM_FUNC_EXIT();
- }
- /**
- * @ipa_mpm_vote_unvote_pcie_clk - Vote/Unvote PCIe Clock per probe_id
- * Returns if success or failure.
- * @ipa_mpm_clk_vote_type - Vote or Unvote for PCIe Clock
- * @probe_id - MHI probe_id per client.
- * @is_force - Forcebly casts vote - should be true only in probe.
- * @is_acted - Output param - This indicates the clk is actually voted or not
- * The flag output is checked only when we vote for clocks.
- * Return value: PCIe clock voting is success or failure.
- */
- static int ipa_mpm_vote_unvote_pcie_clk(enum ipa_mpm_clk_vote_type vote,
- int probe_id,
- bool is_force,
- bool *is_acted)
- {
- int result = 0;
- if (probe_id >= IPA_MPM_MHIP_CH_ID_MAX) {
- IPA_MPM_ERR("probe_id not found\n");
- return -EINVAL;
- }
- if (vote > CLK_OFF) {
- IPA_MPM_ERR("Invalid vote\n");
- return -EINVAL;
- }
- if (!is_acted) {
- IPA_MPM_ERR("Invalid clk_vote ptr\n");
- return -EFAULT;
- }
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- if (ipa_mpm_ctx->md[probe_id].mhi_dev == NULL) {
- IPA_MPM_ERR("MHI not initialized yet\n");
- *is_acted = false;
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- return 0;
- }
- if (!ipa_mpm_ctx->md[probe_id].init_complete &&
- !is_force) {
- /*
- * SSR might be in progress, dont have to vote/unvote for
- * IPA clocks as it will be taken care in remove_cb/subsequent
- * probe.
- */
- IPA_MPM_DBG("SSR in progress, return\n");
- *is_acted = false;
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- return 0;
- }
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- IPA_MPM_DBG("PCIe clock vote/unvote = %d probe_id = %d clk_cnt = %d\n",
- vote, probe_id,
- atomic_read(&ipa_mpm_ctx->md[probe_id].clk_cnt.pcie_clk_cnt));
- if (vote == CLK_ON) {
- result = mhi_device_get_sync(
- ipa_mpm_ctx->md[probe_id].mhi_dev,
- MHI_VOTE_BUS | MHI_VOTE_DEVICE);
- if (result) {
- IPA_MPM_ERR("mhi_sync_get failed for probe_id %d\n",
- result, probe_id);
- *is_acted = false;
- return result;
- }
- IPA_MPM_DBG("probe_id %d PCIE clock now ON\n", probe_id);
- atomic_inc(&ipa_mpm_ctx->md[probe_id].clk_cnt.pcie_clk_cnt);
- atomic_inc(&ipa_mpm_ctx->pcie_clk_total_cnt);
- } else {
- if ((atomic_read(
- &ipa_mpm_ctx->md[probe_id].clk_cnt.pcie_clk_cnt)
- == 0)) {
- IPA_MPM_DBG("probe_id %d PCIE clock already devoted\n",
- probe_id);
- WARN_ON(1);
- *is_acted = true;
- return 0;
- }
- mhi_device_put(ipa_mpm_ctx->md[probe_id].mhi_dev,
- MHI_VOTE_BUS | MHI_VOTE_DEVICE);
- IPA_MPM_DBG("probe_id %d PCIE clock off\n", probe_id);
- atomic_dec(&ipa_mpm_ctx->md[probe_id].clk_cnt.pcie_clk_cnt);
- atomic_dec(&ipa_mpm_ctx->pcie_clk_total_cnt);
- }
- *is_acted = true;
- return result;
- }
- /*
- * Turning on/OFF IPA Clock is done only once- for all clients
- */
- static void ipa_mpm_vote_unvote_ipa_clk(enum ipa_mpm_clk_vote_type vote,
- int probe_id)
- {
- if (vote > CLK_OFF)
- return;
- IPA_MPM_DBG("IPA clock vote/unvote = %d probe_id = %d clk_cnt = %d\n",
- vote, probe_id,
- atomic_read(&ipa_mpm_ctx->md[probe_id].clk_cnt.ipa_clk_cnt));
- if (vote == CLK_ON) {
- IPA_ACTIVE_CLIENTS_INC_SPECIAL(ipa_mpm_mhip_chan_str[probe_id]);
- IPA_MPM_DBG("IPA clock now ON for probe_id %d\n", probe_id);
- atomic_inc(&ipa_mpm_ctx->md[probe_id].clk_cnt.ipa_clk_cnt);
- atomic_inc(&ipa_mpm_ctx->ipa_clk_total_cnt);
- } else {
- if ((atomic_read
- (&ipa_mpm_ctx->md[probe_id].clk_cnt.ipa_clk_cnt)
- == 0)) {
- IPA_MPM_DBG("probe_id %d IPA clock count < 0\n",
- probe_id);
- WARN_ON(1);
- return;
- }
- IPA_ACTIVE_CLIENTS_DEC_SPECIAL(ipa_mpm_mhip_chan_str[probe_id]);
- IPA_MPM_DBG("probe_id %d IPA clock off\n", probe_id);
- atomic_dec(&ipa_mpm_ctx->md[probe_id].clk_cnt.ipa_clk_cnt);
- atomic_dec(&ipa_mpm_ctx->ipa_clk_total_cnt);
- }
- }
- /**
- * @ipa_mpm_start_stop_remote_mhip_chan - Start/Stop Remote device side MHIP
- * channels.
- * @ipa_mpm_clk_vote_type - Vote or Unvote for PCIe Clock
- * @probe_id - MHI probe_id per client.
- * @ipa_mpm_start_stop_type - Start/Stop remote channels.
- * @is_force - Forcebly casts remote channels to be started/stopped.
- * should be true only in probe.
- * Return value: 0 if success or error value.
- */
- static int ipa_mpm_start_stop_remote_mhip_chan(
- int probe_id,
- enum ipa_mpm_start_stop_type start_stop,
- bool is_force)
- {
- int ret = 0;
- struct mhi_device *mhi_dev = ipa_mpm_ctx->md[probe_id].mhi_dev;
- /* Sanity check to make sure Remote channels can be started.
- * If probe in progress, mhi_prepare_for_transfer will start
- * the remote channels so no need to start it from here.
- */
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- if (!ipa_mpm_ctx->md[probe_id].init_complete && !is_force) {
- IPA_MPM_ERR("MHI not initialized yet, probe in progress\n");
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- return ret;
- }
- /* For error state, expect modem SSR to recover from error */
- if (ipa_mpm_ctx->md[probe_id].remote_state == MPM_MHIP_REMOTE_ERR) {
- IPA_MPM_ERR("Remote channels in err state for %d\n", probe_id);
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- return -EFAULT;
- }
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- if (start_stop == MPM_MHIP_START) {
- if (ipa_mpm_ctx->md[probe_id].remote_state ==
- MPM_MHIP_REMOTE_START) {
- IPA_MPM_DBG("Remote channel already started for %d\n",
- probe_id);
- } else {
- ret = mhi_resume_transfer(mhi_dev);
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- if (ret)
- ipa_mpm_ctx->md[probe_id].remote_state =
- MPM_MHIP_REMOTE_ERR;
- else
- ipa_mpm_ctx->md[probe_id].remote_state =
- MPM_MHIP_REMOTE_START;
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- }
- } else {
- if (ipa_mpm_ctx->md[probe_id].remote_state ==
- MPM_MHIP_REMOTE_STOP) {
- IPA_MPM_DBG("Remote channel already stopped for %d\n",
- probe_id);
- } else {
- ret = mhi_pause_transfer(mhi_dev);
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- if (ret)
- ipa_mpm_ctx->md[probe_id].remote_state =
- MPM_MHIP_REMOTE_ERR;
- else
- ipa_mpm_ctx->md[probe_id].remote_state =
- MPM_MHIP_REMOTE_STOP;
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- }
- }
- return ret;
- }
- static enum mhip_status_type ipa_mpm_start_stop_mhip_chan(
- enum ipa_mpm_mhip_chan mhip_chan,
- int probe_id,
- enum ipa_mpm_start_stop_type start_stop)
- {
- int ipa_ep_idx;
- struct ipa3_ep_context *ep;
- bool is_start;
- enum ipa_client_type ul_chan, dl_chan;
- u32 source_pipe_bitmask = 0;
- enum gsi_status gsi_res = GSI_STATUS_SUCCESS;
- int result;
- IPA_MPM_FUNC_ENTRY();
- if (mhip_chan > IPA_MPM_MHIP_CHAN_BOTH) {
- IPA_MPM_ERR("MHI not initialized yet\n");
- return MHIP_STATUS_FAIL;
- }
- if (probe_id >= IPA_MPM_MHIP_CH_ID_MAX) {
- IPA_MPM_ERR("MHI not initialized yet\n");
- return MHIP_STATUS_FAIL;
- }
- get_ipa3_client(probe_id, &ul_chan, &dl_chan);
- if (mhip_chan == IPA_MPM_MHIP_CHAN_UL) {
- ipa_ep_idx = ipa3_get_ep_mapping(ul_chan);
- } else if (mhip_chan == IPA_MPM_MHIP_CHAN_DL) {
- ipa_ep_idx = ipa3_get_ep_mapping(dl_chan);
- } else if (mhip_chan == IPA_MPM_MHIP_CHAN_BOTH) {
- ipa_ep_idx = ipa3_get_ep_mapping(ul_chan);
- ipa_ep_idx = ipa3_get_ep_mapping(dl_chan);
- }
- if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED) {
- IPA_MPM_ERR("fail to get EP# for idx %d\n", ipa_ep_idx);
- return MHIP_STATUS_EP_NOT_FOUND;
- }
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- if (!ipa_mpm_ctx->md[probe_id].init_complete) {
- IPA_MPM_ERR("MHIP probe %d not initialized\n", probe_id);
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- return MHIP_STATUS_EP_NOT_READY;
- }
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- ep = &ipa3_ctx->ep[ipa_ep_idx];
- if (mhip_chan == IPA_MPM_MHIP_CHAN_UL) {
- IPA_MPM_DBG("current GSI state = %d, action = %d\n",
- ipa_mpm_ctx->md[probe_id].ul_prod.gsi_state,
- start_stop);
- if (ipa_mpm_ctx->md[probe_id].ul_prod.gsi_state <
- GSI_ALLOCATED) {
- IPA_MPM_ERR("GSI chan is not allocated yet\n");
- return MHIP_STATUS_EP_NOT_READY;
- }
- } else if (mhip_chan == IPA_MPM_MHIP_CHAN_DL) {
- IPA_MPM_DBG("current GSI state = %d, action = %d\n",
- ipa_mpm_ctx->md[probe_id].dl_cons.gsi_state,
- start_stop);
- if (ipa_mpm_ctx->md[probe_id].dl_cons.gsi_state <
- GSI_ALLOCATED) {
- IPA_MPM_ERR("GSI chan is not allocated yet\n");
- return MHIP_STATUS_EP_NOT_READY;
- }
- }
- is_start = (start_stop == MPM_MHIP_START) ? true : false;
- if (is_start) {
- if (mhip_chan == IPA_MPM_MHIP_CHAN_UL) {
- if (ipa_mpm_ctx->md[probe_id].ul_prod.gsi_state ==
- GSI_STARTED) {
- IPA_MPM_ERR("GSI chan is already started\n");
- return MHIP_STATUS_NO_OP;
- }
- }
- if (mhip_chan == IPA_MPM_MHIP_CHAN_DL) {
- if (ipa_mpm_ctx->md[probe_id].dl_cons.gsi_state ==
- GSI_STARTED) {
- IPA_MPM_ERR("GSI chan is already started\n");
- return MHIP_STATUS_NO_OP;
- }
- }
- /* Start GSI channel */
- gsi_res = ipa3_start_gsi_channel(ipa_ep_idx);
- if (gsi_res != GSI_STATUS_SUCCESS) {
- IPA_MPM_ERR("Error starting channel: err = %d\n",
- gsi_res);
- goto gsi_chan_fail;
- } else {
- ipa_mpm_change_gsi_state(probe_id, mhip_chan,
- GSI_STARTED);
- }
- } else {
- if (mhip_chan == IPA_MPM_MHIP_CHAN_UL) {
- if (ipa_mpm_ctx->md[probe_id].ul_prod.gsi_state ==
- GSI_STOPPED) {
- IPA_MPM_ERR("GSI chan is already stopped\n");
- return MHIP_STATUS_NO_OP;
- } else if (ipa_mpm_ctx->md[probe_id].ul_prod.gsi_state
- != GSI_STARTED) {
- IPA_MPM_ERR("GSI chan isn't already started\n");
- return MHIP_STATUS_NO_OP;
- }
- }
- if (mhip_chan == IPA_MPM_MHIP_CHAN_DL) {
- if (ipa_mpm_ctx->md[probe_id].dl_cons.gsi_state ==
- GSI_STOPPED) {
- IPA_MPM_ERR("GSI chan is already stopped\n");
- return MHIP_STATUS_NO_OP;
- } else if (ipa_mpm_ctx->md[probe_id].dl_cons.gsi_state
- != GSI_STARTED) {
- IPA_MPM_ERR("GSI chan isn't already started\n");
- return MHIP_STATUS_NO_OP;
- }
- }
- if (mhip_chan == IPA_MPM_MHIP_CHAN_UL) {
- source_pipe_bitmask = 1 <<
- ipa3_get_ep_mapping(ep->client);
- /* First Stop UL GSI channel before unvote PCIe clock */
- result = ipa3_stop_gsi_channel(ipa_ep_idx);
- if (result) {
- IPA_MPM_ERR("UL chan stop failed\n");
- goto gsi_chan_fail;
- } else {
- ipa_mpm_change_gsi_state(probe_id, mhip_chan,
- GSI_STOPPED);
- }
- }
- if (mhip_chan == IPA_MPM_MHIP_CHAN_DL) {
- result = ipa3_stop_gsi_channel(ipa_ep_idx);
- if (result) {
- IPA_MPM_ERR("Fail to stop DL channel\n");
- goto gsi_chan_fail;
- } else {
- ipa_mpm_change_gsi_state(probe_id, mhip_chan,
- GSI_STOPPED);
- }
- }
- }
- IPA_MPM_FUNC_EXIT();
- return MHIP_STATUS_SUCCESS;
- gsi_chan_fail:
- ipa3_disable_data_path(ipa_ep_idx);
- ipa_mpm_change_gsi_state(probe_id, mhip_chan, GSI_ERR);
- ipa_assert();
- return MHIP_STATUS_FAIL;
- }
- int ipa_mpm_notify_wan_state(struct wan_ioctl_notify_wan_state *state)
- {
- int probe_id = IPA_MPM_MHIP_CH_ID_MAX;
- int i;
- static enum mhip_status_type status;
- int ret = 0;
- enum ipa_mpm_mhip_client_type mhip_client = IPA_MPM_MHIP_TETH;
- bool is_acted = true;
- const struct ipa_gsi_ep_config *ep_cfg;
- uint32_t flow_ctrl_mask = 0;
- if (!state)
- return -EPERM;
- if (!ipa3_is_mhip_offload_enabled())
- return -EPERM;
- for (i = 0; i < IPA_MPM_MHIP_CH_ID_MAX; i++) {
- if (ipa_mpm_pipes[i].mhip_client == mhip_client) {
- probe_id = i;
- break;
- }
- }
- if (probe_id == IPA_MPM_MHIP_CH_ID_MAX) {
- IPA_MPM_ERR("Unknown probe_id\n");
- return -EPERM;
- }
- IPA_MPM_DBG("WAN backhaul available for probe_id = %d\n", probe_id);
- if (state->up) {
- /* Start UL MHIP channel for offloading tethering connection */
- ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id,
- false, &is_acted);
- if (ret) {
- IPA_MPM_ERR("Err %d cloking on PCIe clk %d\n", ret);
- return ret;
- }
- /*
- * Make sure to start Device side channels before
- * starting Host side UL channels. This is to make
- * sure device side access host side only after
- * Host IPA gets voted.
- */
- ret = ipa_mpm_start_stop_remote_mhip_chan(probe_id,
- MPM_MHIP_START,
- false);
- if (ret) {
- /*
- * This can fail only when modem is in SSR state.
- * Eventually there would be a remove callback,
- * so return a failure.
- */
- IPA_MPM_ERR("MHIP remote chan start fail = %d\n", ret);
- if (is_acted)
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF,
- probe_id,
- false,
- &is_acted);
- return ret;
- }
- IPA_MPM_DBG("MHIP remote channels are started\n");
- /*
- * Update flow control monitoring end point info.
- * This info will be used to set delay on the end points upon
- * hitting RED water mark.
- */
- ep_cfg = ipa3_get_gsi_ep_info(IPA_CLIENT_WLAN2_PROD);
- if (!ep_cfg)
- IPA_MPM_ERR("ep = %d not allocated yet\n",
- IPA_CLIENT_WLAN2_PROD);
- else
- flow_ctrl_mask |= 1 << (ep_cfg->ipa_gsi_chan_num);
- ep_cfg = ipa3_get_gsi_ep_info(IPA_CLIENT_USB_PROD);
- if (!ep_cfg)
- IPA_MPM_ERR("ep = %d not allocated yet\n",
- IPA_CLIENT_USB_PROD);
- else
- flow_ctrl_mask |= 1 << (ep_cfg->ipa_gsi_chan_num);
- atomic_set(&ipa_mpm_ctx->flow_ctrl_mask, flow_ctrl_mask);
- ret = ipa3_uc_send_update_flow_control(flow_ctrl_mask,
- IPA_MPM_FLOW_CTRL_ADD);
- if (ret)
- IPA_MPM_ERR("Err = %d setting uc flow control\n", ret);
- status = ipa_mpm_start_stop_mhip_chan(
- IPA_MPM_MHIP_CHAN_UL, probe_id, MPM_MHIP_START);
- switch (status) {
- case MHIP_STATUS_SUCCESS:
- ipa_mpm_ctx->md[probe_id].teth_state =
- IPA_MPM_TETH_CONNECTED;
- break;
- case MHIP_STATUS_EP_NOT_READY:
- case MHIP_STATUS_NO_OP:
- IPA_MPM_DBG("UL chan already start, status = %d\n",
- status);
- if (is_acted) {
- return ipa_mpm_vote_unvote_pcie_clk(CLK_OFF,
- probe_id,
- false,
- &is_acted);
- }
- break;
- case MHIP_STATUS_FAIL:
- case MHIP_STATUS_BAD_STATE:
- case MHIP_STATUS_EP_NOT_FOUND:
- IPA_MPM_ERR("UL chan start err =%d\n", status);
- if (is_acted)
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- ipa_assert();
- return -EFAULT;
- default:
- IPA_MPM_ERR("Err not found\n");
- if (is_acted)
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- ret = -EFAULT;
- break;
- }
- ipa_mpm_ctx->md[probe_id].mhip_client = mhip_client;
- } else {
- /*
- * Update flow control monitoring end point info.
- * This info will be used to reset delay on the end points.
- */
- flow_ctrl_mask =
- atomic_read(&ipa_mpm_ctx->flow_ctrl_mask);
- ret = ipa3_uc_send_update_flow_control(flow_ctrl_mask,
- IPA_MPM_FLOW_CTRL_DELETE);
- flow_ctrl_mask = 0;
- atomic_set(&ipa_mpm_ctx->flow_ctrl_mask, 0);
- if (ret) {
- IPA_MPM_ERR("Err = %d resetting uc flow control\n",
- ret);
- ipa_assert();
- }
- /*
- * Make sure to stop Device side channels before
- * stopping Host side UL channels. This is to make
- * sure device side doesn't access host IPA after
- * Host IPA gets devoted.
- */
- ret = ipa_mpm_start_stop_remote_mhip_chan(probe_id,
- MPM_MHIP_STOP,
- false);
- if (ret) {
- /*
- * This can fail only when modem is in SSR state.
- * Eventually there would be a remove callback,
- * so return a failure.
- */
- IPA_MPM_ERR("MHIP remote chan stop fail = %d\n", ret);
- return ret;
- }
- IPA_MPM_DBG("MHIP remote channels are stopped\n");
- status = ipa_mpm_start_stop_mhip_chan(
- IPA_MPM_MHIP_CHAN_UL, probe_id,
- MPM_MHIP_STOP);
- switch (status) {
- case MHIP_STATUS_SUCCESS:
- ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_INIT);
- break;
- case MHIP_STATUS_NO_OP:
- case MHIP_STATUS_EP_NOT_READY:
- IPA_MPM_DBG("UL chan already stop, status = %d\n",
- status);
- break;
- case MHIP_STATUS_FAIL:
- case MHIP_STATUS_BAD_STATE:
- case MHIP_STATUS_EP_NOT_FOUND:
- IPA_MPM_ERR("UL chan cant be stopped err =%d\n",
- status);
- ipa_assert();
- return -EFAULT;
- default:
- IPA_MPM_ERR("Err not found\n");
- return -EFAULT;
- }
- /* Stop UL MHIP channel for offloading tethering connection */
- ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- if (ret) {
- IPA_MPM_ERR("Error cloking off PCIe clk, err = %d\n",
- ret);
- return ret;
- }
- ipa_mpm_ctx->md[probe_id].mhip_client = IPA_MPM_MHIP_NONE;
- }
- return ret;
- }
- static void ipa_mpm_change_gsi_state(int probe_id,
- enum ipa_mpm_mhip_chan mhip_chan,
- enum ipa_mpm_gsi_state next_state)
- {
- if (probe_id >= IPA_MPM_MHIP_CH_ID_MAX)
- return;
- if (mhip_chan == IPA_MPM_MHIP_CHAN_UL) {
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mutex);
- ipa_mpm_ctx->md[probe_id].ul_prod.gsi_state = next_state;
- IPA_MPM_DBG("GSI next_state = %d\n",
- ipa_mpm_ctx->md[probe_id].ul_prod.gsi_state);
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mutex);
- }
- if (mhip_chan == IPA_MPM_MHIP_CHAN_DL) {
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mutex);
- ipa_mpm_ctx->md[probe_id].dl_cons.gsi_state = next_state;
- IPA_MPM_DBG("GSI next_state = %d\n",
- ipa_mpm_ctx->md[probe_id].dl_cons.gsi_state);
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mutex);
- }
- }
- static void ipa_mpm_change_teth_state(int probe_id,
- enum ipa_mpm_teth_state next_state)
- {
- enum ipa_mpm_teth_state curr_state;
- if (probe_id >= IPA_MPM_MHIP_CH_ID_MAX) {
- IPA_MPM_ERR("Unknown probe_id\n");
- return;
- }
- curr_state = ipa_mpm_ctx->md[probe_id].teth_state;
- IPA_MPM_DBG("curr_state = %d, ip_state = %d mhip_s\n",
- curr_state, next_state);
- switch (curr_state) {
- case IPA_MPM_TETH_INIT:
- if (next_state == IPA_MPM_TETH_CONNECTED)
- next_state = IPA_MPM_TETH_INPROGRESS;
- break;
- case IPA_MPM_TETH_INPROGRESS:
- break;
- case IPA_MPM_TETH_CONNECTED:
- break;
- default:
- IPA_MPM_ERR("No change in state\n");
- break;
- }
- ipa_mpm_ctx->md[probe_id].teth_state = next_state;
- IPA_MPM_DBG("next_state = %d\n", next_state);
- }
- static void ipa_mpm_read_channel(enum ipa_client_type chan)
- {
- struct gsi_chan_info chan_info;
- int ipa_ep_idx;
- struct ipa3_ep_context *ep;
- int res;
- ipa_ep_idx = ipa3_get_ep_mapping(chan);
- if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED) {
- IPAERR("failed to get idx");
- return;
- }
- ep = &ipa3_ctx->ep[ipa_ep_idx];
- IPA_MPM_DBG("Reading channel for chan %d, ep = %d, gsi_chan_hdl = %d\n",
- chan, ep, ep->gsi_chan_hdl);
- res = ipa3_get_gsi_chan_info(&chan_info, ep->gsi_chan_hdl);
- if (res)
- IPA_MPM_ERR("Reading of channel failed for ep %d\n", ep);
- }
- /* ipa_mpm_mhi_probe_cb is received for each MHI'/MHI channel
- * Currently we have 4 MHI channels.
- */
- static int ipa_mpm_mhi_probe_cb(struct mhi_device *mhi_dev,
- const struct mhi_device_id *mhi_id)
- {
- struct ipa_mpm_channel *ch;
- int ret;
- enum ipa_client_type ul_prod, dl_cons;
- int probe_id;
- struct ipa_req_chan_out_params ul_out_params, dl_out_params;
- void __iomem *db_addr;
- int ipa_ep_idx;
- struct ipa3_ep_context *ep;
- u32 evt_ring_db_addr_low, evt_ring_db_addr_high;
- u32 wp_addr;
- int pipe_idx;
- bool is_acted = true;
- uint64_t flow_ctrl_mask = 0;
- bool add_delete = false;
- IPA_MPM_FUNC_ENTRY();
- if (ipa_mpm_ctx == NULL) {
- IPA_MPM_ERR("ipa_mpm_ctx is NULL not expected, returning..\n");
- return -ENOMEM;
- }
- probe_id = get_idx_from_id(mhi_id);
- if (probe_id >= IPA_MPM_MHIP_CH_ID_MAX) {
- IPA_MPM_ERR("chan=%pK is not supported for now\n", mhi_id);
- return -EPERM;
- }
- if (ipa_mpm_ctx->md[probe_id].init_complete) {
- IPA_MPM_ERR("Probe initialization already done, returning\n");
- return 0;
- }
- IPA_MPM_DBG("Received probe for id=%d\n", probe_id);
- get_ipa3_client(probe_id, &ul_prod, &dl_cons);
- /* Vote for IPA clock for first time in initialization seq.
- * IPA clock will be devoted when MHI enters LPM
- * PCIe clock will be voted / devoted with every channel probe
- * we receive.
- * ul_prod = Host -> Device
- * dl_cons = Device -> Host
- */
- ipa_mpm_ctx->md[probe_id].mhi_dev = mhi_dev;
- ipa_mpm_ctx->mhi_parent_dev =
- ipa_mpm_ctx->md[probe_id].mhi_dev->dev.parent;
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- ipa_mpm_ctx->md[probe_id].remote_state = MPM_MHIP_REMOTE_STOP;
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id, true, &is_acted);
- if (ret) {
- IPA_MPM_ERR("Err %d voitng PCIe clocks\n", ret);
- return -EPERM;
- }
- ipa_mpm_vote_unvote_ipa_clk(CLK_ON, probe_id);
- ipa_mpm_ctx->md[probe_id].in_lpm = false;
- IPA_MPM_DBG("ul chan = %d, dl_chan = %d\n", ul_prod, dl_cons);
- /*
- * Set up MHI' pipes for Device IPA filling in
- * Channel Context and Event Context.
- * These params will be sent to Device side.
- * UL CHAN = HOST -> Device
- * DL CHAN = Device -> HOST
- * per channel a TRE and EV is allocated.
- * for a UL channel -
- * IPA HOST PROD TRE -> IPA DEVICE CONS EV
- * IPA HOST PROD EV -> IPA DEVICE CONS TRE
- * for a DL channel -
- * IPA Device PROD TRE -> IPA HOST CONS EV
- * IPA Device PROD EV -> IPA HOST CONS TRE
- */
- if (ul_prod != IPA_CLIENT_MAX) {
- /* store UL properties */
- ch = &ipa_mpm_ctx->md[probe_id].ul_prod;
- /* Store Channel properties */
- ch->chan_props.id = mhi_dev->ul_chan_id;
- ch->chan_props.device_db =
- ipa_mpm_ctx->dev_info.chdb_base +
- ch->chan_props.id * 8;
- /* Fill Channel Conext to be sent to Device side */
- ch->chan_props.ch_ctx.chtype =
- IPA_MPM_MHI_HOST_UL_CHANNEL;
- ch->chan_props.ch_ctx.erindex =
- mhi_dev->ul_event_id;
- ch->chan_props.ch_ctx.rlen = (IPA_MPM_RING_LEN) *
- GSI_EVT_RING_RE_SIZE_16B;
- /* Store Event properties */
- ch->evt_props.ev_ctx.update_rp_modc = 0;
- ch->evt_props.ev_ctx.update_rp_intmodt = 0;
- ch->evt_props.ev_ctx.ertype = 1;
- ch->evt_props.ev_ctx.rlen = (IPA_MPM_RING_LEN) *
- GSI_EVT_RING_RE_SIZE_16B;
- ch->evt_props.ev_ctx.buff_size = TRE_BUFF_SIZE;
- ch->evt_props.device_db =
- ipa_mpm_ctx->dev_info.erdb_base +
- ch->chan_props.ch_ctx.erindex * 8;
- /* connect Host GSI pipes with MHI' protocol */
- ret = ipa_mpm_connect_mhip_gsi_pipe(ul_prod,
- probe_id, &ul_out_params);
- if (ret) {
- IPA_MPM_ERR("failed connecting MPM client %d\n",
- ul_prod);
- goto fail_gsi_setup;
- }
- ch->evt_props.ev_ctx.update_rp_addr =
- ipa_mpm_smmu_map_doorbell(
- MHIP_SMMU_DOMAIN_PCIE,
- ul_out_params.db_reg_phs_addr_lsb);
- if (ch->evt_props.ev_ctx.update_rp_addr == 0)
- ipa_assert();
- ipa_mpm_ctx->md[probe_id].ul_prod.db_device_iova =
- ch->evt_props.ev_ctx.update_rp_addr;
- ret = __ipa_mpm_configure_mhi_device(
- ch, probe_id, DMA_TO_HIPA);
- if (ret) {
- IPA_MPM_ERR("configure_mhi_dev fail %d\n",
- ret);
- goto fail_smmu;
- }
- }
- if (dl_cons != IPA_CLIENT_MAX) {
- /* store DL channel properties */
- ch = &ipa_mpm_ctx->md[probe_id].dl_cons;
- /* Store Channel properties */
- ch->chan_props.id = mhi_dev->dl_chan_id;
- ch->chan_props.device_db =
- ipa_mpm_ctx->dev_info.chdb_base +
- ch->chan_props.id * 8;
- /* Fill Channel Conext to be be sent to Dev side */
- ch->chan_props.ch_ctx.chstate = 1;
- ch->chan_props.ch_ctx.chtype =
- IPA_MPM_MHI_HOST_DL_CHANNEL;
- ch->chan_props.ch_ctx.erindex = mhi_dev->dl_event_id;
- ch->chan_props.ch_ctx.rlen = (IPA_MPM_RING_LEN) *
- GSI_EVT_RING_RE_SIZE_16B;
- /* Store Event properties */
- ch->evt_props.ev_ctx.update_rp_modc = 0;
- ch->evt_props.ev_ctx.update_rp_intmodt = 0;
- ch->evt_props.ev_ctx.ertype = 1;
- ch->evt_props.ev_ctx.rlen = (IPA_MPM_RING_LEN) *
- GSI_EVT_RING_RE_SIZE_16B;
- ch->evt_props.ev_ctx.buff_size = TRE_BUFF_SIZE;
- ch->evt_props.device_db =
- ipa_mpm_ctx->dev_info.erdb_base +
- ch->chan_props.ch_ctx.erindex * 8;
- /* connect Host GSI pipes with MHI' protocol */
- ret = ipa_mpm_connect_mhip_gsi_pipe(dl_cons,
- probe_id, &dl_out_params);
- if (ret) {
- IPA_MPM_ERR("connecting MPM client = %d failed\n",
- dl_cons);
- goto fail_gsi_setup;
- }
- ch->evt_props.ev_ctx.update_rp_addr =
- ipa_mpm_smmu_map_doorbell(
- MHIP_SMMU_DOMAIN_PCIE,
- dl_out_params.db_reg_phs_addr_lsb);
- if (ch->evt_props.ev_ctx.update_rp_addr == 0)
- ipa_assert();
- ipa_mpm_ctx->md[probe_id].dl_cons.db_device_iova =
- ch->evt_props.ev_ctx.update_rp_addr;
- ret = __ipa_mpm_configure_mhi_device(ch, probe_id,
- DMA_FROM_HIPA);
- if (ret) {
- IPA_MPM_ERR("mpm_config_mhi_dev failed %d\n", ret);
- goto fail_smmu;
- }
- }
- ret = mhi_prepare_for_transfer(ipa_mpm_ctx->md[probe_id].mhi_dev);
- if (ret) {
- IPA_MPM_ERR("mhi_prepare_for_transfer failed %d\n", ret);
- WARN_ON(1);
- /*
- * WA to handle prepare_for_tx failures.
- * Though prepare for transfer fails, indicate success
- * to MHI driver. remove_cb will be called eventually when
- * Device side comes from where pending cleanup happens.
- */
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- atomic_inc(&ipa_mpm_ctx->probe_cnt);
- ipa_mpm_ctx->md[probe_id].init_complete = false;
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- IPA_MPM_FUNC_EXIT();
- return 0;
- }
- /* mhi_prepare_for_transfer translates to starting remote channels */
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- ipa_mpm_ctx->md[probe_id].remote_state = MPM_MHIP_REMOTE_START;
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- /*
- * Ring initial channel db - Host Side UL and Device side DL channel.
- * To ring doorbell, write "WP" into doorbell register.
- * This WP should be set to 1 element less than ring max.
- */
- /* Ring UL PRODUCER TRANSFER RING (HOST IPA -> DEVICE IPA) Doorbell */
- if (ul_prod != IPA_CLIENT_MAX) {
- IPA_MPM_DBG("Host UL TR PA DB = 0X%0x\n",
- ul_out_params.db_reg_phs_addr_lsb);
- db_addr = ioremap(
- (phys_addr_t)(ul_out_params.db_reg_phs_addr_lsb), 4);
- wp_addr = ipa_mpm_ctx->md[probe_id].ul_prod_ring.tr_pa +
- ((IPA_MPM_RING_LEN - 1) * GSI_CHAN_RE_SIZE_16B);
- iowrite32(wp_addr, db_addr);
- IPA_MPM_DBG("Host UL TR DB = 0X%pK, wp_addr = 0X%0x",
- db_addr, wp_addr);
- iounmap(db_addr);
- ipa_mpm_read_channel(ul_prod);
- /* Ring UL PRODUCER EVENT RING (HOST IPA -> DEVICE IPA) Doorbell
- * Ring the event DB to a value outside the
- * ring range such that rp and wp never meet.
- */
- ipa_ep_idx = ipa3_get_ep_mapping(ul_prod);
- if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED) {
- IPA_MPM_ERR("fail to alloc EP.\n");
- goto fail_start_channel;
- }
- ep = &ipa3_ctx->ep[ipa_ep_idx];
- IPA_MPM_DBG("for ep_idx %d , gsi_evt_ring_hdl = %ld\n",
- ipa_ep_idx, ep->gsi_evt_ring_hdl);
- gsi_query_evt_ring_db_addr(ep->gsi_evt_ring_hdl,
- &evt_ring_db_addr_low, &evt_ring_db_addr_high);
- IPA_MPM_DBG("Host UL ER PA DB = 0X%0x\n",
- evt_ring_db_addr_low);
- db_addr = ioremap((phys_addr_t)(evt_ring_db_addr_low), 4);
- wp_addr = ipa_mpm_ctx->md[probe_id].ul_prod_ring.er_pa +
- ((IPA_MPM_RING_LEN + 1) * GSI_EVT_RING_RE_SIZE_16B);
- IPA_MPM_DBG("Host UL ER DB = 0X%pK, wp_addr = 0X%0x",
- db_addr, wp_addr);
- iowrite32(wp_addr, db_addr);
- iounmap(db_addr);
- /* Ring DEVICE IPA DL CONSUMER Event Doorbell */
- db_addr = ioremap((phys_addr_t)
- (ipa_mpm_ctx->md[probe_id].ul_prod.evt_props.device_db),
- 4);
- wp_addr = ipa_mpm_ctx->md[probe_id].ul_prod_ring.tr_pa +
- ((IPA_MPM_RING_LEN + 1) * GSI_EVT_RING_RE_SIZE_16B);
- iowrite32(wp_addr, db_addr);
- iounmap(db_addr);
- }
- /* Ring DL PRODUCER (DEVICE IPA -> HOST IPA) Doorbell */
- if (dl_cons != IPA_CLIENT_MAX) {
- db_addr = ioremap((phys_addr_t)
- (ipa_mpm_ctx->md[probe_id].dl_cons.chan_props.device_db),
- 4);
- wp_addr = ipa_mpm_ctx->md[probe_id].dl_prod_ring.tr_pa +
- ((IPA_MPM_RING_LEN - 1) * GSI_CHAN_RE_SIZE_16B);
- IPA_MPM_DBG("Device DL TR DB = 0X%pK, wp_addr = 0X%0x",
- db_addr, wp_addr);
- iowrite32(wp_addr, db_addr);
- iounmap(db_addr);
- /*
- * Ring event ring DB on Device side.
- * ipa_mpm should ring the event DB to a value outside the
- * ring range such that rp and wp never meet.
- */
- db_addr =
- ioremap(
- (phys_addr_t)
- (ipa_mpm_ctx->md[probe_id].dl_cons.evt_props.device_db),
- 4);
- wp_addr = ipa_mpm_ctx->md[probe_id].dl_prod_ring.er_pa +
- ((IPA_MPM_RING_LEN + 1) * GSI_EVT_RING_RE_SIZE_16B);
- iowrite32(wp_addr, db_addr);
- IPA_MPM_DBG("Device UL ER DB = 0X%pK,wp_addr = 0X%0x",
- db_addr, wp_addr);
- iounmap(db_addr);
- /* Ring DL EVENT RING CONSUMER (DEVICE IPA CONSUMER) Doorbell */
- ipa_ep_idx = ipa3_get_ep_mapping(dl_cons);
- if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED) {
- IPA_MPM_ERR("fail to alloc EP.\n");
- goto fail_start_channel;
- }
- ep = &ipa3_ctx->ep[ipa_ep_idx];
- gsi_query_evt_ring_db_addr(ep->gsi_evt_ring_hdl,
- &evt_ring_db_addr_low, &evt_ring_db_addr_high);
- IPA_MPM_DBG("Host DL ER PA DB = 0X%0x\n",
- evt_ring_db_addr_low);
- db_addr = ioremap((phys_addr_t)(evt_ring_db_addr_low), 4);
- wp_addr = ipa_mpm_ctx->md[probe_id].dl_prod_ring.tr_pa +
- ((IPA_MPM_RING_LEN + 1) * GSI_EVT_RING_RE_SIZE_16B);
- iowrite32(wp_addr, db_addr);
- IPA_MPM_DBG("Host DL ER DB = 0X%pK, wp_addr = 0X%0x",
- db_addr, wp_addr);
- iounmap(db_addr);
- }
- /* Check if TETH connection is in progress.
- * If teth isn't started by now, then Stop UL channel.
- */
- switch (ipa_mpm_ctx->md[probe_id].teth_state) {
- case IPA_MPM_TETH_INIT:
- /*
- * Make sure to stop Device side channels before
- * stopping Host side UL channels. This is to make
- * sure Device side doesn't access host side IPA if
- * Host IPA gets unvoted.
- */
- ret = ipa_mpm_start_stop_remote_mhip_chan(probe_id,
- MPM_MHIP_STOP, true);
- if (ret) {
- /*
- * This can fail only when modem is in SSR.
- * Eventually there would be a remove callback,
- * so return a failure.
- */
- IPA_MPM_ERR("MHIP remote chan stop fail = %d\n", ret);
- return ret;
- }
- if (ul_prod != IPA_CLIENT_MAX) {
- /* No teth started yet, disable UL channel */
- ipa_ep_idx = ipa3_get_ep_mapping(ul_prod);
- if (ipa_ep_idx == IPA_EP_NOT_ALLOCATED) {
- IPA_MPM_ERR("fail to alloc EP.\n");
- goto fail_stop_channel;
- }
- ret = ipa3_stop_gsi_channel(ipa_ep_idx);
- if (ret) {
- IPA_MPM_ERR("MHIP Stop channel err = %d\n",
- ret);
- goto fail_stop_channel;
- }
- ipa_mpm_change_gsi_state(probe_id,
- IPA_MPM_MHIP_CHAN_UL,
- GSI_STOPPED);
- }
- if (is_acted)
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- true, &is_acted);
- break;
- case IPA_MPM_TETH_INPROGRESS:
- case IPA_MPM_TETH_CONNECTED:
- IPA_MPM_DBG("UL channel is already started, continue\n");
- ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_CONNECTED);
- /* Lift the delay for rmnet USB prod pipe */
- if (probe_id == IPA_MPM_MHIP_CH_ID_1) {
- pipe_idx = ipa3_get_ep_mapping(IPA_CLIENT_USB_PROD);
- ipa3_xdci_ep_delay_rm(pipe_idx);
- }
- break;
- default:
- IPA_MPM_DBG("No op for UL channel, in teth state = %d",
- ipa_mpm_ctx->md[probe_id].teth_state);
- break;
- }
- atomic_inc(&ipa_mpm_ctx->probe_cnt);
- /* Check if ODL/USB DPL pipe is connected before probe */
- if (probe_id == IPA_MPM_MHIP_CH_ID_2) {
- if (ipa3_is_odl_connected())
- ret = ipa_mpm_set_dma_mode(
- IPA_CLIENT_MHI_PRIME_DPL_PROD,
- IPA_CLIENT_ODL_DPL_CONS, false);
- else if (atomic_read(&ipa_mpm_ctx->adpl_over_usb_available))
- ret = ipa_mpm_set_dma_mode(
- IPA_CLIENT_MHI_PRIME_DPL_PROD,
- IPA_CLIENT_USB_DPL_CONS, false);
- if (ret)
- IPA_MPM_ERR("DPL DMA to ODL/USB failed, ret = %d\n",
- ret);
- }
- mutex_lock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- ipa_mpm_ctx->md[probe_id].init_complete = true;
- mutex_unlock(&ipa_mpm_ctx->md[probe_id].mhi_mutex);
- /* Update Flow control Monitoring, only for the teth UL Prod pipes */
- if (probe_id == IPA_MPM_MHIP_CH_ID_0) {
- ipa_ep_idx = ipa3_get_ep_mapping(ul_prod);
- ep = &ipa3_ctx->ep[ipa_ep_idx];
- ret = ipa3_uc_send_enable_flow_control(ep->gsi_chan_hdl,
- IPA_MPM_RING_LEN / 4);
- if (ret) {
- IPA_MPM_ERR("Err %d flow control enable\n", ret);
- goto fail_flow_control;
- }
- IPA_MPM_DBG("Flow Control enabled for %d", probe_id);
- flow_ctrl_mask = atomic_read(&ipa_mpm_ctx->flow_ctrl_mask);
- add_delete = flow_ctrl_mask > 0 ? 1 : 0;
- ret = ipa3_uc_send_update_flow_control(flow_ctrl_mask,
- add_delete);
- if (ret) {
- IPA_MPM_ERR("Err %d flow control update\n", ret);
- goto fail_flow_control;
- }
- IPA_MPM_DBG("Flow Control updated for %d", probe_id);
- }
- IPA_MPM_FUNC_EXIT();
- return 0;
- fail_gsi_setup:
- fail_start_channel:
- fail_stop_channel:
- fail_smmu:
- fail_flow_control:
- if (ipa_mpm_ctx->dev_info.ipa_smmu_enabled)
- IPA_MPM_DBG("SMMU failed\n");
- if (is_acted)
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id, true,
- &is_acted);
- ipa_mpm_vote_unvote_ipa_clk(CLK_OFF, probe_id);
- ipa_assert();
- return ret;
- }
- static void ipa_mpm_init_mhip_channel_info(void)
- {
- /* IPA_MPM_MHIP_CH_ID_0 => MHIP TETH PIPES */
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_0].dl_cons.ipa_client =
- IPA_CLIENT_MHI_PRIME_TETH_PROD;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_0].dl_cons.ep_cfg =
- mhip_dl_teth_ep_cfg;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_0].ul_prod.ipa_client =
- IPA_CLIENT_MHI_PRIME_TETH_CONS;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_0].ul_prod.ep_cfg =
- mhip_ul_teth_ep_cfg;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_0].mhip_client =
- IPA_MPM_MHIP_TETH;
- /* IPA_MPM_MHIP_CH_ID_1 => MHIP RMNET PIPES */
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_1].dl_cons.ipa_client =
- IPA_CLIENT_MHI_PRIME_RMNET_PROD;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_1].dl_cons.ep_cfg =
- mhip_dl_rmnet_ep_cfg;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_1].ul_prod.ipa_client =
- IPA_CLIENT_MHI_PRIME_RMNET_CONS;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_1].ul_prod.ep_cfg =
- mhip_ul_rmnet_ep_cfg;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_1].mhip_client =
- IPA_MPM_MHIP_USB_RMNET;
- /* IPA_MPM_MHIP_CH_ID_2 => MHIP ADPL PIPE */
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_2].dl_cons.ipa_client =
- IPA_CLIENT_MHI_PRIME_DPL_PROD;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_2].dl_cons.ep_cfg =
- mhip_dl_dpl_ep_cfg;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_2].ul_prod.ipa_client =
- IPA_CLIENT_MAX;
- ipa_mpm_pipes[IPA_MPM_MHIP_CH_ID_2].mhip_client =
- IPA_MPM_MHIP_USB_DPL;
- }
- static void ipa_mpm_mhi_remove_cb(struct mhi_device *mhi_dev)
- {
- int mhip_idx;
- IPA_MPM_FUNC_ENTRY();
- for (mhip_idx = 0; mhip_idx < IPA_MPM_MHIP_CH_ID_MAX; mhip_idx++) {
- if (mhi_dev == ipa_mpm_ctx->md[mhip_idx].mhi_dev)
- break;
- }
- if (mhip_idx >= IPA_MPM_MHIP_CH_ID_MAX) {
- IPA_MPM_DBG("remove_cb for mhip_idx = %d not probed before\n",
- mhip_idx);
- return;
- }
- IPA_MPM_DBG("remove_cb for mhip_idx = %d", mhip_idx);
- mutex_lock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex);
- ipa_mpm_ctx->md[mhip_idx].init_complete = false;
- mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex);
- if (mhip_idx == IPA_MPM_MHIP_CH_ID_0)
- ipa3_uc_send_disable_flow_control();
- ipa_mpm_mhip_shutdown(mhip_idx);
- atomic_dec(&ipa_mpm_ctx->probe_cnt);
- if (atomic_read(&ipa_mpm_ctx->probe_cnt) == 0) {
- /* Last probe done, reset Everything here */
- ipa_mpm_ctx->mhi_parent_dev = NULL;
- ipa_mpm_ctx->carved_smmu_cb.next_addr =
- ipa_mpm_ctx->carved_smmu_cb.va_start;
- atomic_set(&ipa_mpm_ctx->pcie_clk_total_cnt, 0);
- for (mhip_idx = 0;
- mhip_idx < IPA_MPM_MHIP_CH_ID_MAX; mhip_idx++) {
- atomic_set(
- &ipa_mpm_ctx->md[mhip_idx].clk_cnt.pcie_clk_cnt,
- 0);
- }
- }
- IPA_MPM_FUNC_EXIT();
- }
- static void ipa_mpm_mhi_status_cb(struct mhi_device *mhi_dev,
- enum MHI_CB mhi_cb)
- {
- int mhip_idx;
- enum mhip_status_type status;
- IPA_MPM_DBG("%d\n", mhi_cb);
- for (mhip_idx = 0; mhip_idx < IPA_MPM_MHIP_CH_ID_MAX; mhip_idx++) {
- if (mhi_dev == ipa_mpm_ctx->md[mhip_idx].mhi_dev)
- break;
- }
- if (mhip_idx >= IPA_MPM_MHIP_CH_ID_MAX) {
- IPA_MPM_DBG("ignoring secondary callbacks\n");
- return;
- }
- mutex_lock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex);
- if (!ipa_mpm_ctx->md[mhip_idx].init_complete) {
- /*
- * SSR might be in progress, dont have to vote/unvote for
- * IPA clocks as it will be taken care in remove_cb/subsequent
- * probe.
- */
- IPA_MPM_DBG("SSR in progress, return\n");
- mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex);
- return;
- }
- mutex_unlock(&ipa_mpm_ctx->md[mhip_idx].mhi_mutex);
- switch (mhi_cb) {
- case MHI_CB_IDLE:
- break;
- case MHI_CB_LPM_ENTER:
- if (!ipa_mpm_ctx->md[mhip_idx].in_lpm) {
- status = ipa_mpm_start_stop_mhip_chan(
- IPA_MPM_MHIP_CHAN_DL,
- mhip_idx, MPM_MHIP_STOP);
- IPA_MPM_DBG("status = %d\n", status);
- ipa_mpm_vote_unvote_ipa_clk(CLK_OFF, mhip_idx);
- ipa_mpm_ctx->md[mhip_idx].in_lpm = true;
- } else {
- IPA_MPM_DBG("Already in lpm\n");
- }
- break;
- case MHI_CB_LPM_EXIT:
- if (ipa_mpm_ctx->md[mhip_idx].in_lpm) {
- ipa_mpm_vote_unvote_ipa_clk(CLK_ON, mhip_idx);
- status = ipa_mpm_start_stop_mhip_chan(
- IPA_MPM_MHIP_CHAN_DL,
- mhip_idx, MPM_MHIP_START);
- IPA_MPM_DBG("status = %d\n", status);
- ipa_mpm_ctx->md[mhip_idx].in_lpm = false;
- } else {
- IPA_MPM_DBG("Already out of lpm\n");
- }
- break;
- case MHI_CB_EE_RDDM:
- case MHI_CB_PENDING_DATA:
- case MHI_CB_SYS_ERROR:
- case MHI_CB_FATAL_ERROR:
- case MHI_CB_EE_MISSION_MODE:
- case MHI_CB_DTR_SIGNAL:
- IPA_MPM_ERR("unexpected event %d\n", mhi_cb);
- break;
- }
- }
- static void ipa_mpm_mhip_map_prot(enum ipa_usb_teth_prot prot,
- enum ipa_mpm_mhip_client_type *mhip_client)
- {
- switch (prot) {
- case IPA_USB_RNDIS:
- *mhip_client = IPA_MPM_MHIP_TETH;
- break;
- case IPA_USB_RMNET:
- *mhip_client = IPA_MPM_MHIP_USB_RMNET;
- break;
- case IPA_USB_DIAG:
- *mhip_client = IPA_MPM_MHIP_USB_DPL;
- break;
- default:
- *mhip_client = IPA_MPM_MHIP_NONE;
- break;
- }
- IPA_MPM_DBG("Mapped xdci prot %d -> MHIP prot %d\n", prot,
- *mhip_client);
- }
- int ipa_mpm_mhip_xdci_pipe_enable(enum ipa_usb_teth_prot xdci_teth_prot)
- {
- int probe_id = IPA_MPM_MHIP_CH_ID_MAX;
- int i;
- enum ipa_mpm_mhip_client_type mhip_client;
- enum mhip_status_type status;
- int pipe_idx;
- bool is_acted = true;
- int ret = 0;
- if (ipa_mpm_ctx == NULL) {
- IPA_MPM_ERR("MPM not platform probed yet, returning ..\n");
- return 0;
- }
- ipa_mpm_mhip_map_prot(xdci_teth_prot, &mhip_client);
- for (i = 0; i < IPA_MPM_MHIP_CH_ID_MAX; i++) {
- if (ipa_mpm_pipes[i].mhip_client == mhip_client) {
- probe_id = i;
- break;
- }
- }
- if (probe_id == IPA_MPM_MHIP_CH_ID_MAX) {
- IPA_MPM_ERR("Unknown probe_id\n");
- return 0;
- }
- IPA_MPM_DBG("Connect xdci prot %d -> mhip_client = %d probe_id = %d\n",
- xdci_teth_prot, mhip_client, probe_id);
- ipa_mpm_ctx->md[probe_id].mhip_client = mhip_client;
- ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON, probe_id,
- false, &is_acted);
- if (ret) {
- IPA_MPM_ERR("Error cloking on PCIe clk, err = %d\n", ret);
- return ret;
- }
- /*
- * Make sure to start Device side channels before
- * starting Host side UL channels. This is to make
- * sure device side access host side IPA only when
- * Host IPA gets voted.
- */
- ret = ipa_mpm_start_stop_remote_mhip_chan(probe_id,
- MPM_MHIP_START, false);
- if (ret) {
- /*
- * This can fail only when modem is in SSR state.
- * Eventually there would be a remove callback,
- * so return a failure. Dont have to unvote PCIE here.
- */
- IPA_MPM_ERR("MHIP remote chan start fail = %d\n",
- ret);
- return ret;
- }
- IPA_MPM_DBG("MHIP remote channel start success\n");
- switch (mhip_client) {
- case IPA_MPM_MHIP_USB_RMNET:
- ipa_mpm_set_dma_mode(IPA_CLIENT_USB_PROD,
- IPA_CLIENT_MHI_PRIME_RMNET_CONS, false);
- break;
- case IPA_MPM_MHIP_USB_DPL:
- IPA_MPM_DBG("connecting DPL prot %d\n", mhip_client);
- ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_CONNECTED);
- atomic_set(&ipa_mpm_ctx->adpl_over_usb_available, 1);
- return 0;
- default:
- IPA_MPM_DBG("mhip_client = %d not processed\n", mhip_client);
- if (is_acted) {
- ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- if (ret) {
- IPA_MPM_ERR("Err unvoting PCIe clk, err = %d\n",
- ret);
- return ret;
- }
- }
- return 0;
- }
- if (mhip_client != IPA_MPM_MHIP_USB_DPL)
- /* Start UL MHIP channel for offloading teth connection */
- status = ipa_mpm_start_stop_mhip_chan(IPA_MPM_MHIP_CHAN_UL,
- probe_id,
- MPM_MHIP_START);
- switch (status) {
- case MHIP_STATUS_SUCCESS:
- case MHIP_STATUS_NO_OP:
- ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_CONNECTED);
- pipe_idx = ipa3_get_ep_mapping(IPA_CLIENT_USB_PROD);
- /* Lift the delay for rmnet USB prod pipe */
- ipa3_xdci_ep_delay_rm(pipe_idx);
- if (status == MHIP_STATUS_NO_OP && is_acted) {
- /* Channels already have been started,
- * we can devote for pcie clocks
- */
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- }
- break;
- case MHIP_STATUS_EP_NOT_READY:
- if (is_acted)
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_INPROGRESS);
- break;
- case MHIP_STATUS_FAIL:
- case MHIP_STATUS_BAD_STATE:
- case MHIP_STATUS_EP_NOT_FOUND:
- IPA_MPM_ERR("UL chan cant be started err =%d\n", status);
- if (is_acted)
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- ret = -EFAULT;
- break;
- default:
- if (is_acted)
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- IPA_MPM_ERR("Err not found\n");
- break;
- }
- return ret;
- }
- int ipa_mpm_mhip_xdci_pipe_disable(enum ipa_usb_teth_prot xdci_teth_prot)
- {
- int probe_id = IPA_MPM_MHIP_CH_ID_MAX;
- int i;
- enum ipa_mpm_mhip_client_type mhip_client;
- enum mhip_status_type status;
- int ret = 0;
- bool is_acted = true;
- if (ipa_mpm_ctx == NULL) {
- IPA_MPM_ERR("MPM not platform probed, returning ..\n");
- return 0;
- }
- ipa_mpm_mhip_map_prot(xdci_teth_prot, &mhip_client);
- for (i = 0; i < IPA_MPM_MHIP_CH_ID_MAX; i++) {
- if (ipa_mpm_pipes[i].mhip_client == mhip_client) {
- probe_id = i;
- break;
- }
- }
- if (probe_id == IPA_MPM_MHIP_CH_ID_MAX) {
- IPA_MPM_ERR("Invalid probe_id\n");
- return 0;
- }
- IPA_MPM_ERR("xdci disconnect prot %d mhip_client = %d probe_id = %d\n",
- xdci_teth_prot, mhip_client, probe_id);
- /*
- * Make sure to stop Device side channels before
- * stopping Host side UL channels. This is to make
- * sure device side doesn't access host side IPA if
- * Host IPA gets unvoted.
- */
- ret = ipa_mpm_start_stop_remote_mhip_chan(probe_id,
- MPM_MHIP_STOP, false);
- if (ret) {
- /*
- * This can fail only when modem is in SSR state.
- * Eventually there would be a remove callback,
- * so return a failure.
- */
- IPA_MPM_ERR("MHIP remote chan stop fail = %d\n", ret);
- return ret;
- }
- IPA_MPM_DBG("MHIP remote channels are stopped\n");
- switch (mhip_client) {
- case IPA_MPM_MHIP_USB_RMNET:
- ret = ipa_mpm_set_dma_mode(IPA_CLIENT_USB_PROD,
- IPA_CLIENT_APPS_LAN_CONS, true);
- if (ret) {
- IPA_MPM_ERR("failed to reset dma mode\n");
- return ret;
- }
- break;
- case IPA_MPM_MHIP_TETH:
- IPA_MPM_DBG("Rndis Disconnect, wait for wan_state ioctl\n");
- return 0;
- case IPA_MPM_MHIP_USB_DPL:
- IPA_MPM_DBG("Teth Disconnecting for DPL\n");
- /* change teth state only if ODL is disconnected */
- if (!ipa3_is_odl_connected()) {
- ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_INIT);
- ipa_mpm_ctx->md[probe_id].mhip_client =
- IPA_MPM_MHIP_NONE;
- }
- ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- if (ret)
- IPA_MPM_ERR("Error clking off PCIe clk err%d\n", ret);
- atomic_set(&ipa_mpm_ctx->adpl_over_usb_available, 0);
- return ret;
- default:
- IPA_MPM_ERR("mhip_client = %d not supported\n", mhip_client);
- return 0;
- }
- status = ipa_mpm_start_stop_mhip_chan(IPA_MPM_MHIP_CHAN_UL,
- probe_id, MPM_MHIP_STOP);
- switch (status) {
- case MHIP_STATUS_SUCCESS:
- case MHIP_STATUS_NO_OP:
- case MHIP_STATUS_EP_NOT_READY:
- ipa_mpm_change_teth_state(probe_id, IPA_MPM_TETH_INIT);
- break;
- case MHIP_STATUS_FAIL:
- case MHIP_STATUS_BAD_STATE:
- case MHIP_STATUS_EP_NOT_FOUND:
- IPA_MPM_ERR("UL chan cant be started err =%d\n", status);
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- return -EFAULT;
- default:
- IPA_MPM_ERR("Err not found\n");
- break;
- }
- ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF, probe_id,
- false, &is_acted);
- if (ret) {
- IPA_MPM_ERR("Error cloking off PCIe clk, err = %d\n", ret);
- return ret;
- }
- ipa_mpm_ctx->md[probe_id].mhip_client = IPA_MPM_MHIP_NONE;
- return ret;
- }
- static int ipa_mpm_populate_smmu_info(struct platform_device *pdev)
- {
- struct ipa_smmu_in_params smmu_in;
- struct ipa_smmu_out_params smmu_out;
- u32 carved_iova_ap_mapping[2];
- struct ipa_smmu_cb_ctx *cb;
- struct ipa_smmu_cb_ctx *ap_cb = ipa3_get_smmu_ctx(IPA_SMMU_CB_AP);
- int ret = 0;
- if (ipa_mpm_ctx->carved_smmu_cb.valid) {
- IPA_MPM_DBG("SMMU Context allocated, returning ..\n");
- return ret;
- }
- cb = &ipa_mpm_ctx->carved_smmu_cb;
- /* get IPA SMMU enabled status */
- smmu_in.smmu_client = IPA_SMMU_AP_CLIENT;
- if (ipa_get_smmu_params(&smmu_in, &smmu_out))
- ipa_mpm_ctx->dev_info.ipa_smmu_enabled = false;
- else
- ipa_mpm_ctx->dev_info.ipa_smmu_enabled =
- smmu_out.smmu_enable;
- /* get cache_coherent enable or not */
- ipa_mpm_ctx->dev_info.is_cache_coherent = ap_cb->is_cache_coherent;
- if (of_property_read_u32_array(pdev->dev.of_node, "qcom,iova-mapping",
- carved_iova_ap_mapping, 2)) {
- IPA_MPM_ERR("failed to read of_node %s\n",
- "qcom,mpm-iova-mapping");
- return -EINVAL;
- }
- ipa_mpm_ctx->dev_info.pcie_smmu_enabled = true;
- if (ipa_mpm_ctx->dev_info.ipa_smmu_enabled !=
- ipa_mpm_ctx->dev_info.pcie_smmu_enabled) {
- IPA_MPM_DBG("PCIE/IPA SMMU config mismatch\n");
- return -EINVAL;
- }
- cb->va_start = carved_iova_ap_mapping[0];
- cb->va_size = carved_iova_ap_mapping[1];
- cb->va_end = cb->va_start + cb->va_size;
- if (cb->va_end >= ap_cb->va_start) {
- IPA_MPM_ERR("MPM iommu and AP overlap addr 0x%lx\n",
- cb->va_start);
- ipa_assert();
- return -EFAULT;
- }
- cb->dev = ipa_mpm_ctx->dev_info.dev;
- cb->valid = true;
- cb->next_addr = cb->va_start;
- if (dma_set_mask_and_coherent(ipa_mpm_ctx->dev_info.dev,
- DMA_BIT_MASK(64))) {
- IPA_MPM_ERR("setting DMA mask to 64 failed.\n");
- return -EINVAL;
- }
- return ret;
- }
- static int ipa_mpm_probe(struct platform_device *pdev)
- {
- int ret = 0;
- int i = 0;
- int idx = 0;
- IPA_MPM_FUNC_ENTRY();
- if (ipa_mpm_ctx) {
- IPA_MPM_DBG("MPM is already probed, returning\n");
- return 0;
- }
- ret = ipa_register_ipa_ready_cb(ipa_mpm_ipa3_ready_cb, (void *)pdev);
- /*
- * If we received -EEXIST, IPA has initialized. So we need
- * to continue the probing process.
- */
- if (!ret) {
- IPA_MPM_DBG("IPA not ready yet, registering callback\n");
- return ret;
- }
- IPA_MPM_DBG("IPA is ready, continue with probe\n");
- ipa_mpm_ctx = kzalloc(sizeof(*ipa_mpm_ctx), GFP_KERNEL);
- if (!ipa_mpm_ctx)
- return -ENOMEM;
- for (i = 0; i < IPA_MPM_MHIP_CH_ID_MAX; i++) {
- mutex_init(&ipa_mpm_ctx->md[i].mutex);
- mutex_init(&ipa_mpm_ctx->md[i].mhi_mutex);
- }
- ipa_mpm_ctx->dev_info.pdev = pdev;
- ipa_mpm_ctx->dev_info.dev = &pdev->dev;
- ipa_mpm_init_mhip_channel_info();
- if (of_property_read_u32(pdev->dev.of_node, "qcom,mhi-chdb-base",
- &ipa_mpm_ctx->dev_info.chdb_base)) {
- IPA_MPM_ERR("failed to read qcom,mhi-chdb-base\n");
- goto fail_probe;
- }
- IPA_MPM_DBG("chdb-base=0x%x\n", ipa_mpm_ctx->dev_info.chdb_base);
- if (of_property_read_u32(pdev->dev.of_node, "qcom,mhi-erdb-base",
- &ipa_mpm_ctx->dev_info.erdb_base)) {
- IPA_MPM_ERR("failed to read qcom,mhi-erdb-base\n");
- goto fail_probe;
- }
- IPA_MPM_DBG("erdb-base=0x%x\n", ipa_mpm_ctx->dev_info.erdb_base);
- ret = ipa_mpm_populate_smmu_info(pdev);
- if (ret) {
- IPA_MPM_DBG("SMMU Config failed\n");
- goto fail_probe;
- }
- atomic_set(&ipa_mpm_ctx->ipa_clk_total_cnt, 0);
- atomic_set(&ipa_mpm_ctx->pcie_clk_total_cnt, 0);
- atomic_set(&ipa_mpm_ctx->flow_ctrl_mask, 0);
- for (idx = 0; idx < IPA_MPM_MHIP_CH_ID_MAX; idx++) {
- ipa_mpm_ctx->md[idx].ul_prod.gsi_state = GSI_INIT;
- ipa_mpm_ctx->md[idx].dl_cons.gsi_state = GSI_INIT;
- atomic_set(&ipa_mpm_ctx->md[idx].clk_cnt.ipa_clk_cnt, 0);
- atomic_set(&ipa_mpm_ctx->md[idx].clk_cnt.pcie_clk_cnt, 0);
- }
- ret = mhi_driver_register(&mhi_driver);
- if (ret) {
- IPA_MPM_ERR("mhi_driver_register failed %d\n", ret);
- goto fail_probe;
- }
- IPA_MPM_FUNC_EXIT();
- return 0;
- fail_probe:
- kfree(ipa_mpm_ctx);
- ipa_mpm_ctx = NULL;
- return -EFAULT;
- }
- static int ipa_mpm_remove(struct platform_device *pdev)
- {
- IPA_MPM_FUNC_ENTRY();
- mhi_driver_unregister(&mhi_driver);
- IPA_MPM_FUNC_EXIT();
- return 0;
- }
- static const struct of_device_id ipa_mpm_dt_match[] = {
- { .compatible = "qcom,ipa-mpm" },
- {},
- };
- MODULE_DEVICE_TABLE(of, ipa_mpm_dt_match);
- static struct platform_driver ipa_ipa_mpm_driver = {
- .driver = {
- .name = "ipa_mpm",
- .of_match_table = ipa_mpm_dt_match,
- },
- .probe = ipa_mpm_probe,
- .remove = ipa_mpm_remove,
- };
- /**
- * ipa_mpm_init() - Registers ipa_mpm as a platform device for a APQ
- *
- * This function is called after bootup for APQ device.
- * ipa_mpm will register itself as a platform device, and probe
- * function will get called.
- *
- * Return: None
- */
- static int __init ipa_mpm_init(void)
- {
- IPA_MPM_DBG("register ipa_mpm platform device\n");
- return platform_driver_register(&ipa_ipa_mpm_driver);
- }
- static void __exit ipa_mpm_exit(void)
- {
- IPA_MPM_DBG("unregister ipa_mpm platform device\n");
- platform_driver_unregister(&ipa_ipa_mpm_driver);
- }
- /**
- * ipa3_is_mhip_offload_enabled() - check if IPA MPM module was initialized
- * successfully. If it is initialized, MHIP is enabled for teth
- *
- * Return value: 1 for yes; 0 for no
- */
- int ipa3_is_mhip_offload_enabled(void)
- {
- if (ipa_mpm_ctx == NULL)
- return 0;
- else
- return 1;
- }
- int ipa_mpm_panic_handler(char *buf, int size)
- {
- int i;
- int cnt = 0;
- cnt = scnprintf(buf, size,
- "\n---- MHIP Active Clients Table ----\n");
- cnt += scnprintf(buf + cnt, size - cnt,
- "Total PCIe active clients count: %d\n",
- atomic_read(&ipa_mpm_ctx->pcie_clk_total_cnt));
- cnt += scnprintf(buf + cnt, size - cnt,
- "Total IPA active clients count: %d\n",
- atomic_read(&ipa_mpm_ctx->ipa_clk_total_cnt));
- for (i = 0; i < IPA_MPM_MHIP_CH_ID_MAX; i++) {
- cnt += scnprintf(buf + cnt, size - cnt,
- "client id: %d ipa vote cnt: %d pcie vote cnt\n", i,
- atomic_read(&ipa_mpm_ctx->md[i].clk_cnt.ipa_clk_cnt),
- atomic_read(&ipa_mpm_ctx->md[i].clk_cnt.pcie_clk_cnt));
- }
- return cnt;
- }
- /**
- * ipa3_get_mhip_gsi_stats() - Query MHIP gsi stats from uc
- * @stats: [inout] stats blob from client populated by driver
- *
- * Returns: 0 on success, negative on failure
- *
- * @note Cannot be called from atomic context
- *
- */
- int ipa3_get_mhip_gsi_stats(struct ipa_uc_dbg_ring_stats *stats)
- {
- int i;
- if (!ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_mmio) {
- IPAERR("bad parms NULL mhip_gsi_stats_mmio\n");
- return -EINVAL;
- }
- IPA_ACTIVE_CLIENTS_INC_SIMPLE();
- for (i = 0; i < MAX_MHIP_CHANNELS; i++) {
- stats->ring[i].ringFull = ioread32(
- ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_mmio
- + i * IPA3_UC_DEBUG_STATS_OFF +
- IPA3_UC_DEBUG_STATS_RINGFULL_OFF);
- stats->ring[i].ringEmpty = ioread32(
- ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_mmio
- + i * IPA3_UC_DEBUG_STATS_OFF +
- IPA3_UC_DEBUG_STATS_RINGEMPTY_OFF);
- stats->ring[i].ringUsageHigh = ioread32(
- ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_mmio
- + i * IPA3_UC_DEBUG_STATS_OFF +
- IPA3_UC_DEBUG_STATS_RINGUSAGEHIGH_OFF);
- stats->ring[i].ringUsageLow = ioread32(
- ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_mmio
- + i * IPA3_UC_DEBUG_STATS_OFF +
- IPA3_UC_DEBUG_STATS_RINGUSAGELOW_OFF);
- stats->ring[i].RingUtilCount = ioread32(
- ipa3_ctx->mhip_ctx.dbg_stats.uc_dbg_stats_mmio
- + i * IPA3_UC_DEBUG_STATS_OFF +
- IPA3_UC_DEBUG_STATS_RINGUTILCOUNT_OFF);
- }
- IPA_ACTIVE_CLIENTS_DEC_SIMPLE();
- return 0;
- }
- /**
- * ipa3_mpm_enable_adpl_over_odl() - Enable or disable ADPL over ODL
- * @enable: true for enable, false for disable
- *
- * Returns: 0 on success, negative on failure
- *
- */
- int ipa3_mpm_enable_adpl_over_odl(bool enable)
- {
- int ret;
- bool is_acted = true;
- IPA_MPM_FUNC_ENTRY();
- if (!ipa3_is_mhip_offload_enabled()) {
- IPA_MPM_ERR("mpm ctx is NULL\n");
- return -EPERM;
- }
- if (enable) {
- /* inc clk count and set DMA to ODL */
- IPA_MPM_DBG("mpm enabling ADPL over ODL\n");
- ret = ipa_mpm_vote_unvote_pcie_clk(CLK_ON,
- IPA_MPM_MHIP_CH_ID_2, false, &is_acted);
- if (ret) {
- IPA_MPM_ERR("Err %d cloking on PCIe clk\n", ret);
- return ret;
- }
- ret = ipa_mpm_set_dma_mode(IPA_CLIENT_MHI_PRIME_DPL_PROD,
- IPA_CLIENT_ODL_DPL_CONS, false);
- if (ret) {
- IPA_MPM_ERR("MPM failed to set dma mode to ODL\n");
- if (is_acted)
- ipa_mpm_vote_unvote_pcie_clk(CLK_OFF,
- IPA_MPM_MHIP_CH_ID_2,
- false,
- &is_acted);
- return ret;
- }
- ipa_mpm_change_teth_state(IPA_MPM_MHIP_CH_ID_2,
- IPA_MPM_TETH_CONNECTED);
- } else {
- /* dec clk count and set DMA to USB */
- IPA_MPM_DBG("mpm disabling ADPL over ODL\n");
- ret = ipa_mpm_vote_unvote_pcie_clk(CLK_OFF,
- IPA_MPM_MHIP_CH_ID_2,
- false,
- &is_acted);
- if (ret) {
- IPA_MPM_ERR("Err %d cloking off PCIe clk\n",
- ret);
- return ret;
- }
- ret = ipa_mpm_set_dma_mode(IPA_CLIENT_MHI_PRIME_DPL_PROD,
- IPA_CLIENT_USB_DPL_CONS, false);
- if (ret) {
- IPA_MPM_ERR("MPM failed to set dma mode to USB\n");
- if (ipa_mpm_vote_unvote_pcie_clk(CLK_ON,
- IPA_MPM_MHIP_CH_ID_2,
- false,
- &is_acted))
- IPA_MPM_ERR("Err clocking on pcie\n");
- return ret;
- }
- /* If USB is not available then reset teth state */
- if (atomic_read(&ipa_mpm_ctx->adpl_over_usb_available)) {
- IPA_MPM_DBG("mpm enabling ADPL over USB\n");
- } else {
- ipa_mpm_change_teth_state(IPA_MPM_MHIP_CH_ID_2,
- IPA_MPM_TETH_INIT);
- IPA_MPM_DBG("USB disconnected. ADPL on standby\n");
- }
- }
- IPA_MPM_FUNC_EXIT();
- return ret;
- }
- late_initcall(ipa_mpm_init);
- module_exit(ipa_mpm_exit);
- MODULE_LICENSE("GPL v2");
- MODULE_DESCRIPTION("MHI Proxy Manager Driver");
|