/*
* Tegra host1x Syncpoints
*
* Copyright (c) 2010-2013, 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
#include "syncpt.h"
#include "dev.h"
static struct host1x_syncpt *_host1x_syncpt_alloc(struct host1x *host,
struct device *dev,
int client_managed)
{
int i;
struct host1x_syncpt *sp = host->syncpt;
char *name;
for (i = 0; i < host->info->nb_pts && sp->name; i++, sp++)
;
if (sp->dev)
return NULL;
name = kasprintf(GFP_KERNEL, "%02d-%s", sp->id,
dev ? dev_name(dev) : NULL);
if (!name)
return NULL;
sp->dev = dev;
sp->name = name;
sp->client_managed = client_managed;
return sp;
}
u32 host1x_syncpt_id(struct host1x_syncpt *sp)
{
return sp->id;
}
/*
* Updates the value sent to hardware.
*/
u32 host1x_syncpt_incr_max(struct host1x_syncpt *sp, u32 incrs)
{
return (u32)atomic_add_return(incrs, &sp->max_val);
}
/*
* Write cached syncpoint and waitbase values to hardware.
*/
void host1x_syncpt_restore(struct host1x *host)
{
struct host1x_syncpt *sp_base = host->syncpt;
u32 i;
for (i = 0; i < host1x_syncpt_nb_pts(host); i++)
host1x_hw_syncpt_restore(host, sp_base + i);
for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
host1x_hw_syncpt_restore_wait_base(host, sp_base + i);
wmb();
}
/*
* Update the cached syncpoint and waitbase values by reading them
* from the registers.
*/
void host1x_syncpt_save(struct host1x *host)
{
struct host1x_syncpt *sp_base = host->syncpt;
u32 i;
for (i = 0; i < host1x_syncpt_nb_pts(host); i++) {
if (host1x_syncpt_client_managed(sp_base + i))
host1x_hw_syncpt_load(host, sp_base + i);
else
WARN_ON(!host1x_syncpt_idle(sp_base + i));
}
for (i = 0; i < host1x_syncpt_nb_bases(host); i++)
host1x_hw_syncpt_load_wait_base(host, sp_base + i);
}
/*
* Updates the cached syncpoint value by reading a new value from the hardware
* register
*/
u32 host1x_syncpt_load(struct host1x_syncpt *sp)
{
u32 val;
val = host1x_hw_syncpt_load(sp->host, sp);
trace_host1x_syncpt_load_min(sp->id, val);
return val;
}
/*
* Get the current syncpoint base
*/
u32 host1x_syncpt_load_wait_base(struct host1x_syncpt *sp)
{
u32 val;
host1x_hw_syncpt_load_wait_base(sp->host, sp);
val = sp->base_val;
return val;
}
/*
* Write a cpu syncpoint increment to the hardware, without touching
* the cache. Caller is responsible for host being powered.
*/
void host1x_syncpt_cpu_incr(struct host1x_syncpt *sp)
{
host1x_hw_syncpt_cpu_incr(sp->host, sp);
}
/*
* Increment syncpoint value from cpu, updating cache
*/
void host1x_syncpt_incr(struct host1x_syncpt *sp)
{
if (host1x_syncpt_client_managed(sp))
host1x_syncpt_incr_max(sp, 1);
host1x_syncpt_cpu_incr(sp);
}
int host1x_syncpt_init(struct host1x *host)
{
struct host1x_syncpt *syncpt;
int i;
syncpt = devm_kzalloc(host->dev, sizeof(*syncpt) * host->info->nb_pts,
GFP_KERNEL);
if (!syncpt)
return -ENOMEM;
for (i = 0; i < host->info->nb_pts; ++i) {
syncpt[i].id = i;
syncpt[i].host = host;
}
host->syncpt = syncpt;
host1x_syncpt_restore(host);
return 0;
}
struct host1x_syncpt *host1x_syncpt_request(struct device *dev,
int client_managed)
{
struct host1x *host = dev_get_drvdata(dev->parent);
return _host1x_syncpt_alloc(host, dev, client_managed);
}
void host1x_syncpt_free(struct host1x_syncpt *sp)
{
if (!sp)
return;
kfree(sp->name);
sp->dev = NULL;
sp->name = NULL;
sp->client_managed = 0;
}
void host1x_syncpt_deinit(struct host1x *host)
{
int i;
struct host1x_syncpt *sp = host->syncpt;
for (i = 0; i < host->info->nb_pts; i++, sp++)
kfree(sp->name);
}
int host1x_syncpt_nb_pts(struct host1x *host)
{
return host->info->nb_pts;
}
int host1x_syncpt_nb_bases(struct host1x *host)
{
return host->info->nb_bases;
}
int host1x_syncpt_nb_mlocks(struct host1x *host)
{
return host->info->nb_mlocks;
}
struct host1x_syncpt *host1x_syncpt_get(struct host1x *host, u32 id)
{
if (host->info->nb_pts < id)
return NULL;
return host->syncpt + id;
}