mirror of https://github.com/Qortal/Brooklyn
You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
144 lines
3.3 KiB
144 lines
3.3 KiB
// SPDX-License-Identifier: GPL-2.0-only |
|
/* |
|
* Tegra host1x Syncpoints |
|
* |
|
* Copyright (c) 2010-2013, NVIDIA Corporation. |
|
*/ |
|
|
|
#include <linux/io.h> |
|
|
|
#include "../dev.h" |
|
#include "../syncpt.h" |
|
|
|
/* |
|
* Write the current syncpoint value back to hw. |
|
*/ |
|
static void syncpt_restore(struct host1x_syncpt *sp) |
|
{ |
|
u32 min = host1x_syncpt_read_min(sp); |
|
struct host1x *host = sp->host; |
|
|
|
host1x_sync_writel(host, min, HOST1X_SYNC_SYNCPT(sp->id)); |
|
} |
|
|
|
/* |
|
* Write the current waitbase value back to hw. |
|
*/ |
|
static void syncpt_restore_wait_base(struct host1x_syncpt *sp) |
|
{ |
|
#if HOST1X_HW < 7 |
|
struct host1x *host = sp->host; |
|
|
|
host1x_sync_writel(host, sp->base_val, |
|
HOST1X_SYNC_SYNCPT_BASE(sp->id)); |
|
#endif |
|
} |
|
|
|
/* |
|
* Read waitbase value from hw. |
|
*/ |
|
static void syncpt_read_wait_base(struct host1x_syncpt *sp) |
|
{ |
|
#if HOST1X_HW < 7 |
|
struct host1x *host = sp->host; |
|
|
|
sp->base_val = |
|
host1x_sync_readl(host, HOST1X_SYNC_SYNCPT_BASE(sp->id)); |
|
#endif |
|
} |
|
|
|
/* |
|
* Updates the last value read from hardware. |
|
*/ |
|
static u32 syncpt_load(struct host1x_syncpt *sp) |
|
{ |
|
struct host1x *host = sp->host; |
|
u32 old, live; |
|
|
|
/* Loop in case there's a race writing to min_val */ |
|
do { |
|
old = host1x_syncpt_read_min(sp); |
|
live = host1x_sync_readl(host, HOST1X_SYNC_SYNCPT(sp->id)); |
|
} while ((u32)atomic_cmpxchg(&sp->min_val, old, live) != old); |
|
|
|
if (!host1x_syncpt_check_max(sp, live)) |
|
dev_err(host->dev, "%s failed: id=%u, min=%d, max=%d\n", |
|
__func__, sp->id, host1x_syncpt_read_min(sp), |
|
host1x_syncpt_read_max(sp)); |
|
|
|
return live; |
|
} |
|
|
|
/* |
|
* Write a cpu syncpoint increment to the hardware, without touching |
|
* the cache. |
|
*/ |
|
static int syncpt_cpu_incr(struct host1x_syncpt *sp) |
|
{ |
|
struct host1x *host = sp->host; |
|
u32 reg_offset = sp->id / 32; |
|
|
|
if (!host1x_syncpt_client_managed(sp) && |
|
host1x_syncpt_idle(sp)) |
|
return -EINVAL; |
|
|
|
host1x_sync_writel(host, BIT(sp->id % 32), |
|
HOST1X_SYNC_SYNCPT_CPU_INCR(reg_offset)); |
|
wmb(); |
|
|
|
return 0; |
|
} |
|
|
|
/** |
|
* syncpt_assign_to_channel() - Assign syncpoint to channel |
|
* @sp: syncpoint |
|
* @ch: channel |
|
* |
|
* On chips with the syncpoint protection feature (Tegra186+), assign @sp to |
|
* @ch, preventing other channels from incrementing the syncpoints. If @ch is |
|
* NULL, unassigns the syncpoint. |
|
* |
|
* On older chips, do nothing. |
|
*/ |
|
static void syncpt_assign_to_channel(struct host1x_syncpt *sp, |
|
struct host1x_channel *ch) |
|
{ |
|
#if HOST1X_HW >= 6 |
|
struct host1x *host = sp->host; |
|
|
|
if (!host->hv_regs) |
|
return; |
|
|
|
host1x_sync_writel(host, |
|
HOST1X_SYNC_SYNCPT_CH_APP_CH(ch ? ch->id : 0xff), |
|
HOST1X_SYNC_SYNCPT_CH_APP(sp->id)); |
|
#endif |
|
} |
|
|
|
/** |
|
* syncpt_enable_protection() - Enable syncpoint protection |
|
* @host: host1x instance |
|
* |
|
* On chips with the syncpoint protection feature (Tegra186+), enable this |
|
* feature. On older chips, do nothing. |
|
*/ |
|
static void syncpt_enable_protection(struct host1x *host) |
|
{ |
|
#if HOST1X_HW >= 6 |
|
if (!host->hv_regs) |
|
return; |
|
|
|
host1x_hypervisor_writel(host, HOST1X_HV_SYNCPT_PROT_EN_CH_EN, |
|
HOST1X_HV_SYNCPT_PROT_EN); |
|
#endif |
|
} |
|
|
|
static const struct host1x_syncpt_ops host1x_syncpt_ops = { |
|
.restore = syncpt_restore, |
|
.restore_wait_base = syncpt_restore_wait_base, |
|
.load_wait_base = syncpt_read_wait_base, |
|
.load = syncpt_load, |
|
.cpu_incr = syncpt_cpu_incr, |
|
.assign_to_channel = syncpt_assign_to_channel, |
|
.enable_protection = syncpt_enable_protection, |
|
};
|
|
|