/*
* drivers/video/tegra/host/host1x/host1x_counter.c
*
* Tegra Graphics Host Counter support
*
* Copyright (c) 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 "dev.h"
#include "chip_support.h"
static void host1x_tickctrl_debug_init(struct platform_device *dev);
static int host1x_tickctrl_init_channel(struct platform_device *dev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
void __iomem *regs = pdata->channel->aperture;
nvhost_module_busy(nvhost_get_parent(dev));
/* Initialize counter */
writel(0, regs + host1x_channel_tickcount_hi_r());
writel(0, regs + host1x_channel_tickcount_lo_r());
writel(0, regs + host1x_channel_stallcount_hi_r());
writel(0, regs + host1x_channel_stallcount_lo_r());
writel(0, regs + host1x_channel_xfercount_hi_r());
writel(0, regs + host1x_channel_xfercount_lo_r());
writel(host1x_channel_channelctrl_enabletickcnt_f(1),
regs + host1x_channel_channelctrl_r());
writel(host1x_channel_stallctrl_enable_channel_stall_f(1),
regs + host1x_channel_stallctrl_r());
writel(host1x_channel_xferctrl_enable_channel_xfer_f(1),
regs + host1x_channel_xferctrl_r());
nvhost_module_idle(nvhost_get_parent(dev));
host1x_tickctrl_debug_init(dev);
return 0;
}
static void host1x_tickctrl_deinit_channel(struct platform_device *dev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
void __iomem *regs = pdata->channel->aperture;
nvhost_module_busy(nvhost_get_parent(dev));
writel(host1x_channel_stallctrl_enable_channel_stall_f(0),
regs + host1x_channel_stallctrl_r());
writel(host1x_channel_xferctrl_enable_channel_xfer_f(0),
regs + host1x_channel_xferctrl_r());
writel(host1x_channel_channelctrl_enabletickcnt_f(0),
regs + host1x_channel_channelctrl_r());
nvhost_module_idle(nvhost_get_parent(dev));
}
static u64 readl64(void __iomem *reg_hi, void __iomem *reg_lo)
{
u32 hi, lo, hi2;
do {
hi = readl(reg_hi);
lo = readl(reg_lo);
rmb();
hi2 = readl(reg_hi);
} while (hi2 != hi);
return ((u64)hi << 32) | (u64)lo;
}
static int host1x_tickctrl_tickcount(struct platform_device *dev, u64 *val)
{
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
void __iomem *regs = pdata->channel->aperture;
nvhost_module_busy(nvhost_get_parent(dev));
*val = readl64(regs + host1x_channel_tickcount_hi_r(),
regs + host1x_channel_tickcount_lo_r());
rmb();
nvhost_module_idle(nvhost_get_parent(dev));
return 0;
}
static int host1x_tickctrl_stallcount(struct platform_device *dev, u64 *val)
{
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
void __iomem *regs = pdata->channel->aperture;
nvhost_module_busy(nvhost_get_parent(dev));
*val = readl64(regs + host1x_channel_stallcount_hi_r(),
regs + host1x_channel_stallcount_lo_r());
rmb();
nvhost_module_idle(nvhost_get_parent(dev));
return 0;
}
static int host1x_tickctrl_xfercount(struct platform_device *dev, u64 *val)
{
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
void __iomem *regs = pdata->channel->aperture;
nvhost_module_busy(nvhost_get_parent(dev));
*val = readl64(regs + host1x_channel_xfercount_hi_r(),
regs + host1x_channel_xfercount_lo_r());
rmb();
nvhost_module_idle(nvhost_get_parent(dev));
return 0;
}
static int tickcount_show(struct seq_file *s, void *unused)
{
struct platform_device *dev = s->private;
u64 cnt;
int err;
err = tickctrl_op().tickcount(dev, &cnt);
if (!err)
seq_printf(s, "%lld\n", cnt);
return err;
}
static int tickcount_open(struct inode *inode, struct file *file)
{
if (!tickctrl_op().tickcount)
return -ENODEV;
return single_open(file, tickcount_show, inode->i_private);
}
static const struct file_operations tickcount_fops = {
.open = tickcount_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int stallcount_show(struct seq_file *s, void *unused)
{
struct platform_device *dev = s->private;
u64 cnt;
int err;
err = tickctrl_op().stallcount(dev, &cnt);
if (!err)
seq_printf(s, "%lld\n", cnt);
return err;
}
static int stallcount_open(struct inode *inode, struct file *file)
{
if (!tickctrl_op().stallcount)
return -ENODEV;
return single_open(file, stallcount_show, inode->i_private);
}
static const struct file_operations stallcount_fops = {
.open = stallcount_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static int xfercount_show(struct seq_file *s, void *unused)
{
struct platform_device *dev = s->private;
u64 cnt;
int err;
err = tickctrl_op().xfercount(dev, &cnt);
if (!err)
seq_printf(s, "%lld\n", cnt);
return err;
}
static int xfercount_open(struct inode *inode, struct file *file)
{
if (!tickctrl_op().xfercount)
return -ENODEV;
return single_open(file, xfercount_show, inode->i_private);
}
static const struct file_operations xfercount_fops = {
.open = xfercount_open,
.read = seq_read,
.llseek = seq_lseek,
.release = single_release,
};
static void host1x_tickctrl_debug_init(struct platform_device *dev)
{
struct nvhost_device_data *pdata = platform_get_drvdata(dev);
debugfs_create_file("stallcount", S_IRUGO, pdata->debugfs, dev,
&stallcount_fops);
debugfs_create_file("xfercount", S_IRUGO, pdata->debugfs, dev,
&xfercount_fops);
debugfs_create_file("tickcount", S_IRUGO, pdata->debugfs, dev,
&tickcount_fops);
}
static const struct nvhost_tickctrl_ops host1x_tickctrl_ops = {
.init_channel = host1x_tickctrl_init_channel,
.deinit_channel = host1x_tickctrl_deinit_channel,
.tickcount = host1x_tickctrl_tickcount,
.stallcount = host1x_tickctrl_stallcount,
.xfercount = host1x_tickctrl_xfercount,
};