12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303230423052306230723082309231023112312231323142315231623172318231923202321232223232324232523262327232823292330233123322333233423352336233723382339234023412342234323442345234623472348234923502351235223532354235523562357235823592360236123622363236423652366236723682369237023712372237323742375237623772378237923802381238223832384238523862387238823892390239123922393239423952396239723982399240024012402240324042405240624072408240924102411241224132414241524162417241824192420242124222423242424252426242724282429243024312432243324342435243624372438243924402441244224432444244524462447244824492450245124522453245424552456245724582459246024612462246324642465246624672468246924702471247224732474247524762477247824792480248124822483248424852486248724882489249024912492249324942495249624972498249925002501250225032504250525062507250825092510251125122513251425152516251725182519252025212522252325242525252625272528252925302531253225332534253525362537253825392540254125422543254425452546254725482549255025512552255325542555255625572558255925602561256225632564256525662567256825692570257125722573257425752576257725782579258025812582258325842585258625872588258925902591259225932594259525962597259825992600260126022603260426052606260726082609261026112612261326142615261626172618261926202621262226232624262526262627262826292630263126322633263426352636263726382639264026412642264326442645264626472648264926502651265226532654265526562657265826592660266126622663266426652666266726682669267026712672267326742675267626772678267926802681268226832684268526862687268826892690269126922693269426952696269726982699270027012702270327042705270627072708270927102711271227132714271527162717271827192720272127222723272427252726272727282729273027312732273327342735273627372738273927402741274227432744274527462747274827492750275127522753275427552756275727582759276027612762276327642765 |
- // SPDX-License-Identifier: GPL-2.0-only
- /*
- * Copyright (c) 2011-2021, The Linux Foundation. All rights reserved.
- * Copyright (c) 2022-2024, Qualcomm Innovation Center, Inc. All rights reserved.
- */
- #include <linux/bitfield.h>
- #include <linux/compat.h>
- #include <linux/io.h>
- #include <linux/iopoll.h>
- #include <linux/of_platform.h>
- #include <linux/scatterlist.h>
- #include <linux/qcom-iommu-util.h>
- #include <linux/qcom-io-pgtable.h>
- #include <linux/seq_file.h>
- #include <linux/delay.h>
- #include <linux/qcom_scm.h>
- #include <linux/random.h>
- #include <linux/regulator/consumer.h>
- #include <soc/qcom/secure_buffer.h>
- #include "adreno.h"
- #include "kgsl_device.h"
- #include "kgsl_iommu.h"
- #include "kgsl_mmu.h"
- #include "kgsl_pwrctrl.h"
- #include "kgsl_sharedmem.h"
- #include "kgsl_trace.h"
- #define KGSL_IOMMU_SPLIT_TABLE_BASE 0x0001ff8000000000ULL
- #define KGSL_IOMMU_IDR1_OFFSET 0x24
- #define IDR1_NUMPAGENDXB GENMASK(30, 28)
- #define IDR1_PAGESIZE BIT(31)
- static const struct kgsl_mmu_pt_ops secure_pt_ops;
- static const struct kgsl_mmu_pt_ops default_pt_ops;
- static const struct kgsl_mmu_pt_ops iopgtbl_pt_ops;
- /* Zero page for non-secure VBOs */
- static struct page *kgsl_vbo_zero_page;
- /*
- * struct kgsl_iommu_addr_entry - entry in the kgsl_pagetable rbtree.
- * @base: starting virtual address of the entry
- * @size: size of the entry
- * @node: the rbtree node
- */
- struct kgsl_iommu_addr_entry {
- uint64_t base;
- uint64_t size;
- struct rb_node node;
- };
- static struct kmem_cache *addr_entry_cache;
- /* These are dummy TLB ops for the io-pgtable instances */
- static void _tlb_flush_all(void *cookie)
- {
- }
- static void _tlb_flush_walk(unsigned long iova, size_t size,
- size_t granule, void *cookie)
- {
- }
- static void _tlb_add_page(struct iommu_iotlb_gather *gather,
- unsigned long iova, size_t granule, void *cookie)
- {
- }
- static const struct iommu_flush_ops kgsl_iopgtbl_tlb_ops = {
- .tlb_flush_all = _tlb_flush_all,
- .tlb_flush_walk = _tlb_flush_walk,
- .tlb_add_page = _tlb_add_page,
- };
- static struct kgsl_iommu_pt *to_iommu_pt(struct kgsl_pagetable *pagetable)
- {
- return container_of(pagetable, struct kgsl_iommu_pt, base);
- }
- static u32 get_llcc_flags(struct kgsl_mmu *mmu)
- {
- if (!test_bit(KGSL_MMU_LLCC_ENABLE, &mmu->features))
- return 0;
- if (mmu->subtype == KGSL_IOMMU_SMMU_V500)
- return 0;
- else
- return IOMMU_USE_UPSTREAM_HINT;
- }
- static int _iommu_get_protection_flags(struct kgsl_mmu *mmu,
- struct kgsl_memdesc *memdesc)
- {
- int flags = IOMMU_READ | IOMMU_WRITE | IOMMU_NOEXEC;
- flags |= get_llcc_flags(mmu);
- if (memdesc->flags & KGSL_MEMFLAGS_GPUREADONLY)
- flags &= ~IOMMU_WRITE;
- if (memdesc->priv & KGSL_MEMDESC_PRIVILEGED)
- flags |= IOMMU_PRIV;
- if (memdesc->flags & KGSL_MEMFLAGS_IOCOHERENT)
- flags |= IOMMU_CACHE;
- if (memdesc->priv & KGSL_MEMDESC_UCODE)
- flags &= ~IOMMU_NOEXEC;
- return flags;
- }
- /* Get a scattterlist for the subrange in the child memdesc */
- static int get_sg_from_child(struct sg_table *sgt, struct kgsl_memdesc *child,
- u64 offset, u64 length)
- {
- int npages = (length >> PAGE_SHIFT);
- int pgoffset = (offset >> PAGE_SHIFT);
- struct scatterlist *target_sg;
- struct sg_page_iter iter;
- int i = 0, ret;
- if (child->pages)
- return sg_alloc_table_from_pages(sgt,
- child->pages + pgoffset, npages, 0,
- length, GFP_KERNEL);
- ret = sg_alloc_table(sgt, npages, GFP_KERNEL);
- if (ret)
- return ret;
- target_sg = sgt->sgl;
- for_each_sgtable_page(child->sgt, &iter, pgoffset) {
- sg_set_page(target_sg, sg_page_iter_page(&iter), PAGE_SIZE, 0);
- target_sg = sg_next(target_sg);
- if (++i == npages)
- break;
- }
- return 0;
- }
- static struct iommu_domain *to_iommu_domain(struct kgsl_iommu_context *context)
- {
- return context->domain;
- }
- static struct kgsl_iommu *to_kgsl_iommu(struct kgsl_pagetable *pt)
- {
- return &pt->mmu->iommu;
- }
- /*
- * One page allocation for a guard region to protect against over-zealous
- * GPU pre-fetch
- */
- static struct page *kgsl_guard_page;
- static struct page *kgsl_secure_guard_page;
- static struct page *iommu_get_guard_page(struct kgsl_memdesc *memdesc)
- {
- if (kgsl_memdesc_is_secured(memdesc)) {
- if (!kgsl_secure_guard_page)
- kgsl_secure_guard_page = kgsl_alloc_secure_page();
- return kgsl_secure_guard_page;
- }
- if (!kgsl_guard_page)
- kgsl_guard_page = alloc_page(GFP_KERNEL | __GFP_ZERO |
- __GFP_NORETRY | __GFP_HIGHMEM);
- return kgsl_guard_page;
- }
- static size_t iommu_pgsize(unsigned long pgsize_bitmap, unsigned long iova,
- phys_addr_t paddr, size_t size, size_t *count)
- {
- unsigned int pgsize_idx, pgsize_idx_next;
- unsigned long pgsizes;
- size_t offset, pgsize, pgsize_next;
- unsigned long addr_merge = paddr | iova;
- /* Page sizes supported by the hardware and small enough for @size */
- pgsizes = pgsize_bitmap & GENMASK(__fls(size), 0);
- /* Constrain the page sizes further based on the maximum alignment */
- if (likely(addr_merge))
- pgsizes &= GENMASK(__ffs(addr_merge), 0);
- /* Make sure we have at least one suitable page size */
- if (!pgsizes)
- return 0;
- /* Pick the biggest page size remaining */
- pgsize_idx = __fls(pgsizes);
- pgsize = BIT(pgsize_idx);
- if (!count)
- return pgsize;
- /* Find the next biggest support page size, if it exists */
- pgsizes = pgsize_bitmap & ~GENMASK(pgsize_idx, 0);
- if (!pgsizes)
- goto out_set_count;
- pgsize_idx_next = __ffs(pgsizes);
- pgsize_next = BIT(pgsize_idx_next);
- /*
- * There's no point trying a bigger page size unless the virtual
- * and physical addresses are similarly offset within the larger page.
- */
- if ((iova ^ paddr) & (pgsize_next - 1))
- goto out_set_count;
- /* Calculate the offset to the next page size alignment boundary */
- offset = pgsize_next - (addr_merge & (pgsize_next - 1));
- /*
- * If size is big enough to accommodate the larger page, reduce
- * the number of smaller pages.
- */
- if (offset + pgsize_next <= size)
- size = offset;
- out_set_count:
- *count = size >> pgsize_idx;
- return pgsize;
- }
- static int _iopgtbl_unmap_pages(struct kgsl_iommu_pt *pt, u64 gpuaddr,
- size_t size)
- {
- struct io_pgtable_ops *ops = pt->pgtbl_ops;
- size_t unmapped = 0;
- while (unmapped < size) {
- size_t ret, size_to_unmap, remaining, pgcount;
- remaining = (size - unmapped);
- size_to_unmap = iommu_pgsize(pt->info.cfg.pgsize_bitmap,
- gpuaddr, gpuaddr, remaining, &pgcount);
- if (size_to_unmap == 0)
- break;
- #if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
- ret = qcom_arm_lpae_unmap_pages(ops, gpuaddr, size_to_unmap,
- pgcount, NULL);
- #else
- ret = ops->unmap_pages(ops, gpuaddr, size_to_unmap,
- pgcount, NULL);
- #endif
- if (ret == 0)
- break;
- gpuaddr += ret;
- unmapped += ret;
- }
- return (unmapped == size) ? 0 : -EINVAL;
- }
- static void kgsl_iommu_flush_tlb(struct kgsl_mmu *mmu)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- iommu_flush_iotlb_all(to_iommu_domain(&iommu->user_context));
- /* As LPAC is optional, check LPAC domain is present before flush */
- if (iommu->lpac_context.domain)
- iommu_flush_iotlb_all(to_iommu_domain(&iommu->lpac_context));
- }
- static int _iopgtbl_unmap(struct kgsl_iommu_pt *pt, u64 gpuaddr, size_t size)
- {
- struct io_pgtable_ops *ops = pt->pgtbl_ops;
- int ret = 0;
- size_t unmapped;
- if (IS_ENABLED(CONFIG_IOMMU_IO_PGTABLE_LPAE)) {
- ret = _iopgtbl_unmap_pages(pt, gpuaddr, size);
- if (ret)
- return ret;
- goto flush;
- }
- unmapped = ops->unmap_pages(ops, gpuaddr, PAGE_SIZE,
- size >> PAGE_SHIFT, NULL);
- if (unmapped != size)
- return -EINVAL;
- /*
- * Skip below logic for 6.1 kernel version and above as
- * qcom_skip_tlb_management() API takes care of avoiding
- * TLB operations during slumber.
- */
- flush:
- if (KERNEL_VERSION(6, 1, 0) > LINUX_VERSION_CODE) {
- struct kgsl_device *device = KGSL_MMU_DEVICE(pt->base.mmu);
- /* Skip TLB Operations if GPU is in slumber */
- if (mutex_trylock(&device->mutex)) {
- if (device->state == KGSL_STATE_SLUMBER) {
- mutex_unlock(&device->mutex);
- return 0;
- }
- mutex_unlock(&device->mutex);
- }
- }
- kgsl_iommu_flush_tlb(pt->base.mmu);
- return 0;
- }
- static size_t _iopgtbl_map_sg(struct kgsl_iommu_pt *pt, u64 gpuaddr,
- struct sg_table *sgt, int prot)
- {
- struct io_pgtable_ops *ops = pt->pgtbl_ops;
- struct scatterlist *sg;
- size_t mapped = 0;
- u64 addr = gpuaddr;
- int ret, i;
- #if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
- if (IS_ENABLED(CONFIG_IOMMU_IO_PGTABLE_LPAE)) {
- ret = qcom_arm_lpae_map_sg(ops, addr, sgt->sgl, sgt->nents, prot,
- GFP_KERNEL, &mapped);
- #else
- if (ops->map_sg) {
- ret = ops->map_sg(ops, addr, sgt->sgl, sgt->nents, prot,
- GFP_KERNEL, &mapped);
- #endif
- if (ret) {
- _iopgtbl_unmap(pt, gpuaddr, mapped);
- return 0;
- }
- return mapped;
- }
- for_each_sg(sgt->sgl, sg, sgt->nents, i) {
- size_t size = sg->length, map_size = 0;
- phys_addr_t phys = sg_phys(sg);
- ret = ops->map_pages(ops, addr, phys, PAGE_SIZE, size >> PAGE_SHIFT,
- prot, GFP_KERNEL, &map_size);
- if (ret) {
- _iopgtbl_unmap(pt, gpuaddr, mapped);
- return 0;
- }
- addr += size;
- mapped += map_size;
- }
- return mapped;
- }
- static void kgsl_iommu_send_tlb_hint(struct kgsl_mmu *mmu, bool hint)
- {
- struct kgsl_device *device = KGSL_MMU_DEVICE(mmu);
- #if (KERNEL_VERSION(6, 1, 0) <= LINUX_VERSION_CODE)
- struct kgsl_iommu *iommu = &mmu->iommu;
- /*
- * Send hint to SMMU driver for skipping TLB operations during slumber.
- * This will help to avoid unnecessary cx gdsc toggling.
- */
- qcom_skip_tlb_management(&iommu->user_context.pdev->dev, hint);
- if (iommu->lpac_context.domain)
- qcom_skip_tlb_management(&iommu->lpac_context.pdev->dev, hint);
- #endif
- /*
- * TLB operations are skipped during slumber. Incase CX doesn't
- * go down, it can result in incorrect translations due to stale
- * TLB entries. Flush TLB before boot up to ensure fresh start.
- */
- if (!hint)
- kgsl_iommu_flush_tlb(mmu);
- /* TLB hint for GMU domain */
- gmu_core_send_tlb_hint(device, hint);
- }
- static int
- kgsl_iopgtbl_map_child(struct kgsl_pagetable *pt, struct kgsl_memdesc *memdesc,
- u64 offset, struct kgsl_memdesc *child, u64 child_offset, u64 length)
- {
- struct kgsl_iommu_pt *iommu_pt = to_iommu_pt(pt);
- struct sg_table sgt;
- u32 flags;
- size_t mapped;
- int ret;
- ret = get_sg_from_child(&sgt, child, child_offset, length);
- if (ret)
- return ret;
- /* Inherit the flags from the child for this mapping */
- flags = _iommu_get_protection_flags(pt->mmu, child);
- mapped = _iopgtbl_map_sg(iommu_pt, memdesc->gpuaddr + offset, &sgt, flags);
- sg_free_table(&sgt);
- return mapped ? 0 : -ENOMEM;
- }
- static int
- kgsl_iopgtbl_unmap_range(struct kgsl_pagetable *pt, struct kgsl_memdesc *memdesc,
- u64 offset, u64 length)
- {
- if (WARN_ON(offset >= memdesc->size ||
- (offset + length) > memdesc->size))
- return -ERANGE;
- return _iopgtbl_unmap(to_iommu_pt(pt), memdesc->gpuaddr + offset,
- length);
- }
- static size_t _iopgtbl_map_page_to_range(struct kgsl_iommu_pt *pt,
- struct page *page, u64 gpuaddr, size_t range, int prot)
- {
- struct io_pgtable_ops *ops = pt->pgtbl_ops;
- size_t mapped = 0, map_size = 0;
- u64 addr = gpuaddr;
- int ret;
- while (range) {
- ret = ops->map_pages(ops, addr, page_to_phys(page), PAGE_SIZE,
- 1, prot, GFP_KERNEL, &map_size);
- if (ret) {
- _iopgtbl_unmap(pt, gpuaddr, mapped);
- return 0;
- }
- mapped += map_size;
- addr += PAGE_SIZE;
- range -= PAGE_SIZE;
- }
- return mapped;
- }
- static int kgsl_iopgtbl_map_zero_page_to_range(struct kgsl_pagetable *pt,
- struct kgsl_memdesc *memdesc, u64 offset, u64 length)
- {
- /*
- * The SMMU only does the PRT compare at the bottom level of the page table, because
- * there is not an easy way for the hardware to perform this check at earlier levels.
- * Mark this page writable to avoid page faults while writing to it. Since the address
- * of this zero page is programmed in PRR register, MMU will intercept any accesses to
- * the page before they go to DDR and will terminate the transaction.
- */
- u32 flags = IOMMU_READ | IOMMU_WRITE | IOMMU_NOEXEC | get_llcc_flags(pt->mmu);
- struct kgsl_iommu_pt *iommu_pt = to_iommu_pt(pt);
- struct page *page = kgsl_vbo_zero_page;
- if (WARN_ON(!page))
- return -ENODEV;
- if (WARN_ON((offset >= memdesc->size) ||
- (offset + length) > memdesc->size))
- return -ERANGE;
- if (!_iopgtbl_map_page_to_range(iommu_pt, page, memdesc->gpuaddr + offset,
- length, flags))
- return -ENOMEM;
- return 0;
- }
- static int kgsl_iopgtbl_map(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc)
- {
- struct kgsl_iommu_pt *pt = to_iommu_pt(pagetable);
- size_t mapped, padding;
- int prot;
- /* Get the protection flags for the user context */
- prot = _iommu_get_protection_flags(pagetable->mmu, memdesc);
- if (!memdesc->sgt) {
- struct sg_table sgt;
- int ret;
- ret = sg_alloc_table_from_pages(&sgt, memdesc->pages,
- memdesc->page_count, 0, memdesc->size, GFP_KERNEL);
- if (ret)
- return ret;
- mapped = _iopgtbl_map_sg(pt, memdesc->gpuaddr, &sgt, prot);
- sg_free_table(&sgt);
- } else {
- mapped = _iopgtbl_map_sg(pt, memdesc->gpuaddr, memdesc->sgt,
- prot);
- }
- if (mapped == 0)
- return -ENOMEM;
- padding = kgsl_memdesc_footprint(memdesc) - mapped;
- if (padding) {
- struct page *page = iommu_get_guard_page(memdesc);
- size_t ret;
- if (page)
- ret = _iopgtbl_map_page_to_range(pt, page,
- memdesc->gpuaddr + mapped, padding,
- prot & ~IOMMU_WRITE);
- if (!page || !ret) {
- _iopgtbl_unmap(pt, memdesc->gpuaddr, mapped);
- return -ENOMEM;
- }
- }
- return 0;
- }
- static int kgsl_iopgtbl_unmap(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc)
- {
- return _iopgtbl_unmap(to_iommu_pt(pagetable), memdesc->gpuaddr,
- kgsl_memdesc_footprint(memdesc));
- }
- static int _iommu_unmap(struct iommu_domain *domain, u64 addr, size_t size)
- {
- size_t unmapped = 0;
- if (!domain)
- return 0;
- /* Sign extend TTBR1 addresses all the way to avoid warning */
- if (addr & (1ULL << 48))
- addr |= 0xffff000000000000;
- unmapped = iommu_unmap(domain, addr, size);
- return (unmapped == size) ? 0 : -ENOMEM;
- }
- static size_t _iommu_map_page_to_range(struct iommu_domain *domain,
- struct page *page, u64 gpuaddr, size_t range, int prot)
- {
- size_t mapped = 0;
- u64 addr = gpuaddr;
- if (!page)
- return 0;
- /* Sign extend TTBR1 addresses all the way to avoid warning */
- if (gpuaddr & (1ULL << 48))
- gpuaddr |= 0xffff000000000000;
- while (range) {
- #if (KERNEL_VERSION(6, 2, 0) <= LINUX_VERSION_CODE)
- int ret = iommu_map(domain, addr, page_to_phys(page),
- PAGE_SIZE, prot, GFP_KERNEL);
- #else
- int ret = iommu_map(domain, addr, page_to_phys(page),
- PAGE_SIZE, prot);
- #endif
- if (ret) {
- iommu_unmap(domain, gpuaddr, mapped);
- return 0;
- }
- addr += PAGE_SIZE;
- mapped += PAGE_SIZE;
- range -= PAGE_SIZE;
- }
- return mapped;
- }
- static size_t _iommu_map_sg(struct iommu_domain *domain, u64 gpuaddr,
- struct sg_table *sgt, int prot)
- {
- /* Sign extend TTBR1 addresses all the way to avoid warning */
- if (gpuaddr & (1ULL << 48))
- gpuaddr |= 0xffff000000000000;
- return kgsl_mmu_map_sg(domain, gpuaddr, sgt->sgl, sgt->orig_nents, prot);
- }
- static int
- _kgsl_iommu_map(struct kgsl_mmu *mmu, struct iommu_domain *domain,
- struct kgsl_memdesc *memdesc)
- {
- int prot = _iommu_get_protection_flags(mmu, memdesc);
- size_t mapped, padding;
- int ret = 0;
- /*
- * For paged memory allocated through kgsl, memdesc->pages is not NULL.
- * Allocate sgt here just for its map operation. Contiguous memory
- * already has its sgt, so no need to allocate it here.
- */
- if (!memdesc->pages) {
- mapped = _iommu_map_sg(domain, memdesc->gpuaddr,
- memdesc->sgt, prot);
- } else {
- struct sg_table sgt;
- ret = sg_alloc_table_from_pages(&sgt, memdesc->pages,
- memdesc->page_count, 0, memdesc->size, GFP_KERNEL);
- if (ret)
- return ret;
- mapped = _iommu_map_sg(domain, memdesc->gpuaddr, &sgt, prot);
- sg_free_table(&sgt);
- }
- if (!mapped)
- return -ENOMEM;
- padding = kgsl_memdesc_footprint(memdesc) - mapped;
- if (padding) {
- struct page *page = iommu_get_guard_page(memdesc);
- size_t guard_mapped;
- if (page)
- guard_mapped = _iommu_map_page_to_range(domain, page,
- memdesc->gpuaddr + mapped, padding, prot & ~IOMMU_WRITE);
- if (!page || !guard_mapped) {
- _iommu_unmap(domain, memdesc->gpuaddr, mapped);
- ret = -ENOMEM;
- }
- }
- return ret;
- }
- static int kgsl_iommu_secure_map(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc)
- {
- struct kgsl_iommu *iommu = &pagetable->mmu->iommu;
- struct iommu_domain *domain = to_iommu_domain(&iommu->secure_context);
- return _kgsl_iommu_map(pagetable->mmu, domain, memdesc);
- }
- /*
- * Return true if the address is in the TTBR0 region. This is used for cases
- * when the "default" pagetable is used for both TTBR0 and TTBR1
- */
- static bool is_lower_address(struct kgsl_mmu *mmu, u64 addr)
- {
- return (test_bit(KGSL_MMU_IOPGTABLE, &mmu->features) &&
- addr < KGSL_IOMMU_SPLIT_TABLE_BASE);
- }
- static int _kgsl_iommu_unmap(struct iommu_domain *domain,
- struct kgsl_memdesc *memdesc)
- {
- if (memdesc->size == 0 || memdesc->gpuaddr == 0)
- return -EINVAL;
- return _iommu_unmap(domain, memdesc->gpuaddr,
- kgsl_memdesc_footprint(memdesc));
- }
- /* Map on the default pagetable and the LPAC pagetable if it exists */
- static int kgsl_iommu_default_map(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc)
- {
- struct kgsl_mmu *mmu = pagetable->mmu;
- struct kgsl_iommu *iommu = &mmu->iommu;
- struct iommu_domain *domain, *lpac;
- int ret;
- if (is_lower_address(mmu, memdesc->gpuaddr))
- return kgsl_iopgtbl_map(pagetable, memdesc);
- domain = to_iommu_domain(&iommu->user_context);
- /* Map the object to the default GPU domain */
- ret = _kgsl_iommu_map(mmu, domain, memdesc);
- /* Also map the object to the LPAC domain if it exists */
- lpac = to_iommu_domain(&iommu->lpac_context);
- if (!ret && lpac) {
- ret = _kgsl_iommu_map(mmu, lpac, memdesc);
- /* On failure, also unmap from the default domain */
- if (ret)
- _kgsl_iommu_unmap(domain, memdesc);
- }
- return ret;
- }
- static int kgsl_iommu_secure_unmap(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc)
- {
- struct kgsl_iommu *iommu = &pagetable->mmu->iommu;
- if (memdesc->size == 0 || memdesc->gpuaddr == 0)
- return -EINVAL;
- return _kgsl_iommu_unmap(to_iommu_domain(&iommu->secure_context),
- memdesc);
- }
- static int kgsl_iommu_default_unmap(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc)
- {
- struct kgsl_mmu *mmu = pagetable->mmu;
- struct kgsl_iommu *iommu = &mmu->iommu;
- int ret;
- if (memdesc->size == 0 || memdesc->gpuaddr == 0)
- return -EINVAL;
- if (is_lower_address(mmu, memdesc->gpuaddr))
- return kgsl_iopgtbl_unmap(pagetable, memdesc);
- /* Unmap from the default domain */
- ret = _kgsl_iommu_unmap(to_iommu_domain(&iommu->user_context), memdesc);
- /* Unmap from the LPAC domain if it exists */
- ret |= _kgsl_iommu_unmap(to_iommu_domain(&iommu->lpac_context), memdesc);
- return ret;
- }
- static bool kgsl_iommu_addr_is_global(struct kgsl_mmu *mmu, u64 addr)
- {
- if (test_bit(KGSL_MMU_IOPGTABLE, &mmu->features))
- return (addr >= KGSL_IOMMU_SPLIT_TABLE_BASE);
- return ((addr >= KGSL_IOMMU_GLOBAL_MEM_BASE(mmu)) &&
- (addr < KGSL_IOMMU_GLOBAL_MEM_BASE(mmu) +
- KGSL_IOMMU_GLOBAL_MEM_SIZE));
- }
- static void __iomem *kgsl_iommu_reg(struct kgsl_iommu_context *ctx,
- u32 offset)
- {
- struct kgsl_iommu *iommu = KGSL_IOMMU(ctx->kgsldev);
- if (!iommu->cb0_offset) {
- u32 reg =
- readl_relaxed(iommu->regbase + KGSL_IOMMU_IDR1_OFFSET);
- iommu->pagesize =
- FIELD_GET(IDR1_PAGESIZE, reg) ? SZ_64K : SZ_4K;
- /*
- * The number of pages in the global address space or
- * translation bank address space is 2^(NUMPAGENDXB + 1).
- */
- iommu->cb0_offset = iommu->pagesize *
- (1 << (FIELD_GET(IDR1_NUMPAGENDXB, reg) + 1));
- }
- return (void __iomem *) (iommu->regbase + iommu->cb0_offset +
- (ctx->cb_num * iommu->pagesize) + offset);
- }
- static u64 KGSL_IOMMU_GET_CTX_REG_Q(struct kgsl_iommu_context *ctx, u32 offset)
- {
- void __iomem *addr = kgsl_iommu_reg(ctx, offset);
- return readq_relaxed(addr);
- }
- static void KGSL_IOMMU_SET_CTX_REG(struct kgsl_iommu_context *ctx, u32 offset,
- u32 val)
- {
- void __iomem *addr = kgsl_iommu_reg(ctx, offset);
- writel_relaxed(val, addr);
- }
- static u32 KGSL_IOMMU_GET_CTX_REG(struct kgsl_iommu_context *ctx, u32 offset)
- {
- void __iomem *addr = kgsl_iommu_reg(ctx, offset);
- return readl_relaxed(addr);
- }
- static int kgsl_iommu_get_gpuaddr(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc);
- static void kgsl_iommu_map_secure_global(struct kgsl_mmu *mmu,
- struct kgsl_memdesc *memdesc)
- {
- if (IS_ERR_OR_NULL(mmu->securepagetable))
- return;
- if (!memdesc->gpuaddr) {
- int ret = kgsl_iommu_get_gpuaddr(mmu->securepagetable,
- memdesc);
- if (WARN_ON(ret))
- return;
- }
- kgsl_iommu_secure_map(mmu->securepagetable, memdesc);
- }
- #define KGSL_GLOBAL_MEM_PAGES (KGSL_IOMMU_GLOBAL_MEM_SIZE >> PAGE_SHIFT)
- static u64 global_get_offset(struct kgsl_device *device, u64 size,
- unsigned long priv)
- {
- int start = 0, bit;
- if (!device->global_map) {
- device->global_map =
- kcalloc(BITS_TO_LONGS(KGSL_GLOBAL_MEM_PAGES),
- sizeof(unsigned long), GFP_KERNEL);
- if (!device->global_map)
- return (unsigned long) -ENOMEM;
- }
- if (priv & KGSL_MEMDESC_RANDOM) {
- u32 offset = KGSL_GLOBAL_MEM_PAGES - (size >> PAGE_SHIFT);
- start = get_random_u32() % offset;
- }
- while (start >= 0) {
- bit = bitmap_find_next_zero_area(device->global_map,
- KGSL_GLOBAL_MEM_PAGES, start, size >> PAGE_SHIFT, 0);
- if (bit < KGSL_GLOBAL_MEM_PAGES)
- break;
- /*
- * Later implementations might want to randomize this to reduce
- * predictability
- */
- start--;
- }
- if (WARN_ON(start < 0))
- return (unsigned long) -ENOMEM;
- bitmap_set(device->global_map, bit, size >> PAGE_SHIFT);
- return bit << PAGE_SHIFT;
- }
- static void kgsl_iommu_map_global(struct kgsl_mmu *mmu,
- struct kgsl_memdesc *memdesc, u32 padding)
- {
- struct kgsl_device *device = KGSL_MMU_DEVICE(mmu);
- if (memdesc->flags & KGSL_MEMFLAGS_SECURE) {
- kgsl_iommu_map_secure_global(mmu, memdesc);
- return;
- }
- if (!memdesc->gpuaddr) {
- u64 offset;
- offset = global_get_offset(device, memdesc->size + padding,
- memdesc->priv);
- if (IS_ERR_VALUE(offset))
- return;
- memdesc->gpuaddr = mmu->defaultpagetable->global_base + offset;
- }
- kgsl_iommu_default_map(mmu->defaultpagetable, memdesc);
- }
- /* Print the mem entry for the pagefault debugging */
- static void print_entry(struct device *dev, struct kgsl_mem_entry *entry,
- pid_t pid)
- {
- char name[32];
- if (!entry) {
- dev_crit(dev, "**EMPTY**\n");
- return;
- }
- kgsl_get_memory_usage(name, sizeof(name), entry->memdesc.flags);
- dev_err(dev, "[%016llX - %016llX] %s %s (pid = %d) (%s)\n",
- entry->memdesc.gpuaddr,
- entry->memdesc.gpuaddr + entry->memdesc.size - 1,
- entry->memdesc.priv & KGSL_MEMDESC_GUARD_PAGE ? "(+guard)" : "",
- entry->pending_free ? "(pending free)" : "",
- pid, name);
- }
- /* Check if the address in the list of recently freed memory */
- static void kgsl_iommu_check_if_freed(struct device *dev,
- struct kgsl_iommu_context *context, u64 addr, u32 ptname)
- {
- uint64_t gpuaddr = addr;
- uint64_t size = 0;
- uint64_t flags = 0;
- char name[32];
- pid_t pid;
- if (!kgsl_memfree_find_entry(ptname, &gpuaddr, &size, &flags, &pid))
- return;
- kgsl_get_memory_usage(name, sizeof(name), flags);
- dev_err(dev, "---- premature free ----\n");
- dev_err(dev, "[%8.8llX-%8.8llX] (%s) was already freed by pid %d\n",
- gpuaddr, gpuaddr + size, name, pid);
- }
- static struct kgsl_process_private *kgsl_iommu_get_process(u64 ptbase)
- {
- struct kgsl_process_private *p;
- struct kgsl_iommu_pt *iommu_pt;
- read_lock(&kgsl_driver.proclist_lock);
- list_for_each_entry(p, &kgsl_driver.process_list, list) {
- iommu_pt = to_iommu_pt(p->pagetable);
- if (iommu_pt->ttbr0 == MMU_SW_PT_BASE(ptbase)) {
- if (!kgsl_process_private_get(p))
- p = NULL;
- read_unlock(&kgsl_driver.proclist_lock);
- return p;
- }
- }
- read_unlock(&kgsl_driver.proclist_lock);
- return NULL;
- }
- static void kgsl_iommu_add_fault_info(struct kgsl_context *context,
- unsigned long addr, int flags)
- {
- struct kgsl_pagefault_report *report;
- u32 fault_flag = 0;
- if (!context || !(context->flags & KGSL_CONTEXT_FAULT_INFO))
- return;
- report = kzalloc(sizeof(struct kgsl_pagefault_report), GFP_KERNEL);
- if (!report)
- return;
- if (flags & IOMMU_FAULT_TRANSLATION)
- fault_flag = KGSL_PAGEFAULT_TYPE_TRANSLATION;
- else if (flags & IOMMU_FAULT_PERMISSION)
- fault_flag = KGSL_PAGEFAULT_TYPE_PERMISSION;
- else if (flags & IOMMU_FAULT_EXTERNAL)
- fault_flag = KGSL_PAGEFAULT_TYPE_EXTERNAL;
- else if (flags & IOMMU_FAULT_TRANSACTION_STALLED)
- fault_flag = KGSL_PAGEFAULT_TYPE_TRANSACTION_STALLED;
- fault_flag |= (flags & IOMMU_FAULT_WRITE) ? KGSL_PAGEFAULT_TYPE_WRITE :
- KGSL_PAGEFAULT_TYPE_READ;
- report->fault_addr = addr;
- report->fault_type = fault_flag;
- if (kgsl_add_fault(context, KGSL_FAULT_TYPE_PAGEFAULT, report))
- kfree(report);
- }
- static void kgsl_iommu_print_fault(struct kgsl_mmu *mmu,
- struct kgsl_iommu_context *ctxt, unsigned long addr,
- u64 ptbase, u32 contextid,
- int flags, struct kgsl_process_private *private,
- struct kgsl_context *context)
- {
- struct kgsl_device *device = KGSL_MMU_DEVICE(mmu);
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- const struct adreno_gpudev *gpudev = ADRENO_GPU_DEVICE(adreno_dev);
- struct kgsl_mem_entry *prev = NULL, *next = NULL, *entry;
- const char *fault_type = NULL;
- const char *comm = NULL;
- u32 ptname = KGSL_MMU_GLOBAL_PT;
- int id;
- if (private) {
- comm = private->comm;
- ptname = pid_nr(private->pid);
- }
- trace_kgsl_mmu_pagefault(device, addr,
- ptname, comm,
- (flags & IOMMU_FAULT_WRITE) ? "write" : "read");
- if (flags & IOMMU_FAULT_TRANSLATION)
- fault_type = "translation";
- else if (flags & IOMMU_FAULT_PERMISSION)
- fault_type = "permission";
- else if (flags & IOMMU_FAULT_EXTERNAL)
- fault_type = "external";
- else if (flags & IOMMU_FAULT_TRANSACTION_STALLED)
- fault_type = "transaction stalled";
- else
- fault_type = "unknown";
- /* FIXME: This seems buggy */
- if (test_bit(KGSL_FT_PAGEFAULT_LOG_ONE_PER_PAGE, &mmu->pfpolicy))
- if (!kgsl_mmu_log_fault_addr(mmu, ptbase, addr))
- return;
- if (!__ratelimit(&ctxt->ratelimit))
- return;
- dev_crit(device->dev,
- "GPU PAGE FAULT: addr = %lX pid= %d name=%s drawctxt=%d context pid = %d\n", addr,
- ptname, comm, contextid, context ? context->tid : 0);
- dev_crit(device->dev,
- "context=%s TTBR0=0x%llx (%s %s fault)\n",
- ctxt->name, ptbase,
- (flags & IOMMU_FAULT_WRITE) ? "write" : "read", fault_type);
- if (gpudev->iommu_fault_block) {
- u32 fsynr1 = KGSL_IOMMU_GET_CTX_REG(ctxt,
- KGSL_IOMMU_CTX_FSYNR1);
- dev_crit(device->dev,
- "FAULTING BLOCK: %s\n",
- gpudev->iommu_fault_block(device, fsynr1));
- }
- /* Don't print the debug if this is a permissions fault */
- if ((flags & IOMMU_FAULT_PERMISSION))
- return;
- kgsl_iommu_check_if_freed(device->dev, ctxt, addr, ptname);
- /*
- * Don't print any debug information if the address is
- * in the global region. These are rare and nobody needs
- * to know the addresses that are in here
- */
- if (kgsl_iommu_addr_is_global(mmu, addr)) {
- dev_crit(device->dev, "Fault in global memory\n");
- return;
- }
- if (!private)
- return;
- dev_crit(device->dev, "---- nearby memory ----\n");
- spin_lock(&private->mem_lock);
- idr_for_each_entry(&private->mem_idr, entry, id) {
- u64 cur = entry->memdesc.gpuaddr;
- if (cur < addr) {
- if (!prev || prev->memdesc.gpuaddr < cur)
- prev = entry;
- }
- if (cur > addr) {
- if (!next || next->memdesc.gpuaddr > cur)
- next = entry;
- }
- }
- print_entry(device->dev, prev, pid_nr(private->pid));
- dev_crit(device->dev, "<- fault @ %16.16lx\n", addr);
- print_entry(device->dev, next, pid_nr(private->pid));
- spin_unlock(&private->mem_lock);
- }
- /*
- * Return true if the IOMMU should stall and trigger a snapshot on a pagefault
- */
- static bool kgsl_iommu_check_stall_on_fault(struct kgsl_iommu_context *ctx,
- struct kgsl_mmu *mmu, int flags)
- {
- struct kgsl_device *device = KGSL_MMU_DEVICE(mmu);
- if (!(flags & IOMMU_FAULT_TRANSACTION_STALLED))
- return false;
- if (!test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE, &mmu->pfpolicy))
- return false;
- if (test_bit(KGSL_MMU_PAGEFAULT_TERMINATE, &mmu->features))
- return false;
- /*
- * Sometimes, there can be multiple invocations of the fault handler.
- * Make sure we trigger reset/recovery only once.
- */
- if (ctx->stalled_on_fault)
- return false;
- if (!mutex_trylock(&device->mutex))
- return true;
- /*
- * Turn off GPU IRQ so we don't get faults from it too.
- * The device mutex must be held to change power state
- */
- if (gmu_core_isenabled(device))
- kgsl_pwrctrl_irq(device, false);
- else
- kgsl_pwrctrl_change_state(device, KGSL_STATE_AWARE);
- mutex_unlock(&device->mutex);
- return true;
- }
- static int kgsl_iommu_fault_handler(struct kgsl_mmu *mmu,
- struct kgsl_iommu_context *ctx, unsigned long addr, int flags)
- {
- struct kgsl_device *device = KGSL_MMU_DEVICE(mmu);
- u64 ptbase;
- u32 contextidr;
- bool stall;
- struct kgsl_process_private *private;
- struct kgsl_context *context;
- ptbase = KGSL_IOMMU_GET_CTX_REG_Q(ctx, KGSL_IOMMU_CTX_TTBR0);
- contextidr = KGSL_IOMMU_GET_CTX_REG(ctx, KGSL_IOMMU_CTX_CONTEXTIDR);
- private = kgsl_iommu_get_process(ptbase);
- context = kgsl_context_get(device, contextidr);
- stall = kgsl_iommu_check_stall_on_fault(ctx, mmu, flags);
- kgsl_iommu_print_fault(mmu, ctx, addr, ptbase, contextidr, flags, private,
- context);
- kgsl_iommu_add_fault_info(context, addr, flags);
- if (stall) {
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- u32 sctlr;
- /*
- * Disable context fault interrupts as we do not clear FSR in
- * the ISR. Will be re-enabled after FSR is cleared.
- */
- sctlr = KGSL_IOMMU_GET_CTX_REG(ctx, KGSL_IOMMU_CTX_SCTLR);
- sctlr &= ~(0x1 << KGSL_IOMMU_SCTLR_CFIE_SHIFT);
- KGSL_IOMMU_SET_CTX_REG(ctx, KGSL_IOMMU_CTX_SCTLR, sctlr);
- /* This is used by reset/recovery path */
- ctx->stalled_on_fault = true;
- /* Go ahead with recovery*/
- if (adreno_dev->dispatch_ops && adreno_dev->dispatch_ops->fault)
- adreno_dev->dispatch_ops->fault(adreno_dev,
- ADRENO_IOMMU_PAGE_FAULT);
- }
- kgsl_context_put(context);
- kgsl_process_private_put(private);
- /* Return -EBUSY to keep the IOMMU driver from resuming on a stall */
- return stall ? -EBUSY : 0;
- }
- static int kgsl_iommu_default_fault_handler(struct iommu_domain *domain,
- struct device *dev, unsigned long addr, int flags, void *token)
- {
- struct kgsl_mmu *mmu = token;
- struct kgsl_iommu *iommu = &mmu->iommu;
- return kgsl_iommu_fault_handler(mmu, &iommu->user_context,
- addr, flags);
- }
- static int kgsl_iommu_lpac_fault_handler(struct iommu_domain *domain,
- struct device *dev, unsigned long addr, int flags, void *token)
- {
- struct kgsl_mmu *mmu = token;
- struct kgsl_iommu *iommu = &mmu->iommu;
- struct kgsl_iommu_context *ctx = &iommu->lpac_context;
- u32 fsynr0, fsynr1;
- fsynr0 = KGSL_IOMMU_GET_CTX_REG(ctx, KGSL_IOMMU_CTX_FSYNR0);
- fsynr1 = KGSL_IOMMU_GET_CTX_REG(ctx, KGSL_IOMMU_CTX_FSYNR1);
- dev_crit(KGSL_MMU_DEVICE(mmu)->dev,
- "LPAC PAGE FAULT iova=0x%16lx, fsynr0=0x%x, fsynr1=0x%x\n",
- addr, fsynr0, fsynr1);
- return kgsl_iommu_fault_handler(mmu, &iommu->lpac_context,
- addr, flags);
- }
- static int kgsl_iommu_secure_fault_handler(struct iommu_domain *domain,
- struct device *dev, unsigned long addr, int flags, void *token)
- {
- struct kgsl_mmu *mmu = token;
- struct kgsl_iommu *iommu = &mmu->iommu;
- return kgsl_iommu_fault_handler(mmu, &iommu->secure_context,
- addr, flags);
- }
- /*
- * kgsl_iommu_disable_clk() - Disable iommu clocks
- * Disable IOMMU clocks
- */
- static void kgsl_iommu_disable_clk(struct kgsl_mmu *mmu)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- atomic_dec(&iommu->clk_enable_count);
- /*
- * Make sure the clk refcounts are good. An unbalance may
- * cause the clocks to be off when we need them on.
- */
- WARN_ON(atomic_read(&iommu->clk_enable_count) < 0);
- clk_bulk_disable_unprepare(iommu->num_clks, iommu->clks);
- if (!IS_ERR_OR_NULL(iommu->cx_gdsc))
- regulator_disable(iommu->cx_gdsc);
- }
- /*
- * kgsl_iommu_enable_clk - Enable iommu clocks
- * Enable all the IOMMU clocks
- */
- static void kgsl_iommu_enable_clk(struct kgsl_mmu *mmu)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- if (!IS_ERR_OR_NULL(iommu->cx_gdsc))
- WARN_ON(regulator_enable(iommu->cx_gdsc));
- WARN_ON(clk_bulk_prepare_enable(iommu->num_clks, iommu->clks));
- atomic_inc(&iommu->clk_enable_count);
- }
- /* kgsl_iommu_get_ttbr0 - Get TTBR0 setting for a pagetable */
- static u64 kgsl_iommu_get_ttbr0(struct kgsl_pagetable *pagetable)
- {
- struct kgsl_iommu_pt *pt = to_iommu_pt(pagetable);
- /* This will be zero if KGSL_MMU_IOPGTABLE is not enabled */
- return pt->ttbr0;
- }
- /* Set TTBR0 for the given context with the specific configuration */
- static void kgsl_iommu_set_ttbr0(struct kgsl_iommu_context *context,
- struct kgsl_mmu *mmu, const struct io_pgtable_cfg *pgtbl_cfg)
- {
- struct adreno_smmu_priv *adreno_smmu;
- /* Quietly return if the context doesn't have a domain */
- if (!context->domain)
- return;
- adreno_smmu = dev_get_drvdata(&context->pdev->dev);
- /* Enable CX and clocks before we call into SMMU to setup registers */
- kgsl_iommu_enable_clk(mmu);
- adreno_smmu->set_ttbr0_cfg(adreno_smmu->cookie, pgtbl_cfg);
- kgsl_iommu_disable_clk(mmu);
- }
- static int kgsl_iommu_get_context_bank(struct kgsl_pagetable *pt, struct kgsl_context *context)
- {
- struct kgsl_iommu *iommu = to_kgsl_iommu(pt);
- struct iommu_domain *domain;
- if (kgsl_context_is_lpac(context))
- domain = to_iommu_domain(&iommu->lpac_context);
- else
- domain = to_iommu_domain(&iommu->user_context);
- return qcom_iommu_get_context_bank_nr(domain);
- }
- static int kgsl_iommu_get_asid(struct kgsl_pagetable *pt, struct kgsl_context *context)
- {
- struct kgsl_iommu *iommu = to_kgsl_iommu(pt);
- struct iommu_domain *domain;
- if (kgsl_context_is_lpac(context))
- domain = to_iommu_domain(&iommu->lpac_context);
- else
- domain = to_iommu_domain(&iommu->user_context);
- return qcom_iommu_get_asid_nr(domain);
- }
- static void kgsl_iommu_destroy_default_pagetable(struct kgsl_pagetable *pagetable)
- {
- struct kgsl_device *device = KGSL_MMU_DEVICE(pagetable->mmu);
- struct kgsl_iommu_pt *pt = to_iommu_pt(pagetable);
- struct kgsl_global_memdesc *md;
- list_for_each_entry(md, &device->globals, node) {
- if (md->memdesc.flags & KGSL_MEMFLAGS_SECURE)
- continue;
- kgsl_iommu_default_unmap(pagetable, &md->memdesc);
- }
- kfree(pt);
- }
- static void kgsl_iommu_destroy_pagetable(struct kgsl_pagetable *pagetable)
- {
- struct kgsl_iommu_pt *pt = to_iommu_pt(pagetable);
- qcom_free_io_pgtable_ops(pt->pgtbl_ops);
- kfree(pt);
- }
- static void _enable_gpuhtw_llc(struct kgsl_mmu *mmu, struct iommu_domain *domain)
- {
- if (!test_bit(KGSL_MMU_LLCC_ENABLE, &mmu->features))
- return;
- if (mmu->subtype == KGSL_IOMMU_SMMU_V500) {
- if (!test_bit(KGSL_MMU_IO_COHERENT, &mmu->features))
- iommu_set_pgtable_quirks(domain,
- IO_PGTABLE_QUIRK_QCOM_USE_LLC_NWA);
- } else
- iommu_set_pgtable_quirks(domain, IO_PGTABLE_QUIRK_ARM_OUTER_WBWA);
- }
- int kgsl_set_smmu_aperture(struct kgsl_device *device,
- struct kgsl_iommu_context *context)
- {
- int ret;
- if (!test_bit(KGSL_MMU_SMMU_APERTURE, &device->mmu.features))
- return 0;
- ret = qcom_scm_kgsl_set_smmu_aperture(context->cb_num);
- if (ret == -EBUSY)
- ret = qcom_scm_kgsl_set_smmu_aperture(context->cb_num);
- if (ret)
- dev_err(&device->pdev->dev, "Unable to set the SMMU aperture: %d. The aperture needs to be set to use per-process pagetables\n",
- ret);
- return ret;
- }
- int kgsl_set_smmu_lpac_aperture(struct kgsl_device *device,
- struct kgsl_iommu_context *context)
- {
- int ret;
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- if (!test_bit(KGSL_MMU_SMMU_APERTURE, &device->mmu.features) ||
- !ADRENO_FEATURE(adreno_dev, ADRENO_LPAC))
- return 0;
- ret = qcom_scm_kgsl_set_smmu_lpac_aperture(context->cb_num);
- if (ret == -EBUSY)
- ret = qcom_scm_kgsl_set_smmu_lpac_aperture(context->cb_num);
- if (ret)
- dev_err(&device->pdev->dev, "Unable to set the LPAC SMMU aperture: %d. The aperture needs to be set to use per-process pagetables\n",
- ret);
- return ret;
- }
- /* FIXME: better name feor this function */
- static int kgsl_iopgtbl_alloc(struct kgsl_iommu_context *ctx, struct kgsl_iommu_pt *pt)
- {
- struct adreno_smmu_priv *adreno_smmu = dev_get_drvdata(&ctx->pdev->dev);
- const struct io_pgtable_cfg *cfg = NULL;
- void *domain = (void *)adreno_smmu->cookie;
- if (adreno_smmu->cookie)
- cfg = adreno_smmu->get_ttbr1_cfg(adreno_smmu->cookie);
- if (!cfg)
- return -ENODEV;
- pt->info = adreno_smmu->pgtbl_info;
- pt->info.cfg = *cfg;
- pt->info.cfg.quirks &= ~IO_PGTABLE_QUIRK_ARM_TTBR1;
- pt->info.cfg.tlb = &kgsl_iopgtbl_tlb_ops;
- pt->pgtbl_ops = qcom_alloc_io_pgtable_ops(QCOM_ARM_64_LPAE_S1, &pt->info, domain);
- if (!pt->pgtbl_ops)
- return -ENOMEM;
- pt->ttbr0 = pt->info.cfg.arm_lpae_s1_cfg.ttbr;
- return 0;
- }
- static struct kgsl_pagetable *kgsl_iommu_default_pagetable(struct kgsl_mmu *mmu)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- struct kgsl_iommu_pt *iommu_pt;
- int ret;
- iommu_pt = kzalloc(sizeof(*iommu_pt), GFP_KERNEL);
- if (!iommu_pt)
- return ERR_PTR(-ENOMEM);
- kgsl_mmu_pagetable_init(mmu, &iommu_pt->base, KGSL_MMU_GLOBAL_PT);
- iommu_pt->base.fault_addr = U64_MAX;
- iommu_pt->base.rbtree = RB_ROOT;
- iommu_pt->base.pt_ops = &default_pt_ops;
- if (test_bit(KGSL_MMU_64BIT, &mmu->features)) {
- iommu_pt->base.compat_va_start = KGSL_IOMMU_SVM_BASE32(mmu);
- iommu_pt->base.compat_va_end = KGSL_IOMMU_SVM_END32(mmu);
- iommu_pt->base.va_start = KGSL_IOMMU_VA_BASE64;
- iommu_pt->base.va_end = KGSL_IOMMU_VA_END64;
- } else {
- iommu_pt->base.va_start = KGSL_IOMMU_SVM_BASE32(mmu);
- iommu_pt->base.va_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu);
- iommu_pt->base.compat_va_start = iommu_pt->base.va_start;
- iommu_pt->base.compat_va_end = iommu_pt->base.va_end;
- }
- if (!test_bit(KGSL_MMU_IOPGTABLE, &mmu->features)) {
- iommu_pt->base.global_base = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu);
- kgsl_mmu_pagetable_add(mmu, &iommu_pt->base);
- return &iommu_pt->base;
- }
- iommu_pt->base.global_base = KGSL_IOMMU_SPLIT_TABLE_BASE;
- /*
- * Set up a "default' TTBR0 for the pagetable - this would only be used
- * in cases when the per-process pagetable allocation failed for some
- * reason
- */
- ret = kgsl_iopgtbl_alloc(&iommu->user_context, iommu_pt);
- if (ret) {
- kfree(iommu_pt);
- return ERR_PTR(ret);
- }
- kgsl_mmu_pagetable_add(mmu, &iommu_pt->base);
- return &iommu_pt->base;
- }
- #if IS_ENABLED(CONFIG_QCOM_SECURE_BUFFER)
- static struct kgsl_pagetable *kgsl_iommu_secure_pagetable(struct kgsl_mmu *mmu)
- {
- struct kgsl_iommu_pt *iommu_pt;
- if (!mmu->secured)
- return ERR_PTR(-EPERM);
- iommu_pt = kzalloc(sizeof(*iommu_pt), GFP_KERNEL);
- if (!iommu_pt)
- return ERR_PTR(-ENOMEM);
- kgsl_mmu_pagetable_init(mmu, &iommu_pt->base, KGSL_MMU_SECURE_PT);
- iommu_pt->base.fault_addr = U64_MAX;
- iommu_pt->base.rbtree = RB_ROOT;
- iommu_pt->base.pt_ops = &secure_pt_ops;
- iommu_pt->base.compat_va_start = KGSL_IOMMU_SECURE_BASE32;
- iommu_pt->base.compat_va_end = KGSL_IOMMU_SECURE_END32;
- iommu_pt->base.va_start = KGSL_IOMMU_SECURE_BASE(mmu);
- iommu_pt->base.va_end = KGSL_IOMMU_SECURE_END(mmu);
- kgsl_mmu_pagetable_add(mmu, &iommu_pt->base);
- return &iommu_pt->base;
- }
- #else
- static struct kgsl_pagetable *kgsl_iommu_secure_pagetable(struct kgsl_mmu *mmu)
- {
- return ERR_PTR(-EPERM);
- }
- #endif
- static struct kgsl_pagetable *kgsl_iopgtbl_pagetable(struct kgsl_mmu *mmu, u32 name)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- struct kgsl_iommu_pt *pt;
- int ret;
- pt = kzalloc(sizeof(*pt), GFP_KERNEL);
- if (!pt)
- return ERR_PTR(-ENOMEM);
- kgsl_mmu_pagetable_init(mmu, &pt->base, name);
- pt->base.fault_addr = U64_MAX;
- pt->base.rbtree = RB_ROOT;
- pt->base.pt_ops = &iopgtbl_pt_ops;
- if (test_bit(KGSL_MMU_64BIT, &mmu->features)) {
- pt->base.compat_va_start = KGSL_IOMMU_SVM_BASE32(mmu);
- pt->base.compat_va_end = KGSL_IOMMU_SVM_END32(mmu);
- pt->base.va_start = KGSL_IOMMU_VA_BASE64;
- pt->base.va_end = KGSL_IOMMU_VA_END64;
- if (is_compat_task()) {
- pt->base.svm_start = KGSL_IOMMU_SVM_BASE32(mmu);
- pt->base.svm_end = KGSL_IOMMU_SVM_END32(mmu);
- } else {
- pt->base.svm_start = KGSL_IOMMU_SVM_BASE64;
- pt->base.svm_end = KGSL_IOMMU_SVM_END64;
- }
- } else {
- pt->base.va_start = KGSL_IOMMU_SVM_BASE32(mmu);
- pt->base.va_end = KGSL_IOMMU_GLOBAL_MEM_BASE(mmu);
- pt->base.compat_va_start = pt->base.va_start;
- pt->base.compat_va_end = pt->base.va_end;
- pt->base.svm_start = KGSL_IOMMU_SVM_BASE32(mmu);
- pt->base.svm_end = KGSL_IOMMU_SVM_END32(mmu);
- }
- /*
- * We expect the 64-bit SVM and non-SVM ranges not to overlap so that
- * va_hint points to VA space at the top of the 64-bit non-SVM range.
- */
- BUILD_BUG_ON_MSG(!((KGSL_IOMMU_VA_BASE64 >= KGSL_IOMMU_SVM_END64) ||
- (KGSL_IOMMU_SVM_BASE64 >= KGSL_IOMMU_VA_END64)),
- "64-bit SVM and non-SVM ranges should not overlap");
- /* Set up the hint for 64-bit non-SVM VA on per-process pagetables */
- pt->base.va_hint = pt->base.va_start;
- ret = kgsl_iopgtbl_alloc(&iommu->user_context, pt);
- if (ret) {
- kfree(pt);
- return ERR_PTR(ret);
- }
- kgsl_mmu_pagetable_add(mmu, &pt->base);
- return &pt->base;
- }
- static struct kgsl_pagetable *kgsl_iommu_getpagetable(struct kgsl_mmu *mmu,
- unsigned long name)
- {
- struct kgsl_pagetable *pt;
- /* If we already know the pagetable, return it */
- pt = kgsl_get_pagetable(name);
- if (pt)
- return pt;
- /* If io-pgtables are not in effect, just use the default pagetable */
- if (!test_bit(KGSL_MMU_IOPGTABLE, &mmu->features))
- return mmu->defaultpagetable;
- pt = kgsl_iopgtbl_pagetable(mmu, name);
- /*
- * If the io-pgtable allocation didn't work then fall back to the
- * default pagetable for this cycle
- */
- if (!pt)
- return mmu->defaultpagetable;
- return pt;
- }
- static void kgsl_iommu_detach_context(struct kgsl_iommu_context *context)
- {
- if (!context->domain)
- return;
- iommu_detach_device(context->domain, &context->pdev->dev);
- iommu_domain_free(context->domain);
- context->domain = NULL;
- platform_device_put(context->pdev);
- context->pdev = NULL;
- }
- static void kgsl_iommu_close(struct kgsl_mmu *mmu)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- /* First put away the default pagetables */
- kgsl_mmu_putpagetable(mmu->defaultpagetable);
- kgsl_mmu_putpagetable(mmu->securepagetable);
- /*
- * Flush the workqueue to ensure pagetables are
- * destroyed before proceeding further
- */
- flush_workqueue(kgsl_driver.workqueue);
- mmu->defaultpagetable = NULL;
- mmu->securepagetable = NULL;
- kgsl_iommu_set_ttbr0(&iommu->lpac_context, mmu, NULL);
- kgsl_iommu_set_ttbr0(&iommu->user_context, mmu, NULL);
- /* Next, detach the context banks */
- kgsl_iommu_detach_context(&iommu->user_context);
- kgsl_iommu_detach_context(&iommu->lpac_context);
- kgsl_iommu_detach_context(&iommu->secure_context);
- kgsl_free_secure_page(kgsl_secure_guard_page);
- kgsl_secure_guard_page = NULL;
- if (kgsl_guard_page != NULL) {
- __free_page(kgsl_guard_page);
- kgsl_guard_page = NULL;
- }
- kmem_cache_destroy(addr_entry_cache);
- addr_entry_cache = NULL;
- }
- /* Program the PRR marker and enable it in the ACTLR register */
- static void _iommu_context_set_prr(struct kgsl_mmu *mmu,
- struct kgsl_iommu_context *ctx)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- struct page *page = kgsl_vbo_zero_page;
- u32 val;
- if (ctx->cb_num < 0)
- return;
- /* Quietly return if the context doesn't have a domain */
- if (!ctx->domain)
- return;
- if (!page)
- return;
- writel_relaxed(lower_32_bits(page_to_phys(page)),
- iommu->regbase + KGSL_IOMMU_PRR_CFG_LADDR);
- writel_relaxed(upper_32_bits(page_to_phys(page)),
- iommu->regbase + KGSL_IOMMU_PRR_CFG_UADDR);
- val = KGSL_IOMMU_GET_CTX_REG(ctx, KGSL_IOMMU_CTX_ACTLR);
- val |= FIELD_PREP(KGSL_IOMMU_ACTLR_PRR_ENABLE, 1);
- KGSL_IOMMU_SET_CTX_REG(ctx, KGSL_IOMMU_CTX_ACTLR, val);
- /* Make sure all of the preceding writes have posted */
- wmb();
- }
- static void kgsl_iommu_configure_gpu_sctlr(struct kgsl_mmu *mmu,
- unsigned long pf_policy,
- struct kgsl_iommu_context *ctx)
- {
- u32 sctlr_val;
- /*
- * If pagefault policy is GPUHALT_ENABLE,
- * If terminate feature flag is enabled:
- * 1) Program CFCFG to 0 to terminate the faulting transaction
- * 2) Program HUPCF to 0 (terminate subsequent transactions
- * in the presence of an outstanding fault)
- * Else configure stall:
- * 1) Program CFCFG to 1 to enable STALL mode
- * 2) Program HUPCF to 0 (Stall subsequent
- * transactions in the presence of an outstanding fault)
- * else
- * 1) Program CFCFG to 0 to disable STALL mode (0=Terminate)
- * 2) Program HUPCF to 1 (Process subsequent transactions
- * independently of any outstanding fault)
- */
- sctlr_val = KGSL_IOMMU_GET_CTX_REG(ctx, KGSL_IOMMU_CTX_SCTLR);
- if (test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE, &pf_policy)) {
- if (test_bit(KGSL_MMU_PAGEFAULT_TERMINATE, &mmu->features)) {
- sctlr_val &= ~(0x1 << KGSL_IOMMU_SCTLR_CFCFG_SHIFT);
- sctlr_val &= ~(0x1 << KGSL_IOMMU_SCTLR_HUPCF_SHIFT);
- } else {
- sctlr_val |= (0x1 << KGSL_IOMMU_SCTLR_CFCFG_SHIFT);
- sctlr_val &= ~(0x1 << KGSL_IOMMU_SCTLR_HUPCF_SHIFT);
- }
- } else {
- sctlr_val &= ~(0x1 << KGSL_IOMMU_SCTLR_CFCFG_SHIFT);
- sctlr_val |= (0x1 << KGSL_IOMMU_SCTLR_HUPCF_SHIFT);
- }
- KGSL_IOMMU_SET_CTX_REG(ctx, KGSL_IOMMU_CTX_SCTLR, sctlr_val);
- }
- static int kgsl_iommu_start(struct kgsl_mmu *mmu)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- kgsl_iommu_enable_clk(mmu);
- /* Set the following registers only when the MMU type is QSMMU */
- if (mmu->subtype != KGSL_IOMMU_SMMU_V500) {
- /* Enable hazard check from GPU_SMMU_HUM_CFG */
- writel_relaxed(0x02, iommu->regbase + 0x6800);
- /* Write to GPU_SMMU_DORA_ORDERING to disable reordering */
- writel_relaxed(0x01, iommu->regbase + 0x64a0);
- /* make sure register write committed */
- wmb();
- }
- kgsl_iommu_configure_gpu_sctlr(mmu, mmu->pfpolicy, &iommu->user_context);
- _iommu_context_set_prr(mmu, &iommu->user_context);
- if (mmu->secured)
- _iommu_context_set_prr(mmu, &iommu->secure_context);
- if (iommu->lpac_context.domain) {
- _iommu_context_set_prr(mmu, &iommu->lpac_context);
- kgsl_iommu_configure_gpu_sctlr(mmu, mmu->pfpolicy, &iommu->lpac_context);
- }
- kgsl_iommu_disable_clk(mmu);
- return 0;
- }
- static void kgsl_iommu_context_clear_fsr(struct kgsl_mmu *mmu, struct kgsl_iommu_context *ctx)
- {
- unsigned int sctlr_val;
- if (ctx->stalled_on_fault) {
- kgsl_iommu_enable_clk(mmu);
- KGSL_IOMMU_SET_CTX_REG(ctx, KGSL_IOMMU_CTX_FSR, 0xffffffff);
- /*
- * Re-enable context fault interrupts after clearing
- * FSR to prevent the interrupt from firing repeatedly
- */
- sctlr_val = KGSL_IOMMU_GET_CTX_REG(ctx, KGSL_IOMMU_CTX_SCTLR);
- sctlr_val |= (0x1 << KGSL_IOMMU_SCTLR_CFIE_SHIFT);
- KGSL_IOMMU_SET_CTX_REG(ctx, KGSL_IOMMU_CTX_SCTLR, sctlr_val);
- /*
- * Make sure the above register writes
- * are not reordered across the barrier
- * as we use writel_relaxed to write them
- */
- wmb();
- kgsl_iommu_disable_clk(mmu);
- ctx->stalled_on_fault = false;
- }
- }
- static void kgsl_iommu_clear_fsr(struct kgsl_mmu *mmu)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- kgsl_iommu_context_clear_fsr(mmu, &iommu->user_context);
- if (iommu->lpac_context.domain)
- kgsl_iommu_context_clear_fsr(mmu, &iommu->lpac_context);
- }
- static void kgsl_iommu_context_pagefault_resume(struct kgsl_iommu *iommu, struct kgsl_iommu_context *ctx,
- bool terminate)
- {
- u32 sctlr_val = 0;
- if (!ctx->stalled_on_fault)
- return;
- if (!terminate)
- goto clear_fsr;
- sctlr_val = KGSL_IOMMU_GET_CTX_REG(ctx, KGSL_IOMMU_CTX_SCTLR);
- /*
- * As part of recovery, GBIF halt sequence should be performed.
- * In a worst case scenario, if any GPU block is generating a
- * stream of un-ending faulting transactions, SMMU would enter
- * stall-on-fault mode again after resuming and not let GBIF
- * halt succeed. In order to avoid that situation and terminate
- * those faulty transactions, set CFCFG and HUPCF to 0.
- */
- sctlr_val &= ~(0x1 << KGSL_IOMMU_SCTLR_CFCFG_SHIFT);
- sctlr_val &= ~(0x1 << KGSL_IOMMU_SCTLR_HUPCF_SHIFT);
- KGSL_IOMMU_SET_CTX_REG(ctx, KGSL_IOMMU_CTX_SCTLR, sctlr_val);
- /*
- * Make sure the above register write is not reordered across
- * the barrier as we use writel_relaxed to write it.
- */
- wmb();
- clear_fsr:
- /*
- * This will only clear fault bits in FSR. FSR.SS will still
- * be set. Writing to RESUME (below) is the only way to clear
- * FSR.SS bit.
- */
- KGSL_IOMMU_SET_CTX_REG(ctx, KGSL_IOMMU_CTX_FSR, 0xffffffff);
- /*
- * Make sure the above register write is not reordered across
- * the barrier as we use writel_relaxed to write it.
- */
- wmb();
- /*
- * Write 1 to RESUME.TnR to terminate the stalled transaction.
- * This will also allow the SMMU to process new transactions.
- */
- KGSL_IOMMU_SET_CTX_REG(ctx, KGSL_IOMMU_CTX_RESUME, 1);
- /*
- * Make sure the above register writes are not reordered across
- * the barrier as we use writel_relaxed to write them.
- */
- wmb();
- }
- static void kgsl_iommu_pagefault_resume(struct kgsl_mmu *mmu, bool terminate)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- kgsl_iommu_context_pagefault_resume(iommu, &iommu->user_context, terminate);
- if (iommu->lpac_context.domain)
- kgsl_iommu_context_pagefault_resume(iommu, &iommu->lpac_context, terminate);
- }
- static u64
- kgsl_iommu_get_current_ttbr0(struct kgsl_mmu *mmu, struct kgsl_context *context)
- {
- u64 val;
- struct kgsl_iommu *iommu = &mmu->iommu;
- struct kgsl_iommu_context *ctx = &iommu->user_context;
- if (kgsl_context_is_lpac(context))
- ctx = &iommu->lpac_context;
- /*
- * We cannot enable or disable the clocks in interrupt context, this
- * function is called from interrupt context if there is an axi error
- */
- if (in_interrupt())
- return 0;
- if (ctx->cb_num < 0)
- return 0;
- kgsl_iommu_enable_clk(mmu);
- val = KGSL_IOMMU_GET_CTX_REG_Q(ctx, KGSL_IOMMU_CTX_TTBR0);
- kgsl_iommu_disable_clk(mmu);
- return val;
- }
- /*
- * kgsl_iommu_set_pf_policy() - Set the pagefault policy for IOMMU
- * @mmu: Pointer to mmu structure
- * @pf_policy: The pagefault polict to set
- *
- * Check if the new policy indicated by pf_policy is same as current
- * policy, if same then return else set the policy
- */
- static int kgsl_iommu_set_pf_policy_ctxt(struct kgsl_mmu *mmu,
- unsigned long pf_policy, struct kgsl_iommu_context *ctx)
- {
- int cur, new;
- struct kgsl_iommu *iommu = &mmu->iommu;
- cur = test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE, &mmu->pfpolicy);
- new = test_bit(KGSL_FT_PAGEFAULT_GPUHALT_ENABLE, &pf_policy);
- if (cur == new)
- return 0;
- kgsl_iommu_enable_clk(mmu);
- kgsl_iommu_configure_gpu_sctlr(mmu, pf_policy, &iommu->user_context);
- if (iommu->lpac_context.domain)
- kgsl_iommu_configure_gpu_sctlr(mmu, pf_policy, &iommu->lpac_context);
- kgsl_iommu_disable_clk(mmu);
- return 0;
- }
- static int kgsl_iommu_set_pf_policy(struct kgsl_mmu *mmu,
- unsigned long pf_policy)
- {
- struct kgsl_iommu *iommu = &mmu->iommu;
- kgsl_iommu_set_pf_policy_ctxt(mmu, pf_policy, &iommu->user_context);
- if (iommu->lpac_context.domain)
- kgsl_iommu_set_pf_policy_ctxt(mmu, pf_policy, &iommu->lpac_context);
- return 0;
- }
- static struct kgsl_iommu_addr_entry *_find_gpuaddr(
- struct kgsl_pagetable *pagetable, uint64_t gpuaddr)
- {
- struct rb_node *node = pagetable->rbtree.rb_node;
- while (node != NULL) {
- struct kgsl_iommu_addr_entry *entry = rb_entry(node,
- struct kgsl_iommu_addr_entry, node);
- if (gpuaddr < entry->base)
- node = node->rb_left;
- else if (gpuaddr > entry->base)
- node = node->rb_right;
- else
- return entry;
- }
- return NULL;
- }
- static int _remove_gpuaddr(struct kgsl_pagetable *pagetable,
- uint64_t gpuaddr)
- {
- struct kgsl_iommu_addr_entry *entry;
- entry = _find_gpuaddr(pagetable, gpuaddr);
- if (WARN(!entry, "GPU address %llx doesn't exist\n", gpuaddr))
- return -ENOMEM;
- /*
- * If the hint was based on this entry, adjust it to the end of the
- * previous entry.
- */
- if (pagetable->va_hint == (entry->base + entry->size)) {
- struct kgsl_iommu_addr_entry *prev =
- rb_entry_safe(rb_prev(&entry->node),
- struct kgsl_iommu_addr_entry, node);
- pagetable->va_hint = pagetable->va_start;
- if (prev)
- pagetable->va_hint = max_t(u64, prev->base + prev->size,
- pagetable->va_start);
- }
- rb_erase(&entry->node, &pagetable->rbtree);
- kmem_cache_free(addr_entry_cache, entry);
- return 0;
- }
- static int _insert_gpuaddr(struct kgsl_pagetable *pagetable,
- uint64_t gpuaddr, uint64_t size)
- {
- struct rb_node **node, *parent = NULL;
- struct kgsl_iommu_addr_entry *new =
- kmem_cache_alloc(addr_entry_cache, GFP_ATOMIC);
- if (new == NULL)
- return -ENOMEM;
- new->base = gpuaddr;
- new->size = size;
- node = &pagetable->rbtree.rb_node;
- while (*node != NULL) {
- struct kgsl_iommu_addr_entry *this;
- parent = *node;
- this = rb_entry(parent, struct kgsl_iommu_addr_entry, node);
- if (new->base < this->base)
- node = &parent->rb_left;
- else if (new->base > this->base)
- node = &parent->rb_right;
- else {
- /* Duplicate entry */
- WARN_RATELIMIT(1, "duplicate gpuaddr: 0x%llx\n", gpuaddr);
- kmem_cache_free(addr_entry_cache, new);
- return -EEXIST;
- }
- }
- rb_link_node(&new->node, parent, node);
- rb_insert_color(&new->node, &pagetable->rbtree);
- return 0;
- }
- static u64 _get_unmapped_area_hint(struct kgsl_pagetable *pagetable,
- u64 bottom, u64 top, u64 size, u64 align)
- {
- u64 hint;
- /*
- * VA fragmentation can be a problem on global and secure pagetables
- * that are common to all processes, or if we're constrained to a 32-bit
- * range. Don't use the va_hint in these cases.
- */
- if (!pagetable->va_hint || !upper_32_bits(top))
- return (u64) -EINVAL;
- /* Satisfy requested alignment */
- hint = ALIGN(pagetable->va_hint, align);
- /*
- * The va_hint is the highest VA that was allocated in the non-SVM
- * region. The 64-bit SVM and non-SVM regions do not overlap. So, we
- * know there is no VA allocated at this gpuaddr. Therefore, we only
- * need to check whether we have enough space for this allocation.
- */
- if ((hint + size) > top)
- return (u64) -ENOMEM;
- pagetable->va_hint = hint + size;
- return hint;
- }
- static uint64_t _get_unmapped_area(struct kgsl_pagetable *pagetable,
- uint64_t bottom, uint64_t top, uint64_t size,
- uint64_t align)
- {
- struct rb_node *node;
- uint64_t start;
- /* Check if we can assign a gpuaddr based on the last allocation */
- start = _get_unmapped_area_hint(pagetable, bottom, top, size, align);
- if (!IS_ERR_VALUE(start))
- return start;
- /* Fall back to searching through the range. */
- node = rb_first(&pagetable->rbtree);
- bottom = ALIGN(bottom, align);
- start = bottom;
- while (node != NULL) {
- uint64_t gap;
- struct kgsl_iommu_addr_entry *entry = rb_entry(node,
- struct kgsl_iommu_addr_entry, node);
- /*
- * Skip any entries that are outside of the range, but make sure
- * to account for some that might straddle the lower bound
- */
- if (entry->base < bottom) {
- if (entry->base + entry->size > bottom)
- start = ALIGN(entry->base + entry->size, align);
- node = rb_next(node);
- continue;
- }
- /* Stop if we went over the top */
- if (entry->base >= top)
- break;
- /* Make sure there is a gap to consider */
- if (start < entry->base) {
- gap = entry->base - start;
- if (gap >= size)
- return start;
- }
- /* Stop if there is no more room in the region */
- if (entry->base + entry->size >= top)
- return (uint64_t) -ENOMEM;
- /* Start the next cycle at the end of the current entry */
- start = ALIGN(entry->base + entry->size, align);
- node = rb_next(node);
- }
- if (start + size <= top)
- return start;
- return (uint64_t) -ENOMEM;
- }
- static uint64_t _get_unmapped_area_topdown(struct kgsl_pagetable *pagetable,
- uint64_t bottom, uint64_t top, uint64_t size,
- uint64_t align)
- {
- struct rb_node *node = rb_last(&pagetable->rbtree);
- uint64_t end = top;
- uint64_t mask = ~(align - 1);
- struct kgsl_iommu_addr_entry *entry;
- /* Make sure that the bottom is correctly aligned */
- bottom = ALIGN(bottom, align);
- /* Make sure the requested size will fit in the range */
- if (size > (top - bottom))
- return -ENOMEM;
- /* Walk back through the list to find the highest entry in the range */
- for (node = rb_last(&pagetable->rbtree); node != NULL; node = rb_prev(node)) {
- entry = rb_entry(node, struct kgsl_iommu_addr_entry, node);
- if (entry->base < top)
- break;
- }
- while (node != NULL) {
- uint64_t offset;
- entry = rb_entry(node, struct kgsl_iommu_addr_entry, node);
- /* If the entire entry is below the range the search is over */
- if ((entry->base + entry->size) < bottom)
- break;
- /* Get the top of the entry properly aligned */
- offset = ALIGN(entry->base + entry->size, align);
- /*
- * Try to allocate the memory from the top of the gap,
- * making sure that it fits between the top of this entry and
- * the bottom of the previous one
- */
- if ((end > size) && (offset < end)) {
- uint64_t chunk = (end - size) & mask;
- if (chunk >= offset)
- return chunk;
- }
- /*
- * If we get here and the current entry is outside of the range
- * then we are officially out of room
- */
- if (entry->base < bottom)
- return (uint64_t) -ENOMEM;
- /* Set the top of the gap to the current entry->base */
- end = entry->base;
- /* And move on to the next lower entry */
- node = rb_prev(node);
- }
- /* If we get here then there are no more entries in the region */
- if ((end > size) && (((end - size) & mask) >= bottom))
- return (end - size) & mask;
- return (uint64_t) -ENOMEM;
- }
- static uint64_t kgsl_iommu_find_svm_region(struct kgsl_pagetable *pagetable,
- uint64_t start, uint64_t end, uint64_t size,
- uint64_t alignment)
- {
- uint64_t addr;
- /* Avoid black holes */
- if (WARN(end <= start, "Bad search range: 0x%llx-0x%llx", start, end))
- return (uint64_t) -EINVAL;
- spin_lock(&pagetable->lock);
- addr = _get_unmapped_area_topdown(pagetable,
- start, end, size, alignment);
- spin_unlock(&pagetable->lock);
- return addr;
- }
- static bool iommu_addr_in_svm_ranges(struct kgsl_pagetable *pagetable,
- u64 gpuaddr, u64 size)
- {
- u64 end = gpuaddr + size;
- /* Make sure size is not zero and we don't wrap around */
- if (end <= gpuaddr)
- return false;
- if ((gpuaddr >= pagetable->compat_va_start && gpuaddr < pagetable->compat_va_end) &&
- (end > pagetable->compat_va_start &&
- end <= pagetable->compat_va_end))
- return true;
- if ((gpuaddr >= pagetable->svm_start && gpuaddr < pagetable->svm_end) &&
- (end > pagetable->svm_start &&
- end <= pagetable->svm_end))
- return true;
- return false;
- }
- static int kgsl_iommu_set_svm_region(struct kgsl_pagetable *pagetable,
- uint64_t gpuaddr, uint64_t size)
- {
- int ret = -ENOMEM;
- struct rb_node *node;
- /* Make sure the requested address doesn't fall out of SVM range */
- if (!iommu_addr_in_svm_ranges(pagetable, gpuaddr, size))
- return -ENOMEM;
- spin_lock(&pagetable->lock);
- node = pagetable->rbtree.rb_node;
- while (node != NULL) {
- uint64_t start, end;
- struct kgsl_iommu_addr_entry *entry = rb_entry(node,
- struct kgsl_iommu_addr_entry, node);
- start = entry->base;
- end = entry->base + entry->size;
- if (gpuaddr + size <= start)
- node = node->rb_left;
- else if (end <= gpuaddr)
- node = node->rb_right;
- else
- goto out;
- }
- ret = _insert_gpuaddr(pagetable, gpuaddr, size);
- out:
- spin_unlock(&pagetable->lock);
- return ret;
- }
- static int get_gpuaddr(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc, u64 start, u64 end,
- u64 size, u64 align)
- {
- u64 addr;
- int ret;
- spin_lock(&pagetable->lock);
- addr = _get_unmapped_area(pagetable, start, end, size, align);
- if (addr == (u64) -ENOMEM) {
- spin_unlock(&pagetable->lock);
- return -ENOMEM;
- }
- ret = _insert_gpuaddr(pagetable, addr, size);
- spin_unlock(&pagetable->lock);
- if (ret == 0) {
- memdesc->gpuaddr = addr;
- memdesc->pagetable = pagetable;
- }
- return ret;
- }
- static int kgsl_iommu_get_gpuaddr(struct kgsl_pagetable *pagetable,
- struct kgsl_memdesc *memdesc)
- {
- int ret = 0;
- u64 start, end, size, align;
- if (WARN_ON(kgsl_memdesc_use_cpu_map(memdesc)))
- return -EINVAL;
- if (memdesc->flags & KGSL_MEMFLAGS_SECURE &&
- pagetable->name != KGSL_MMU_SECURE_PT)
- return -EINVAL;
- size = kgsl_memdesc_footprint(memdesc);
- align = max_t(uint64_t, 1 << kgsl_memdesc_get_align(memdesc),
- PAGE_SIZE);
- if (memdesc->flags & KGSL_MEMFLAGS_FORCE_32BIT) {
- start = pagetable->compat_va_start;
- end = pagetable->compat_va_end;
- } else {
- start = pagetable->va_start;
- end = pagetable->va_end;
- }
- ret = get_gpuaddr(pagetable, memdesc, start, end, size, align);
- /* if OOM, retry once after flushing lockless_workqueue */
- if (ret == -ENOMEM) {
- flush_workqueue(kgsl_driver.lockless_workqueue);
- ret = get_gpuaddr(pagetable, memdesc, start, end, size, align);
- }
- return ret;
- }
- static void kgsl_iommu_put_gpuaddr(struct kgsl_memdesc *memdesc)
- {
- if (memdesc->pagetable == NULL)
- return;
- spin_lock(&memdesc->pagetable->lock);
- _remove_gpuaddr(memdesc->pagetable, memdesc->gpuaddr);
- spin_unlock(&memdesc->pagetable->lock);
- }
- static int kgsl_iommu_svm_range(struct kgsl_pagetable *pagetable,
- uint64_t *lo, uint64_t *hi, uint64_t memflags)
- {
- bool gpu_compat = (memflags & KGSL_MEMFLAGS_FORCE_32BIT) != 0;
- if (lo != NULL)
- *lo = gpu_compat ? pagetable->compat_va_start : pagetable->svm_start;
- if (hi != NULL)
- *hi = gpu_compat ? pagetable->compat_va_end : pagetable->svm_end;
- return 0;
- }
- static bool kgsl_iommu_addr_in_range(struct kgsl_pagetable *pagetable,
- uint64_t gpuaddr, uint64_t size)
- {
- u64 end = gpuaddr + size;
- /* Make sure we don't wrap around */
- if (gpuaddr == 0 || end < gpuaddr)
- return false;
- if (gpuaddr >= pagetable->va_start && end <= pagetable->va_end)
- return true;
- if (gpuaddr >= pagetable->compat_va_start &&
- end <= pagetable->compat_va_end)
- return true;
- if (gpuaddr >= pagetable->svm_start && end <= pagetable->svm_end)
- return true;
- return false;
- }
- static int kgsl_iommu_setup_context(struct kgsl_mmu *mmu,
- struct device_node *parent,
- struct kgsl_iommu_context *context, const char *name,
- iommu_fault_handler_t handler)
- {
- struct device_node *node = of_find_node_by_name(parent, name);
- struct platform_device *pdev;
- struct kgsl_device *device = KGSL_MMU_DEVICE(mmu);
- int ret;
- if (!node)
- return -ENOENT;
- pdev = of_find_device_by_node(node);
- ret = of_dma_configure(&pdev->dev, node, true);
- of_node_put(node);
- if (ret)
- return ret;
- context->cb_num = -1;
- context->name = name;
- context->kgsldev = device;
- context->pdev = pdev;
- ratelimit_default_init(&context->ratelimit);
- /* Set the adreno_smmu priv data for the device */
- dev_set_drvdata(&pdev->dev, &context->adreno_smmu);
- /* Create a new context */
- context->domain = iommu_domain_alloc(&platform_bus_type);
- if (!context->domain) {
- /*FIXME: Put back the pdev here? */
- return -ENODEV;
- }
- _enable_gpuhtw_llc(mmu, context->domain);
- ret = iommu_attach_device(context->domain, &context->pdev->dev);
- if (ret) {
- /* FIXME: put back the device here? */
- iommu_domain_free(context->domain);
- context->domain = NULL;
- return ret;
- }
- iommu_set_fault_handler(context->domain, handler, mmu);
- context->cb_num = qcom_iommu_get_context_bank_nr(context->domain);
- if (context->cb_num >= 0)
- return 0;
- dev_err(&device->pdev->dev, "Couldn't get the context bank for %s: %d\n",
- context->name, context->cb_num);
- iommu_detach_device(context->domain, &context->pdev->dev);
- iommu_domain_free(context->domain);
- /* FIXME: put back the device here? */
- context->domain = NULL;
- return context->cb_num;
- }
- static int iommu_probe_user_context(struct kgsl_device *device,
- struct device_node *node)
- {
- struct kgsl_iommu *iommu = KGSL_IOMMU(device);
- struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
- struct kgsl_mmu *mmu = &device->mmu;
- struct kgsl_iommu_pt *pt;
- int ret;
- ret = kgsl_iommu_setup_context(mmu, node, &iommu->user_context,
- "gfx3d_user", kgsl_iommu_default_fault_handler);
- if (ret)
- return ret;
- /*
- * It is problamatic if smmu driver does system suspend before consumer
- * device (gpu). So smmu driver creates a device_link to act as a
- * supplier which in turn will ensure correct order during system
- * suspend. In kgsl, since we don't initialize iommu on the gpu device,
- * we should create a device_link between kgsl iommu device and gpu
- * device to maintain a correct suspend order between smmu device and
- * gpu device.
- */
- if (!device_link_add(&device->pdev->dev, &iommu->user_context.pdev->dev,
- DL_FLAG_AUTOREMOVE_CONSUMER))
- dev_err(&iommu->user_context.pdev->dev,
- "Unable to create device link to gpu device\n");
- ret = kgsl_iommu_setup_context(mmu, node, &iommu->lpac_context,
- "gfx3d_lpac", kgsl_iommu_lpac_fault_handler);
- /* LPAC is optional, ignore setup failures in absence of LPAC feature */
- if ((ret < 0) && ADRENO_FEATURE(adreno_dev, ADRENO_LPAC))
- goto err;
- /*
- * FIXME: If adreno_smmu->cookie wasn't initialized then we can't do
- * IOPGTABLE
- */
- /* Make the default pagetable */
- mmu->defaultpagetable = kgsl_iommu_default_pagetable(mmu);
- if (IS_ERR(mmu->defaultpagetable))
- return PTR_ERR(mmu->defaultpagetable);
- /* If IOPGTABLE isn't enabled then we are done */
- if (!test_bit(KGSL_MMU_IOPGTABLE, &mmu->features))
- return 0;
- pt = to_iommu_pt(mmu->defaultpagetable);
- /* Enable TTBR0 on the default and LPAC contexts */
- kgsl_iommu_set_ttbr0(&iommu->user_context, mmu, &pt->info.cfg);
- ret = kgsl_set_smmu_aperture(device, &iommu->user_context);
- if (ret)
- goto err;
- kgsl_iommu_set_ttbr0(&iommu->lpac_context, mmu, &pt->info.cfg);
- ret = kgsl_set_smmu_lpac_aperture(device, &iommu->lpac_context);
- if (ret < 0) {
- kgsl_iommu_detach_context(&iommu->lpac_context);
- goto err;
- }
- return 0;
- err:
- kgsl_mmu_putpagetable(mmu->defaultpagetable);
- mmu->defaultpagetable = NULL;
- kgsl_iommu_detach_context(&iommu->user_context);
- return ret;
- }
- static int iommu_probe_secure_context(struct kgsl_device *device,
- struct device_node *parent)
- {
- struct device_node *node;
- struct platform_device *pdev;
- int ret;
- struct kgsl_iommu *iommu = KGSL_IOMMU(device);
- struct kgsl_mmu *mmu = &device->mmu;
- struct kgsl_iommu_context *context = &iommu->secure_context;
- int secure_vmid = VMID_CP_PIXEL;
- if (!mmu->secured)
- return -EPERM;
- node = of_find_node_by_name(parent, "gfx3d_secure");
- if (!node)
- return -ENOENT;
- pdev = of_find_device_by_node(node);
- ret = of_dma_configure(&pdev->dev, node, true);
- of_node_put(node);
- if (ret)
- return ret;
- context->cb_num = -1;
- context->name = "gfx3d_secure";
- context->kgsldev = device;
- context->pdev = pdev;
- ratelimit_default_init(&context->ratelimit);
- context->domain = iommu_domain_alloc(&platform_bus_type);
- if (!context->domain) {
- /* FIXME: put away the device */
- return -ENODEV;
- }
- ret = qcom_iommu_set_secure_vmid(context->domain, secure_vmid);
- if (ret) {
- dev_err(&device->pdev->dev, "Unable to set the secure VMID: %d\n", ret);
- iommu_domain_free(context->domain);
- context->domain = NULL;
- /* FIXME: put away the device */
- return ret;
- }
- _enable_gpuhtw_llc(mmu, context->domain);
- ret = iommu_attach_device(context->domain, &context->pdev->dev);
- if (ret) {
- iommu_domain_free(context->domain);
- /* FIXME: Put way the device */
- context->domain = NULL;
- return ret;
- }
- iommu_set_fault_handler(context->domain,
- kgsl_iommu_secure_fault_handler, mmu);
- context->cb_num = qcom_iommu_get_context_bank_nr(context->domain);
- if (context->cb_num < 0) {
- iommu_detach_device(context->domain, &context->pdev->dev);
- iommu_domain_free(context->domain);
- context->domain = NULL;
- return context->cb_num;
- }
- mmu->securepagetable = kgsl_iommu_secure_pagetable(mmu);
- if (IS_ERR(mmu->securepagetable))
- mmu->secured = false;
- return 0;
- }
- static const char * const kgsl_iommu_clocks[] = {
- "gcc_gpu_memnoc_gfx",
- "gcc_gpu_snoc_dvm_gfx",
- "gpu_cc_ahb",
- "gpu_cc_cx_gmu",
- "gpu_cc_hlos1_vote_gpu_smmu",
- "gpu_cc_hub_aon",
- "gpu_cc_hub_cx_int",
- "gcc_bimc_gpu_axi",
- "gcc_gpu_ahb",
- "gcc_gpu_axi_clk",
- "gcc_smmu_cfg_clk",
- "gcc_gfx_tcu_clk",
- };
- static const struct kgsl_mmu_ops kgsl_iommu_ops;
- static void kgsl_iommu_check_config(struct kgsl_mmu *mmu,
- struct device_node *parent)
- {
- struct device_node *node = of_find_node_by_name(parent, "gfx3d_user");
- struct device_node *phandle;
- if (!node)
- return;
- phandle = of_parse_phandle(node, "iommus", 0);
- if (phandle) {
- if (of_device_is_compatible(phandle, "qcom,qsmmu-v500"))
- mmu->subtype = KGSL_IOMMU_SMMU_V500;
- if (of_device_is_compatible(phandle, "qcom,adreno-smmu"))
- set_bit(KGSL_MMU_IOPGTABLE, &mmu->features);
- of_node_put(phandle);
- }
- of_node_put(node);
- }
- int kgsl_iommu_bind(struct kgsl_device *device, struct platform_device *pdev)
- {
- u32 val[2];
- int ret, i;
- struct kgsl_iommu *iommu = KGSL_IOMMU(device);
- struct kgsl_mmu *mmu = &device->mmu;
- struct device_node *node = pdev->dev.of_node;
- struct kgsl_global_memdesc *md;
- /* Create a kmem cache for the pagetable address objects */
- if (!addr_entry_cache) {
- addr_entry_cache = KMEM_CACHE(kgsl_iommu_addr_entry, 0);
- if (!addr_entry_cache) {
- ret = -ENOMEM;
- goto err;
- }
- }
- ret = of_property_read_u32_array(node, "reg", val, 2);
- if (ret) {
- dev_err(&device->pdev->dev,
- "%pOF: Unable to read KGSL IOMMU register range\n",
- node);
- goto err;
- }
- iommu->regbase = devm_ioremap(&device->pdev->dev, val[0], val[1]);
- if (!iommu->regbase) {
- dev_err(&device->pdev->dev, "Couldn't map IOMMU registers\n");
- ret = -ENOMEM;
- goto err;
- }
- iommu->pdev = pdev;
- iommu->num_clks = 0;
- iommu->clks = devm_kcalloc(&pdev->dev, ARRAY_SIZE(kgsl_iommu_clocks),
- sizeof(*iommu->clks), GFP_KERNEL);
- if (!iommu->clks) {
- ret = -ENOMEM;
- goto err;
- }
- for (i = 0; i < ARRAY_SIZE(kgsl_iommu_clocks); i++) {
- struct clk *c;
- c = devm_clk_get(&device->pdev->dev, kgsl_iommu_clocks[i]);
- if (IS_ERR(c))
- continue;
- iommu->clks[iommu->num_clks].id = kgsl_iommu_clocks[i];
- iommu->clks[iommu->num_clks++].clk = c;
- }
- /* Get the CX regulator if it is available */
- iommu->cx_gdsc = devm_regulator_get(&pdev->dev, "vddcx");
- set_bit(KGSL_MMU_PAGED, &mmu->features);
- mmu->type = KGSL_MMU_TYPE_IOMMU;
- mmu->mmu_ops = &kgsl_iommu_ops;
- /* Peek at the phandle to set up configuration */
- kgsl_iommu_check_config(mmu, node);
- /* Probe the default pagetable */
- ret = iommu_probe_user_context(device, node);
- if (ret)
- goto err;
- /* Probe the secure pagetable (this is optional) */
- iommu_probe_secure_context(device, node);
- /* Map any globals that might have been created early */
- list_for_each_entry(md, &device->globals, node) {
- if (md->memdesc.flags & KGSL_MEMFLAGS_SECURE) {
- if (IS_ERR_OR_NULL(mmu->securepagetable))
- continue;
- kgsl_iommu_secure_map(mmu->securepagetable,
- &md->memdesc);
- } else
- kgsl_iommu_default_map(mmu->defaultpagetable,
- &md->memdesc);
- }
- /* QDSS is supported only when QCOM_KGSL_QDSS_STM is enabled */
- if (IS_ENABLED(CONFIG_QCOM_KGSL_QDSS_STM))
- device->qdss_desc = kgsl_allocate_global_fixed(device,
- "qcom,gpu-qdss-stm", "gpu-qdss");
- device->qtimer_desc = kgsl_allocate_global_fixed(device,
- "qcom,gpu-timer", "gpu-qtimer");
- /*
- * Only support VBOs on MMU500 hardware that supports the PRR
- * marker register to ignore writes to the zero page
- */
- if ((mmu->subtype == KGSL_IOMMU_SMMU_V500) &&
- test_bit(KGSL_MMU_SUPPORT_VBO, &mmu->features)) {
- /*
- * We need to allocate a page because we need a known physical
- * address to program in the PRR register but the hardware
- * should intercept accesses to the page before they go to DDR
- * so this should be mostly just a placeholder
- */
- kgsl_vbo_zero_page = alloc_page(GFP_KERNEL | __GFP_ZERO |
- __GFP_NORETRY | __GFP_HIGHMEM);
- }
- if (!kgsl_vbo_zero_page)
- clear_bit(KGSL_MMU_SUPPORT_VBO, &mmu->features);
- return 0;
- err:
- kmem_cache_destroy(addr_entry_cache);
- addr_entry_cache = NULL;
- return ret;
- }
- static const struct kgsl_mmu_ops kgsl_iommu_ops = {
- .mmu_close = kgsl_iommu_close,
- .mmu_start = kgsl_iommu_start,
- .mmu_clear_fsr = kgsl_iommu_clear_fsr,
- .mmu_get_current_ttbr0 = kgsl_iommu_get_current_ttbr0,
- .mmu_enable_clk = kgsl_iommu_enable_clk,
- .mmu_disable_clk = kgsl_iommu_disable_clk,
- .mmu_set_pf_policy = kgsl_iommu_set_pf_policy,
- .mmu_pagefault_resume = kgsl_iommu_pagefault_resume,
- .mmu_getpagetable = kgsl_iommu_getpagetable,
- .mmu_map_global = kgsl_iommu_map_global,
- .mmu_send_tlb_hint = kgsl_iommu_send_tlb_hint,
- };
- static const struct kgsl_mmu_pt_ops iopgtbl_pt_ops = {
- .mmu_map = kgsl_iopgtbl_map,
- .mmu_map_child = kgsl_iopgtbl_map_child,
- .mmu_map_zero_page_to_range = kgsl_iopgtbl_map_zero_page_to_range,
- .mmu_unmap = kgsl_iopgtbl_unmap,
- .mmu_unmap_range = kgsl_iopgtbl_unmap_range,
- .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
- .get_ttbr0 = kgsl_iommu_get_ttbr0,
- .get_context_bank = kgsl_iommu_get_context_bank,
- .get_asid = kgsl_iommu_get_asid,
- .get_gpuaddr = kgsl_iommu_get_gpuaddr,
- .put_gpuaddr = kgsl_iommu_put_gpuaddr,
- .set_svm_region = kgsl_iommu_set_svm_region,
- .find_svm_region = kgsl_iommu_find_svm_region,
- .svm_range = kgsl_iommu_svm_range,
- .addr_in_range = kgsl_iommu_addr_in_range,
- };
- static const struct kgsl_mmu_pt_ops secure_pt_ops = {
- .mmu_map = kgsl_iommu_secure_map,
- .mmu_unmap = kgsl_iommu_secure_unmap,
- .mmu_destroy_pagetable = kgsl_iommu_destroy_pagetable,
- .get_context_bank = kgsl_iommu_get_context_bank,
- .get_asid = kgsl_iommu_get_asid,
- .get_gpuaddr = kgsl_iommu_get_gpuaddr,
- .put_gpuaddr = kgsl_iommu_put_gpuaddr,
- .addr_in_range = kgsl_iommu_addr_in_range,
- };
- static const struct kgsl_mmu_pt_ops default_pt_ops = {
- .mmu_map = kgsl_iommu_default_map,
- .mmu_unmap = kgsl_iommu_default_unmap,
- .mmu_destroy_pagetable = kgsl_iommu_destroy_default_pagetable,
- .get_ttbr0 = kgsl_iommu_get_ttbr0,
- .get_context_bank = kgsl_iommu_get_context_bank,
- .get_asid = kgsl_iommu_get_asid,
- .get_gpuaddr = kgsl_iommu_get_gpuaddr,
- .put_gpuaddr = kgsl_iommu_put_gpuaddr,
- .addr_in_range = kgsl_iommu_addr_in_range,
- };
|