229 lines
6.1 KiB
C
229 lines
6.1 KiB
C
/*
|
|
* 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 <http://www.gnu.org/licenses/>.
|
|
*/
|
|
|
|
#include <linux/nvhost.h>
|
|
#include <linux/io.h>
|
|
#include <linux/debugfs.h>
|
|
#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,
|
|
};
|