18c2ecf20Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0-only 28c2ecf20Sopenharmony_ci/* 38c2ecf20Sopenharmony_ci * Tegra host1x Syncpoints 48c2ecf20Sopenharmony_ci * 58c2ecf20Sopenharmony_ci * Copyright (c) 2010-2015, NVIDIA Corporation. 68c2ecf20Sopenharmony_ci */ 78c2ecf20Sopenharmony_ci 88c2ecf20Sopenharmony_ci#include <linux/module.h> 98c2ecf20Sopenharmony_ci#include <linux/device.h> 108c2ecf20Sopenharmony_ci#include <linux/slab.h> 118c2ecf20Sopenharmony_ci 128c2ecf20Sopenharmony_ci#include <trace/events/host1x.h> 138c2ecf20Sopenharmony_ci 148c2ecf20Sopenharmony_ci#include "syncpt.h" 158c2ecf20Sopenharmony_ci#include "dev.h" 168c2ecf20Sopenharmony_ci#include "intr.h" 178c2ecf20Sopenharmony_ci#include "debug.h" 188c2ecf20Sopenharmony_ci 198c2ecf20Sopenharmony_ci#define SYNCPT_CHECK_PERIOD (2 * HZ) 208c2ecf20Sopenharmony_ci#define MAX_STUCK_CHECK_COUNT 15 218c2ecf20Sopenharmony_ci 228c2ecf20Sopenharmony_cistatic struct host1x_syncpt_base * 238c2ecf20Sopenharmony_cihost1x_syncpt_base_request(struct host1x *host) 248c2ecf20Sopenharmony_ci{ 258c2ecf20Sopenharmony_ci struct host1x_syncpt_base *bases = host->bases; 268c2ecf20Sopenharmony_ci unsigned int i; 278c2ecf20Sopenharmony_ci 288c2ecf20Sopenharmony_ci for (i = 0; i < host->info->nb_bases; i++) 298c2ecf20Sopenharmony_ci if (!bases[i].requested) 308c2ecf20Sopenharmony_ci break; 318c2ecf20Sopenharmony_ci 328c2ecf20Sopenharmony_ci if (i >= host->info->nb_bases) 338c2ecf20Sopenharmony_ci return NULL; 348c2ecf20Sopenharmony_ci 358c2ecf20Sopenharmony_ci bases[i].requested = true; 368c2ecf20Sopenharmony_ci return &bases[i]; 378c2ecf20Sopenharmony_ci} 388c2ecf20Sopenharmony_ci 398c2ecf20Sopenharmony_cistatic void host1x_syncpt_base_free(struct host1x_syncpt_base *base) 408c2ecf20Sopenharmony_ci{ 418c2ecf20Sopenharmony_ci if (base) 428c2ecf20Sopenharmony_ci base->requested = false; 438c2ecf20Sopenharmony_ci} 448c2ecf20Sopenharmony_ci 458c2ecf20Sopenharmony_cistatic struct host1x_syncpt *host1x_syncpt_alloc(struct host1x *host, 468c2ecf20Sopenharmony_ci struct host1x_client *client, 478c2ecf20Sopenharmony_ci unsigned long flags) 488c2ecf20Sopenharmony_ci{ 498c2ecf20Sopenharmony_ci struct host1x_syncpt *sp = host->syncpt; 508c2ecf20Sopenharmony_ci unsigned int i; 518c2ecf20Sopenharmony_ci char *name; 528c2ecf20Sopenharmony_ci 538c2ecf20Sopenharmony_ci mutex_lock(&host->syncpt_mutex); 548c2ecf20Sopenharmony_ci 558c2ecf20Sopenharmony_ci for (i = 0; i < host->info->nb_pts && sp->name; i++, sp++) 568c2ecf20Sopenharmony_ci ; 578c2ecf20Sopenharmony_ci 588c2ecf20Sopenharmony_ci if (i >= host->info->nb_pts) 598c2ecf20Sopenharmony_ci goto unlock; 608c2ecf20Sopenharmony_ci 618c2ecf20Sopenharmony_ci if (flags & HOST1X_SYNCPT_HAS_BASE) { 628c2ecf20Sopenharmony_ci sp->base = host1x_syncpt_base_request(host); 638c2ecf20Sopenharmony_ci if (!sp->base) 648c2ecf20Sopenharmony_ci goto unlock; 658c2ecf20Sopenharmony_ci } 668c2ecf20Sopenharmony_ci 678c2ecf20Sopenharmony_ci name = kasprintf(GFP_KERNEL, "%02u-%s", sp->id, 688c2ecf20Sopenharmony_ci client ? dev_name(client->dev) : NULL); 698c2ecf20Sopenharmony_ci if (!name) 708c2ecf20Sopenharmony_ci goto free_base; 718c2ecf20Sopenharmony_ci 728c2ecf20Sopenharmony_ci sp->client = client; 738c2ecf20Sopenharmony_ci sp->name = name; 748c2ecf20Sopenharmony_ci 758c2ecf20Sopenharmony_ci if (flags & HOST1X_SYNCPT_CLIENT_MANAGED) 768c2ecf20Sopenharmony_ci sp->client_managed = true; 778c2ecf20Sopenharmony_ci else 788c2ecf20Sopenharmony_ci sp->client_managed = false; 798c2ecf20Sopenharmony_ci 808c2ecf20Sopenharmony_ci mutex_unlock(&host->syncpt_mutex); 818c2ecf20Sopenharmony_ci return sp; 828c2ecf20Sopenharmony_ci 838c2ecf20Sopenharmony_cifree_base: 848c2ecf20Sopenharmony_ci host1x_syncpt_base_free(sp->base); 858c2ecf20Sopenharmony_ci sp->base = NULL; 868c2ecf20Sopenharmony_ciunlock: 878c2ecf20Sopenharmony_ci mutex_unlock(&host->syncpt_mutex); 888c2ecf20Sopenharmony_ci return NULL; 898c2ecf20Sopenharmony_ci} 908c2ecf20Sopenharmony_ci 918c2ecf20Sopenharmony_ci/** 928c2ecf20Sopenharmony_ci * host1x_syncpt_id() - retrieve syncpoint ID 938c2ecf20Sopenharmony_ci * @sp: host1x syncpoint 948c2ecf20Sopenharmony_ci * 958c2ecf20Sopenharmony_ci * Given a pointer to a struct host1x_syncpt, retrieves its ID. This ID is 968c2ecf20Sopenharmony_ci * often used as a value to program into registers that control how hardware 978c2ecf20Sopenharmony_ci * blocks interact with syncpoints. 988c2ecf20Sopenharmony_ci */ 998c2ecf20Sopenharmony_ciu32 host1x_syncpt_id(struct host1x_syncpt *sp) 1008c2ecf20Sopenharmony_ci{ 1018c2ecf20Sopenharmony_ci return sp->id; 1028c2ecf20Sopenharmony_ci} 1038c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_id); 1048c2ecf20Sopenharmony_ci 1058c2ecf20Sopenharmony_ci/** 1068c2ecf20Sopenharmony_ci * host1x_syncpt_incr_max() - update the value sent to hardware 1078c2ecf20Sopenharmony_ci * @sp: host1x syncpoint 1088c2ecf20Sopenharmony_ci * @incrs: number of increments 1098c2ecf20Sopenharmony_ci */ 1108c2ecf20Sopenharmony_ciu32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs) 1118c2ecf20Sopenharmony_ci{ 1128c2ecf20Sopenharmony_ci return (u32)atomic_add_return(incrs, &sp->max_val); 1138c2ecf20Sopenharmony_ci} 1148c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_incr_max); 1158c2ecf20Sopenharmony_ci 1168c2ecf20Sopenharmony_ci /* 1178c2ecf20Sopenharmony_ci * Write cached syncpoint and waitbase values to hardware. 1188c2ecf20Sopenharmony_ci */ 1198c2ecf20Sopenharmony_civoid host1x_syncpt_restore(struct host1x *host) 1208c2ecf20Sopenharmony_ci{ 1218c2ecf20Sopenharmony_ci struct host1x_syncpt *sp_base = host->syncpt; 1228c2ecf20Sopenharmony_ci unsigned int i; 1238c2ecf20Sopenharmony_ci 1248c2ecf20Sopenharmony_ci for (i = 0; i < host1x_syncpt_nb_pts(host); i++) 1258c2ecf20Sopenharmony_ci host1x_hw_syncpt_restore(host, sp_base + i); 1268c2ecf20Sopenharmony_ci 1278c2ecf20Sopenharmony_ci for (i = 0; i < host1x_syncpt_nb_bases(host); i++) 1288c2ecf20Sopenharmony_ci host1x_hw_syncpt_restore_wait_base(host, sp_base + i); 1298c2ecf20Sopenharmony_ci 1308c2ecf20Sopenharmony_ci wmb(); 1318c2ecf20Sopenharmony_ci} 1328c2ecf20Sopenharmony_ci 1338c2ecf20Sopenharmony_ci/* 1348c2ecf20Sopenharmony_ci * Update the cached syncpoint and waitbase values by reading them 1358c2ecf20Sopenharmony_ci * from the registers. 1368c2ecf20Sopenharmony_ci */ 1378c2ecf20Sopenharmony_civoid host1x_syncpt_save(struct host1x *host) 1388c2ecf20Sopenharmony_ci{ 1398c2ecf20Sopenharmony_ci struct host1x_syncpt *sp_base = host->syncpt; 1408c2ecf20Sopenharmony_ci unsigned int i; 1418c2ecf20Sopenharmony_ci 1428c2ecf20Sopenharmony_ci for (i = 0; i < host1x_syncpt_nb_pts(host); i++) { 1438c2ecf20Sopenharmony_ci if (host1x_syncpt_client_managed(sp_base + i)) 1448c2ecf20Sopenharmony_ci host1x_hw_syncpt_load(host, sp_base + i); 1458c2ecf20Sopenharmony_ci else 1468c2ecf20Sopenharmony_ci WARN_ON(!host1x_syncpt_idle(sp_base + i)); 1478c2ecf20Sopenharmony_ci } 1488c2ecf20Sopenharmony_ci 1498c2ecf20Sopenharmony_ci for (i = 0; i < host1x_syncpt_nb_bases(host); i++) 1508c2ecf20Sopenharmony_ci host1x_hw_syncpt_load_wait_base(host, sp_base + i); 1518c2ecf20Sopenharmony_ci} 1528c2ecf20Sopenharmony_ci 1538c2ecf20Sopenharmony_ci/* 1548c2ecf20Sopenharmony_ci * Updates the cached syncpoint value by reading a new value from the hardware 1558c2ecf20Sopenharmony_ci * register 1568c2ecf20Sopenharmony_ci */ 1578c2ecf20Sopenharmony_ciu32 host1x_syncpt_load(struct host1x_syncpt *sp) 1588c2ecf20Sopenharmony_ci{ 1598c2ecf20Sopenharmony_ci u32 val; 1608c2ecf20Sopenharmony_ci 1618c2ecf20Sopenharmony_ci val = host1x_hw_syncpt_load(sp->host, sp); 1628c2ecf20Sopenharmony_ci trace_host1x_syncpt_load_min(sp->id, val); 1638c2ecf20Sopenharmony_ci 1648c2ecf20Sopenharmony_ci return val; 1658c2ecf20Sopenharmony_ci} 1668c2ecf20Sopenharmony_ci 1678c2ecf20Sopenharmony_ci/* 1688c2ecf20Sopenharmony_ci * Get the current syncpoint base 1698c2ecf20Sopenharmony_ci */ 1708c2ecf20Sopenharmony_ciu32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp) 1718c2ecf20Sopenharmony_ci{ 1728c2ecf20Sopenharmony_ci host1x_hw_syncpt_load_wait_base(sp->host, sp); 1738c2ecf20Sopenharmony_ci 1748c2ecf20Sopenharmony_ci return sp->base_val; 1758c2ecf20Sopenharmony_ci} 1768c2ecf20Sopenharmony_ci 1778c2ecf20Sopenharmony_ci/** 1788c2ecf20Sopenharmony_ci * host1x_syncpt_incr() - increment syncpoint value from CPU, updating cache 1798c2ecf20Sopenharmony_ci * @sp: host1x syncpoint 1808c2ecf20Sopenharmony_ci */ 1818c2ecf20Sopenharmony_ciint host1x_syncpt_incr(struct host1x_syncpt *sp) 1828c2ecf20Sopenharmony_ci{ 1838c2ecf20Sopenharmony_ci return host1x_hw_syncpt_cpu_incr(sp->host, sp); 1848c2ecf20Sopenharmony_ci} 1858c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_incr); 1868c2ecf20Sopenharmony_ci 1878c2ecf20Sopenharmony_ci/* 1888c2ecf20Sopenharmony_ci * Updated sync point form hardware, and returns true if syncpoint is expired, 1898c2ecf20Sopenharmony_ci * false if we may need to wait 1908c2ecf20Sopenharmony_ci */ 1918c2ecf20Sopenharmony_cistatic bool syncpt_load_min_is_expired(struct host1x_syncpt *sp, u32 thresh) 1928c2ecf20Sopenharmony_ci{ 1938c2ecf20Sopenharmony_ci host1x_hw_syncpt_load(sp->host, sp); 1948c2ecf20Sopenharmony_ci 1958c2ecf20Sopenharmony_ci return host1x_syncpt_is_expired(sp, thresh); 1968c2ecf20Sopenharmony_ci} 1978c2ecf20Sopenharmony_ci 1988c2ecf20Sopenharmony_ci/** 1998c2ecf20Sopenharmony_ci * host1x_syncpt_wait() - wait for a syncpoint to reach a given value 2008c2ecf20Sopenharmony_ci * @sp: host1x syncpoint 2018c2ecf20Sopenharmony_ci * @thresh: threshold 2028c2ecf20Sopenharmony_ci * @timeout: maximum time to wait for the syncpoint to reach the given value 2038c2ecf20Sopenharmony_ci * @value: return location for the syncpoint value 2048c2ecf20Sopenharmony_ci */ 2058c2ecf20Sopenharmony_ciint host1x_syncpt_wait(struct host1x_syncpt *sp, u32 thresh, long timeout, 2068c2ecf20Sopenharmony_ci u32 *value) 2078c2ecf20Sopenharmony_ci{ 2088c2ecf20Sopenharmony_ci DECLARE_WAIT_QUEUE_HEAD_ONSTACK(wq); 2098c2ecf20Sopenharmony_ci void *ref; 2108c2ecf20Sopenharmony_ci struct host1x_waitlist *waiter; 2118c2ecf20Sopenharmony_ci int err = 0, check_count = 0; 2128c2ecf20Sopenharmony_ci u32 val; 2138c2ecf20Sopenharmony_ci 2148c2ecf20Sopenharmony_ci if (value) 2158c2ecf20Sopenharmony_ci *value = 0; 2168c2ecf20Sopenharmony_ci 2178c2ecf20Sopenharmony_ci /* first check cache */ 2188c2ecf20Sopenharmony_ci if (host1x_syncpt_is_expired(sp, thresh)) { 2198c2ecf20Sopenharmony_ci if (value) 2208c2ecf20Sopenharmony_ci *value = host1x_syncpt_load(sp); 2218c2ecf20Sopenharmony_ci 2228c2ecf20Sopenharmony_ci return 0; 2238c2ecf20Sopenharmony_ci } 2248c2ecf20Sopenharmony_ci 2258c2ecf20Sopenharmony_ci /* try to read from register */ 2268c2ecf20Sopenharmony_ci val = host1x_hw_syncpt_load(sp->host, sp); 2278c2ecf20Sopenharmony_ci if (host1x_syncpt_is_expired(sp, thresh)) { 2288c2ecf20Sopenharmony_ci if (value) 2298c2ecf20Sopenharmony_ci *value = val; 2308c2ecf20Sopenharmony_ci 2318c2ecf20Sopenharmony_ci goto done; 2328c2ecf20Sopenharmony_ci } 2338c2ecf20Sopenharmony_ci 2348c2ecf20Sopenharmony_ci if (!timeout) { 2358c2ecf20Sopenharmony_ci err = -EAGAIN; 2368c2ecf20Sopenharmony_ci goto done; 2378c2ecf20Sopenharmony_ci } 2388c2ecf20Sopenharmony_ci 2398c2ecf20Sopenharmony_ci /* allocate a waiter */ 2408c2ecf20Sopenharmony_ci waiter = kzalloc(sizeof(*waiter), GFP_KERNEL); 2418c2ecf20Sopenharmony_ci if (!waiter) { 2428c2ecf20Sopenharmony_ci err = -ENOMEM; 2438c2ecf20Sopenharmony_ci goto done; 2448c2ecf20Sopenharmony_ci } 2458c2ecf20Sopenharmony_ci 2468c2ecf20Sopenharmony_ci /* schedule a wakeup when the syncpoint value is reached */ 2478c2ecf20Sopenharmony_ci err = host1x_intr_add_action(sp->host, sp, thresh, 2488c2ecf20Sopenharmony_ci HOST1X_INTR_ACTION_WAKEUP_INTERRUPTIBLE, 2498c2ecf20Sopenharmony_ci &wq, waiter, &ref); 2508c2ecf20Sopenharmony_ci if (err) 2518c2ecf20Sopenharmony_ci goto done; 2528c2ecf20Sopenharmony_ci 2538c2ecf20Sopenharmony_ci err = -EAGAIN; 2548c2ecf20Sopenharmony_ci /* Caller-specified timeout may be impractically low */ 2558c2ecf20Sopenharmony_ci if (timeout < 0) 2568c2ecf20Sopenharmony_ci timeout = LONG_MAX; 2578c2ecf20Sopenharmony_ci 2588c2ecf20Sopenharmony_ci /* wait for the syncpoint, or timeout, or signal */ 2598c2ecf20Sopenharmony_ci while (timeout) { 2608c2ecf20Sopenharmony_ci long check = min_t(long, SYNCPT_CHECK_PERIOD, timeout); 2618c2ecf20Sopenharmony_ci int remain; 2628c2ecf20Sopenharmony_ci 2638c2ecf20Sopenharmony_ci remain = wait_event_interruptible_timeout(wq, 2648c2ecf20Sopenharmony_ci syncpt_load_min_is_expired(sp, thresh), 2658c2ecf20Sopenharmony_ci check); 2668c2ecf20Sopenharmony_ci if (remain > 0 || host1x_syncpt_is_expired(sp, thresh)) { 2678c2ecf20Sopenharmony_ci if (value) 2688c2ecf20Sopenharmony_ci *value = host1x_syncpt_load(sp); 2698c2ecf20Sopenharmony_ci 2708c2ecf20Sopenharmony_ci err = 0; 2718c2ecf20Sopenharmony_ci 2728c2ecf20Sopenharmony_ci break; 2738c2ecf20Sopenharmony_ci } 2748c2ecf20Sopenharmony_ci 2758c2ecf20Sopenharmony_ci if (remain < 0) { 2768c2ecf20Sopenharmony_ci err = remain; 2778c2ecf20Sopenharmony_ci break; 2788c2ecf20Sopenharmony_ci } 2798c2ecf20Sopenharmony_ci 2808c2ecf20Sopenharmony_ci timeout -= check; 2818c2ecf20Sopenharmony_ci 2828c2ecf20Sopenharmony_ci if (timeout && check_count <= MAX_STUCK_CHECK_COUNT) { 2838c2ecf20Sopenharmony_ci dev_warn(sp->host->dev, 2848c2ecf20Sopenharmony_ci "%s: syncpoint id %u (%s) stuck waiting %d, timeout=%ld\n", 2858c2ecf20Sopenharmony_ci current->comm, sp->id, sp->name, 2868c2ecf20Sopenharmony_ci thresh, timeout); 2878c2ecf20Sopenharmony_ci 2888c2ecf20Sopenharmony_ci host1x_debug_dump_syncpts(sp->host); 2898c2ecf20Sopenharmony_ci 2908c2ecf20Sopenharmony_ci if (check_count == MAX_STUCK_CHECK_COUNT) 2918c2ecf20Sopenharmony_ci host1x_debug_dump(sp->host); 2928c2ecf20Sopenharmony_ci 2938c2ecf20Sopenharmony_ci check_count++; 2948c2ecf20Sopenharmony_ci } 2958c2ecf20Sopenharmony_ci } 2968c2ecf20Sopenharmony_ci 2978c2ecf20Sopenharmony_ci host1x_intr_put_ref(sp->host, sp->id, ref); 2988c2ecf20Sopenharmony_ci 2998c2ecf20Sopenharmony_cidone: 3008c2ecf20Sopenharmony_ci return err; 3018c2ecf20Sopenharmony_ci} 3028c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_wait); 3038c2ecf20Sopenharmony_ci 3048c2ecf20Sopenharmony_ci/* 3058c2ecf20Sopenharmony_ci * Returns true if syncpoint is expired, false if we may need to wait 3068c2ecf20Sopenharmony_ci */ 3078c2ecf20Sopenharmony_cibool host1x_syncpt_is_expired(struct host1x_syncpt *sp, u32 thresh) 3088c2ecf20Sopenharmony_ci{ 3098c2ecf20Sopenharmony_ci u32 current_val; 3108c2ecf20Sopenharmony_ci u32 future_val; 3118c2ecf20Sopenharmony_ci 3128c2ecf20Sopenharmony_ci smp_rmb(); 3138c2ecf20Sopenharmony_ci 3148c2ecf20Sopenharmony_ci current_val = (u32)atomic_read(&sp->min_val); 3158c2ecf20Sopenharmony_ci future_val = (u32)atomic_read(&sp->max_val); 3168c2ecf20Sopenharmony_ci 3178c2ecf20Sopenharmony_ci /* Note the use of unsigned arithmetic here (mod 1<<32). 3188c2ecf20Sopenharmony_ci * 3198c2ecf20Sopenharmony_ci * c = current_val = min_val = the current value of the syncpoint. 3208c2ecf20Sopenharmony_ci * t = thresh = the value we are checking 3218c2ecf20Sopenharmony_ci * f = future_val = max_val = the value c will reach when all 3228c2ecf20Sopenharmony_ci * outstanding increments have completed. 3238c2ecf20Sopenharmony_ci * 3248c2ecf20Sopenharmony_ci * Note that c always chases f until it reaches f. 3258c2ecf20Sopenharmony_ci * 3268c2ecf20Sopenharmony_ci * Dtf = (f - t) 3278c2ecf20Sopenharmony_ci * Dtc = (c - t) 3288c2ecf20Sopenharmony_ci * 3298c2ecf20Sopenharmony_ci * Consider all cases: 3308c2ecf20Sopenharmony_ci * 3318c2ecf20Sopenharmony_ci * A) .....c..t..f..... Dtf < Dtc need to wait 3328c2ecf20Sopenharmony_ci * B) .....c.....f..t.. Dtf > Dtc expired 3338c2ecf20Sopenharmony_ci * C) ..t..c.....f..... Dtf > Dtc expired (Dct very large) 3348c2ecf20Sopenharmony_ci * 3358c2ecf20Sopenharmony_ci * Any case where f==c: always expired (for any t). Dtf == Dcf 3368c2ecf20Sopenharmony_ci * Any case where t==c: always expired (for any f). Dtf >= Dtc (because Dtc==0) 3378c2ecf20Sopenharmony_ci * Any case where t==f!=c: always wait. Dtf < Dtc (because Dtf==0, 3388c2ecf20Sopenharmony_ci * Dtc!=0) 3398c2ecf20Sopenharmony_ci * 3408c2ecf20Sopenharmony_ci * Other cases: 3418c2ecf20Sopenharmony_ci * 3428c2ecf20Sopenharmony_ci * A) .....t..f..c..... Dtf < Dtc need to wait 3438c2ecf20Sopenharmony_ci * A) .....f..c..t..... Dtf < Dtc need to wait 3448c2ecf20Sopenharmony_ci * A) .....f..t..c..... Dtf > Dtc expired 3458c2ecf20Sopenharmony_ci * 3468c2ecf20Sopenharmony_ci * So: 3478c2ecf20Sopenharmony_ci * Dtf >= Dtc implies EXPIRED (return true) 3488c2ecf20Sopenharmony_ci * Dtf < Dtc implies WAIT (return false) 3498c2ecf20Sopenharmony_ci * 3508c2ecf20Sopenharmony_ci * Note: If t is expired then we *cannot* wait on it. We would wait 3518c2ecf20Sopenharmony_ci * forever (hang the system). 3528c2ecf20Sopenharmony_ci * 3538c2ecf20Sopenharmony_ci * Note: do NOT get clever and remove the -thresh from both sides. It 3548c2ecf20Sopenharmony_ci * is NOT the same. 3558c2ecf20Sopenharmony_ci * 3568c2ecf20Sopenharmony_ci * If future valueis zero, we have a client managed sync point. In that 3578c2ecf20Sopenharmony_ci * case we do a direct comparison. 3588c2ecf20Sopenharmony_ci */ 3598c2ecf20Sopenharmony_ci if (!host1x_syncpt_client_managed(sp)) 3608c2ecf20Sopenharmony_ci return future_val - thresh >= current_val - thresh; 3618c2ecf20Sopenharmony_ci else 3628c2ecf20Sopenharmony_ci return (s32)(current_val - thresh) >= 0; 3638c2ecf20Sopenharmony_ci} 3648c2ecf20Sopenharmony_ci 3658c2ecf20Sopenharmony_ciint host1x_syncpt_init(struct host1x *host) 3668c2ecf20Sopenharmony_ci{ 3678c2ecf20Sopenharmony_ci struct host1x_syncpt_base *bases; 3688c2ecf20Sopenharmony_ci struct host1x_syncpt *syncpt; 3698c2ecf20Sopenharmony_ci unsigned int i; 3708c2ecf20Sopenharmony_ci 3718c2ecf20Sopenharmony_ci syncpt = devm_kcalloc(host->dev, host->info->nb_pts, sizeof(*syncpt), 3728c2ecf20Sopenharmony_ci GFP_KERNEL); 3738c2ecf20Sopenharmony_ci if (!syncpt) 3748c2ecf20Sopenharmony_ci return -ENOMEM; 3758c2ecf20Sopenharmony_ci 3768c2ecf20Sopenharmony_ci bases = devm_kcalloc(host->dev, host->info->nb_bases, sizeof(*bases), 3778c2ecf20Sopenharmony_ci GFP_KERNEL); 3788c2ecf20Sopenharmony_ci if (!bases) 3798c2ecf20Sopenharmony_ci return -ENOMEM; 3808c2ecf20Sopenharmony_ci 3818c2ecf20Sopenharmony_ci for (i = 0; i < host->info->nb_pts; i++) { 3828c2ecf20Sopenharmony_ci syncpt[i].id = i; 3838c2ecf20Sopenharmony_ci syncpt[i].host = host; 3848c2ecf20Sopenharmony_ci 3858c2ecf20Sopenharmony_ci /* 3868c2ecf20Sopenharmony_ci * Unassign syncpt from channels for purposes of Tegra186 3878c2ecf20Sopenharmony_ci * syncpoint protection. This prevents any channel from 3888c2ecf20Sopenharmony_ci * accessing it until it is reassigned. 3898c2ecf20Sopenharmony_ci */ 3908c2ecf20Sopenharmony_ci host1x_hw_syncpt_assign_to_channel(host, &syncpt[i], NULL); 3918c2ecf20Sopenharmony_ci } 3928c2ecf20Sopenharmony_ci 3938c2ecf20Sopenharmony_ci for (i = 0; i < host->info->nb_bases; i++) 3948c2ecf20Sopenharmony_ci bases[i].id = i; 3958c2ecf20Sopenharmony_ci 3968c2ecf20Sopenharmony_ci mutex_init(&host->syncpt_mutex); 3978c2ecf20Sopenharmony_ci host->syncpt = syncpt; 3988c2ecf20Sopenharmony_ci host->bases = bases; 3998c2ecf20Sopenharmony_ci 4008c2ecf20Sopenharmony_ci host1x_syncpt_restore(host); 4018c2ecf20Sopenharmony_ci host1x_hw_syncpt_enable_protection(host); 4028c2ecf20Sopenharmony_ci 4038c2ecf20Sopenharmony_ci /* Allocate sync point to use for clearing waits for expired fences */ 4048c2ecf20Sopenharmony_ci host->nop_sp = host1x_syncpt_alloc(host, NULL, 0); 4058c2ecf20Sopenharmony_ci if (!host->nop_sp) 4068c2ecf20Sopenharmony_ci return -ENOMEM; 4078c2ecf20Sopenharmony_ci 4088c2ecf20Sopenharmony_ci return 0; 4098c2ecf20Sopenharmony_ci} 4108c2ecf20Sopenharmony_ci 4118c2ecf20Sopenharmony_ci/** 4128c2ecf20Sopenharmony_ci * host1x_syncpt_request() - request a syncpoint 4138c2ecf20Sopenharmony_ci * @client: client requesting the syncpoint 4148c2ecf20Sopenharmony_ci * @flags: flags 4158c2ecf20Sopenharmony_ci * 4168c2ecf20Sopenharmony_ci * host1x client drivers can use this function to allocate a syncpoint for 4178c2ecf20Sopenharmony_ci * subsequent use. A syncpoint returned by this function will be reserved for 4188c2ecf20Sopenharmony_ci * use by the client exclusively. When no longer using a syncpoint, a host1x 4198c2ecf20Sopenharmony_ci * client driver needs to release it using host1x_syncpt_free(). 4208c2ecf20Sopenharmony_ci */ 4218c2ecf20Sopenharmony_cistruct host1x_syncpt *host1x_syncpt_request(struct host1x_client *client, 4228c2ecf20Sopenharmony_ci unsigned long flags) 4238c2ecf20Sopenharmony_ci{ 4248c2ecf20Sopenharmony_ci struct host1x *host = dev_get_drvdata(client->host->parent); 4258c2ecf20Sopenharmony_ci 4268c2ecf20Sopenharmony_ci return host1x_syncpt_alloc(host, client, flags); 4278c2ecf20Sopenharmony_ci} 4288c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_request); 4298c2ecf20Sopenharmony_ci 4308c2ecf20Sopenharmony_ci/** 4318c2ecf20Sopenharmony_ci * host1x_syncpt_free() - free a requested syncpoint 4328c2ecf20Sopenharmony_ci * @sp: host1x syncpoint 4338c2ecf20Sopenharmony_ci * 4348c2ecf20Sopenharmony_ci * Release a syncpoint previously allocated using host1x_syncpt_request(). A 4358c2ecf20Sopenharmony_ci * host1x client driver should call this when the syncpoint is no longer in 4368c2ecf20Sopenharmony_ci * use. Note that client drivers must ensure that the syncpoint doesn't remain 4378c2ecf20Sopenharmony_ci * under the control of hardware after calling this function, otherwise two 4388c2ecf20Sopenharmony_ci * clients may end up trying to access the same syncpoint concurrently. 4398c2ecf20Sopenharmony_ci */ 4408c2ecf20Sopenharmony_civoid host1x_syncpt_free(struct host1x_syncpt *sp) 4418c2ecf20Sopenharmony_ci{ 4428c2ecf20Sopenharmony_ci if (!sp) 4438c2ecf20Sopenharmony_ci return; 4448c2ecf20Sopenharmony_ci 4458c2ecf20Sopenharmony_ci mutex_lock(&sp->host->syncpt_mutex); 4468c2ecf20Sopenharmony_ci 4478c2ecf20Sopenharmony_ci host1x_syncpt_base_free(sp->base); 4488c2ecf20Sopenharmony_ci kfree(sp->name); 4498c2ecf20Sopenharmony_ci sp->base = NULL; 4508c2ecf20Sopenharmony_ci sp->client = NULL; 4518c2ecf20Sopenharmony_ci sp->name = NULL; 4528c2ecf20Sopenharmony_ci sp->client_managed = false; 4538c2ecf20Sopenharmony_ci 4548c2ecf20Sopenharmony_ci mutex_unlock(&sp->host->syncpt_mutex); 4558c2ecf20Sopenharmony_ci} 4568c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_free); 4578c2ecf20Sopenharmony_ci 4588c2ecf20Sopenharmony_civoid host1x_syncpt_deinit(struct host1x *host) 4598c2ecf20Sopenharmony_ci{ 4608c2ecf20Sopenharmony_ci struct host1x_syncpt *sp = host->syncpt; 4618c2ecf20Sopenharmony_ci unsigned int i; 4628c2ecf20Sopenharmony_ci 4638c2ecf20Sopenharmony_ci for (i = 0; i < host->info->nb_pts; i++, sp++) 4648c2ecf20Sopenharmony_ci kfree(sp->name); 4658c2ecf20Sopenharmony_ci} 4668c2ecf20Sopenharmony_ci 4678c2ecf20Sopenharmony_ci/** 4688c2ecf20Sopenharmony_ci * host1x_syncpt_read_max() - read maximum syncpoint value 4698c2ecf20Sopenharmony_ci * @sp: host1x syncpoint 4708c2ecf20Sopenharmony_ci * 4718c2ecf20Sopenharmony_ci * The maximum syncpoint value indicates how many operations there are in 4728c2ecf20Sopenharmony_ci * queue, either in channel or in a software thread. 4738c2ecf20Sopenharmony_ci */ 4748c2ecf20Sopenharmony_ciu32 host1x_syncpt_read_max(struct host1x_syncpt *sp) 4758c2ecf20Sopenharmony_ci{ 4768c2ecf20Sopenharmony_ci smp_rmb(); 4778c2ecf20Sopenharmony_ci 4788c2ecf20Sopenharmony_ci return (u32)atomic_read(&sp->max_val); 4798c2ecf20Sopenharmony_ci} 4808c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_read_max); 4818c2ecf20Sopenharmony_ci 4828c2ecf20Sopenharmony_ci/** 4838c2ecf20Sopenharmony_ci * host1x_syncpt_read_min() - read minimum syncpoint value 4848c2ecf20Sopenharmony_ci * @sp: host1x syncpoint 4858c2ecf20Sopenharmony_ci * 4868c2ecf20Sopenharmony_ci * The minimum syncpoint value is a shadow of the current sync point value in 4878c2ecf20Sopenharmony_ci * hardware. 4888c2ecf20Sopenharmony_ci */ 4898c2ecf20Sopenharmony_ciu32 host1x_syncpt_read_min(struct host1x_syncpt *sp) 4908c2ecf20Sopenharmony_ci{ 4918c2ecf20Sopenharmony_ci smp_rmb(); 4928c2ecf20Sopenharmony_ci 4938c2ecf20Sopenharmony_ci return (u32)atomic_read(&sp->min_val); 4948c2ecf20Sopenharmony_ci} 4958c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_read_min); 4968c2ecf20Sopenharmony_ci 4978c2ecf20Sopenharmony_ci/** 4988c2ecf20Sopenharmony_ci * host1x_syncpt_read() - read the current syncpoint value 4998c2ecf20Sopenharmony_ci * @sp: host1x syncpoint 5008c2ecf20Sopenharmony_ci */ 5018c2ecf20Sopenharmony_ciu32 host1x_syncpt_read(struct host1x_syncpt *sp) 5028c2ecf20Sopenharmony_ci{ 5038c2ecf20Sopenharmony_ci return host1x_syncpt_load(sp); 5048c2ecf20Sopenharmony_ci} 5058c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_read); 5068c2ecf20Sopenharmony_ci 5078c2ecf20Sopenharmony_ciunsigned int host1x_syncpt_nb_pts(struct host1x *host) 5088c2ecf20Sopenharmony_ci{ 5098c2ecf20Sopenharmony_ci return host->info->nb_pts; 5108c2ecf20Sopenharmony_ci} 5118c2ecf20Sopenharmony_ci 5128c2ecf20Sopenharmony_ciunsigned int host1x_syncpt_nb_bases(struct host1x *host) 5138c2ecf20Sopenharmony_ci{ 5148c2ecf20Sopenharmony_ci return host->info->nb_bases; 5158c2ecf20Sopenharmony_ci} 5168c2ecf20Sopenharmony_ci 5178c2ecf20Sopenharmony_ciunsigned int host1x_syncpt_nb_mlocks(struct host1x *host) 5188c2ecf20Sopenharmony_ci{ 5198c2ecf20Sopenharmony_ci return host->info->nb_mlocks; 5208c2ecf20Sopenharmony_ci} 5218c2ecf20Sopenharmony_ci 5228c2ecf20Sopenharmony_ci/** 5238c2ecf20Sopenharmony_ci * host1x_syncpt_get() - obtain a syncpoint by ID 5248c2ecf20Sopenharmony_ci * @host: host1x controller 5258c2ecf20Sopenharmony_ci * @id: syncpoint ID 5268c2ecf20Sopenharmony_ci */ 5278c2ecf20Sopenharmony_cistruct host1x_syncpt *host1x_syncpt_get(struct host1x *host, unsigned int id) 5288c2ecf20Sopenharmony_ci{ 5298c2ecf20Sopenharmony_ci if (id >= host->info->nb_pts) 5308c2ecf20Sopenharmony_ci return NULL; 5318c2ecf20Sopenharmony_ci 5328c2ecf20Sopenharmony_ci return host->syncpt + id; 5338c2ecf20Sopenharmony_ci} 5348c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_get); 5358c2ecf20Sopenharmony_ci 5368c2ecf20Sopenharmony_ci/** 5378c2ecf20Sopenharmony_ci * host1x_syncpt_get_base() - obtain the wait base associated with a syncpoint 5388c2ecf20Sopenharmony_ci * @sp: host1x syncpoint 5398c2ecf20Sopenharmony_ci */ 5408c2ecf20Sopenharmony_cistruct host1x_syncpt_base *host1x_syncpt_get_base(struct host1x_syncpt *sp) 5418c2ecf20Sopenharmony_ci{ 5428c2ecf20Sopenharmony_ci return sp ? sp->base : NULL; 5438c2ecf20Sopenharmony_ci} 5448c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_get_base); 5458c2ecf20Sopenharmony_ci 5468c2ecf20Sopenharmony_ci/** 5478c2ecf20Sopenharmony_ci * host1x_syncpt_base_id() - retrieve the ID of a syncpoint wait base 5488c2ecf20Sopenharmony_ci * @base: host1x syncpoint wait base 5498c2ecf20Sopenharmony_ci */ 5508c2ecf20Sopenharmony_ciu32 host1x_syncpt_base_id(struct host1x_syncpt_base *base) 5518c2ecf20Sopenharmony_ci{ 5528c2ecf20Sopenharmony_ci return base->id; 5538c2ecf20Sopenharmony_ci} 5548c2ecf20Sopenharmony_ciEXPORT_SYMBOL(host1x_syncpt_base_id); 555