1*6579324aSTerje Bergstrom /* 2*6579324aSTerje Bergstrom * Tegra host1x Job 3*6579324aSTerje Bergstrom * 4*6579324aSTerje Bergstrom * Copyright (c) 2010-2013, NVIDIA Corporation. 5*6579324aSTerje Bergstrom * 6*6579324aSTerje Bergstrom * This program is free software; you can redistribute it and/or modify it 7*6579324aSTerje Bergstrom * under the terms and conditions of the GNU General Public License, 8*6579324aSTerje Bergstrom * version 2, as published by the Free Software Foundation. 9*6579324aSTerje Bergstrom * 10*6579324aSTerje Bergstrom * This program is distributed in the hope it will be useful, but WITHOUT 11*6579324aSTerje Bergstrom * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or 12*6579324aSTerje Bergstrom * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for 13*6579324aSTerje Bergstrom * more details. 14*6579324aSTerje Bergstrom * 15*6579324aSTerje Bergstrom * You should have received a copy of the GNU General Public License 16*6579324aSTerje Bergstrom * along with this program. If not, see <http://www.gnu.org/licenses/>. 17*6579324aSTerje Bergstrom */ 18*6579324aSTerje Bergstrom 19*6579324aSTerje Bergstrom #include <linux/dma-mapping.h> 20*6579324aSTerje Bergstrom #include <linux/err.h> 21*6579324aSTerje Bergstrom #include <linux/kref.h> 22*6579324aSTerje Bergstrom #include <linux/module.h> 23*6579324aSTerje Bergstrom #include <linux/scatterlist.h> 24*6579324aSTerje Bergstrom #include <linux/slab.h> 25*6579324aSTerje Bergstrom #include <linux/vmalloc.h> 26*6579324aSTerje Bergstrom #include <trace/events/host1x.h> 27*6579324aSTerje Bergstrom 28*6579324aSTerje Bergstrom #include "channel.h" 29*6579324aSTerje Bergstrom #include "dev.h" 30*6579324aSTerje Bergstrom #include "host1x_bo.h" 31*6579324aSTerje Bergstrom #include "job.h" 32*6579324aSTerje Bergstrom #include "syncpt.h" 33*6579324aSTerje Bergstrom 34*6579324aSTerje Bergstrom struct host1x_job *host1x_job_alloc(struct host1x_channel *ch, 35*6579324aSTerje Bergstrom u32 num_cmdbufs, u32 num_relocs, 36*6579324aSTerje Bergstrom u32 num_waitchks) 37*6579324aSTerje Bergstrom { 38*6579324aSTerje Bergstrom struct host1x_job *job = NULL; 39*6579324aSTerje Bergstrom unsigned int num_unpins = num_cmdbufs + num_relocs; 40*6579324aSTerje Bergstrom u64 total; 41*6579324aSTerje Bergstrom void *mem; 42*6579324aSTerje Bergstrom 43*6579324aSTerje Bergstrom /* Check that we're not going to overflow */ 44*6579324aSTerje Bergstrom total = sizeof(struct host1x_job) + 45*6579324aSTerje Bergstrom num_relocs * sizeof(struct host1x_reloc) + 46*6579324aSTerje Bergstrom num_unpins * sizeof(struct host1x_job_unpin_data) + 47*6579324aSTerje Bergstrom num_waitchks * sizeof(struct host1x_waitchk) + 48*6579324aSTerje Bergstrom num_cmdbufs * sizeof(struct host1x_job_gather) + 49*6579324aSTerje Bergstrom num_unpins * sizeof(dma_addr_t) + 50*6579324aSTerje Bergstrom num_unpins * sizeof(u32 *); 51*6579324aSTerje Bergstrom if (total > ULONG_MAX) 52*6579324aSTerje Bergstrom return NULL; 53*6579324aSTerje Bergstrom 54*6579324aSTerje Bergstrom mem = job = kzalloc(total, GFP_KERNEL); 55*6579324aSTerje Bergstrom if (!job) 56*6579324aSTerje Bergstrom return NULL; 57*6579324aSTerje Bergstrom 58*6579324aSTerje Bergstrom kref_init(&job->ref); 59*6579324aSTerje Bergstrom job->channel = ch; 60*6579324aSTerje Bergstrom 61*6579324aSTerje Bergstrom /* Redistribute memory to the structs */ 62*6579324aSTerje Bergstrom mem += sizeof(struct host1x_job); 63*6579324aSTerje Bergstrom job->relocarray = num_relocs ? mem : NULL; 64*6579324aSTerje Bergstrom mem += num_relocs * sizeof(struct host1x_reloc); 65*6579324aSTerje Bergstrom job->unpins = num_unpins ? mem : NULL; 66*6579324aSTerje Bergstrom mem += num_unpins * sizeof(struct host1x_job_unpin_data); 67*6579324aSTerje Bergstrom job->waitchk = num_waitchks ? mem : NULL; 68*6579324aSTerje Bergstrom mem += num_waitchks * sizeof(struct host1x_waitchk); 69*6579324aSTerje Bergstrom job->gathers = num_cmdbufs ? mem : NULL; 70*6579324aSTerje Bergstrom mem += num_cmdbufs * sizeof(struct host1x_job_gather); 71*6579324aSTerje Bergstrom job->addr_phys = num_unpins ? mem : NULL; 72*6579324aSTerje Bergstrom 73*6579324aSTerje Bergstrom job->reloc_addr_phys = job->addr_phys; 74*6579324aSTerje Bergstrom job->gather_addr_phys = &job->addr_phys[num_relocs]; 75*6579324aSTerje Bergstrom 76*6579324aSTerje Bergstrom return job; 77*6579324aSTerje Bergstrom } 78*6579324aSTerje Bergstrom 79*6579324aSTerje Bergstrom struct host1x_job *host1x_job_get(struct host1x_job *job) 80*6579324aSTerje Bergstrom { 81*6579324aSTerje Bergstrom kref_get(&job->ref); 82*6579324aSTerje Bergstrom return job; 83*6579324aSTerje Bergstrom } 84*6579324aSTerje Bergstrom 85*6579324aSTerje Bergstrom static void job_free(struct kref *ref) 86*6579324aSTerje Bergstrom { 87*6579324aSTerje Bergstrom struct host1x_job *job = container_of(ref, struct host1x_job, ref); 88*6579324aSTerje Bergstrom 89*6579324aSTerje Bergstrom kfree(job); 90*6579324aSTerje Bergstrom } 91*6579324aSTerje Bergstrom 92*6579324aSTerje Bergstrom void host1x_job_put(struct host1x_job *job) 93*6579324aSTerje Bergstrom { 94*6579324aSTerje Bergstrom kref_put(&job->ref, job_free); 95*6579324aSTerje Bergstrom } 96*6579324aSTerje Bergstrom 97*6579324aSTerje Bergstrom void host1x_job_add_gather(struct host1x_job *job, struct host1x_bo *bo, 98*6579324aSTerje Bergstrom u32 words, u32 offset) 99*6579324aSTerje Bergstrom { 100*6579324aSTerje Bergstrom struct host1x_job_gather *cur_gather = &job->gathers[job->num_gathers]; 101*6579324aSTerje Bergstrom 102*6579324aSTerje Bergstrom cur_gather->words = words; 103*6579324aSTerje Bergstrom cur_gather->bo = bo; 104*6579324aSTerje Bergstrom cur_gather->offset = offset; 105*6579324aSTerje Bergstrom job->num_gathers++; 106*6579324aSTerje Bergstrom } 107*6579324aSTerje Bergstrom 108*6579324aSTerje Bergstrom /* 109*6579324aSTerje Bergstrom * NULL an already satisfied WAIT_SYNCPT host method, by patching its 110*6579324aSTerje Bergstrom * args in the command stream. The method data is changed to reference 111*6579324aSTerje Bergstrom * a reserved (never given out or incr) HOST1X_SYNCPT_RESERVED syncpt 112*6579324aSTerje Bergstrom * with a matching threshold value of 0, so is guaranteed to be popped 113*6579324aSTerje Bergstrom * by the host HW. 114*6579324aSTerje Bergstrom */ 115*6579324aSTerje Bergstrom static void host1x_syncpt_patch_offset(struct host1x_syncpt *sp, 116*6579324aSTerje Bergstrom struct host1x_bo *h, u32 offset) 117*6579324aSTerje Bergstrom { 118*6579324aSTerje Bergstrom void *patch_addr = NULL; 119*6579324aSTerje Bergstrom 120*6579324aSTerje Bergstrom /* patch the wait */ 121*6579324aSTerje Bergstrom patch_addr = host1x_bo_kmap(h, offset >> PAGE_SHIFT); 122*6579324aSTerje Bergstrom if (patch_addr) { 123*6579324aSTerje Bergstrom host1x_syncpt_patch_wait(sp, 124*6579324aSTerje Bergstrom patch_addr + (offset & ~PAGE_MASK)); 125*6579324aSTerje Bergstrom host1x_bo_kunmap(h, offset >> PAGE_SHIFT, patch_addr); 126*6579324aSTerje Bergstrom } else 127*6579324aSTerje Bergstrom pr_err("Could not map cmdbuf for wait check\n"); 128*6579324aSTerje Bergstrom } 129*6579324aSTerje Bergstrom 130*6579324aSTerje Bergstrom /* 131*6579324aSTerje Bergstrom * Check driver supplied waitchk structs for syncpt thresholds 132*6579324aSTerje Bergstrom * that have already been satisfied and NULL the comparison (to 133*6579324aSTerje Bergstrom * avoid a wrap condition in the HW). 134*6579324aSTerje Bergstrom */ 135*6579324aSTerje Bergstrom static int do_waitchks(struct host1x_job *job, struct host1x *host, 136*6579324aSTerje Bergstrom struct host1x_bo *patch) 137*6579324aSTerje Bergstrom { 138*6579324aSTerje Bergstrom int i; 139*6579324aSTerje Bergstrom 140*6579324aSTerje Bergstrom /* compare syncpt vs wait threshold */ 141*6579324aSTerje Bergstrom for (i = 0; i < job->num_waitchk; i++) { 142*6579324aSTerje Bergstrom struct host1x_waitchk *wait = &job->waitchk[i]; 143*6579324aSTerje Bergstrom struct host1x_syncpt *sp = 144*6579324aSTerje Bergstrom host1x_syncpt_get(host, wait->syncpt_id); 145*6579324aSTerje Bergstrom 146*6579324aSTerje Bergstrom /* validate syncpt id */ 147*6579324aSTerje Bergstrom if (wait->syncpt_id > host1x_syncpt_nb_pts(host)) 148*6579324aSTerje Bergstrom continue; 149*6579324aSTerje Bergstrom 150*6579324aSTerje Bergstrom /* skip all other gathers */ 151*6579324aSTerje Bergstrom if (patch != wait->bo) 152*6579324aSTerje Bergstrom continue; 153*6579324aSTerje Bergstrom 154*6579324aSTerje Bergstrom trace_host1x_syncpt_wait_check(wait->bo, wait->offset, 155*6579324aSTerje Bergstrom wait->syncpt_id, wait->thresh, 156*6579324aSTerje Bergstrom host1x_syncpt_read_min(sp)); 157*6579324aSTerje Bergstrom 158*6579324aSTerje Bergstrom if (host1x_syncpt_is_expired(sp, wait->thresh)) { 159*6579324aSTerje Bergstrom dev_dbg(host->dev, 160*6579324aSTerje Bergstrom "drop WAIT id %d (%s) thresh 0x%x, min 0x%x\n", 161*6579324aSTerje Bergstrom wait->syncpt_id, sp->name, wait->thresh, 162*6579324aSTerje Bergstrom host1x_syncpt_read_min(sp)); 163*6579324aSTerje Bergstrom 164*6579324aSTerje Bergstrom host1x_syncpt_patch_offset(sp, patch, wait->offset); 165*6579324aSTerje Bergstrom } 166*6579324aSTerje Bergstrom 167*6579324aSTerje Bergstrom wait->bo = NULL; 168*6579324aSTerje Bergstrom } 169*6579324aSTerje Bergstrom 170*6579324aSTerje Bergstrom return 0; 171*6579324aSTerje Bergstrom } 172*6579324aSTerje Bergstrom 173*6579324aSTerje Bergstrom static unsigned int pin_job(struct host1x_job *job) 174*6579324aSTerje Bergstrom { 175*6579324aSTerje Bergstrom unsigned int i; 176*6579324aSTerje Bergstrom 177*6579324aSTerje Bergstrom job->num_unpins = 0; 178*6579324aSTerje Bergstrom 179*6579324aSTerje Bergstrom for (i = 0; i < job->num_relocs; i++) { 180*6579324aSTerje Bergstrom struct host1x_reloc *reloc = &job->relocarray[i]; 181*6579324aSTerje Bergstrom struct sg_table *sgt; 182*6579324aSTerje Bergstrom dma_addr_t phys_addr; 183*6579324aSTerje Bergstrom 184*6579324aSTerje Bergstrom reloc->target = host1x_bo_get(reloc->target); 185*6579324aSTerje Bergstrom if (!reloc->target) 186*6579324aSTerje Bergstrom goto unpin; 187*6579324aSTerje Bergstrom 188*6579324aSTerje Bergstrom phys_addr = host1x_bo_pin(reloc->target, &sgt); 189*6579324aSTerje Bergstrom if (!phys_addr) 190*6579324aSTerje Bergstrom goto unpin; 191*6579324aSTerje Bergstrom 192*6579324aSTerje Bergstrom job->addr_phys[job->num_unpins] = phys_addr; 193*6579324aSTerje Bergstrom job->unpins[job->num_unpins].bo = reloc->target; 194*6579324aSTerje Bergstrom job->unpins[job->num_unpins].sgt = sgt; 195*6579324aSTerje Bergstrom job->num_unpins++; 196*6579324aSTerje Bergstrom } 197*6579324aSTerje Bergstrom 198*6579324aSTerje Bergstrom for (i = 0; i < job->num_gathers; i++) { 199*6579324aSTerje Bergstrom struct host1x_job_gather *g = &job->gathers[i]; 200*6579324aSTerje Bergstrom struct sg_table *sgt; 201*6579324aSTerje Bergstrom dma_addr_t phys_addr; 202*6579324aSTerje Bergstrom 203*6579324aSTerje Bergstrom g->bo = host1x_bo_get(g->bo); 204*6579324aSTerje Bergstrom if (!g->bo) 205*6579324aSTerje Bergstrom goto unpin; 206*6579324aSTerje Bergstrom 207*6579324aSTerje Bergstrom phys_addr = host1x_bo_pin(g->bo, &sgt); 208*6579324aSTerje Bergstrom if (!phys_addr) 209*6579324aSTerje Bergstrom goto unpin; 210*6579324aSTerje Bergstrom 211*6579324aSTerje Bergstrom job->addr_phys[job->num_unpins] = phys_addr; 212*6579324aSTerje Bergstrom job->unpins[job->num_unpins].bo = g->bo; 213*6579324aSTerje Bergstrom job->unpins[job->num_unpins].sgt = sgt; 214*6579324aSTerje Bergstrom job->num_unpins++; 215*6579324aSTerje Bergstrom } 216*6579324aSTerje Bergstrom 217*6579324aSTerje Bergstrom return job->num_unpins; 218*6579324aSTerje Bergstrom 219*6579324aSTerje Bergstrom unpin: 220*6579324aSTerje Bergstrom host1x_job_unpin(job); 221*6579324aSTerje Bergstrom return 0; 222*6579324aSTerje Bergstrom } 223*6579324aSTerje Bergstrom 224*6579324aSTerje Bergstrom static unsigned int do_relocs(struct host1x_job *job, struct host1x_bo *cmdbuf) 225*6579324aSTerje Bergstrom { 226*6579324aSTerje Bergstrom int i = 0; 227*6579324aSTerje Bergstrom u32 last_page = ~0; 228*6579324aSTerje Bergstrom void *cmdbuf_page_addr = NULL; 229*6579324aSTerje Bergstrom 230*6579324aSTerje Bergstrom /* pin & patch the relocs for one gather */ 231*6579324aSTerje Bergstrom while (i < job->num_relocs) { 232*6579324aSTerje Bergstrom struct host1x_reloc *reloc = &job->relocarray[i]; 233*6579324aSTerje Bergstrom u32 reloc_addr = (job->reloc_addr_phys[i] + 234*6579324aSTerje Bergstrom reloc->target_offset) >> reloc->shift; 235*6579324aSTerje Bergstrom u32 *target; 236*6579324aSTerje Bergstrom 237*6579324aSTerje Bergstrom /* skip all other gathers */ 238*6579324aSTerje Bergstrom if (!(reloc->cmdbuf && cmdbuf == reloc->cmdbuf)) { 239*6579324aSTerje Bergstrom i++; 240*6579324aSTerje Bergstrom continue; 241*6579324aSTerje Bergstrom } 242*6579324aSTerje Bergstrom 243*6579324aSTerje Bergstrom if (last_page != reloc->cmdbuf_offset >> PAGE_SHIFT) { 244*6579324aSTerje Bergstrom if (cmdbuf_page_addr) 245*6579324aSTerje Bergstrom host1x_bo_kunmap(cmdbuf, last_page, 246*6579324aSTerje Bergstrom cmdbuf_page_addr); 247*6579324aSTerje Bergstrom 248*6579324aSTerje Bergstrom cmdbuf_page_addr = host1x_bo_kmap(cmdbuf, 249*6579324aSTerje Bergstrom reloc->cmdbuf_offset >> PAGE_SHIFT); 250*6579324aSTerje Bergstrom last_page = reloc->cmdbuf_offset >> PAGE_SHIFT; 251*6579324aSTerje Bergstrom 252*6579324aSTerje Bergstrom if (unlikely(!cmdbuf_page_addr)) { 253*6579324aSTerje Bergstrom pr_err("Could not map cmdbuf for relocation\n"); 254*6579324aSTerje Bergstrom return -ENOMEM; 255*6579324aSTerje Bergstrom } 256*6579324aSTerje Bergstrom } 257*6579324aSTerje Bergstrom 258*6579324aSTerje Bergstrom target = cmdbuf_page_addr + (reloc->cmdbuf_offset & ~PAGE_MASK); 259*6579324aSTerje Bergstrom *target = reloc_addr; 260*6579324aSTerje Bergstrom 261*6579324aSTerje Bergstrom /* mark this gather as handled */ 262*6579324aSTerje Bergstrom reloc->cmdbuf = 0; 263*6579324aSTerje Bergstrom } 264*6579324aSTerje Bergstrom 265*6579324aSTerje Bergstrom if (cmdbuf_page_addr) 266*6579324aSTerje Bergstrom host1x_bo_kunmap(cmdbuf, last_page, cmdbuf_page_addr); 267*6579324aSTerje Bergstrom 268*6579324aSTerje Bergstrom return 0; 269*6579324aSTerje Bergstrom } 270*6579324aSTerje Bergstrom 271*6579324aSTerje Bergstrom static int check_reloc(struct host1x_reloc *reloc, struct host1x_bo *cmdbuf, 272*6579324aSTerje Bergstrom unsigned int offset) 273*6579324aSTerje Bergstrom { 274*6579324aSTerje Bergstrom offset *= sizeof(u32); 275*6579324aSTerje Bergstrom 276*6579324aSTerje Bergstrom if (reloc->cmdbuf != cmdbuf || reloc->cmdbuf_offset != offset) 277*6579324aSTerje Bergstrom return -EINVAL; 278*6579324aSTerje Bergstrom 279*6579324aSTerje Bergstrom return 0; 280*6579324aSTerje Bergstrom } 281*6579324aSTerje Bergstrom 282*6579324aSTerje Bergstrom struct host1x_firewall { 283*6579324aSTerje Bergstrom struct host1x_job *job; 284*6579324aSTerje Bergstrom struct device *dev; 285*6579324aSTerje Bergstrom 286*6579324aSTerje Bergstrom unsigned int num_relocs; 287*6579324aSTerje Bergstrom struct host1x_reloc *reloc; 288*6579324aSTerje Bergstrom 289*6579324aSTerje Bergstrom struct host1x_bo *cmdbuf_id; 290*6579324aSTerje Bergstrom unsigned int offset; 291*6579324aSTerje Bergstrom 292*6579324aSTerje Bergstrom u32 words; 293*6579324aSTerje Bergstrom u32 class; 294*6579324aSTerje Bergstrom u32 reg; 295*6579324aSTerje Bergstrom u32 mask; 296*6579324aSTerje Bergstrom u32 count; 297*6579324aSTerje Bergstrom }; 298*6579324aSTerje Bergstrom 299*6579324aSTerje Bergstrom static int check_mask(struct host1x_firewall *fw) 300*6579324aSTerje Bergstrom { 301*6579324aSTerje Bergstrom u32 mask = fw->mask; 302*6579324aSTerje Bergstrom u32 reg = fw->reg; 303*6579324aSTerje Bergstrom 304*6579324aSTerje Bergstrom while (mask) { 305*6579324aSTerje Bergstrom if (fw->words == 0) 306*6579324aSTerje Bergstrom return -EINVAL; 307*6579324aSTerje Bergstrom 308*6579324aSTerje Bergstrom if (mask & 1) { 309*6579324aSTerje Bergstrom if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) { 310*6579324aSTerje Bergstrom bool bad_reloc = check_reloc(fw->reloc, 311*6579324aSTerje Bergstrom fw->cmdbuf_id, 312*6579324aSTerje Bergstrom fw->offset); 313*6579324aSTerje Bergstrom if (!fw->num_relocs || bad_reloc) 314*6579324aSTerje Bergstrom return -EINVAL; 315*6579324aSTerje Bergstrom fw->reloc++; 316*6579324aSTerje Bergstrom fw->num_relocs--; 317*6579324aSTerje Bergstrom } 318*6579324aSTerje Bergstrom fw->words--; 319*6579324aSTerje Bergstrom fw->offset++; 320*6579324aSTerje Bergstrom } 321*6579324aSTerje Bergstrom mask >>= 1; 322*6579324aSTerje Bergstrom reg++; 323*6579324aSTerje Bergstrom } 324*6579324aSTerje Bergstrom 325*6579324aSTerje Bergstrom return 0; 326*6579324aSTerje Bergstrom } 327*6579324aSTerje Bergstrom 328*6579324aSTerje Bergstrom static int check_incr(struct host1x_firewall *fw) 329*6579324aSTerje Bergstrom { 330*6579324aSTerje Bergstrom u32 count = fw->count; 331*6579324aSTerje Bergstrom u32 reg = fw->reg; 332*6579324aSTerje Bergstrom 333*6579324aSTerje Bergstrom while (fw) { 334*6579324aSTerje Bergstrom if (fw->words == 0) 335*6579324aSTerje Bergstrom return -EINVAL; 336*6579324aSTerje Bergstrom 337*6579324aSTerje Bergstrom if (fw->job->is_addr_reg(fw->dev, fw->class, reg)) { 338*6579324aSTerje Bergstrom bool bad_reloc = check_reloc(fw->reloc, fw->cmdbuf_id, 339*6579324aSTerje Bergstrom fw->offset); 340*6579324aSTerje Bergstrom if (!fw->num_relocs || bad_reloc) 341*6579324aSTerje Bergstrom return -EINVAL; 342*6579324aSTerje Bergstrom fw->reloc++; 343*6579324aSTerje Bergstrom fw->num_relocs--; 344*6579324aSTerje Bergstrom } 345*6579324aSTerje Bergstrom reg++; 346*6579324aSTerje Bergstrom fw->words--; 347*6579324aSTerje Bergstrom fw->offset++; 348*6579324aSTerje Bergstrom count--; 349*6579324aSTerje Bergstrom } 350*6579324aSTerje Bergstrom 351*6579324aSTerje Bergstrom return 0; 352*6579324aSTerje Bergstrom } 353*6579324aSTerje Bergstrom 354*6579324aSTerje Bergstrom static int check_nonincr(struct host1x_firewall *fw) 355*6579324aSTerje Bergstrom { 356*6579324aSTerje Bergstrom int is_addr_reg = fw->job->is_addr_reg(fw->dev, fw->class, fw->reg); 357*6579324aSTerje Bergstrom u32 count = fw->count; 358*6579324aSTerje Bergstrom 359*6579324aSTerje Bergstrom while (count) { 360*6579324aSTerje Bergstrom if (fw->words == 0) 361*6579324aSTerje Bergstrom return -EINVAL; 362*6579324aSTerje Bergstrom 363*6579324aSTerje Bergstrom if (is_addr_reg) { 364*6579324aSTerje Bergstrom bool bad_reloc = check_reloc(fw->reloc, fw->cmdbuf_id, 365*6579324aSTerje Bergstrom fw->offset); 366*6579324aSTerje Bergstrom if (!fw->num_relocs || bad_reloc) 367*6579324aSTerje Bergstrom return -EINVAL; 368*6579324aSTerje Bergstrom fw->reloc++; 369*6579324aSTerje Bergstrom fw->num_relocs--; 370*6579324aSTerje Bergstrom } 371*6579324aSTerje Bergstrom fw->words--; 372*6579324aSTerje Bergstrom fw->offset++; 373*6579324aSTerje Bergstrom count--; 374*6579324aSTerje Bergstrom } 375*6579324aSTerje Bergstrom 376*6579324aSTerje Bergstrom return 0; 377*6579324aSTerje Bergstrom } 378*6579324aSTerje Bergstrom 379*6579324aSTerje Bergstrom static int validate(struct host1x_job *job, struct device *dev, 380*6579324aSTerje Bergstrom struct host1x_job_gather *g) 381*6579324aSTerje Bergstrom { 382*6579324aSTerje Bergstrom u32 *cmdbuf_base; 383*6579324aSTerje Bergstrom int err = 0; 384*6579324aSTerje Bergstrom struct host1x_firewall fw; 385*6579324aSTerje Bergstrom 386*6579324aSTerje Bergstrom fw.job = job; 387*6579324aSTerje Bergstrom fw.dev = dev; 388*6579324aSTerje Bergstrom fw.reloc = job->relocarray; 389*6579324aSTerje Bergstrom fw.num_relocs = job->num_relocs; 390*6579324aSTerje Bergstrom fw.cmdbuf_id = g->bo; 391*6579324aSTerje Bergstrom 392*6579324aSTerje Bergstrom fw.offset = 0; 393*6579324aSTerje Bergstrom fw.class = 0; 394*6579324aSTerje Bergstrom 395*6579324aSTerje Bergstrom if (!job->is_addr_reg) 396*6579324aSTerje Bergstrom return 0; 397*6579324aSTerje Bergstrom 398*6579324aSTerje Bergstrom cmdbuf_base = host1x_bo_mmap(g->bo); 399*6579324aSTerje Bergstrom if (!cmdbuf_base) 400*6579324aSTerje Bergstrom return -ENOMEM; 401*6579324aSTerje Bergstrom 402*6579324aSTerje Bergstrom fw.words = g->words; 403*6579324aSTerje Bergstrom while (fw.words && !err) { 404*6579324aSTerje Bergstrom u32 word = cmdbuf_base[fw.offset]; 405*6579324aSTerje Bergstrom u32 opcode = (word & 0xf0000000) >> 28; 406*6579324aSTerje Bergstrom 407*6579324aSTerje Bergstrom fw.mask = 0; 408*6579324aSTerje Bergstrom fw.reg = 0; 409*6579324aSTerje Bergstrom fw.count = 0; 410*6579324aSTerje Bergstrom fw.words--; 411*6579324aSTerje Bergstrom fw.offset++; 412*6579324aSTerje Bergstrom 413*6579324aSTerje Bergstrom switch (opcode) { 414*6579324aSTerje Bergstrom case 0: 415*6579324aSTerje Bergstrom fw.class = word >> 6 & 0x3ff; 416*6579324aSTerje Bergstrom fw.mask = word & 0x3f; 417*6579324aSTerje Bergstrom fw.reg = word >> 16 & 0xfff; 418*6579324aSTerje Bergstrom err = check_mask(&fw); 419*6579324aSTerje Bergstrom if (err) 420*6579324aSTerje Bergstrom goto out; 421*6579324aSTerje Bergstrom break; 422*6579324aSTerje Bergstrom case 1: 423*6579324aSTerje Bergstrom fw.reg = word >> 16 & 0xfff; 424*6579324aSTerje Bergstrom fw.count = word & 0xffff; 425*6579324aSTerje Bergstrom err = check_incr(&fw); 426*6579324aSTerje Bergstrom if (err) 427*6579324aSTerje Bergstrom goto out; 428*6579324aSTerje Bergstrom break; 429*6579324aSTerje Bergstrom 430*6579324aSTerje Bergstrom case 2: 431*6579324aSTerje Bergstrom fw.reg = word >> 16 & 0xfff; 432*6579324aSTerje Bergstrom fw.count = word & 0xffff; 433*6579324aSTerje Bergstrom err = check_nonincr(&fw); 434*6579324aSTerje Bergstrom if (err) 435*6579324aSTerje Bergstrom goto out; 436*6579324aSTerje Bergstrom break; 437*6579324aSTerje Bergstrom 438*6579324aSTerje Bergstrom case 3: 439*6579324aSTerje Bergstrom fw.mask = word & 0xffff; 440*6579324aSTerje Bergstrom fw.reg = word >> 16 & 0xfff; 441*6579324aSTerje Bergstrom err = check_mask(&fw); 442*6579324aSTerje Bergstrom if (err) 443*6579324aSTerje Bergstrom goto out; 444*6579324aSTerje Bergstrom break; 445*6579324aSTerje Bergstrom case 4: 446*6579324aSTerje Bergstrom case 5: 447*6579324aSTerje Bergstrom case 14: 448*6579324aSTerje Bergstrom break; 449*6579324aSTerje Bergstrom default: 450*6579324aSTerje Bergstrom err = -EINVAL; 451*6579324aSTerje Bergstrom break; 452*6579324aSTerje Bergstrom } 453*6579324aSTerje Bergstrom } 454*6579324aSTerje Bergstrom 455*6579324aSTerje Bergstrom /* No relocs should remain at this point */ 456*6579324aSTerje Bergstrom if (fw.num_relocs) 457*6579324aSTerje Bergstrom err = -EINVAL; 458*6579324aSTerje Bergstrom 459*6579324aSTerje Bergstrom out: 460*6579324aSTerje Bergstrom host1x_bo_munmap(g->bo, cmdbuf_base); 461*6579324aSTerje Bergstrom 462*6579324aSTerje Bergstrom return err; 463*6579324aSTerje Bergstrom } 464*6579324aSTerje Bergstrom 465*6579324aSTerje Bergstrom static inline int copy_gathers(struct host1x_job *job, struct device *dev) 466*6579324aSTerje Bergstrom { 467*6579324aSTerje Bergstrom size_t size = 0; 468*6579324aSTerje Bergstrom size_t offset = 0; 469*6579324aSTerje Bergstrom int i; 470*6579324aSTerje Bergstrom 471*6579324aSTerje Bergstrom for (i = 0; i < job->num_gathers; i++) { 472*6579324aSTerje Bergstrom struct host1x_job_gather *g = &job->gathers[i]; 473*6579324aSTerje Bergstrom size += g->words * sizeof(u32); 474*6579324aSTerje Bergstrom } 475*6579324aSTerje Bergstrom 476*6579324aSTerje Bergstrom job->gather_copy_mapped = dma_alloc_writecombine(dev, size, 477*6579324aSTerje Bergstrom &job->gather_copy, 478*6579324aSTerje Bergstrom GFP_KERNEL); 479*6579324aSTerje Bergstrom if (!job->gather_copy_mapped) { 480*6579324aSTerje Bergstrom int err = PTR_ERR(job->gather_copy_mapped); 481*6579324aSTerje Bergstrom job->gather_copy_mapped = NULL; 482*6579324aSTerje Bergstrom return err; 483*6579324aSTerje Bergstrom } 484*6579324aSTerje Bergstrom 485*6579324aSTerje Bergstrom job->gather_copy_size = size; 486*6579324aSTerje Bergstrom 487*6579324aSTerje Bergstrom for (i = 0; i < job->num_gathers; i++) { 488*6579324aSTerje Bergstrom struct host1x_job_gather *g = &job->gathers[i]; 489*6579324aSTerje Bergstrom void *gather; 490*6579324aSTerje Bergstrom 491*6579324aSTerje Bergstrom gather = host1x_bo_mmap(g->bo); 492*6579324aSTerje Bergstrom memcpy(job->gather_copy_mapped + offset, gather + g->offset, 493*6579324aSTerje Bergstrom g->words * sizeof(u32)); 494*6579324aSTerje Bergstrom host1x_bo_munmap(g->bo, gather); 495*6579324aSTerje Bergstrom 496*6579324aSTerje Bergstrom g->base = job->gather_copy; 497*6579324aSTerje Bergstrom g->offset = offset; 498*6579324aSTerje Bergstrom g->bo = NULL; 499*6579324aSTerje Bergstrom 500*6579324aSTerje Bergstrom offset += g->words * sizeof(u32); 501*6579324aSTerje Bergstrom } 502*6579324aSTerje Bergstrom 503*6579324aSTerje Bergstrom return 0; 504*6579324aSTerje Bergstrom } 505*6579324aSTerje Bergstrom 506*6579324aSTerje Bergstrom int host1x_job_pin(struct host1x_job *job, struct device *dev) 507*6579324aSTerje Bergstrom { 508*6579324aSTerje Bergstrom int err; 509*6579324aSTerje Bergstrom unsigned int i, j; 510*6579324aSTerje Bergstrom struct host1x *host = dev_get_drvdata(dev->parent); 511*6579324aSTerje Bergstrom DECLARE_BITMAP(waitchk_mask, host1x_syncpt_nb_pts(host)); 512*6579324aSTerje Bergstrom 513*6579324aSTerje Bergstrom bitmap_zero(waitchk_mask, host1x_syncpt_nb_pts(host)); 514*6579324aSTerje Bergstrom for (i = 0; i < job->num_waitchk; i++) { 515*6579324aSTerje Bergstrom u32 syncpt_id = job->waitchk[i].syncpt_id; 516*6579324aSTerje Bergstrom if (syncpt_id < host1x_syncpt_nb_pts(host)) 517*6579324aSTerje Bergstrom set_bit(syncpt_id, waitchk_mask); 518*6579324aSTerje Bergstrom } 519*6579324aSTerje Bergstrom 520*6579324aSTerje Bergstrom /* get current syncpt values for waitchk */ 521*6579324aSTerje Bergstrom for_each_set_bit(i, waitchk_mask, host1x_syncpt_nb_pts(host)) 522*6579324aSTerje Bergstrom host1x_syncpt_load(host->syncpt + i); 523*6579324aSTerje Bergstrom 524*6579324aSTerje Bergstrom /* pin memory */ 525*6579324aSTerje Bergstrom err = pin_job(job); 526*6579324aSTerje Bergstrom if (!err) 527*6579324aSTerje Bergstrom goto out; 528*6579324aSTerje Bergstrom 529*6579324aSTerje Bergstrom /* patch gathers */ 530*6579324aSTerje Bergstrom for (i = 0; i < job->num_gathers; i++) { 531*6579324aSTerje Bergstrom struct host1x_job_gather *g = &job->gathers[i]; 532*6579324aSTerje Bergstrom 533*6579324aSTerje Bergstrom /* process each gather mem only once */ 534*6579324aSTerje Bergstrom if (g->handled) 535*6579324aSTerje Bergstrom continue; 536*6579324aSTerje Bergstrom 537*6579324aSTerje Bergstrom g->base = job->gather_addr_phys[i]; 538*6579324aSTerje Bergstrom 539*6579324aSTerje Bergstrom for (j = 0; j < job->num_gathers; j++) 540*6579324aSTerje Bergstrom if (job->gathers[j].bo == g->bo) 541*6579324aSTerje Bergstrom job->gathers[j].handled = true; 542*6579324aSTerje Bergstrom 543*6579324aSTerje Bergstrom err = 0; 544*6579324aSTerje Bergstrom 545*6579324aSTerje Bergstrom if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL)) 546*6579324aSTerje Bergstrom err = validate(job, dev, g); 547*6579324aSTerje Bergstrom 548*6579324aSTerje Bergstrom if (err) 549*6579324aSTerje Bergstrom dev_err(dev, "Job invalid (err=%d)\n", err); 550*6579324aSTerje Bergstrom 551*6579324aSTerje Bergstrom if (!err) 552*6579324aSTerje Bergstrom err = do_relocs(job, g->bo); 553*6579324aSTerje Bergstrom 554*6579324aSTerje Bergstrom if (!err) 555*6579324aSTerje Bergstrom err = do_waitchks(job, host, g->bo); 556*6579324aSTerje Bergstrom 557*6579324aSTerje Bergstrom if (err) 558*6579324aSTerje Bergstrom break; 559*6579324aSTerje Bergstrom } 560*6579324aSTerje Bergstrom 561*6579324aSTerje Bergstrom if (IS_ENABLED(CONFIG_TEGRA_HOST1X_FIREWALL) && !err) { 562*6579324aSTerje Bergstrom err = copy_gathers(job, dev); 563*6579324aSTerje Bergstrom if (err) { 564*6579324aSTerje Bergstrom host1x_job_unpin(job); 565*6579324aSTerje Bergstrom return err; 566*6579324aSTerje Bergstrom } 567*6579324aSTerje Bergstrom } 568*6579324aSTerje Bergstrom 569*6579324aSTerje Bergstrom out: 570*6579324aSTerje Bergstrom wmb(); 571*6579324aSTerje Bergstrom 572*6579324aSTerje Bergstrom return err; 573*6579324aSTerje Bergstrom } 574*6579324aSTerje Bergstrom 575*6579324aSTerje Bergstrom void host1x_job_unpin(struct host1x_job *job) 576*6579324aSTerje Bergstrom { 577*6579324aSTerje Bergstrom unsigned int i; 578*6579324aSTerje Bergstrom 579*6579324aSTerje Bergstrom for (i = 0; i < job->num_unpins; i++) { 580*6579324aSTerje Bergstrom struct host1x_job_unpin_data *unpin = &job->unpins[i]; 581*6579324aSTerje Bergstrom host1x_bo_unpin(unpin->bo, unpin->sgt); 582*6579324aSTerje Bergstrom host1x_bo_put(unpin->bo); 583*6579324aSTerje Bergstrom } 584*6579324aSTerje Bergstrom job->num_unpins = 0; 585*6579324aSTerje Bergstrom 586*6579324aSTerje Bergstrom if (job->gather_copy_size) 587*6579324aSTerje Bergstrom dma_free_writecombine(job->channel->dev, job->gather_copy_size, 588*6579324aSTerje Bergstrom job->gather_copy_mapped, 589*6579324aSTerje Bergstrom job->gather_copy); 590*6579324aSTerje Bergstrom } 591*6579324aSTerje Bergstrom 592*6579324aSTerje Bergstrom /* 593*6579324aSTerje Bergstrom * Debug routine used to dump job entries 594*6579324aSTerje Bergstrom */ 595*6579324aSTerje Bergstrom void host1x_job_dump(struct device *dev, struct host1x_job *job) 596*6579324aSTerje Bergstrom { 597*6579324aSTerje Bergstrom dev_dbg(dev, " SYNCPT_ID %d\n", job->syncpt_id); 598*6579324aSTerje Bergstrom dev_dbg(dev, " SYNCPT_VAL %d\n", job->syncpt_end); 599*6579324aSTerje Bergstrom dev_dbg(dev, " FIRST_GET 0x%x\n", job->first_get); 600*6579324aSTerje Bergstrom dev_dbg(dev, " TIMEOUT %d\n", job->timeout); 601*6579324aSTerje Bergstrom dev_dbg(dev, " NUM_SLOTS %d\n", job->num_slots); 602*6579324aSTerje Bergstrom dev_dbg(dev, " NUM_HANDLES %d\n", job->num_unpins); 603*6579324aSTerje Bergstrom } 604