diff options
Diffstat (limited to 'drivers/video/tegra/host/host1x/host1x_syncpt.c')
-rw-r--r-- | drivers/video/tegra/host/host1x/host1x_syncpt.c | 180 |
1 files changed, 180 insertions, 0 deletions
diff --git a/drivers/video/tegra/host/host1x/host1x_syncpt.c b/drivers/video/tegra/host/host1x/host1x_syncpt.c new file mode 100644 index 000000000000..8cca9dbbbc08 --- /dev/null +++ b/drivers/video/tegra/host/host1x/host1x_syncpt.c @@ -0,0 +1,180 @@ +/* + * drivers/video/tegra/host/host1x/host1x_syncpt.c + * + * Tegra Graphics Host Syncpoints for HOST1X + * + * Copyright (c) 2010-2012, NVIDIA Corporation. + * + * This program is free software; you can redistribute it and/or modify it + * under the terms and conditions of the GNU General Public License, + * version 2, as published by the Free Software Foundation. + * + * This program is distributed in the hope it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see <http://www.gnu.org/licenses/>. + */ + +#include <linux/nvhost_ioctl.h> +#include <linux/io.h> +#include <trace/events/nvhost.h> +#include "nvhost_syncpt.h" +#include "nvhost_acm.h" +#include "host1x.h" +#include "chip_support.h" + +/** + * Write the current syncpoint value back to hw. + */ +static void t20_syncpt_reset(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + int min = nvhost_syncpt_read_min(sp, id); + writel(min, dev->sync_aperture + (host1x_sync_syncpt_0_r() + id * 4)); +} + +/** + * Write the current waitbase value back to hw. + */ +static void t20_syncpt_reset_wait_base(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + writel(sp->base_val[id], + dev->sync_aperture + (host1x_sync_syncpt_base_0_r() + id * 4)); +} + +/** + * Read waitbase value from hw. + */ +static void t20_syncpt_read_wait_base(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + sp->base_val[id] = readl(dev->sync_aperture + + (host1x_sync_syncpt_base_0_r() + id * 4)); +} + +/** + * Updates the last value read from hardware. + * (was nvhost_syncpt_update_min) + */ +static u32 t20_syncpt_update_min(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + void __iomem *sync_regs = dev->sync_aperture; + u32 old, live; + + do { + old = nvhost_syncpt_read_min(sp, id); + live = readl(sync_regs + (host1x_sync_syncpt_0_r() + id * 4)); + } while ((u32)atomic_cmpxchg(&sp->min_val[id], old, live) != old); + + if (!nvhost_syncpt_check_max(sp, id, live)) + dev_err(&syncpt_to_dev(sp)->dev->dev, + "%s failed: id=%u, min=%d, max=%d\n", + __func__, + nvhost_syncpt_read_min(sp, id), + nvhost_syncpt_read_max(sp, id), + id); + + return live; +} + +/** + * Write a cpu syncpoint increment to the hardware, without touching + * the cache. Caller is responsible for host being powered. + */ +static void t20_syncpt_cpu_incr(struct nvhost_syncpt *sp, u32 id) +{ + struct nvhost_master *dev = syncpt_to_dev(sp); + u32 reg_offset = id / 32; + + BUG_ON(!nvhost_module_powered(dev->dev)); + if (!nvhost_syncpt_client_managed(sp, id) + && nvhost_syncpt_min_eq_max(sp, id)) { + dev_err(&syncpt_to_dev(sp)->dev->dev, + "Trying to increment syncpoint id %d beyond max\n", + id); + nvhost_debug_dump(syncpt_to_dev(sp)); + return; + } + writel(BIT_MASK(id), dev->sync_aperture + + host1x_sync_syncpt_cpu_incr_r() + reg_offset * 4); + wmb(); +} + +/* remove a wait pointed to by patch_addr */ +static int host1x_syncpt_patch_wait(struct nvhost_syncpt *sp, + void *patch_addr) +{ + u32 override = nvhost_class_host_wait_syncpt( + NVSYNCPT_GRAPHICS_HOST, 0); + __raw_writel(override, patch_addr); + return 0; +} + + +static const char *t20_syncpt_name(struct nvhost_syncpt *sp, u32 id) +{ + struct host1x_device_info *info = &syncpt_to_dev(sp)->info; + return (id >= info->nb_pts) ? NULL : info->syncpt_names[id]; +} + +static void t20_syncpt_debug(struct nvhost_syncpt *sp) +{ + u32 i; + for (i = 0; i < nvhost_syncpt_nb_pts(sp); i++) { + u32 max = nvhost_syncpt_read_max(sp, i); + u32 min = nvhost_syncpt_update_min(sp, i); + if (!max && !min) + continue; + dev_info(&syncpt_to_dev(sp)->dev->dev, + "id %d (%s) min %d max %d\n", + i, syncpt_op().name(sp, i), + min, max); + + } + + for (i = 0; i < nvhost_syncpt_nb_bases(sp); i++) { + u32 base_val; + t20_syncpt_read_wait_base(sp, i); + base_val = sp->base_val[i]; + if (base_val) + dev_info(&syncpt_to_dev(sp)->dev->dev, + "waitbase id %d val %d\n", + i, base_val); + + } +} + +static int syncpt_mutex_try_lock(struct nvhost_syncpt *sp, + unsigned int idx) +{ + void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture; + /* mlock registers returns 0 when the lock is aquired. + * writing 0 clears the lock. */ + return !!readl(sync_regs + (host1x_sync_mlock_0_r() + idx * 4)); +} + +static void syncpt_mutex_unlock(struct nvhost_syncpt *sp, + unsigned int idx) +{ + void __iomem *sync_regs = syncpt_to_dev(sp)->sync_aperture; + + writel(0, sync_regs + (host1x_sync_mlock_0_r() + idx * 4)); +} + +static const struct nvhost_syncpt_ops host1x_syncpt_ops = { + .reset = t20_syncpt_reset, + .reset_wait_base = t20_syncpt_reset_wait_base, + .read_wait_base = t20_syncpt_read_wait_base, + .update_min = t20_syncpt_update_min, + .cpu_incr = t20_syncpt_cpu_incr, + .patch_wait = host1x_syncpt_patch_wait, + .debug = t20_syncpt_debug, + .name = t20_syncpt_name, + .mutex_try_lock = syncpt_mutex_try_lock, + .mutex_unlock = syncpt_mutex_unlock, +}; |