1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720172117221723172417251726172717281729173017311732173317341735173617371738173917401741174217431744174517461747174817491750175117521753175417551756175717581759176017611762176317641765176617671768176917701771177217731774177517761777177817791780178117821783178417851786178717881789179017911792179317941795179617971798179918001801180218031804180518061807180818091810181118121813181418151816181718181819182018211822182318241825182618271828182918301831183218331834183518361837183818391840184118421843184418451846184718481849185018511852185318541855185618571858185918601861186218631864186518661867186818691870187118721873187418751876187718781879188018811882188318841885188618871888188918901891189218931894189518961897189818991900190119021903190419051906190719081909191019111912191319141915191619171918191919201921192219231924192519261927192819291930193119321933193419351936193719381939194019411942194319441945194619471948194919501951195219531954195519561957195819591960196119621963196419651966196719681969197019711972197319741975197619771978197919801981198219831984198519861987198819891990199119921993199419951996199719981999200020012002200320042005200620072008200920102011201220132014201520162017201820192020202120222023202420252026202720282029203020312032203320342035203620372038203920402041204220432044204520462047204820492050205120522053205420552056205720582059206020612062206320642065206620672068206920702071207220732074207520762077207820792080208120822083208420852086208720882089209020912092209320942095209620972098209921002101210221032104210521062107210821092110211121122113211421152116211721182119212021212122212321242125212621272128212921302131213221332134213521362137213821392140214121422143214421452146214721482149215021512152215321542155215621572158215921602161216221632164216521662167216821692170217121722173217421752176217721782179218021812182218321842185218621872188218921902191219221932194219521962197219821992200220122022203220422052206220722082209221022112212221322142215221622172218221922202221222222232224222522262227222822292230223122322233223422352236223722382239224022412242224322442245224622472248224922502251225222532254225522562257225822592260226122622263226422652266226722682269227022712272227322742275227622772278227922802281228222832284228522862287228822892290229122922293229422952296229722982299230023012302230323042305230623072308230923102311231223132314231523162317231823192320232123222323232423252326232723282329233023312332233323342335233623372338233923402341234223432344234523462347234823492350235123522353235423552356235723582359236023612362236323642365236623672368236923702371237223732374237523762377237823792380238123822383238423852386238723882389239023912392239323942395239623972398239924002401240224032404240524062407240824092410241124122413241424152416241724182419242024212422242324242425242624272428242924302431243224332434243524362437243824392440244124422443244424452446244724482449245024512452245324542455245624572458245924602461246224632464246524662467246824692470247124722473247424752476247724782479248024812482248324842485248624872488248924902491249224932494249524962497249824992500250125022503250425052506250725082509251025112512251325142515251625172518251925202521252225232524252525262527252825292530253125322533253425352536253725382539254025412542254325442545254625472548254925502551255225532554255525562557255825592560256125622563256425652566256725682569257025712572257325742575257625772578257925802581258225832584258525862587258825892590259125922593259425952596259725982599260026012602260326042605260626072608260926102611261226132614261526162617261826192620262126222623262426252626262726282629263026312632263326342635263626372638263926402641264226432644264526462647264826492650265126522653265426552656265726582659266026612662266326642665266626672668266926702671267226732674267526762677267826792680268126822683268426852686268726882689269026912692269326942695269626972698269927002701270227032704270527062707270827092710271127122713271427152716271727182719272027212722272327242725272627272728272927302731273227332734273527362737273827392740274127422743274427452746274727482749275027512752275327542755275627572758275927602761276227632764276527662767276827692770277127722773277427752776277727782779278027812782278327842785278627872788278927902791279227932794279527962797279827992800280128022803280428052806280728082809281028112812281328142815281628172818281928202821282228232824282528262827282828292830283128322833283428352836283728382839284028412842284328442845284628472848284928502851285228532854285528562857285828592860286128622863286428652866286728682869287028712872287328742875287628772878287928802881288228832884288528862887288828892890289128922893289428952896289728982899290029012902290329042905290629072908290929102911291229132914291529162917291829192920292129222923292429252926292729282929293029312932293329342935293629372938293929402941294229432944294529462947294829492950295129522953295429552956295729582959296029612962296329642965296629672968296929702971297229732974297529762977297829792980298129822983298429852986298729882989299029912992299329942995299629972998299930003001300230033004300530063007300830093010301130123013301430153016301730183019302030213022302330243025302630273028302930303031303230333034303530363037303830393040304130423043304430453046304730483049305030513052305330543055305630573058305930603061306230633064306530663067306830693070307130723073307430753076307730783079308030813082308330843085308630873088308930903091309230933094309530963097309830993100310131023103310431053106310731083109311031113112311331143115311631173118311931203121312231233124312531263127312831293130313131323133313431353136313731383139314031413142314331443145314631473148314931503151315231533154315531563157315831593160316131623163316431653166316731683169317031713172317331743175317631773178317931803181318231833184318531863187318831893190319131923193319431953196319731983199320032013202320332043205320632073208320932103211321232133214321532163217321832193220322132223223322432253226322732283229323032313232323332343235323632373238323932403241324232433244324532463247324832493250325132523253325432553256325732583259326032613262326332643265326632673268326932703271327232733274327532763277327832793280328132823283328432853286328732883289329032913292329332943295329632973298329933003301330233033304330533063307330833093310331133123313331433153316331733183319332033213322332333243325332633273328332933303331333233333334333533363337333833393340334133423343334433453346334733483349335033513352335333543355335633573358335933603361336233633364 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2019-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024 Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/acpi.h>
- #include <linux/adreno-smmu-priv.h>
- #include <linux/delay.h>
- #include <linux/bitfield.h>
- #include <linux/interrupt.h>
- #include <linux/io.h>
- #include <linux/iopoll.h>
- #include <linux/of.h>
- #include <linux/of_platform.h>
- #include <linux/platform_device.h>
- #include <linux/of_device.h>
- #include <linux/qcom_scm.h>
- #include <linux/slab.h>
- #include <linux/workqueue.h>
- #include "arm-smmu.h"
- #include "arm-smmu-qcom.h"
- #define QCOM_DUMMY_VAL -1
- #define IMPL_DEF4_MICRO_MMU_CTRL 0
- #define IMPL_DEF4_CLK_ON_STATUS 0x50
- #define IMPL_DEF4_CLK_ON_CLIENT_STATUS 0x54
- #define MICRO_MMU_CTRL_LOCAL_HALT_REQ BIT(2)
- #define MICRO_MMU_CTRL_IDLE BIT(3)
- #include "arm-smmu-debug.h"
- #include <linux/debugfs.h>
- #include <linux/uaccess.h>
- /* Definitions for implementation-defined registers */
- #define ACTLR_QCOM_OSH BIT(28)
- #define ACTLR_QCOM_ISH BIT(29)
- #define ACTLR_QCOM_NSH BIT(30)
- struct arm_smmu_impl_def_reg {
- u32 offset;
- u32 value;
- };
- struct qsmmuv2_archdata {
- spinlock_t atos_lock;
- struct arm_smmu_impl_def_reg *impl_def_attach_registers;
- unsigned int num_impl_def_attach_registers;
- struct arm_smmu_device smmu;
- };
- #define to_qsmmuv2_archdata(smmu) \
- container_of(smmu, struct qsmmuv2_archdata, smmu)
- static int qsmmuv2_wait_for_halt(struct arm_smmu_device *smmu)
- {
- void __iomem *reg = arm_smmu_page(smmu, ARM_SMMU_IMPL_DEF4);
- struct device *dev = smmu->dev;
- u32 tmp;
- if (readl_poll_timeout_atomic(reg + IMPL_DEF4_MICRO_MMU_CTRL, tmp,
- (tmp & MICRO_MMU_CTRL_IDLE), 0, 30000)) {
- dev_err(dev, "Couldn't halt SMMU!\n");
- return -EBUSY;
- }
- return 0;
- }
- static int __qsmmuv2_halt(struct arm_smmu_device *smmu, bool wait)
- {
- u32 val;
- val = arm_smmu_readl(smmu, ARM_SMMU_IMPL_DEF4,
- IMPL_DEF4_MICRO_MMU_CTRL);
- val |= MICRO_MMU_CTRL_LOCAL_HALT_REQ;
- arm_smmu_writel(smmu, ARM_SMMU_IMPL_DEF4, IMPL_DEF4_MICRO_MMU_CTRL,
- val);
- return wait ? qsmmuv2_wait_for_halt(smmu) : 0;
- }
- static int qsmmuv2_halt(struct arm_smmu_device *smmu)
- {
- return __qsmmuv2_halt(smmu, true);
- }
- static int qsmmuv2_halt_nowait(struct arm_smmu_device *smmu)
- {
- return __qsmmuv2_halt(smmu, false);
- }
- static void qsmmuv2_resume(struct arm_smmu_device *smmu)
- {
- u32 val;
- val = arm_smmu_readl(smmu, ARM_SMMU_IMPL_DEF4,
- IMPL_DEF4_MICRO_MMU_CTRL);
- val &= ~MICRO_MMU_CTRL_LOCAL_HALT_REQ;
- arm_smmu_writel(smmu, ARM_SMMU_IMPL_DEF4, IMPL_DEF4_MICRO_MMU_CTRL,
- val);
- }
- static phys_addr_t __qsmmuv2_iova_to_phys_hard(
- struct arm_smmu_domain *smmu_domain,
- dma_addr_t iova)
- {
- struct arm_smmu_device *smmu = smmu_domain->smmu;
- struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- struct device *dev = smmu->dev;
- int idx = cfg->cbndx;
- void __iomem *reg;
- u32 tmp;
- u64 phys;
- unsigned long va;
- /* ATS1 registers can only be written atomically */
- va = iova & ~0xfffUL;
- if (cfg->fmt == ARM_SMMU_CTX_FMT_AARCH64)
- arm_smmu_cb_writeq(smmu, idx, ARM_SMMU_CB_ATS1PR, va);
- else
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_ATS1PR, va);
- reg = arm_smmu_page(smmu, ARM_SMMU_CB(smmu, idx));
- if (readl_poll_timeout_atomic(reg + ARM_SMMU_CB_ATSR, tmp,
- !(tmp & ARM_SMMU_ATSR_ACTIVE), 5, 50)) {
- dev_err(dev, "iova to phys timed out on %pad.\n", &iova);
- phys = 0;
- return phys;
- }
- phys = arm_smmu_cb_readq(smmu, idx, ARM_SMMU_CB_PAR);
- if (phys & ARM_SMMU_CB_PAR_F) {
- dev_err(dev, "translation fault!\n");
- dev_err(dev, "PAR = 0x%llx\n", phys);
- phys = 0;
- } else {
- phys = (phys & (PHYS_MASK & ~0xfffULL)) | (iova & 0xfff);
- }
- return phys;
- }
- static phys_addr_t qsmmuv2_iova_to_phys_hard(
- struct arm_smmu_domain *smmu_domain,
- struct qcom_iommu_atos_txn *txn)
- {
- struct arm_smmu_device *smmu = smmu_domain->smmu;
- struct qsmmuv2_archdata *data = to_qsmmuv2_archdata(smmu);
- int idx = smmu_domain->cfg.cbndx;
- dma_addr_t iova = txn->addr;
- phys_addr_t phys = 0;
- unsigned long flags;
- u32 sctlr, sctlr_orig, fsr;
- spin_lock_irqsave(&data->atos_lock, flags);
- qsmmuv2_halt_nowait(smmu);
- /* disable stall mode momentarily */
- sctlr_orig = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_SCTLR);
- sctlr = sctlr_orig & ~(ARM_SMMU_SCTLR_CFCFG);
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr);
- /* clear FSR to allow ATOS to log any faults */
- fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR);
- if (fsr & ARM_SMMU_FSR_FAULT) {
- /* Clear pending interrupts */
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr);
- /*
- * Barrier required to ensure that the FSR is cleared
- * before resuming SMMU operation
- */
- wmb();
- /*
- * TBU halt takes care of resuming any stalled transcation.
- * Kept it here for completeness sake.
- */
- if (fsr & ARM_SMMU_FSR_SS)
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_RESUME,
- ARM_SMMU_RESUME_TERMINATE);
- }
- qsmmuv2_wait_for_halt(smmu);
- phys = __qsmmuv2_iova_to_phys_hard(smmu_domain, iova);
- /* restore SCTLR */
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr_orig);
- qsmmuv2_resume(smmu);
- spin_unlock_irqrestore(&data->atos_lock, flags);
- return phys;
- }
- static void qsmmuv2_tlb_sync_timeout(struct arm_smmu_device *smmu)
- {
- u32 clk_on, clk_on_client;
- dev_err_ratelimited(smmu->dev,
- "TLB sync timed out -- SMMU may be deadlocked\n");
- clk_on = arm_smmu_readl(smmu, ARM_SMMU_IMPL_DEF4,
- IMPL_DEF4_CLK_ON_STATUS);
- clk_on_client = arm_smmu_readl(smmu, ARM_SMMU_IMPL_DEF4,
- IMPL_DEF4_CLK_ON_CLIENT_STATUS);
- dev_err_ratelimited(smmu->dev,
- "clk on 0x%x, clk on client 0x%x status\n",
- clk_on, clk_on_client);
- BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
- }
- static int qsmmuv2_device_reset(struct arm_smmu_device *smmu)
- {
- struct qsmmuv2_archdata *data = to_qsmmuv2_archdata(smmu);
- struct arm_smmu_impl_def_reg *regs = data->impl_def_attach_registers;
- u32 i;
- /* Program implementation defined registers */
- qsmmuv2_halt(smmu);
- for (i = 0; i < data->num_impl_def_attach_registers; ++i)
- arm_smmu_gr0_write(smmu, regs[i].offset, regs[i].value);
- qsmmuv2_resume(smmu);
- return 0;
- }
- static void qsmmuv2_init_cb(struct arm_smmu_domain *smmu_domain,
- struct device *dev)
- {
- struct arm_smmu_device *smmu = smmu_domain->smmu;
- int idx = smmu_domain->cfg.cbndx;
- const struct iommu_flush_ops *tlb;
- u32 val;
- tlb = smmu_domain->flush_ops;
- val = ACTLR_QCOM_ISH | ACTLR_QCOM_OSH | ACTLR_QCOM_NSH;
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_ACTLR, val);
- /*
- * Flush the context bank after modifying ACTLR to ensure there
- * are no cache entries with stale state
- */
- tlb->tlb_flush_all(smmu_domain);
- }
- static int arm_smmu_parse_impl_def_registers(struct arm_smmu_device *smmu)
- {
- struct device *dev = smmu->dev;
- struct qsmmuv2_archdata *data = to_qsmmuv2_archdata(smmu);
- int i, ntuples, ret;
- u32 *tuples;
- struct arm_smmu_impl_def_reg *regs, *regit;
- if (!of_find_property(dev->of_node, "attach-impl-defs", &ntuples))
- return 0;
- ntuples /= sizeof(u32);
- if (ntuples % 2) {
- dev_err(dev,
- "Invalid number of attach-impl-defs registers: %d\n",
- ntuples);
- return -EINVAL;
- }
- regs = devm_kzalloc(dev, sizeof(*data->impl_def_attach_registers) *
- ntuples, GFP_KERNEL);
- if (!regs)
- return -ENOMEM;
- tuples = kzalloc(sizeof(u32) * ntuples * 2, GFP_KERNEL);
- if (!tuples)
- return -ENOMEM;
- ret = of_property_read_u32_array(dev->of_node, "attach-impl-defs",
- tuples, ntuples);
- if (ret) {
- kfree(tuples);
- return ret;
- }
- for (i = 0, regit = regs; i < ntuples; i += 2, ++regit) {
- regit->offset = tuples[i];
- regit->value = tuples[i + 1];
- }
- kfree(tuples);
- data->impl_def_attach_registers = regs;
- data->num_impl_def_attach_registers = ntuples / 2;
- return 0;
- }
- static struct qcom_smmu *to_qcom_smmu(struct arm_smmu_device *smmu)
- {
- return container_of(smmu, struct qcom_smmu, smmu);
- }
- static void qcom_smmu_tlb_sync(struct arm_smmu_device *smmu, int page,
- int sync, int status)
- {
- unsigned int spin_cnt, delay;
- u32 reg;
- arm_smmu_writel(smmu, page, sync, QCOM_DUMMY_VAL);
- for (delay = 1; delay < TLB_LOOP_TIMEOUT; delay *= 2) {
- for (spin_cnt = TLB_SPIN_COUNT; spin_cnt > 0; spin_cnt--) {
- reg = arm_smmu_readl(smmu, page, status);
- if (!(reg & ARM_SMMU_sTLBGSTATUS_GSACTIVE))
- return;
- cpu_relax();
- }
- udelay(delay);
- }
- qcom_smmu_tlb_sync_debug(smmu);
- }
- static void qcom_adreno_smmu_write_sctlr(struct arm_smmu_device *smmu, int idx,
- u32 reg)
- {
- struct qcom_smmu *qsmmu = to_qcom_smmu(smmu);
- /*
- * On the GPU device we want to process subsequent transactions after a
- * fault to keep the GPU from hanging
- */
- reg |= ARM_SMMU_SCTLR_HUPCF;
- if (qsmmu->stall_enabled & BIT(idx))
- reg |= ARM_SMMU_SCTLR_CFCFG;
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, reg);
- }
- static void qcom_adreno_smmu_get_fault_info(const void *cookie,
- struct adreno_smmu_fault_info *info)
- {
- struct arm_smmu_domain *smmu_domain = (void *)cookie;
- struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- struct arm_smmu_device *smmu = smmu_domain->smmu;
- info->fsr = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSR);
- info->fsynr0 = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSYNR0);
- info->fsynr1 = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_FSYNR1);
- info->far = arm_smmu_cb_readq(smmu, cfg->cbndx, ARM_SMMU_CB_FAR);
- info->cbfrsynra = arm_smmu_gr1_read(smmu, ARM_SMMU_GR1_CBFRSYNRA(cfg->cbndx));
- info->ttbr0 = arm_smmu_cb_readq(smmu, cfg->cbndx, ARM_SMMU_CB_TTBR0);
- info->contextidr = arm_smmu_cb_read(smmu, cfg->cbndx, ARM_SMMU_CB_CONTEXTIDR);
- }
- static void qcom_adreno_smmu_set_stall(const void *cookie, bool enabled)
- {
- struct arm_smmu_domain *smmu_domain = (void *)cookie;
- struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- struct qcom_smmu *qsmmu = to_qcom_smmu(smmu_domain->smmu);
- if (enabled)
- qsmmu->stall_enabled |= BIT(cfg->cbndx);
- else
- qsmmu->stall_enabled &= ~BIT(cfg->cbndx);
- }
- static void qcom_adreno_smmu_resume_translation(const void *cookie, bool terminate)
- {
- struct arm_smmu_domain *smmu_domain = (void *)cookie;
- struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- struct arm_smmu_device *smmu = smmu_domain->smmu;
- u32 reg = 0;
- if (terminate)
- reg |= ARM_SMMU_RESUME_TERMINATE;
- arm_smmu_cb_write(smmu, cfg->cbndx, ARM_SMMU_CB_RESUME, reg);
- }
- #define QCOM_ADRENO_SMMU_GPU_SID 0
- #define QCOM_ADRENO_SMMU_GPU_LPAC_SID 1
- static bool qcom_adreno_smmu_is_gpu_device(struct device *dev)
- {
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
- int i;
- /*
- * The GPU will always use SID 0 so that is a handy way to uniquely
- * identify it and configure it for per-instance pagetables
- */
- for (i = 0; i < fwspec->num_ids; i++) {
- u16 sid = FIELD_GET(ARM_SMMU_SMR_ID, fwspec->ids[i]);
- if (sid == QCOM_ADRENO_SMMU_GPU_SID ||
- sid == QCOM_ADRENO_SMMU_GPU_LPAC_SID)
- return true;
- }
- return false;
- }
- static const struct io_pgtable_cfg *qcom_adreno_smmu_get_ttbr1_cfg(
- const void *cookie)
- {
- struct arm_smmu_domain *smmu_domain = (void *)cookie;
- struct io_pgtable *pgtable =
- io_pgtable_ops_to_pgtable(smmu_domain->pgtbl_ops);
- return &pgtable->cfg;
- }
- /*
- * Local implementation to configure TTBR0 with the specified pagetable config.
- * The GPU driver will call this to enable TTBR0 when per-instance pagetables
- * are active
- */
- static int qcom_adreno_smmu_set_ttbr0_cfg(const void *cookie,
- const struct io_pgtable_cfg *pgtbl_cfg)
- {
- struct arm_smmu_domain *smmu_domain = (void *)cookie;
- struct io_pgtable *pgtable = io_pgtable_ops_to_pgtable(smmu_domain->pgtbl_ops);
- struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- struct arm_smmu_cb *cb = &smmu_domain->smmu->cbs[cfg->cbndx];
- /* The domain must have split pagetables already enabled */
- if (cb->tcr[0] & ARM_SMMU_TCR_EPD1)
- return -EINVAL;
- /* If the pagetable config is NULL, disable TTBR0 */
- if (!pgtbl_cfg) {
- /* Do nothing if it is already disabled */
- if ((cb->tcr[0] & ARM_SMMU_TCR_EPD0))
- return -EINVAL;
- /* Set TCR to the original configuration */
- cb->tcr[0] = arm_smmu_lpae_tcr(&pgtable->cfg);
- cb->ttbr[0] = FIELD_PREP(ARM_SMMU_TTBRn_ASID, cb->cfg->asid);
- } else {
- u32 tcr = cb->tcr[0];
- /* Don't call this again if TTBR0 is already enabled */
- if (!(cb->tcr[0] & ARM_SMMU_TCR_EPD0))
- return -EINVAL;
- tcr |= arm_smmu_lpae_tcr(pgtbl_cfg);
- tcr &= ~(ARM_SMMU_TCR_EPD0 | ARM_SMMU_TCR_EPD1);
- cb->tcr[0] = tcr;
- cb->ttbr[0] = pgtbl_cfg->arm_lpae_s1_cfg.ttbr;
- cb->ttbr[0] |= FIELD_PREP(ARM_SMMU_TTBRn_ASID, cb->cfg->asid);
- }
- arm_smmu_write_context_bank(smmu_domain->smmu, cb->cfg->cbndx);
- return 0;
- }
- static int qcom_adreno_smmu_alloc_context_bank(struct arm_smmu_domain *smmu_domain,
- struct arm_smmu_device *smmu,
- struct device *dev, int start)
- {
- int count;
- /*
- * Assign context bank 0 and 1 to the GPU device so the GPU hardware can
- * switch pagetables
- */
- if (qcom_adreno_smmu_is_gpu_device(dev)) {
- start = 0;
- count = 2;
- } else {
- start = 2;
- count = smmu->num_context_banks;
- }
- return __arm_smmu_alloc_bitmap(smmu->context_map, start, count);
- }
- static bool qcom_adreno_can_do_ttbr1(struct arm_smmu_device *smmu)
- {
- const struct device_node *np = smmu->dev->of_node;
- if (of_device_is_compatible(np, "qcom,msm8996-smmu-v2"))
- return false;
- return true;
- }
- static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[];
- static int qcom_adreno_smmu_init_context(struct arm_smmu_domain *smmu_domain,
- struct io_pgtable_cfg *pgtbl_cfg, struct device *dev)
- {
- struct adreno_smmu_priv *priv;
- const struct device_node *np = smmu_domain->smmu->dev->of_node;
- struct qcom_io_pgtable_info *input_info =
- container_of(pgtbl_cfg, struct qcom_io_pgtable_info, cfg);
- smmu_domain->cfg.flush_walk_prefer_tlbiasid = true;
- /* Only enable split pagetables for the GPU device (SID 0) */
- if (!qcom_adreno_smmu_is_gpu_device(dev))
- return 0;
- /*
- * All targets that use the qcom,adreno-smmu compatible string *should*
- * be AARCH64 stage 1 but double check because the arm-smmu code assumes
- * that is the case when the TTBR1 quirk is enabled
- */
- if (qcom_adreno_can_do_ttbr1(smmu_domain->smmu) &&
- (smmu_domain->stage == ARM_SMMU_DOMAIN_S1) &&
- (smmu_domain->cfg.fmt == ARM_SMMU_CTX_FMT_AARCH64))
- pgtbl_cfg->quirks |= IO_PGTABLE_QUIRK_ARM_TTBR1;
- /*
- * Initialize private interface with GPU:
- */
- priv = dev_get_drvdata(dev);
- priv->cookie = smmu_domain;
- priv->get_ttbr1_cfg = qcom_adreno_smmu_get_ttbr1_cfg;
- priv->set_ttbr0_cfg = qcom_adreno_smmu_set_ttbr0_cfg;
- priv->get_fault_info = qcom_adreno_smmu_get_fault_info;
- priv->pgtbl_info = *input_info;
- /*
- * These functions are only compatible with the data structures used by the
- * QCOM SMMU implementation hooks, and are thus not appropriate to set for other
- * implementations (e.g. QSMMUV500).
- *
- * Providing these functions as part of the GPU interface also makes little sense
- * as context banks are set to stall by default anyway.
- */
- if (of_match_node(qcom_smmu_impl_of_match, np)) {
- priv->set_stall = qcom_adreno_smmu_set_stall;
- priv->resume_translation = qcom_adreno_smmu_resume_translation;
- }
- return 0;
- }
- static const struct of_device_id qcom_smmu_client_of_match[] __maybe_unused = {
- { .compatible = "qcom,adreno" },
- { .compatible = "qcom,mdp4" },
- { .compatible = "qcom,mdss" },
- { .compatible = "qcom,sc7180-mdss" },
- { .compatible = "qcom,sc7180-mss-pil" },
- { .compatible = "qcom,sc7280-mdss" },
- { .compatible = "qcom,sc7280-mss-pil" },
- { .compatible = "qcom,sc8180x-mdss" },
- { .compatible = "qcom,sm8250-mdss" },
- { .compatible = "qcom,sdm845-mdss" },
- { .compatible = "qcom,sdm845-mss-pil" },
- { }
- };
- static int qcom_smmu_init_context(struct arm_smmu_domain *smmu_domain,
- struct io_pgtable_cfg *pgtbl_cfg, struct device *dev)
- {
- smmu_domain->cfg.flush_walk_prefer_tlbiasid = true;
- return 0;
- }
- static int qcom_smmu_cfg_probe(struct arm_smmu_device *smmu)
- {
- unsigned int last_s2cr = ARM_SMMU_GR0_S2CR(smmu->num_mapping_groups - 1);
- struct qcom_smmu *qsmmu = to_qcom_smmu(smmu);
- u32 reg;
- u32 smr;
- int i;
- /*
- * With some firmware versions writes to S2CR of type FAULT are
- * ignored, and writing BYPASS will end up written as FAULT in the
- * register. Perform a write to S2CR to detect if this is the case and
- * if so reserve a context bank to emulate bypass streams.
- */
- reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, S2CR_TYPE_BYPASS) |
- FIELD_PREP(ARM_SMMU_S2CR_CBNDX, 0xff) |
- FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, S2CR_PRIVCFG_DEFAULT);
- arm_smmu_gr0_write(smmu, last_s2cr, reg);
- reg = arm_smmu_gr0_read(smmu, last_s2cr);
- if (FIELD_GET(ARM_SMMU_S2CR_TYPE, reg) != S2CR_TYPE_BYPASS) {
- qsmmu->bypass_quirk = true;
- qsmmu->bypass_cbndx = smmu->num_context_banks - 1;
- set_bit(qsmmu->bypass_cbndx, smmu->context_map);
- arm_smmu_cb_write(smmu, qsmmu->bypass_cbndx, ARM_SMMU_CB_SCTLR, 0);
- reg = FIELD_PREP(ARM_SMMU_CBAR_TYPE, CBAR_TYPE_S1_TRANS_S2_BYPASS);
- arm_smmu_gr1_write(smmu, ARM_SMMU_GR1_CBAR(qsmmu->bypass_cbndx), reg);
- }
- for (i = 0; i < smmu->num_mapping_groups; i++) {
- smr = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_SMR(i));
- if (FIELD_GET(ARM_SMMU_SMR_VALID, smr)) {
- /* Ignore valid bit for SMR mask extraction. */
- smr &= ~ARM_SMMU_SMR_VALID;
- smmu->smrs[i].id = FIELD_GET(ARM_SMMU_SMR_ID, smr);
- smmu->smrs[i].mask = FIELD_GET(ARM_SMMU_SMR_MASK, smr);
- smmu->smrs[i].valid = true;
- smmu->smrs[i].used = true;
- smmu->s2crs[i].type = S2CR_TYPE_BYPASS;
- smmu->s2crs[i].privcfg = S2CR_PRIVCFG_DEFAULT;
- smmu->s2crs[i].cbndx = 0xff;
- }
- }
- return 0;
- }
- static void qcom_smmu_write_s2cr(struct arm_smmu_device *smmu, int idx)
- {
- struct arm_smmu_s2cr *s2cr = smmu->s2crs + idx;
- struct qcom_smmu *qsmmu = to_qcom_smmu(smmu);
- u32 cbndx = s2cr->cbndx;
- u32 type = s2cr->type;
- u32 reg;
- if (qsmmu->bypass_quirk) {
- if (type == S2CR_TYPE_BYPASS) {
- /*
- * Firmware with quirky S2CR handling will substitute
- * BYPASS writes with FAULT, so point the stream to the
- * reserved context bank and ask for translation on the
- * stream
- */
- type = S2CR_TYPE_TRANS;
- cbndx = qsmmu->bypass_cbndx;
- } else if (type == S2CR_TYPE_FAULT) {
- /*
- * Firmware with quirky S2CR handling will ignore FAULT
- * writes, so trick it to write FAULT by asking for a
- * BYPASS.
- */
- type = S2CR_TYPE_BYPASS;
- cbndx = 0xff;
- }
- }
- reg = FIELD_PREP(ARM_SMMU_S2CR_TYPE, type) |
- FIELD_PREP(ARM_SMMU_S2CR_CBNDX, cbndx) |
- FIELD_PREP(ARM_SMMU_S2CR_PRIVCFG, s2cr->privcfg);
- arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_S2CR(idx), reg);
- }
- static int qcom_smmu_def_domain_type(struct device *dev)
- {
- const struct of_device_id *match =
- of_match_device(qcom_smmu_client_of_match, dev);
- return match ? IOMMU_DOMAIN_IDENTITY : 0;
- }
- static int qcom_sdm845_smmu500_reset(struct arm_smmu_device *smmu)
- {
- int ret;
- /*
- * To address performance degradation in non-real time clients,
- * such as USB and UFS, turn off wait-for-safe on sdm845 based boards,
- * such as MTP and db845, whose firmwares implement secure monitor
- * call handlers to turn on/off the wait-for-safe logic.
- */
- ret = qcom_scm_qsmmu500_wait_safe_toggle(0);
- if (ret)
- dev_warn(smmu->dev, "Failed to turn off SAFE logic\n");
- return ret;
- }
- static int qcom_smmu500_reset(struct arm_smmu_device *smmu)
- {
- const struct device_node *np = smmu->dev->of_node;
- arm_mmu500_reset(smmu);
- if (of_device_is_compatible(np, "qcom,sdm845-smmu-500"))
- return qcom_sdm845_smmu500_reset(smmu);
- return 0;
- }
- static const struct arm_smmu_impl qcom_smmu_impl = {
- .init_context = qcom_smmu_init_context,
- .cfg_probe = qcom_smmu_cfg_probe,
- .def_domain_type = qcom_smmu_def_domain_type,
- .reset = qcom_smmu500_reset,
- .write_s2cr = qcom_smmu_write_s2cr,
- .tlb_sync = qcom_smmu_tlb_sync,
- };
- #define TBUID_SHIFT 10
- #define DEBUG_SID_HALT_REG 0x0
- #define DEBUG_SID_HALT_REQ BIT(16)
- #define DEBUG_SID_HALT_SID GENMASK(9, 0)
- #define DEBUG_VA_ADDR_REG 0x8
- #define DEBUG_TXN_TRIGG_REG 0x18
- #define DEBUG_TXN_AXPROT GENMASK(8, 6)
- #define DEBUG_TXN_AXCACHE GENMASK(5, 2)
- #define DEBUG_TXN_WRITE BIT(1)
- #define DEBUG_TXN_AXPROT_PRIV 0x1
- #define DEBUG_TXN_AXPROT_UNPRIV 0x0
- #define DEBUG_TXN_AXPROT_NSEC 0x2
- #define DEBUG_TXN_AXPROT_SEC 0x0
- #define DEBUG_TXN_AXPROT_INST 0x4
- #define DEBUG_TXN_AXPROT_DATA 0x0
- #define DEBUG_TXN_READ (0x0 << 1)
- #define DEBUG_TXN_TRIGGER BIT(0)
- #define DEBUG_SR_HALT_ACK_REG 0x20
- #define DEBUG_SR_HALT_ACK_VAL (0x1 << 1)
- #define DEBUG_SR_ECATS_RUNNING_VAL (0x1 << 0)
- #define DEBUG_PAR_REG 0x28
- #define DEBUG_PAR_PA GENMASK_ULL(47, 12)
- #define DEBUG_PAR_FAULT_VAL BIT(0)
- #define DEBUG_AXUSER_REG 0x30
- #define DEBUG_AXUSER_CDMID GENMASK_ULL(43, 36)
- #define DEBUG_AXUSER_CDMID_VAL 255
- #define TBU_DBG_TIMEOUT_US 100
- #define TBU_MICRO_IDLE_DELAY_US 5
- /* QTB constants */
- #define QTB_DBG_TIMEOUT_US 100
- #define QTB_SWID_LOW 0x0
- #define QTB_OVR_DBG_FENCEREQ 0x410
- #define QTB_OVR_DBG_FENCEREQ_HALT BIT(0)
- #define QTB_OVR_DBG_FENCEACK 0x418
- #define QTB_OVR_DBG_FENCEACK_ACK BIT(0)
- #define QTB_OVR_ECATS_INFLD0 0x430
- #define QTB_OVR_ECATS_INFLD0_PCIE_NO_SNOOP BIT(21)
- #define QTB_OVR_ECATS_INFLD0_SEC_SID BIT(20)
- #define QTB_OVR_ECATS_INFLD0_QAD GENMASK(19, 16)
- #define QTB_OVR_ECATS_INFLD0_SID GENMASK(9, 0)
- #define QTB_OVR_ECATS_INFLD1 0x438
- #define QTB_OVR_ECATS_INFLD1_PNU BIT(13)
- #define QTB_OVR_ECATS_INFLD1_IND BIT(12)
- #define QTB_OVR_ECATS_INFLD1_DIRTY BIT(11)
- #define QTB_OVR_ECATS_INFLD1_TR_TYPE GENMASK(10, 8)
- #define QTB_OVR_ECATS_INFLD1_TR_TYPE_SHARED 4
- #define QTB_OVR_ECATS_INFLD1_ALLOC GENMASK(7, 4)
- #define QTB_OVR_ECATS_INFLD1_NON_SEC BIT(3)
- #define QTB_OVR_ECATS_INFLD1_OPC GENMASK(2, 0)
- #define QTB_OVR_ECATS_INFLD1_OPC_WRI 1
- #define QTB_OVR_ECATS_INFLD2 0x440
- #define QTB_OVR_ECATS_TRIGGER 0x448
- #define QTB_OVR_ECATS_TRIGGER_START BIT(0)
- #define QTB_OVR_ECATS_STATUS 0x450
- #define QTB_OVR_ECATS_STATUS_DONE BIT(0)
- #define QTB_OVR_ECATS_OUTFLD0 0x458
- #define QTB_OVR_ECATS_OUTFLD0_PA GENMASK_ULL(63, 12)
- #define QTB_OVR_ECATS_OUTFLD0_FAULT_TYPE GENMASK(5, 4)
- #define QTB_OVR_ECATS_OUTFLD0_FAULT BIT(0)
- #define QTB_NS_DBG_PORT_N_OT_SNAPSHOT(port_num) (0xc10 + (0x10 * port_num))
- #define TCU_TESTBUS_SEL_ALL 0x7
- #define TBU_TESTBUS_SEL_ALL 0x7f
- struct actlr_setting {
- struct arm_smmu_smr smr;
- u32 actlr;
- };
- struct qsmmuv500_archdata {
- struct arm_smmu_device smmu;
- struct list_head tbus;
- struct actlr_setting *actlrs;
- u32 actlr_tbl_size;
- struct work_struct outstanding_tnx_work;
- spinlock_t atos_lock;
- void __iomem *tcu_base;
- };
- #define to_qsmmuv500_archdata(smmu) \
- container_of(smmu, struct qsmmuv500_archdata, smmu)
- struct qsmmuv500_group_iommudata {
- bool has_actlr;
- u32 actlr;
- };
- #define to_qsmmuv500_group_iommudata(group) \
- ((struct qsmmuv500_group_iommudata *) \
- (iommu_group_get_iommudata(group)))
- struct qsmmuv500_tbu_device {
- struct list_head list;
- struct device *dev;
- struct arm_smmu_device *smmu;
- void __iomem *base;
- const struct qsmmuv500_tbu_impl *impl;
- struct arm_smmu_power_resources *pwr;
- u32 sid_start;
- u32 num_sids;
- u32 iova_width;
- /* Protects halt count */
- spinlock_t halt_lock;
- u32 halt_count;
- unsigned int *irqs;
- };
- struct qsmmuv500_tbu_impl {
- int (*halt_req)(struct qsmmuv500_tbu_device *tbu);
- int (*halt_poll)(struct qsmmuv500_tbu_device *tbu);
- void (*resume)(struct qsmmuv500_tbu_device *tbu);
- phys_addr_t (*trigger_atos)(struct qsmmuv500_tbu_device *tbu, dma_addr_t iova, u32 sid,
- unsigned long trans_flags);
- void (*write_sync)(struct qsmmuv500_tbu_device *tbu);
- void (*log_outstanding_transactions)(struct qsmmuv500_tbu_device *tbu);
- };
- struct arm_tbu_device {
- struct qsmmuv500_tbu_device tbu;
- bool has_micro_idle;
- };
- #define to_arm_tbu(tbu) container_of(tbu, struct arm_tbu_device, tbu)
- struct qtb500_device {
- struct qsmmuv500_tbu_device tbu;
- bool no_halt;
- u32 num_ports;
- void __iomem *debugchain_base;
- void __iomem *transactiontracker_base;
- };
- #define to_qtb500(tbu) container_of(tbu, struct qtb500_device, tbu)
- static int arm_tbu_halt_req(struct qsmmuv500_tbu_device *tbu)
- {
- void __iomem *tbu_base = tbu->base;
- u32 halt;
- halt = readl_relaxed(tbu_base + DEBUG_SID_HALT_REG);
- halt |= DEBUG_SID_HALT_REQ;
- writel_relaxed(halt, tbu_base + DEBUG_SID_HALT_REG);
- return 0;
- }
- static int arm_tbu_halt_poll(struct qsmmuv500_tbu_device *tbu)
- {
- void __iomem *tbu_base = tbu->base;
- u32 halt, status;
- if (readl_poll_timeout_atomic(tbu_base + DEBUG_SR_HALT_ACK_REG, status,
- (status & DEBUG_SR_HALT_ACK_VAL),
- 0, TBU_DBG_TIMEOUT_US)) {
- dev_err(tbu->dev, "Couldn't halt TBU!\n");
- halt = readl_relaxed(tbu_base + DEBUG_SID_HALT_REG);
- halt &= ~DEBUG_SID_HALT_REQ;
- writel_relaxed(halt, tbu_base + DEBUG_SID_HALT_REG);
- return -ETIMEDOUT;
- }
- return 0;
- }
- static void arm_tbu_resume(struct qsmmuv500_tbu_device *tbu)
- {
- void __iomem *base = tbu->base;
- u32 val;
- val = readl_relaxed(base + DEBUG_SID_HALT_REG);
- val &= ~DEBUG_SID_HALT_REQ;
- writel_relaxed(val, base + DEBUG_SID_HALT_REG);
- }
- static phys_addr_t arm_tbu_trigger_atos(struct qsmmuv500_tbu_device *tbu, dma_addr_t iova, u32 sid,
- unsigned long trans_flags)
- {
- void __iomem *tbu_base = tbu->base;
- phys_addr_t phys = 0;
- u64 val;
- ktime_t timeout;
- bool ecats_timedout = false;
- /* Set address and stream-id */
- val = readq_relaxed(tbu_base + DEBUG_SID_HALT_REG);
- val &= ~DEBUG_SID_HALT_SID;
- val |= FIELD_PREP(DEBUG_SID_HALT_SID, sid);
- writeq_relaxed(val, tbu_base + DEBUG_SID_HALT_REG);
- writeq_relaxed(iova, tbu_base + DEBUG_VA_ADDR_REG);
- val = FIELD_PREP(DEBUG_AXUSER_CDMID, DEBUG_AXUSER_CDMID_VAL);
- writeq_relaxed(val, tbu_base + DEBUG_AXUSER_REG);
- /* Write-back Read and Write-Allocate */
- val = FIELD_PREP(DEBUG_TXN_AXCACHE, 0xF);
- /* Non-secure Access */
- val |= FIELD_PREP(DEBUG_TXN_AXPROT, DEBUG_TXN_AXPROT_NSEC);
- /* Write or Read Access */
- if (trans_flags & IOMMU_TRANS_WRITE)
- val |= DEBUG_TXN_WRITE;
- /* Priviledged or Unpriviledged Access */
- if (trans_flags & IOMMU_TRANS_PRIV)
- val |= FIELD_PREP(DEBUG_TXN_AXPROT, DEBUG_TXN_AXPROT_PRIV);
- /* Data or Instruction Access */
- if (trans_flags & IOMMU_TRANS_INST)
- val |= FIELD_PREP(DEBUG_TXN_AXPROT, DEBUG_TXN_AXPROT_INST);
- val |= DEBUG_TXN_TRIGGER;
- writeq_relaxed(val, tbu_base + DEBUG_TXN_TRIGG_REG);
- timeout = ktime_add_us(ktime_get(), TBU_DBG_TIMEOUT_US);
- for (;;) {
- val = readl_relaxed(tbu_base + DEBUG_SR_HALT_ACK_REG);
- if (!(val & DEBUG_SR_ECATS_RUNNING_VAL))
- break;
- val = readl_relaxed(tbu_base + DEBUG_PAR_REG);
- if (val & DEBUG_PAR_FAULT_VAL)
- break;
- if (ktime_compare(ktime_get(), timeout) > 0) {
- ecats_timedout = true;
- break;
- }
- }
- val = readq_relaxed(tbu_base + DEBUG_PAR_REG);
- if (val & DEBUG_PAR_FAULT_VAL)
- dev_err(tbu->dev, "ECATS generated a fault interrupt! PAR = %llx, SID=0x%x\n",
- val, sid);
- else if (ecats_timedout)
- dev_err_ratelimited(tbu->dev, "ECATS translation timed out!\n");
- else
- phys = FIELD_GET(DEBUG_PAR_PA, val);
- /* Reset hardware */
- writeq_relaxed(0, tbu_base + DEBUG_TXN_TRIGG_REG);
- writeq_relaxed(0, tbu_base + DEBUG_VA_ADDR_REG);
- val = readl_relaxed(tbu_base + DEBUG_SID_HALT_REG);
- val &= ~DEBUG_SID_HALT_SID;
- writel_relaxed(val, tbu_base + DEBUG_SID_HALT_REG);
- return phys;
- }
- static void arm_tbu_write_sync(struct qsmmuv500_tbu_device *tbu)
- {
- readl_relaxed(tbu->base + DEBUG_SR_HALT_ACK_REG);
- }
- static void arm_tbu_log_outstanding_transactions(struct qsmmuv500_tbu_device *tbu)
- {
- void __iomem *base = tbu->base;
- u64 outstanding_tnxs;
- u64 tcr_cntl_val, res;
- tcr_cntl_val = readq_relaxed(base + TNX_TCR_CNTL);
- /* Write 1 into MATCH_MASK_UPD of TNX_TCR_CNTL */
- writeq_relaxed(tcr_cntl_val | TNX_TCR_CNTL_MATCH_MASK_UPD,
- base + TNX_TCR_CNTL);
- /*
- * Simultaneously write 0 into MATCH_MASK_UPD, 0 into
- * ALWAYS_CAPTURE, 0 into MATCH_MASK_VALID, and 1 into
- * TBU_OT_CAPTURE_EN of TNX_TCR_CNTL
- */
- tcr_cntl_val &= ~(TNX_TCR_CNTL_MATCH_MASK_UPD |
- TNX_TCR_CNTL_ALWAYS_CAPTURE |
- TNX_TCR_CNTL_MATCH_MASK_VALID);
- writeq_relaxed(tcr_cntl_val | TNX_TCR_CNTL_TBU_OT_CAPTURE_EN,
- base + TNX_TCR_CNTL);
- /* Poll for CAPTURE1_VALID to become 1 on TNX_TCR_CNTL_2 */
- if (readq_poll_timeout_atomic(base + TNX_TCR_CNTL_2, res,
- res & TNX_TCR_CNTL_2_CAP1_VALID,
- 0, TBU_DBG_TIMEOUT_US)) {
- dev_err_ratelimited(tbu->dev,
- "Timeout on TNX snapshot poll\n");
- goto poll_timeout;
- }
- /* Read Register CAPTURE1_SNAPSHOT_1 */
- outstanding_tnxs = readq_relaxed(base + CAPTURE1_SNAPSHOT_1);
- dev_err_ratelimited(tbu->dev,
- "Outstanding Transaction Bitmap: 0x%llx\n",
- outstanding_tnxs);
- poll_timeout:
- /* Write TBU_OT_CAPTURE_EN to 0 of TNX_TCR_CNTL */
- writeq_relaxed(tcr_cntl_val & ~TNX_TCR_CNTL_TBU_OT_CAPTURE_EN,
- tbu->base + TNX_TCR_CNTL);
- }
- static const struct qsmmuv500_tbu_impl arm_tbu_impl = {
- .halt_req = arm_tbu_halt_req,
- .halt_poll = arm_tbu_halt_poll,
- .resume = arm_tbu_resume,
- .trigger_atos = arm_tbu_trigger_atos,
- .write_sync = arm_tbu_write_sync,
- .log_outstanding_transactions = arm_tbu_log_outstanding_transactions,
- };
- /*
- * Prior to accessing registers in the TBU local register space,
- * TBU must be woken from micro idle.
- */
- static int __arm_tbu_micro_idle_cfg(struct arm_smmu_device *smmu,
- u32 val, u32 mask)
- {
- void __iomem *reg;
- u32 tmp, new;
- unsigned long flags;
- int ret;
- /* Protect APPS_SMMU_TBU_REG_ACCESS register. */
- spin_lock_irqsave(&smmu->global_sync_lock, flags);
- new = arm_smmu_readl(smmu, ARM_SMMU_IMPL_DEF5,
- APPS_SMMU_TBU_REG_ACCESS_REQ_NS);
- new &= ~mask;
- new |= val;
- arm_smmu_writel(smmu, ARM_SMMU_IMPL_DEF5,
- APPS_SMMU_TBU_REG_ACCESS_REQ_NS,
- new);
- reg = arm_smmu_page(smmu, ARM_SMMU_IMPL_DEF5);
- reg += APPS_SMMU_TBU_REG_ACCESS_ACK_NS;
- ret = readl_poll_timeout_atomic(reg, tmp, ((tmp & mask) == val), 0, 200);
- if (ret)
- dev_WARN(smmu->dev, "Timed out configuring micro idle! %x instead of %x\n", tmp,
- new);
- /*
- * While the micro-idle guard sequence registers may have been configured
- * properly, it is possible that the intended effect has not been realized
- * by the power management hardware due to delays in the system.
- *
- * Spin for a short amount of time to allow for the desired configuration to
- * take effect before proceeding.
- */
- udelay(TBU_MICRO_IDLE_DELAY_US);
- spin_unlock_irqrestore(&smmu->global_sync_lock, flags);
- return ret;
- }
- int arm_tbu_micro_idle_wake(struct arm_smmu_power_resources *pwr)
- {
- struct qsmmuv500_tbu_device *tbu = dev_get_drvdata(pwr->dev);
- struct arm_tbu_device *arm_tbu = to_arm_tbu(tbu);
- u32 val;
- if (!arm_tbu->has_micro_idle)
- return 0;
- val = tbu->sid_start >> 10;
- val = 1 << val;
- return __arm_tbu_micro_idle_cfg(tbu->smmu, val, val);
- }
- void arm_tbu_micro_idle_allow(struct arm_smmu_power_resources *pwr)
- {
- struct qsmmuv500_tbu_device *tbu = dev_get_drvdata(pwr->dev);
- struct arm_tbu_device *arm_tbu = to_arm_tbu(tbu);
- u32 val;
- if (!arm_tbu->has_micro_idle)
- return;
- val = tbu->sid_start >> 10;
- val = 1 << val;
- __arm_tbu_micro_idle_cfg(tbu->smmu, 0, val);
- }
- static struct qsmmuv500_tbu_device *arm_tbu_impl_init(struct qsmmuv500_tbu_device *tbu)
- {
- struct arm_tbu_device *arm_tbu;
- struct device *dev = tbu->dev;
- arm_tbu = devm_krealloc(dev, tbu, sizeof(*arm_tbu), GFP_KERNEL);
- if (!arm_tbu)
- return ERR_PTR(-ENOMEM);
- arm_tbu->tbu.impl = &arm_tbu_impl;
- arm_tbu->has_micro_idle = of_property_read_bool(dev->of_node, "qcom,micro-idle");
- if (arm_tbu->has_micro_idle) {
- arm_tbu->tbu.pwr->resume = arm_tbu_micro_idle_wake;
- arm_tbu->tbu.pwr->suspend = arm_tbu_micro_idle_allow;
- }
- return &arm_tbu->tbu;
- }
- static int qtb500_tbu_halt_req(struct qsmmuv500_tbu_device *tbu)
- {
- void __iomem *qtb_base = tbu->base;
- struct qtb500_device *qtb = to_qtb500(tbu);
- u64 val;
- if (qtb->no_halt)
- return 0;
- val = readq_relaxed(qtb_base + QTB_OVR_DBG_FENCEREQ);
- val |= QTB_OVR_DBG_FENCEREQ_HALT;
- writeq_relaxed(val, qtb_base + QTB_OVR_DBG_FENCEREQ);
- return 0;
- }
- static int qtb500_tbu_halt_poll(struct qsmmuv500_tbu_device *tbu)
- {
- void __iomem *qtb_base = tbu->base;
- struct qtb500_device *qtb = to_qtb500(tbu);
- u64 val, status;
- if (qtb->no_halt)
- return 0;
- if (readq_poll_timeout_atomic(qtb_base + QTB_OVR_DBG_FENCEACK, status,
- (status & QTB_OVR_DBG_FENCEACK_ACK), 0,
- QTB_DBG_TIMEOUT_US)) {
- dev_err(tbu->dev, "Couldn't halt QTB\n");
- val = readq_relaxed(qtb_base + QTB_OVR_DBG_FENCEREQ);
- val &= ~QTB_OVR_DBG_FENCEREQ_HALT;
- writeq_relaxed(val, qtb_base + QTB_OVR_DBG_FENCEREQ);
- return -ETIMEDOUT;
- }
- return 0;
- }
- static void qtb500_tbu_resume(struct qsmmuv500_tbu_device *tbu)
- {
- void __iomem *qtb_base = tbu->base;
- struct qtb500_device *qtb = to_qtb500(tbu);
- u64 val;
- if (qtb->no_halt)
- return;
- val = readq_relaxed(qtb_base + QTB_OVR_DBG_FENCEREQ);
- val &= ~QTB_OVR_DBG_FENCEREQ_HALT;
- writeq_relaxed(val, qtb_base + QTB_OVR_DBG_FENCEREQ);
- }
- static phys_addr_t qtb500_trigger_atos(struct qsmmuv500_tbu_device *tbu, dma_addr_t iova,
- u32 sid, unsigned long trans_flags)
- {
- void __iomem *qtb_base = tbu->base;
- u64 infld0, infld1, infld2, val;
- phys_addr_t phys = 0;
- ktime_t timeout;
- bool ecats_timedout = false;
- /*
- * Recommended to set:
- *
- * QTB_OVR_ECATS_INFLD0.QAD == 0 (AP Access Domain)
- * QTB_OVR_EACTS_INFLD0.PCIE_NO_SNOOP == 0 (IO-Coherency enabled)
- */
- infld0 = FIELD_PREP(QTB_OVR_ECATS_INFLD0_SID, sid);
- if (trans_flags & IOMMU_TRANS_SEC)
- infld0 |= QTB_OVR_ECATS_INFLD0_SEC_SID;
- infld1 = 0;
- if (trans_flags & IOMMU_TRANS_PRIV)
- infld1 |= QTB_OVR_ECATS_INFLD1_PNU;
- if (trans_flags & IOMMU_TRANS_INST)
- infld1 |= QTB_OVR_ECATS_INFLD1_IND;
- /*
- * Recommended to set:
- *
- * QTB_OVR_ECATS_INFLD1.DIRTY == 0,
- * QTB_OVR_ECATS_INFLD1.TR_TYPE == 4 (Cacheable and Shareable memory)
- * QTB_OVR_ECATS_INFLD1.ALLOC == 0 (No allocation in TLB/caches)
- */
- infld1 |= FIELD_PREP(QTB_OVR_ECATS_INFLD1_TR_TYPE, QTB_OVR_ECATS_INFLD1_TR_TYPE_SHARED);
- if (!(trans_flags & IOMMU_TRANS_SEC))
- infld1 |= QTB_OVR_ECATS_INFLD1_NON_SEC;
- if (trans_flags & IOMMU_TRANS_WRITE)
- infld1 |= FIELD_PREP(QTB_OVR_ECATS_INFLD1_OPC, QTB_OVR_ECATS_INFLD1_OPC_WRI);
- infld2 = iova;
- writeq_relaxed(infld0, qtb_base + QTB_OVR_ECATS_INFLD0);
- writeq_relaxed(infld1, qtb_base + QTB_OVR_ECATS_INFLD1);
- writeq_relaxed(infld2, qtb_base + QTB_OVR_ECATS_INFLD2);
- writeq_relaxed(QTB_OVR_ECATS_TRIGGER_START, qtb_base + QTB_OVR_ECATS_TRIGGER);
- timeout = ktime_add_us(ktime_get(), QTB_DBG_TIMEOUT_US);
- for (;;) {
- val = readq_relaxed(qtb_base + QTB_OVR_ECATS_STATUS);
- if (val & QTB_OVR_ECATS_STATUS_DONE)
- break;
- val = readq_relaxed(qtb_base + QTB_OVR_ECATS_OUTFLD0);
- if (val & QTB_OVR_ECATS_OUTFLD0_FAULT)
- break;
- if (ktime_compare(ktime_get(), timeout) > 0) {
- ecats_timedout = true;
- break;
- }
- }
- val = readq_relaxed(qtb_base + QTB_OVR_ECATS_OUTFLD0);
- if (val & QTB_OVR_ECATS_OUTFLD0_FAULT)
- dev_err(tbu->dev, "ECATS generated a fault interrupt! OUTFLD0 = 0x%llx SID = 0x%x\n",
- val, sid);
- else if (ecats_timedout)
- dev_err_ratelimited(tbu->dev, "ECATS translation timed out!\n");
- else
- phys = FIELD_GET(QTB_OVR_ECATS_OUTFLD0_PA, val);
- /* Reset hardware for next transaction. */
- writeq_relaxed(0, qtb_base + QTB_OVR_ECATS_TRIGGER);
- return phys;
- }
- static void qtb500_tbu_write_sync(struct qsmmuv500_tbu_device *tbu)
- {
- readl_relaxed(tbu->base + QTB_SWID_LOW);
- }
- static void qtb500_log_outstanding_transactions(struct qsmmuv500_tbu_device *tbu)
- {
- void __iomem *qtb_base = tbu->base;
- struct qtb500_device *qtb = to_qtb500(tbu);
- u64 outstanding_tnx;
- int i;
- for (i = 0; i < qtb->num_ports; i++) {
- outstanding_tnx = readq_relaxed(qtb_base + QTB_NS_DBG_PORT_N_OT_SNAPSHOT(i));
- dev_err(tbu->dev, "port %d outstanding transactions bitmap: 0x%llx\n", i,
- outstanding_tnx);
- }
- }
- static const struct qsmmuv500_tbu_impl qtb500_impl = {
- .halt_req = qtb500_tbu_halt_req,
- .halt_poll = qtb500_tbu_halt_poll,
- .resume = qtb500_tbu_resume,
- .trigger_atos = qtb500_trigger_atos,
- .write_sync = qtb500_tbu_write_sync,
- .log_outstanding_transactions = qtb500_log_outstanding_transactions,
- };
- static struct qsmmuv500_tbu_device *qtb500_impl_init(struct qsmmuv500_tbu_device *tbu)
- {
- struct resource *ttres;
- struct qtb500_device *qtb;
- struct device *dev = tbu->dev;
- struct platform_device *pdev = to_platform_device(dev);
- #ifdef CONFIG_ARM_SMMU_TESTBUS
- struct resource *res;
- #endif
- int ret;
- qtb = devm_krealloc(dev, tbu, sizeof(*qtb), GFP_KERNEL);
- if (!qtb)
- return ERR_PTR(-ENOMEM);
- qtb->tbu.impl = &qtb500_impl;
- ret = of_property_read_u32(dev->of_node, "qcom,num-qtb-ports", &qtb->num_ports);
- if (ret)
- return ERR_PTR(ret);
- qtb->no_halt = of_property_read_bool(dev->of_node, "qcom,no-qtb-atos-halt");
- #ifdef CONFIG_ARM_SMMU_TESTBUS
- res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "debugchain-base");
- if (!res) {
- dev_info(dev, "Unable to get the debugchain-base\n");
- return ERR_PTR(-EINVAL);
- goto end;
- }
- qtb->debugchain_base = devm_ioremap_resource(dev, res);
- if (IS_ERR(qtb->debugchain_base)) {
- dev_info(dev, "devm_ioremap failure, overlapping regs\n");
- /*
- * use ioremap for qtb's sharing same debug chain register space
- * for eg : sf and hf qtb's on mmnoc.
- */
- qtb->debugchain_base = ioremap(res->start, resource_size(res));
- if (IS_ERR(qtb->debugchain_base)) {
- dev_err(dev, "unable to ioremap the debugchain-base\n");
- return ERR_PTR(-EINVAL);
- }
- }
- end:
- #endif
- ttres = platform_get_resource_byname(pdev, IORESOURCE_MEM, "transactiontracker-base");
- if (ttres) {
- qtb->transactiontracker_base = devm_ioremap_resource(dev, ttres);
- if (IS_ERR(qtb->transactiontracker_base))
- dev_info(dev, "devm_ioremap failure for transaction tracker\n");
- } else {
- qtb->transactiontracker_base = NULL;
- dev_info(dev, "Unable to get the transactiontracker-base\n");
- }
- return &qtb->tbu;
- }
- static struct qsmmuv500_tbu_device *qsmmuv500_find_tbu(struct arm_smmu_device *smmu, u32 sid)
- {
- struct qsmmuv500_tbu_device *tbu = NULL;
- struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
- if (!list_empty(&data->tbus)) {
- list_for_each_entry(tbu, &data->tbus, list) {
- if (tbu->sid_start <= sid &&
- sid < tbu->sid_start + tbu->num_sids)
- return tbu;
- }
- }
- return NULL;
- }
- static int qsmmuv500_tbu_halt(struct qsmmuv500_tbu_device *tbu,
- struct arm_smmu_domain *smmu_domain)
- {
- unsigned long flags;
- struct arm_smmu_device *smmu = smmu_domain->smmu;
- int ret = 0, idx = smmu_domain->cfg.cbndx;
- u32 fsr;
- if (of_property_read_bool(tbu->dev->of_node, "qcom,opt-out-tbu-halting")) {
- dev_notice(tbu->dev, "TBU opted-out for halting!\n");
- return -EBUSY;
- }
- spin_lock_irqsave(&tbu->halt_lock, flags);
- if (tbu->halt_count) {
- tbu->halt_count++;
- goto out;
- }
- ret = tbu->impl->halt_req(tbu);
- if (ret)
- goto out;
- fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR);
- if ((fsr & ARM_SMMU_FSR_FAULT) && (fsr & ARM_SMMU_FSR_SS)) {
- u32 sctlr_orig, sctlr;
- /*
- * We are in a fault; Our request to halt the bus will not
- * complete until transactions in front of us (such as the fault
- * itself) have completed. Disable iommu faults and terminate
- * any existing transactions.
- */
- sctlr_orig = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_SCTLR);
- sctlr = sctlr_orig & ~(ARM_SMMU_SCTLR_CFCFG | ARM_SMMU_SCTLR_CFIE);
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr);
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr);
- /*
- * Barrier required to ensure that the FSR is cleared
- * before resuming SMMU operation
- */
- wmb();
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_RESUME,
- ARM_SMMU_RESUME_TERMINATE);
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr_orig);
- }
- ret = tbu->impl->halt_poll(tbu);
- if (ret)
- goto out;
- tbu->halt_count = 1;
- out:
- spin_unlock_irqrestore(&tbu->halt_lock, flags);
- return ret;
- }
- static void qsmmuv500_tbu_resume(struct qsmmuv500_tbu_device *tbu)
- {
- unsigned long flags;
- spin_lock_irqsave(&tbu->halt_lock, flags);
- if (WARN(!tbu->halt_count, "%s bad tbu->halt_count", dev_name(tbu->dev))) {
- goto out;
- } else if (tbu->halt_count > 1) {
- tbu->halt_count--;
- goto out;
- }
- tbu->impl->resume(tbu);
- tbu->halt_count = 0;
- out:
- spin_unlock_irqrestore(&tbu->halt_lock, flags);
- }
- /*
- * Provides mutually exclusive access to the registers used by the
- * outstanding transaction snapshot feature and the transaction
- * snapshot capture feature.
- */
- static DEFINE_MUTEX(capture_reg_lock);
- static DEFINE_SPINLOCK(testbus_lock);
- __maybe_unused static struct dentry *get_iommu_debug_dir(void)
- {
- struct dentry *iommu_debug_dir;
- int ret;
- iommu_debug_dir = debugfs_lookup("iommu-debug", NULL);
- if (IS_ERR_OR_NULL(iommu_debug_dir)) {
- iommu_debug_dir = debugfs_create_dir("iommu-debug", NULL);
- if (IS_ERR_OR_NULL(iommu_debug_dir)) {
- ret = PTR_ERR(iommu_debug_dir);
- pr_err_ratelimited("Unable to create iommu-debug directory, ret=%d\n",
- ret);
- return NULL;
- }
- }
- return iommu_debug_dir;
- }
- #ifdef CONFIG_ARM_SMMU_TESTBUS_DEBUGFS
- static struct dentry *debugfs_testbus_dir;
- static ssize_t arm_smmu_debug_debugchain_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *offset)
- {
- char *buf;
- ssize_t retval;
- size_t buflen;
- int buf_len;
- struct qsmmuv500_tbu_device *tbu = file->private_data;
- struct qtb500_device *qtb = to_qtb500(tbu);
- void __iomem *debugchain_base = qtb->debugchain_base;
- long chain_length = 0;
- u64 val;
- if (*offset)
- return 0;
- arm_smmu_power_on(tbu->pwr);
- chain_length = arm_smmu_debug_qtb_debugchain_load(debugchain_base);
- buf_len = chain_length * sizeof(u64);
- buf = kzalloc(buf_len, GFP_KERNEL);
- if (!buf)
- return -ENOMEM;
- arm_smmu_debug_qtb_debugchain_dump(debugchain_base);
- do {
- val = arm_smmu_debug_qtb_debugchain_dump(debugchain_base);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf), "0x%0x\n", val);
- } while (chain_length--);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- buflen = min(count, strlen(buf));
- if (copy_to_user(ubuf, buf, buflen)) {
- pr_err_ratelimited("Couldn't copy_to_user\n");
- retval = -EFAULT;
- } else {
- *offset = 1;
- retval = buflen;
- }
- return retval;
- }
- static ssize_t arm_smmu_debug_testbus_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *offset,
- enum testbus_sel tbu, enum testbus_ops ops)
- {
- char buf[100];
- ssize_t retval;
- size_t buflen;
- int buf_len = sizeof(buf);
- if (*offset)
- return 0;
- memset(buf, 0, buf_len);
- if (tbu == SEL_TBU) {
- struct qsmmuv500_tbu_device *tbu = file->private_data;
- void __iomem *tbu_base = tbu->base;
- long val;
- arm_smmu_power_on(tbu->pwr);
- if (ops == TESTBUS_SELECT)
- val = arm_smmu_debug_tbu_testbus_select(tbu_base,
- READ, 0);
- else
- val = arm_smmu_debug_tbu_testbus_output(tbu_base);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- scnprintf(buf, buf_len, "0x%0x\n", val);
- } else {
- struct arm_smmu_device *smmu = file->private_data;
- struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
- phys_addr_t phys_addr = smmu->phys_addr;
- void __iomem *tcu_base = data->tcu_base;
- arm_smmu_power_on(smmu->pwr);
- if (ops == TESTBUS_SELECT) {
- scnprintf(buf, buf_len, "TCU clk testbus sel: 0x%0x\n",
- arm_smmu_debug_tcu_testbus_select(phys_addr,
- tcu_base, CLK_TESTBUS, READ, 0));
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "TCU testbus sel : 0x%0x\n",
- arm_smmu_debug_tcu_testbus_select(phys_addr,
- tcu_base, PTW_AND_CACHE_TESTBUS,
- READ, 0));
- } else {
- scnprintf(buf, buf_len, "0x%0x\n",
- arm_smmu_debug_tcu_testbus_output(phys_addr));
- }
- arm_smmu_power_off(smmu, smmu->pwr);
- }
- buflen = min(count, strlen(buf));
- if (copy_to_user(ubuf, buf, buflen)) {
- pr_err_ratelimited("Couldn't copy_to_user\n");
- retval = -EFAULT;
- } else {
- *offset = 1;
- retval = buflen;
- }
- return retval;
- }
- static ssize_t arm_smmu_debug_tcu_testbus_sel_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *offset)
- {
- struct arm_smmu_device *smmu = file->private_data;
- struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
- void __iomem *tcu_base = data->tcu_base;
- phys_addr_t phys_addr = smmu->phys_addr;
- char *comma;
- char buf[100];
- u64 sel, val;
- if (count >= 100) {
- pr_err_ratelimited("Value too large\n");
- return -EINVAL;
- }
- memset(buf, 0, 100);
- if (copy_from_user(buf, ubuf, count)) {
- pr_err_ratelimited("Couldn't copy from user\n");
- return -EFAULT;
- }
- comma = strnchr(buf, count, ',');
- if (!comma)
- goto invalid_format;
- /* split up the words */
- *comma = '\0';
- if (kstrtou64(buf, 0, &sel))
- goto invalid_format;
- if (sel != 1 && sel != 2)
- goto invalid_format;
- if (kstrtou64(comma + 1, 0, &val))
- goto invalid_format;
- arm_smmu_power_on(smmu->pwr);
- if (sel == 1)
- arm_smmu_debug_tcu_testbus_select(phys_addr,
- tcu_base, CLK_TESTBUS, WRITE, val);
- else if (sel == 2)
- arm_smmu_debug_tcu_testbus_select(phys_addr,
- tcu_base, PTW_AND_CACHE_TESTBUS, WRITE, val);
- arm_smmu_power_off(smmu, smmu->pwr);
- return count;
- invalid_format:
- pr_err_ratelimited("Invalid format. Expected: <1, testbus select> for tcu CLK testbus (or) <2, testbus select> for tcu PTW/CACHE testbuses\n");
- return -EINVAL;
- }
- static ssize_t arm_smmu_debug_tcu_testbus_sel_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *offset)
- {
- return arm_smmu_debug_testbus_read(file, ubuf,
- count, offset, SEL_TCU, TESTBUS_SELECT);
- }
- static const struct file_operations arm_smmu_debug_tcu_testbus_sel_fops = {
- .open = simple_open,
- .write = arm_smmu_debug_tcu_testbus_sel_write,
- .read = arm_smmu_debug_tcu_testbus_sel_read,
- };
- static ssize_t arm_smmu_debug_tcu_testbus_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *offset)
- {
- return arm_smmu_debug_testbus_read(file, ubuf,
- count, offset, SEL_TCU, TESTBUS_OUTPUT);
- }
- static const struct file_operations arm_smmu_debug_tcu_testbus_fops = {
- .open = simple_open,
- .read = arm_smmu_debug_tcu_testbus_read,
- };
- static int qsmmuv500_tcu_testbus_init(struct arm_smmu_device *smmu)
- {
- struct dentry *testbus_dir, *iommu_debug_dir;
- iommu_debug_dir = get_iommu_debug_dir();
- if (!iommu_debug_dir)
- return 0;
- if (!debugfs_testbus_dir) {
- debugfs_testbus_dir = debugfs_create_dir("testbus",
- iommu_debug_dir);
- if (IS_ERR(debugfs_testbus_dir)) {
- pr_err_ratelimited("Couldn't create iommu/testbus debugfs directory\n");
- return -ENODEV;
- }
- }
- testbus_dir = debugfs_create_dir(dev_name(smmu->dev),
- debugfs_testbus_dir);
- if (IS_ERR(testbus_dir)) {
- pr_err_ratelimited("Couldn't create iommu/testbus/%s debugfs directory\n",
- dev_name(smmu->dev));
- goto err;
- }
- if (IS_ERR(debugfs_create_file("tcu_testbus_sel", 0400,
- testbus_dir, smmu,
- &arm_smmu_debug_tcu_testbus_sel_fops))) {
- pr_err_ratelimited("Couldn't create iommu/testbus/%s/tcu_testbus_sel debugfs file\n",
- dev_name(smmu->dev));
- goto err_rmdir;
- }
- if (IS_ERR(debugfs_create_file("tcu_testbus_output", 0400,
- testbus_dir, smmu,
- &arm_smmu_debug_tcu_testbus_fops))) {
- pr_err_ratelimited("Couldn't create iommu/testbus/%s/tcu_testbus_output debugfs file\n",
- dev_name(smmu->dev));
- goto err_rmdir;
- }
- return 0;
- err_rmdir:
- debugfs_remove_recursive(testbus_dir);
- err:
- return 0;
- }
- static ssize_t arm_smmu_debug_tbu_testbus_sel_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *offset)
- {
- struct qsmmuv500_tbu_device *tbu = file->private_data;
- void __iomem *tbu_base = tbu->base;
- u64 val;
- if (kstrtoull_from_user(ubuf, count, 0, &val)) {
- pr_err_ratelimited("Invalid format for tbu testbus select\n");
- return -EINVAL;
- }
- if (of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500"))
- return 0;
- arm_smmu_power_on(tbu->pwr);
- arm_smmu_debug_tbu_testbus_select(tbu_base, WRITE, val);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- return count;
- }
- static ssize_t arm_smmu_debug_tbu_testbus_sel_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *offset)
- {
- return arm_smmu_debug_testbus_read(file, ubuf,
- count, offset, SEL_TBU, TESTBUS_SELECT);
- }
- static const struct file_operations arm_smmu_debug_tbu_testbus_sel_fops = {
- .open = simple_open,
- .write = arm_smmu_debug_tbu_testbus_sel_write,
- .read = arm_smmu_debug_tbu_testbus_sel_read,
- };
- static ssize_t arm_smmu_debug_tbu_testbus_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *offset)
- {
- struct qsmmuv500_tbu_device *tbu = file->private_data;
- if (of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500"))
- return arm_smmu_debug_debugchain_read(file, ubuf,
- count, offset);
- else
- return arm_smmu_debug_testbus_read(file, ubuf,
- count, offset, SEL_TBU, TESTBUS_OUTPUT);
- }
- static const struct file_operations arm_smmu_debug_tbu_testbus_fops = {
- .open = simple_open,
- .read = arm_smmu_debug_tbu_testbus_read,
- };
- static int qsmmuv500_tbu_testbus_init(struct qsmmuv500_tbu_device *tbu)
- {
- struct dentry *testbus_dir, *iommu_debug_dir;
- iommu_debug_dir = get_iommu_debug_dir();
- if (!iommu_debug_dir)
- return 0;
- if (!debugfs_testbus_dir) {
- debugfs_testbus_dir = debugfs_create_dir("testbus",
- iommu_debug_dir);
- if (IS_ERR(debugfs_testbus_dir)) {
- pr_err_ratelimited("Couldn't create iommu/testbus debugfs directory\n");
- return -ENODEV;
- }
- }
- testbus_dir = debugfs_create_dir(dev_name(tbu->dev),
- debugfs_testbus_dir);
- if (IS_ERR(testbus_dir)) {
- pr_err_ratelimited("Couldn't create iommu/testbus/%s debugfs directory\n",
- dev_name(tbu->dev));
- goto err;
- }
- if (IS_ERR(debugfs_create_file("tbu_testbus_sel", 0400,
- testbus_dir, tbu,
- &arm_smmu_debug_tbu_testbus_sel_fops))) {
- pr_err_ratelimited("Couldn't create iommu/testbus/%s/tbu_testbus_sel debugfs file\n",
- dev_name(tbu->dev));
- goto err_rmdir;
- }
- if (IS_ERR(debugfs_create_file("tbu_testbus_output", 0400,
- testbus_dir, tbu,
- &arm_smmu_debug_tbu_testbus_fops))) {
- pr_err_ratelimited("Couldn't create iommu/testbus/%s/tbu_testbus_output debugfs file\n",
- dev_name(tbu->dev));
- goto err_rmdir;
- }
- return 0;
- err_rmdir:
- debugfs_remove_recursive(testbus_dir);
- err:
- return 0;
- }
- #else
- static int qsmmuv500_tcu_testbus_init(struct arm_smmu_device *smmu)
- {
- return 0;
- }
- static int qsmmuv500_tbu_testbus_init(struct qsmmuv500_tbu_device *tbu)
- {
- return 0;
- }
- #endif
- static void arm_smmu_testbus_dump(struct arm_smmu_device *smmu, u16 sid)
- {
- if (smmu->model == QCOM_SMMUV500 &&
- IS_ENABLED(CONFIG_ARM_SMMU_TESTBUS_DUMP)) {
- struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
- struct qsmmuv500_tbu_device *tbu;
- tbu = qsmmuv500_find_tbu(smmu, sid);
- spin_lock(&testbus_lock);
- if (tbu) {
- if (of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500")) {
- struct qtb500_device *qtb = to_qtb500(tbu);
- qtb500_tbu_halt_req(tbu);
- if (!qtb500_tbu_halt_poll(tbu)) {
- arm_smmu_debug_dump_debugchain(tbu->dev,
- qtb->debugchain_base);
- qtb500_tbu_resume(tbu);
- }
- arm_smmu_debug_dump_qtb_regs(tbu->dev, tbu->base);
- } else {
- arm_smmu_debug_dump_tbu_testbus(tbu->dev,
- tbu->base,
- TBU_TESTBUS_SEL_ALL);
- }
- } else {
- arm_smmu_debug_dump_tcu_testbus(smmu->dev,
- smmu->phys_addr,
- data->tcu_base,
- TCU_TESTBUS_SEL_ALL);
- }
- spin_unlock(&testbus_lock);
- }
- }
- static void qsmmuv500_log_outstanding_transactions(struct work_struct *work)
- {
- struct qsmmuv500_tbu_device *tbu = NULL;
- struct qsmmuv500_archdata *data = container_of(work,
- struct qsmmuv500_archdata,
- outstanding_tnx_work);
- struct arm_smmu_device *smmu = &data->smmu;
- if (!mutex_trylock(&capture_reg_lock)) {
- dev_warn_ratelimited(smmu->dev,
- "Tnx snapshot regs in use, not dumping OT tnxs.\n");
- goto bug;
- }
- if (arm_smmu_power_on(smmu->pwr)) {
- dev_err_ratelimited(smmu->dev,
- "%s: Failed to power on SMMU.\n",
- __func__);
- goto unlock;
- }
- if (!list_empty(&data->tbus)) {
- list_for_each_entry(tbu, &data->tbus, list) {
- if (arm_smmu_power_on(tbu->pwr)) {
- dev_err_ratelimited(tbu->dev,
- "%s: Failed to power on TBU.\n", __func__);
- continue;
- }
- tbu->impl->log_outstanding_transactions(tbu);
- arm_smmu_power_off(smmu, tbu->pwr);
- }
- }
- arm_smmu_power_off(smmu, smmu->pwr);
- unlock:
- mutex_unlock(&capture_reg_lock);
- bug:
- BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
- }
- static struct qsmmuv500_tbu_device *qsmmuv500_tbu_impl_init(struct qsmmuv500_tbu_device *tbu)
- {
- if (of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500"))
- return qtb500_impl_init(tbu);
- return arm_tbu_impl_init(tbu);
- }
- static int qsmmuv500_tbu_probe(struct platform_device *pdev)
- {
- struct device *dev = &pdev->dev;
- struct qsmmuv500_tbu_device *tbu;
- struct resource *res;
- const __be32 *cell;
- int ret, len;
- tbu = devm_kzalloc(dev, sizeof(*tbu), GFP_KERNEL);
- if (!tbu)
- return -ENOMEM;
- tbu->dev = dev;
- /*
- * ARM TBUs need to have power resources initialized before its
- * implementation defined initialization occurs to setup the
- * suspend and resure power callbacks.
- */
- tbu->pwr = arm_smmu_init_power_resources(dev);
- if (IS_ERR(tbu->pwr))
- return PTR_ERR(tbu->pwr);
- tbu = qsmmuv500_tbu_impl_init(tbu);
- if (IS_ERR(tbu))
- return PTR_ERR(tbu);
- INIT_LIST_HEAD(&tbu->list);
- spin_lock_init(&tbu->halt_lock);
- res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
- if (!res)
- return -EINVAL;
- tbu->base = devm_ioremap(dev, res->start, resource_size(res));
- if (!tbu->base)
- return -ENOMEM;
- cell = of_get_property(dev->of_node, "qcom,stream-id-range", &len);
- if (!cell || len < 8)
- return -EINVAL;
- tbu->sid_start = of_read_number(cell, 1);
- tbu->num_sids = of_read_number(cell + 1, 1);
- ret = of_property_read_u32(dev->of_node, "qcom,iova-width", &tbu->iova_width);
- if (ret < 0)
- return ret;
- dev_set_drvdata(dev, tbu);
- return 0;
- }
- static const struct of_device_id qsmmuv500_tbu_of_match[] = {
- {.compatible = "qcom,qsmmuv500-tbu"},
- {}
- };
- struct platform_driver qsmmuv500_tbu_driver = {
- .driver = {
- .name = "qsmmuv500-tbu",
- .of_match_table = of_match_ptr(qsmmuv500_tbu_of_match),
- },
- .probe = qsmmuv500_tbu_probe,
- };
- static ssize_t arm_smmu_debug_capturebus_snapshot_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *offset)
- {
- struct qsmmuv500_tbu_device *tbu = file->private_data;
- struct arm_smmu_device *smmu = tbu->smmu;
- void __iomem *tbu_base = tbu->base;
- u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT];
- u64 gfxttlogs[TTQTB_Capture_Points][2*TTQTB_Regs_Per_Capture_Points];
- u64 ttlogs[TTQTB_Capture_Points][4*TTQTB_Regs_Per_Capture_Points];
- u64 ttlogs_time[2*TTQTB_Capture_Points];
- struct qtb500_device *qtb = to_qtb500(tbu);
- void __iomem *transactiontracker_base = qtb->transactiontracker_base;
- char buf[8192];
- ssize_t retval;
- size_t buflen;
- int buf_len = sizeof(buf);
- int qtb_type;
- int i, j, x, y;
- if (*offset)
- return 0;
- memset(buf, 0, buf_len);
- if (arm_smmu_power_on(smmu->pwr))
- return -EINVAL;
- if (arm_smmu_power_on(tbu->pwr)) {
- arm_smmu_power_off(smmu, smmu->pwr);
- return -EINVAL;
- }
- if (!mutex_trylock(&capture_reg_lock)) {
- dev_warn_ratelimited(smmu->dev,
- "capture bus regs in use, not dumping it.\n");
- return -EBUSY;
- }
- if (of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500")) {
- if (of_device_is_compatible(smmu->dev->of_node, "qcom,adreno-smmu"))
- qtb_type = 1;
- else
- qtb_type = 2;
- arm_smmu_debug_qtb_transtrac_collect(transactiontracker_base, gfxttlogs, ttlogs,
- ttlogs_time, qtb_type);
- arm_smmu_debug_qtb_transtrac_reset(transactiontracker_base);
- mutex_unlock(&capture_reg_lock);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- arm_smmu_power_off(smmu, smmu->pwr);
- for (i = 0, x = 0; i < TTQTB_Capture_Points &&
- x < 2*TTQTB_Capture_Points; i++, x += 2) {
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Latency_%d : 0x%lx\n",
- i, ttlogs_time[x]);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Timestamp_%d : 0x%lx\n",
- i, ttlogs_time[x+1]);
- if (qtb_type == 1) {
- for (j = 0, y = 0; j < TTQTB_Regs_Per_Capture_Points &&
- y < 2*TTQTB_Regs_Per_Capture_Points; j++, y += 2) {
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "LogIn_%d_%d : 0x%lx\n",
- i, j, gfxttlogs[i][y]);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "LogOut_%d_%d : 0x%lx\n",
- i, j, gfxttlogs[i][y+1]);
- }
- } else if (qtb_type == 2) {
- for (j = 0, y = 0; j < TTQTB_Regs_Per_Capture_Points &&
- y < 4*TTQTB_Regs_Per_Capture_Points; j++, y += 4) {
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "LogIn_%d_%d_Low : 0x%lx\n",
- i, j, ttlogs[i][y]);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "LogIn_%d_%d_High : 0x%lx\n",
- i, j, ttlogs[i][y+1]);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "LogOut_%d_%d_Low : 0x%lx\n",
- i, j, ttlogs[i][y+2]);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "LogOut_%d_%d_High : 0x%lx\n",
- i, j, ttlogs[i][y+3]);
- }
- }
- }
- buflen = min(count, strlen(buf));
- if (copy_to_user(ubuf, buf, buflen)) {
- dev_err_ratelimited(smmu->dev, "Couldn't copy_to_user\n");
- retval = -EFAULT;
- } else {
- *offset = 1;
- retval = buflen;
- }
- return retval;
- }
- else {
- arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot);
- mutex_unlock(&capture_reg_lock);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- arm_smmu_power_off(smmu, smmu->pwr);
- for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) {
- for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) {
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Capture_%d_Snapshot_%d : 0x%0llx\n",
- i+1, j+1, snapshot[i][j]);
- }
- }
- buflen = min(count, strlen(buf));
- if (copy_to_user(ubuf, buf, buflen)) {
- dev_err_ratelimited(smmu->dev, "Couldn't copy_to_user\n");
- retval = -EFAULT;
- } else {
- *offset = 1;
- retval = buflen;
- }
- return retval;
- }
- }
- static const struct file_operations arm_smmu_debug_capturebus_snapshot_fops = {
- .open = simple_open,
- .read = arm_smmu_debug_capturebus_snapshot_read,
- };
- static ssize_t arm_smmu_debug_capturebus_config_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *offset)
- {
- struct qsmmuv500_tbu_device *tbu = file->private_data;
- struct arm_smmu_device *smmu = tbu->smmu;
- void __iomem *tbu_base = tbu->base;
- struct qtb500_device *qtb = to_qtb500(tbu);
- void __iomem *transactiontracker_base = qtb->transactiontracker_base;
- char *comma1, *comma2;
- char buf[100];
- u64 sel, mask, match, val;
- if (of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500")) {
- if (count >= sizeof(buf)) {
- dev_err_ratelimited(smmu->dev, "Input too large\n");
- goto invalid_format;
- }
- memset(buf, 0, sizeof(buf));
- if (copy_from_user(buf, ubuf, count)) {
- dev_err_ratelimited(smmu->dev, "Couldn't copy from user\n");
- return -EFAULT;
- }
- if (kstrtou64(buf, 0, &sel))
- goto invalid_tt_format;
- if (sel == 0 || sel == 1)
- goto transtracker_configure;
- else
- goto invalid_tt_format;
- transtracker_configure:
- if (arm_smmu_power_on(smmu->pwr))
- return -EINVAL;
- if (arm_smmu_power_on(tbu->pwr)) {
- arm_smmu_power_off(smmu, smmu->pwr);
- return -EINVAL;
- }
- if (!mutex_trylock(&capture_reg_lock)) {
- dev_warn_ratelimited(smmu->dev,
- "Transaction Tracker regs in use, not configuring it.\n");
- return -EBUSY;
- }
- arm_smmu_debug_qtb_transtracker_set_config(transactiontracker_base, sel);
- mutex_unlock(&capture_reg_lock);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- arm_smmu_power_off(smmu, smmu->pwr);
- return count;
- invalid_tt_format:
- dev_err_ratelimited(smmu->dev, "Invalid format\n");
- dev_err_ratelimited(smmu->dev, "This is QTB equipped device\n");
- dev_err_ratelimited(smmu->dev,
- "Expected:<0/1> 0 for Default configuration, 1 for custom configuration\n");
- return -EINVAL;
- }
- else {
- if (count >= sizeof(buf)) {
- dev_err_ratelimited(smmu->dev, "Input too large\n");
- goto invalid_format;
- }
- memset(buf, 0, sizeof(buf));
- if (copy_from_user(buf, ubuf, count)) {
- dev_err_ratelimited(smmu->dev, "Couldn't copy from user\n");
- return -EFAULT;
- }
- comma1 = strnchr(buf, count, ',');
- if (!comma1)
- goto invalid_format;
- *comma1 = '\0';
- if (kstrtou64(buf, 0, &sel))
- goto invalid_format;
- if (sel > 4) {
- goto invalid_format;
- } else if (sel == 4) {
- if (kstrtou64(comma1 + 1, 0, &val))
- goto invalid_format;
- goto program_capturebus;
- }
- comma2 = strnchr(comma1 + 1, count, ',');
- if (!comma2)
- goto invalid_format;
- /* split up the words */
- *comma2 = '\0';
- if (kstrtou64(comma1 + 1, 0, &mask))
- goto invalid_format;
- if (kstrtou64(comma2 + 1, 0, &match))
- goto invalid_format;
- program_capturebus:
- if (arm_smmu_power_on(smmu->pwr))
- return -EINVAL;
- if (arm_smmu_power_on(tbu->pwr)) {
- arm_smmu_power_off(smmu, smmu->pwr);
- return -EINVAL;
- }
- if (!mutex_trylock(&capture_reg_lock)) {
- dev_warn_ratelimited(smmu->dev,
- "capture bus regs in use, not configuring it.\n");
- return -EBUSY;
- }
- if (sel == 4)
- arm_smmu_debug_set_tnx_tcr_cntl(tbu_base, val);
- else
- arm_smmu_debug_set_mask_and_match(tbu_base, sel, mask, match);
- mutex_unlock(&capture_reg_lock);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- arm_smmu_power_off(smmu, smmu->pwr);
- return count;
- invalid_format:
- dev_err_ratelimited(smmu->dev, "Invalid format\n");
- dev_err_ratelimited(smmu->dev,
- "Expected:<1/2/3,Mask,Match> <4,TNX_TCR_CNTL>\n");
- return -EINVAL;
- }
- }
- static ssize_t arm_smmu_debug_capturebus_config_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *offset)
- {
- struct qsmmuv500_tbu_device *tbu = file->private_data;
- struct arm_smmu_device *smmu = tbu->smmu;
- void __iomem *tbu_base = tbu->base;
- struct qtb500_device *qtb = to_qtb500(tbu);
- void __iomem *transactiontracker_base = qtb->transactiontracker_base;
- u64 val;
- u64 config;
- u64 mask[NO_OF_MASK_AND_MATCH], match[NO_OF_MASK_AND_MATCH];
- char buf[400];
- ssize_t retval;
- size_t buflen;
- int buf_len = sizeof(buf);
- int i;
- if (*offset)
- return 0;
- memset(buf, 0, buf_len);
- if (of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500")) {
- if (arm_smmu_power_on(smmu->pwr))
- return -EINVAL;
- if (arm_smmu_power_on(tbu->pwr)) {
- arm_smmu_power_off(smmu, smmu->pwr);
- return -EINVAL;
- }
- if (!mutex_trylock(&capture_reg_lock)) {
- dev_warn_ratelimited(smmu->dev,
- "capture bus regs in use, not configuring it.\n");
- return -EBUSY;
- }
- config = arm_smmu_debug_qtb_transtracker_get_config(transactiontracker_base);
- mutex_unlock(&capture_reg_lock);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- arm_smmu_power_off(smmu, smmu->pwr);
- if (config == (TTQTB_GlbEn | TTQTB_IgnoreCtiTrigIn0 | TTQTB_LogAsstEn))
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Custom configuration selected, MainCtl filter val: 0x%0lx\n",
- config);
- else if (config == (TTQTB_GlbEn | TTQTB_IgnoreCtiTrigIn0 | TTQTB_LogAll))
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Default configuration selected, MainCtl filter val : 0x%0lx\n",
- config);
- buflen = min(count, strlen(buf));
- if (copy_to_user(ubuf, buf, buflen)) {
- dev_err_ratelimited(smmu->dev, "Couldn't copy_to_user\n");
- retval = -EFAULT;
- } else {
- *offset = 1;
- retval = buflen;
- }
- return retval;
- }
- else {
- if (arm_smmu_power_on(smmu->pwr))
- return -EINVAL;
- if (arm_smmu_power_on(tbu->pwr)) {
- arm_smmu_power_off(smmu, smmu->pwr);
- return -EINVAL;
- }
- if (!mutex_trylock(&capture_reg_lock)) {
- dev_warn_ratelimited(smmu->dev,
- "capture bus regs in use, not configuring it.\n");
- return -EBUSY;
- }
- arm_smmu_debug_get_mask_and_match(tbu_base,
- mask, match);
- val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base);
- mutex_unlock(&capture_reg_lock);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- arm_smmu_power_off(smmu, smmu->pwr);
- for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) {
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Mask_%d : 0x%0llx\t", i+1, mask[i]);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Match_%d : 0x%0llx\n", i+1, match[i]);
- }
- scnprintf(buf + strlen(buf), buf_len - strlen(buf), "0x%0lx\n", val);
- buflen = min(count, strlen(buf));
- if (copy_to_user(ubuf, buf, buflen)) {
- dev_err_ratelimited(smmu->dev, "Couldn't copy_to_user\n");
- retval = -EFAULT;
- } else {
- *offset = 1;
- retval = buflen;
- }
- return retval;
- }
- }
- static ssize_t arm_smmu_debug_capturebus_filter_write(struct file *file,
- const char __user *ubuf, size_t count, loff_t *offset)
- {
- struct qsmmuv500_tbu_device *tbu = file->private_data;
- struct arm_smmu_device *smmu = tbu->smmu;
- struct qtb500_device *qtb = to_qtb500(tbu);
- void __iomem *transactiontracker_base = qtb->transactiontracker_base;
- char *comma1;
- char buf[200];
- u64 sel, filter;
- int qtb_type = 0;
- if (count >= sizeof(buf)) {
- dev_err_ratelimited(smmu->dev, "Input too large\n");
- goto invalid_filter_format;
- }
- memset(buf, 0, sizeof(buf));
- if (copy_from_user(buf, ubuf, count)) {
- dev_err_ratelimited(smmu->dev, "Couldn't copy from user\n");
- return -EFAULT;
- }
- comma1 = strnchr(buf, count, ',');
- if (!comma1)
- goto invalid_filter_format;
- *comma1 = '\0';
- if (kstrtou64(buf, 0, &sel))
- goto invalid_filter_format;
- if (sel == 1 || sel == 2 || sel == 3) {
- if (kstrtou64(comma1 + 1, 0, &filter))
- goto invalid_filter_format;
- goto set_capturebus_filter;
- }
- else
- goto invalid_filter_format;
- set_capturebus_filter:
- if (arm_smmu_power_on(smmu->pwr))
- return -EINVAL;
- if (arm_smmu_power_on(tbu->pwr)) {
- arm_smmu_power_off(smmu, smmu->pwr);
- return -EINVAL;
- }
- if (!mutex_trylock(&capture_reg_lock)) {
- dev_warn_ratelimited(smmu->dev,
- "Transaction Tracker regs in use, not configuring it.\n");
- return -EBUSY;
- }
- if (of_device_is_compatible(smmu->dev->of_node, "qcom,adreno-smmu"))
- qtb_type = 1;
- else
- qtb_type = 2;
- arm_smmu_debug_qtb_transtracker_setfilter(transactiontracker_base,
- sel, filter, qtb_type);
- mutex_unlock(&capture_reg_lock);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- arm_smmu_power_off(smmu, smmu->pwr);
- return count;
- invalid_filter_format:
- dev_err_ratelimited(smmu->dev, "Invalid format\n");
- dev_err_ratelimited(smmu->dev, "This is QTB equipped device\n");
- dev_err_ratelimited(smmu->dev,
- "Expected:<1/2/3,TrType/AddressMin/AddressMax>\n");
- return -EINVAL;
- }
- static ssize_t arm_smmu_debug_capturebus_filter_read(struct file *file,
- char __user *ubuf, size_t count, loff_t *offset)
- {
- struct qsmmuv500_tbu_device *tbu = file->private_data;
- struct arm_smmu_device *smmu = tbu->smmu;
- struct qtb500_device *qtb = to_qtb500(tbu);
- void __iomem *transactiontracker_base = qtb->transactiontracker_base;
- u64 filter[3];
- char buf[400];
- ssize_t retval;
- size_t buflen;
- int buf_len = sizeof(buf);
- int i = 0, qtb_type = 0;
- if (*offset)
- return 0;
- memset(buf, 0, buf_len);
- if (arm_smmu_power_on(smmu->pwr))
- return -EINVAL;
- if (arm_smmu_power_on(tbu->pwr)) {
- arm_smmu_power_off(smmu, smmu->pwr);
- return -EINVAL;
- }
- if (!mutex_trylock(&capture_reg_lock)) {
- dev_warn_ratelimited(smmu->dev,
- "Transaction Tracker regs in use, not configuring it.\n");
- return -EBUSY;
- }
- if (of_device_is_compatible(smmu->dev->of_node, "qcom,adreno-smmu"))
- qtb_type = 1;
- else
- qtb_type = 2;
- arm_smmu_debug_qtb_transtracker_getfilter(transactiontracker_base, filter, qtb_type);
- mutex_unlock(&capture_reg_lock);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- arm_smmu_power_off(smmu, smmu->pwr);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Filter_TrType : 0x%lx\n", filter[i]);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Filter_AddressMin : 0x%lx\n", filter[i+1]);
- scnprintf(buf + strlen(buf), buf_len - strlen(buf),
- "Filter_AddressMax : 0x%lx\n", filter[i+2]);
- buflen = min(count, strlen(buf));
- if (copy_to_user(ubuf, buf, buflen)) {
- dev_err_ratelimited(smmu->dev, "Couldn't copy_to_user\n");
- retval = -EFAULT;
- } else {
- *offset = 1;
- retval = buflen;
- }
- return retval;
- }
- static const struct file_operations arm_smmu_debug_capturebus_config_fops = {
- .open = simple_open,
- .write = arm_smmu_debug_capturebus_config_write,
- .read = arm_smmu_debug_capturebus_config_read,
- };
- static const struct file_operations arm_smmu_debug_capturebus_filter_fops = {
- .open = simple_open,
- .write = arm_smmu_debug_capturebus_filter_write,
- .read = arm_smmu_debug_capturebus_filter_read,
- };
- #ifdef CONFIG_ARM_SMMU_CAPTUREBUS_DEBUGFS
- static struct dentry *debugfs_capturebus_dir;
- static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu)
- {
- struct dentry *capturebus_dir, *iommu_debug_dir;
- iommu_debug_dir = get_iommu_debug_dir();
- if (!iommu_debug_dir)
- return 0;
- if (!debugfs_capturebus_dir) {
- debugfs_capturebus_dir = debugfs_create_dir(
- "capturebus", iommu_debug_dir);
- if (IS_ERR(debugfs_capturebus_dir)) {
- dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus debugfs directory\n");
- return PTR_ERR(debugfs_capturebus_dir);
- }
- }
- capturebus_dir = debugfs_create_dir(dev_name(tbu->dev),
- debugfs_capturebus_dir);
- if (IS_ERR(capturebus_dir)) {
- dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s debugfs directory\n",
- dev_name(tbu->dev));
- goto err;
- }
- if (IS_ERR(debugfs_create_file("config", 0400, capturebus_dir, tbu,
- &arm_smmu_debug_capturebus_config_fops))) {
- dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s/config debugfs file\n",
- dev_name(tbu->dev));
- goto err_rmdir;
- }
- if (of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500")) {
- if (IS_ERR(debugfs_create_file("filter", 0400, capturebus_dir, tbu,
- &arm_smmu_debug_capturebus_filter_fops))) {
- dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s/filter debugfs file\n",
- dev_name(tbu->dev));
- goto err_rmdir;
- }
- }
- if (IS_ERR(debugfs_create_file("snapshot", 0400, capturebus_dir, tbu,
- &arm_smmu_debug_capturebus_snapshot_fops))) {
- dev_err_ratelimited(tbu->dev, "Couldn't create iommu/capturebus/%s/snapshot debugfs file\n",
- dev_name(tbu->dev));
- goto err_rmdir;
- }
- return 0;
- err_rmdir:
- debugfs_remove_recursive(capturebus_dir);
- err:
- return -ENODEV;
- }
- #else
- static int qsmmuv500_capturebus_init(struct qsmmuv500_tbu_device *tbu)
- {
- return 0;
- }
- #endif
- static irqreturn_t arm_smmu_debug_capture_bus_match(int irq, void *dev)
- {
- struct qsmmuv500_tbu_device *tbu = dev;
- struct arm_smmu_device *smmu = tbu->smmu;
- void __iomem *tbu_base = tbu->base;
- u64 mask[NO_OF_MASK_AND_MATCH], match[NO_OF_MASK_AND_MATCH];
- u64 snapshot[NO_OF_CAPTURE_POINTS][REGS_PER_CAPTURE_POINT];
- int i, j;
- u64 val;
- if (arm_smmu_power_on(smmu->pwr))
- return IRQ_NONE;
- if (arm_smmu_power_on(tbu->pwr)) {
- arm_smmu_power_off(smmu, smmu->pwr);
- return IRQ_NONE;
- }
- if (!mutex_trylock(&capture_reg_lock)) {
- dev_warn_ratelimited(smmu->dev,
- "capture bus regs in use, not dumping it.\n");
- return IRQ_NONE;
- }
- val = arm_smmu_debug_get_tnx_tcr_cntl(tbu_base);
- arm_smmu_debug_get_mask_and_match(tbu_base, mask, match);
- arm_smmu_debug_get_capture_snapshot(tbu_base, snapshot);
- arm_smmu_debug_clear_intr_and_validbits(tbu_base);
- mutex_unlock(&capture_reg_lock);
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- arm_smmu_power_off(smmu, smmu->pwr);
- dev_info(tbu->dev, "TNX_TCR_CNTL : 0x%0llx\n", val);
- for (i = 0; i < NO_OF_MASK_AND_MATCH; ++i) {
- dev_info(tbu->dev,
- "Mask_%d : 0x%0llx\n", i+1, mask[i]);
- dev_info(tbu->dev,
- "Match_%d : 0x%0llx\n", i+1, match[i]);
- }
- for (i = 0; i < NO_OF_CAPTURE_POINTS ; ++i) {
- for (j = 0; j < REGS_PER_CAPTURE_POINT; ++j) {
- dev_info(tbu->dev,
- "Capture_%d_Snapshot_%d : 0x%0llx\n",
- i+1, j+1, snapshot[i][j]);
- }
- }
- return IRQ_HANDLED;
- }
- static void qsmmuv500_tlb_sync_timeout(struct arm_smmu_device *smmu)
- {
- u32 sync_inv_ack, tbu_pwr_status, sync_inv_progress;
- u32 tbu_inv_pending = 0, tbu_sync_pending = 0;
- u32 tbu_inv_acked = 0, tbu_sync_acked = 0;
- u32 tcu_inv_pending = 0, tcu_sync_pending = 0;
- unsigned long tbu_ids = 0;
- struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
- int ret;
- static DEFINE_RATELIMIT_STATE(_rs,
- DEFAULT_RATELIMIT_INTERVAL,
- DEFAULT_RATELIMIT_BURST);
- dev_err_ratelimited(smmu->dev,
- "TLB sync timed out -- SMMU may be deadlocked\n");
- sync_inv_ack = arm_smmu_readl(smmu,
- ARM_SMMU_IMPL_DEF5,
- ARM_SMMU_STATS_SYNC_INV_TBU_ACK);
- if (sync_inv_ack) {
- tbu_inv_pending = FIELD_GET(TBU_INV_REQ, sync_inv_ack);
- tbu_inv_acked = FIELD_GET(TBU_INV_ACK, sync_inv_ack);
- tbu_sync_pending = FIELD_GET(TBU_SYNC_REQ, sync_inv_ack);
- tbu_sync_acked = FIELD_GET(TBU_SYNC_ACK, sync_inv_ack);
- }
- ret = qcom_scm_io_readl((unsigned long)(smmu->phys_addr +
- ARM_SMMU_TBU_PWR_STATUS), &tbu_pwr_status);
- if (ret) {
- dev_err_ratelimited(smmu->dev,
- "SCM read of TBU power status fails: %d\n",
- ret);
- goto out;
- }
- ret = qcom_scm_io_readl((unsigned long)(smmu->phys_addr +
- ARM_SMMU_MMU2QSS_AND_SAFE_WAIT_CNTR),
- &sync_inv_progress);
- if (ret) {
- dev_err_ratelimited(smmu->dev,
- "SCM read of TBU sync/inv prog fails: %d\n",
- ret);
- goto out;
- }
- if (tbu_pwr_status) {
- if (tbu_sync_pending)
- tbu_ids = tbu_pwr_status & ~tbu_sync_acked;
- else if (tbu_inv_pending)
- tbu_ids = tbu_pwr_status & ~tbu_inv_acked;
- }
- tcu_inv_pending = FIELD_GET(TCU_INV_IN_PRGSS, sync_inv_progress);
- tcu_sync_pending = FIELD_GET(TCU_SYNC_IN_PRGSS, sync_inv_progress);
- if (__ratelimit(&_rs)) {
- unsigned long tbu_id;
- dev_err(smmu->dev,
- "TBU ACK 0x%x TBU PWR 0x%x TCU sync_inv 0x%x\n",
- sync_inv_ack, tbu_pwr_status, sync_inv_progress);
- dev_err(smmu->dev,
- "TCU invalidation %s, TCU sync %s\n",
- tcu_inv_pending?"pending":"completed",
- tcu_sync_pending?"pending":"completed");
- for_each_set_bit(tbu_id, &tbu_ids, sizeof(tbu_ids) *
- BITS_PER_BYTE) {
- struct qsmmuv500_tbu_device *tbu;
- tbu = qsmmuv500_find_tbu(smmu,
- (u16)(tbu_id << TBUID_SHIFT));
- if (tbu) {
- dev_err(smmu->dev,
- "TBU %s ack pending for TBU %s, %s\n",
- tbu_sync_pending?"sync" : "inv",
- dev_name(tbu->dev),
- tbu_sync_pending ?
- "check pending transactions on TBU"
- : "check for TBU power status");
- arm_smmu_testbus_dump(smmu,
- (u16)(tbu_id << TBUID_SHIFT));
- }
- }
- /*dump TCU testbus*/
- arm_smmu_testbus_dump(smmu, U16_MAX);
- }
- if (tcu_sync_pending) {
- schedule_work(&data->outstanding_tnx_work);
- return;
- }
- out:
- if (ret) {
- if (sync_inv_ack) {
- /* TBU PWR status is not available so just dump raw
- * fields
- */
- dev_err(smmu->dev,
- "TBU %s ack pending, got ack for TBUs %d, %s\n",
- tbu_sync_pending ? "sync" : "inv",
- tbu_sync_pending ? tbu_sync_acked:tbu_inv_acked,
- tbu_sync_pending ?
- "check pending transactions on TBU"
- : "check for TBU power status");
- }
- dev_err(smmu->dev, "TBU SYNC_INV_ACK reg 0x%x\n", sync_inv_ack);
- }
- BUG_ON(IS_ENABLED(CONFIG_IOMMU_TLBSYNC_DEBUG));
- }
- static void qsmmuv500_device_remove(struct arm_smmu_device *smmu)
- {
- struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
- cancel_work_sync(&data->outstanding_tnx_work);
- }
- /*
- * Checks whether smr2 is a subset of smr
- */
- static bool smr_is_subset(struct arm_smmu_smr *smr2, struct arm_smmu_smr *smr)
- {
- return (smr->mask & smr2->mask) == smr2->mask &&
- !((smr->id ^ smr2->id) & ~smr->mask);
- }
- /*
- * Zero means failure.
- */
- static phys_addr_t qsmmuv500_iova_to_phys(struct arm_smmu_domain *smmu_domain, dma_addr_t iova,
- u32 sid, unsigned long trans_flags)
- {
- struct arm_smmu_device *smmu = smmu_domain->smmu;
- struct arm_smmu_cfg *cfg = &smmu_domain->cfg;
- struct qsmmuv500_archdata *data = to_qsmmuv500_archdata(smmu);
- struct qsmmuv500_tbu_device *tbu;
- phys_addr_t phys = 0;
- int idx = cfg->cbndx;
- int needs_redo = 0;
- u32 sctlr_orig, sctlr, fsr;
- unsigned long spinlock_flags;
- tbu = qsmmuv500_find_tbu(smmu, sid);
- if (!tbu)
- return 0;
- if (arm_smmu_power_on(tbu->pwr))
- return 0;
- if (iova >= (1ULL << tbu->iova_width)) {
- dev_err_ratelimited(tbu->dev, "ECATS: address too large: %pad\n", &iova);
- return 0;
- }
- if (qsmmuv500_tbu_halt(tbu, smmu_domain))
- goto out_power_off;
- /*
- * ECATS can trigger the fault interrupt, so disable it temporarily
- * and check for an interrupt manually.
- */
- sctlr_orig = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_SCTLR);
- sctlr = sctlr_orig & ~(ARM_SMMU_SCTLR_CFCFG | ARM_SMMU_SCTLR_CFIE);
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr);
- fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR);
- if (fsr & ARM_SMMU_FSR_FAULT) {
- /* Clear pending interrupts */
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr);
- /*
- * Barrier required to ensure that the FSR is cleared
- * before resuming SMMU operation.
- */
- wmb();
- /*
- * TBU halt takes care of resuming any stalled transcation.
- * Kept it here for completeness sake.
- */
- if (fsr & ARM_SMMU_FSR_SS)
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_RESUME,
- ARM_SMMU_RESUME_TERMINATE);
- }
- /* Only one concurrent atos operation */
- spin_lock_irqsave(&data->atos_lock, spinlock_flags);
- /*
- * After a failed translation, the next successful translation will
- * incorrectly be reported as a failure.
- */
- do {
- phys = tbu->impl->trigger_atos(tbu, iova, sid, trans_flags);
- fsr = arm_smmu_cb_read(smmu, idx, ARM_SMMU_CB_FSR);
- if (fsr & ARM_SMMU_FSR_FAULT) {
- /* Clear pending interrupts */
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_FSR, fsr);
- /*
- * Barrier required to ensure that the FSR is cleared
- * before resuming SMMU operation.
- */
- wmb();
- if (fsr & ARM_SMMU_FSR_SS)
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_RESUME,
- ARM_SMMU_RESUME_TERMINATE);
- }
- } while (!phys && needs_redo++ < 2);
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_SCTLR, sctlr_orig);
- spin_unlock_irqrestore(&data->atos_lock, spinlock_flags);
- qsmmuv500_tbu_resume(tbu);
- out_power_off:
- /* Read to complete prior write transcations */
- tbu->impl->write_sync(tbu);
- /* Wait for read to complete before off */
- rmb();
- arm_smmu_power_off(tbu->smmu, tbu->pwr);
- return phys;
- }
- static phys_addr_t qsmmuv500_iova_to_phys_hard(
- struct arm_smmu_domain *smmu_domain,
- struct qcom_iommu_atos_txn *txn)
- {
- return qsmmuv500_iova_to_phys(smmu_domain, txn->addr, txn->id,
- txn->flags);
- }
- static void qsmmuv500_release_group_iommudata(void *data)
- {
- kfree(data);
- }
- /* If a device has a valid actlr, it must match */
- static int qsmmuv500_device_group(struct device *dev,
- struct iommu_group *group)
- {
- struct iommu_fwspec *fwspec = dev_iommu_fwspec_get(dev);
- struct arm_smmu_master_cfg *cfg = dev_iommu_priv_get(dev);
- struct arm_smmu_device *smmu;
- struct qsmmuv500_archdata *data;
- struct qsmmuv500_group_iommudata *iommudata;
- u32 actlr, i, j, idx;
- struct arm_smmu_smr *smr, *smr2;
- if (!fwspec || !cfg)
- return -EINVAL;
- smmu = cfg->smmu;
- data = to_qsmmuv500_archdata(smmu);
- iommudata = to_qsmmuv500_group_iommudata(group);
- if (!iommudata) {
- iommudata = kzalloc(sizeof(*iommudata), GFP_KERNEL);
- if (!iommudata)
- return -ENOMEM;
- iommu_group_set_iommudata(group, iommudata,
- qsmmuv500_release_group_iommudata);
- }
- for (i = 0; i < data->actlr_tbl_size; i++) {
- smr = &data->actlrs[i].smr;
- actlr = data->actlrs[i].actlr;
- for_each_cfg_sme(cfg, fwspec, j, idx) {
- smr2 = &smmu->smrs[idx];
- if (!smr_is_subset(smr2, smr))
- continue;
- dev_dbg(dev, "Matched actlr sid=%x mask=%x actlr=%x\n",
- smr->id, smr->mask, actlr);
- if (!iommudata->has_actlr) {
- iommudata->actlr = actlr;
- iommudata->has_actlr = true;
- } else if (iommudata->actlr != actlr) {
- dev_err(dev, "Invalid actlr setting\n");
- return -EINVAL;
- }
- }
- }
- return 0;
- }
- static void qsmmuv500_init_cb(struct arm_smmu_domain *smmu_domain,
- struct device *dev)
- {
- struct arm_smmu_device *smmu = smmu_domain->smmu;
- struct qsmmuv500_group_iommudata *iommudata =
- to_qsmmuv500_group_iommudata(dev->iommu_group);
- int idx = smmu_domain->cfg.cbndx;
- if (!iommudata->has_actlr)
- return;
- arm_smmu_cb_write(smmu, idx, ARM_SMMU_CB_ACTLR, iommudata->actlr);
- /*
- * Flush the context bank after modifying ACTLR to ensure there
- * are no cache entries with stale state
- */
- iommu_flush_iotlb_all(&smmu_domain->domain);
- }
- static int qsmmuv500_tbu_register(struct device *dev, void *cookie)
- {
- struct resource *res;
- struct qsmmuv500_tbu_device *tbu;
- struct qsmmuv500_archdata *data = cookie;
- struct platform_device *pdev = to_platform_device(dev);
- int i, err, num_irqs = 0;
- if (!dev->driver) {
- dev_err(dev, "TBU failed probe, QSMMUV500 cannot continue!\n");
- return -EINVAL;
- }
- tbu = dev_get_drvdata(dev);
- INIT_LIST_HEAD(&tbu->list);
- tbu->smmu = &data->smmu;
- list_add(&tbu->list, &data->tbus);
- while ((res = platform_get_resource(pdev, IORESOURCE_IRQ, num_irqs)))
- num_irqs++;
- tbu->irqs = devm_kzalloc(dev, sizeof(*tbu->irqs) * num_irqs,
- GFP_KERNEL);
- if (!tbu->irqs)
- return -ENOMEM;
- for (i = 0; i < num_irqs; ++i) {
- int irq = platform_get_irq(pdev, i);
- if (irq < 0) {
- dev_err(dev, "failed to get irq index %d\n", i);
- return -ENODEV;
- }
- tbu->irqs[i] = irq;
- err = devm_request_threaded_irq(tbu->dev, tbu->irqs[i],
- NULL, arm_smmu_debug_capture_bus_match,
- IRQF_ONESHOT | IRQF_SHARED,
- "capture bus", tbu);
- if (err) {
- dev_err(dev, "failed to request capture bus irq%d (%u)\n",
- i, tbu->irqs[i]);
- return err;
- }
- }
- /*
- * Create testbus debugfs only if debugchain base
- * property is set in devicetree in case of qtb500.
- */
- if (!of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500") ||
- to_qtb500(tbu)->debugchain_base)
- qsmmuv500_tbu_testbus_init(tbu);
- /*
- * Create transactiontracker debugfs only if
- * transactiontracker base is set in devicetree in case of qtb500.
- */
- if (!of_device_is_compatible(tbu->dev->of_node, "qcom,qtb500") ||
- to_qtb500(tbu)->transactiontracker_base)
- qsmmuv500_capturebus_init(tbu);
- return 0;
- }
- static int qsmmuv500_read_actlr_tbl(struct qsmmuv500_archdata *data)
- {
- int len, i;
- struct device *dev = data->smmu.dev;
- struct actlr_setting *actlrs;
- const __be32 *cell;
- cell = of_get_property(dev->of_node, "qcom,actlr", NULL);
- if (!cell)
- return 0;
- len = of_property_count_elems_of_size(dev->of_node, "qcom,actlr",
- sizeof(u32) * 3);
- if (len < 0)
- return 0;
- actlrs = devm_kzalloc(dev, sizeof(*actlrs) * len, GFP_KERNEL);
- if (!actlrs)
- return -ENOMEM;
- for (i = 0; i < len; i++) {
- actlrs[i].smr.id = of_read_number(cell++, 1);
- actlrs[i].smr.mask = of_read_number(cell++, 1);
- actlrs[i].actlr = of_read_number(cell++, 1);
- }
- data->actlrs = actlrs;
- data->actlr_tbl_size = len;
- return 0;
- }
- static int qsmmuv500_cfg_probe(struct arm_smmu_device *smmu)
- {
- u32 val;
- val = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR);
- val &= ~ARM_MMU500_ACR_CACHE_LOCK;
- arm_smmu_gr0_write(smmu, ARM_SMMU_GR0_sACR, val);
- val = arm_smmu_gr0_read(smmu, ARM_SMMU_GR0_sACR);
- /*
- * Modifiying the nonsecure copy of the sACR register is only
- * allowed if permission is given in the secure sACR register.
- * Attempt to detect if we were able to update the value.
- */
- WARN_ON(val & ARM_MMU500_ACR_CACHE_LOCK);
- return 0;
- }
- /*
- * Case 1)
- * Client wants to use the standard upstream dma ops from
- * drivers/iommu/dma-iommu.c
- *
- * This requires domain->type == IOMMU_DOMAIN_DMA.
- *
- * Case 2)
- * Client doesn't want to use the default dma domain, and wants to
- * allocate their own via iommu_domain_alloc()
- *
- * There are insufficient context banks to "waste" one on a default
- * dma domain that isn't going to be used. Therefore, give it
- * IOMMU_DOMAIN_IDENTITY. Note that IOMMU_DOMAIN_IDENTITY is treated as
- * IOMMU_DOMAIN_BLOCKED by our hypervisor, which doesn't allow setting
- * S2CR.TYPE = 0x1.
- *
- * Case 3)
- * Client wants to use our fastmap dma ops
- *
- * Per case 1) we cannot use IOMMU_DOMAIN_DMA, since those imply using
- * the standard upstream dma ops. So use IOMMU_DOMAIN_UNMANAGED instead.
- *
- * Case 4)
- * Client wants to use S1 bypass
- *
- * Same as Case 3, except use the platform dma ops.
- *
- * This function can be used for qsmmuv500 and qsmmuv2.
- */
- static int qcom_def_domain_type(struct device *dev)
- {
- const char *str;
- struct device_node *np;
- /* Default to iommu_def_domain_type */
- np = of_parse_phandle(dev->of_node, "qcom,iommu-group", 0);
- if (!np)
- np = dev->of_node;
- if (of_property_read_string(np, "qcom,iommu-dma", &str))
- str = "default";
- if (!strcmp(str, "fastmap") &&
- IS_ENABLED(CONFIG_IOMMU_IO_PGTABLE_FAST))
- return IOMMU_DOMAIN_UNMANAGED;
- if (!strcmp(str, "bypass"))
- return IOMMU_DOMAIN_UNMANAGED;
- if (!strcmp(str, "atomic"))
- return IOMMU_DOMAIN_DMA;
- if (!strcmp(str, "disabled"))
- return IOMMU_DOMAIN_IDENTITY;
- return IOMMU_DOMAIN_DMA;
- }
- static const struct arm_smmu_impl qsmmuv500_impl = {
- .cfg_probe = qsmmuv500_cfg_probe,
- .init_context_bank = qsmmuv500_init_cb,
- .iova_to_phys_hard = qsmmuv500_iova_to_phys_hard,
- .tlb_sync_timeout = qsmmuv500_tlb_sync_timeout,
- .device_remove = qsmmuv500_device_remove,
- .device_group = qsmmuv500_device_group,
- .def_domain_type = qcom_def_domain_type,
- };
- static const struct arm_smmu_impl qsmmuv500_adreno_impl = {
- .init_context = qcom_adreno_smmu_init_context,
- .alloc_context_bank = qcom_adreno_smmu_alloc_context_bank,
- .cfg_probe = qsmmuv500_cfg_probe,
- .init_context_bank = qsmmuv500_init_cb,
- .iova_to_phys_hard = qsmmuv500_iova_to_phys_hard,
- .tlb_sync_timeout = qsmmuv500_tlb_sync_timeout,
- .device_remove = qsmmuv500_device_remove,
- .device_group = qsmmuv500_device_group,
- .def_domain_type = qcom_def_domain_type,
- };
- static const struct arm_smmu_impl qsmmuv2_impl = {
- .init_context_bank = qsmmuv2_init_cb,
- .iova_to_phys_hard = qsmmuv2_iova_to_phys_hard,
- .tlb_sync_timeout = qsmmuv2_tlb_sync_timeout,
- .reset = qsmmuv2_device_reset,
- .def_domain_type = qcom_def_domain_type,
- };
- static const struct arm_smmu_impl qsmmuv2_adreno_impl = {
- .init_context = qcom_adreno_smmu_init_context,
- .alloc_context_bank = qcom_adreno_smmu_alloc_context_bank,
- .init_context_bank = qsmmuv2_init_cb,
- .iova_to_phys_hard = qsmmuv2_iova_to_phys_hard,
- .tlb_sync_timeout = qsmmuv2_tlb_sync_timeout,
- .reset = qsmmuv2_device_reset,
- .def_domain_type = qcom_def_domain_type,
- };
- /* We only have access to arm-architected registers */
- static const struct arm_smmu_impl qsmmuv500_virt_impl = {
- .cfg_probe = qsmmuv500_cfg_probe,
- .init_context_bank = qsmmuv500_init_cb,
- .device_group = qsmmuv500_device_group,
- .def_domain_type = qcom_def_domain_type,
- };
- struct arm_smmu_device *qsmmuv500_create(struct arm_smmu_device *smmu,
- const struct arm_smmu_impl *impl)
- {
- struct device *dev = smmu->dev;
- struct qsmmuv500_archdata *data;
- int ret;
- #if IS_ENABLED(CONFIG_ARM_SMMU_TESTBUS)
- struct platform_device *pdev;
- #endif
- /*
- * devm_krealloc() invokes devm_kmalloc(), so we pass __GFP_ZERO
- * to ensure that fields after smmu are initialized, even if we don't
- * initialize them (e.g. ACTLR related fields).
- */
- data = devm_krealloc(dev, smmu, sizeof(*data), GFP_KERNEL | __GFP_ZERO);
- if (!data)
- return ERR_PTR(-ENOMEM);
- INIT_LIST_HEAD(&data->tbus);
- spin_lock_init(&data->atos_lock);
- INIT_WORK(&data->outstanding_tnx_work,
- qsmmuv500_log_outstanding_transactions);
- data->smmu.impl = impl;
- #if IS_ENABLED(CONFIG_ARM_SMMU_TESTBUS)
- pdev = to_platform_device(dev);
- data->tcu_base = devm_platform_ioremap_resource_byname(pdev, "tcu-base");
- if (IS_ERR(data->tcu_base)) {
- dev_err(dev, "Unable to get the tcu-base\n");
- return ERR_PTR(-EINVAL);
- }
- #endif
- qsmmuv500_tcu_testbus_init(&data->smmu);
- ret = qsmmuv500_read_actlr_tbl(data);
- if (ret)
- return ERR_PTR(ret);
- ret = of_platform_populate(dev->of_node, NULL, NULL, dev);
- if (ret)
- return ERR_PTR(ret);
- /* Attempt to register child devices */
- ret = device_for_each_child(dev, data, qsmmuv500_tbu_register);
- if (ret)
- return ERR_PTR(-EPROBE_DEFER);
- return &data->smmu;
- }
- static struct arm_smmu_device *qsmmuv500_virt_create(struct arm_smmu_device *smmu,
- const struct arm_smmu_impl *impl)
- {
- struct device *dev = smmu->dev;
- struct qsmmuv500_archdata *data;
- int ret;
- data = devm_krealloc(dev, smmu, sizeof(*data), GFP_KERNEL | __GFP_ZERO);
- if (!data)
- return ERR_PTR(-ENOMEM);
- INIT_LIST_HEAD(&data->tbus);
- spin_lock_init(&data->atos_lock);
- INIT_WORK(&data->outstanding_tnx_work,
- qsmmuv500_log_outstanding_transactions);
- data->smmu.impl = impl;
- ret = qsmmuv500_read_actlr_tbl(data);
- if (ret)
- return ERR_PTR(ret);
- return &data->smmu;
- }
- struct arm_smmu_device *qsmmuv500_impl_init(struct arm_smmu_device *smmu)
- {
- if (of_device_is_compatible(smmu->dev->of_node, "qcom,adreno-smmu"))
- return qsmmuv500_create(smmu, &qsmmuv500_adreno_impl);
- if (of_device_is_compatible(smmu->dev->of_node, "qcom,virt-smmu"))
- return qsmmuv500_virt_create(smmu, &qsmmuv500_virt_impl);
- return qsmmuv500_create(smmu, &qsmmuv500_impl);
- }
- static const struct arm_smmu_impl qcom_adreno_smmu_impl = {
- .init_context = qcom_adreno_smmu_init_context,
- .def_domain_type = qcom_smmu_def_domain_type,
- .reset = qcom_smmu500_reset,
- .alloc_context_bank = qcom_adreno_smmu_alloc_context_bank,
- .write_sctlr = qcom_adreno_smmu_write_sctlr,
- .tlb_sync = qcom_smmu_tlb_sync,
- };
- static struct arm_smmu_device *qcom_smmu_create(struct arm_smmu_device *smmu,
- const struct arm_smmu_impl *impl)
- {
- struct qcom_smmu *qsmmu;
- /* Check to make sure qcom_scm has finished probing */
- if (!qcom_scm_is_available())
- return ERR_PTR(-EPROBE_DEFER);
- qsmmu = devm_krealloc(smmu->dev, smmu, sizeof(*qsmmu), GFP_KERNEL);
- if (!qsmmu)
- return ERR_PTR(-ENOMEM);
- qsmmu->smmu.impl = impl;
- qsmmu->cfg = qcom_smmu_impl_data(smmu);
- return &qsmmu->smmu;
- }
- static const struct of_device_id __maybe_unused qcom_smmu_impl_of_match[] = {
- { .compatible = "qcom,msm8998-smmu-v2" },
- { .compatible = "qcom,qcm2290-smmu-500" },
- { .compatible = "qcom,sc7180-smmu-500" },
- { .compatible = "qcom,sc7280-smmu-500" },
- { .compatible = "qcom,sc8180x-smmu-500" },
- { .compatible = "qcom,sc8280xp-smmu-500" },
- { .compatible = "qcom,sdm630-smmu-v2" },
- { .compatible = "qcom,sdm845-smmu-500" },
- { .compatible = "qcom,sm6125-smmu-500" },
- { .compatible = "qcom,sm6350-smmu-500" },
- { .compatible = "qcom,sm6375-smmu-500" },
- { .compatible = "qcom,sm8150-smmu-500" },
- { .compatible = "qcom,sm8250-smmu-500" },
- { .compatible = "qcom,sm8350-smmu-500" },
- { .compatible = "qcom,sm8450-smmu-500" },
- { }
- };
- #ifdef CONFIG_ACPI
- static struct acpi_platform_list qcom_acpi_platlist[] = {
- { "LENOVO", "CB-01 ", 0x8180, ACPI_SIG_IORT, equal, "QCOM SMMU" },
- { "QCOM ", "QCOMEDK2", 0x8180, ACPI_SIG_IORT, equal, "QCOM SMMU" },
- { }
- };
- #endif
- struct arm_smmu_device *qcom_smmu_impl_init(struct arm_smmu_device *smmu)
- {
- const struct device_node *np = smmu->dev->of_node;
- #ifdef CONFIG_ACPI
- if (np == NULL) {
- /* Match platform for ACPI boot */
- if (acpi_match_platform_list(qcom_acpi_platlist) >= 0)
- return qcom_smmu_create(smmu, &qcom_smmu_impl);
- }
- #endif
- /*
- * Do not change this order of implementation, i.e., first adreno
- * smmu impl and then apss smmu since we can have both implementing
- * arm,mmu-500 in which case we will miss setting adreno smmu specific
- * features if the order is changed.
- */
- if (of_device_is_compatible(np, "qcom,adreno-smmu"))
- return qcom_smmu_create(smmu, &qcom_adreno_smmu_impl);
- if (of_match_node(qcom_smmu_impl_of_match, np))
- return qcom_smmu_create(smmu, &qcom_smmu_impl);
- return smmu;
- }
- struct arm_smmu_device *qsmmuv2_impl_init(struct arm_smmu_device *smmu)
- {
- struct device *dev = smmu->dev;
- struct qsmmuv2_archdata *data;
- struct platform_device *pdev;
- int ret;
- data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL);
- if (!data)
- return ERR_PTR(-ENOMEM);
- pdev = to_platform_device(dev);
- spin_lock_init(&data->atos_lock);
- data->smmu = *smmu;
- if (of_device_is_compatible(smmu->dev->of_node, "qcom,adreno-smmu"))
- data->smmu.impl = &qsmmuv2_adreno_impl;
- else
- data->smmu.impl = &qsmmuv2_impl;
- ret = arm_smmu_parse_impl_def_registers(&data->smmu);
- if (ret)
- return ERR_PTR(ret);
- return &data->smmu;
- }
|