/* * 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 . */ #include #include #include #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, };