blob: 30c84d08c8d569c0f8f3898089cfe3c48c7770d1 [file] [log] [blame] [edit]
// SPDX-License-Identifier: GPL-2.0-only
/*
* Copyright (c) 2013-2020, The Linux Foundation. All rights reserved.
* Copyright (c) 2022 Qualcomm Innovation Center, Inc. All rights reserved.
*/
#include <linux/of.h>
#include <linux/of_platform.h>
#include "adreno.h"
#define TO_ADRENO_CORESIGHT_ATTR(_attr) \
container_of(_attr, struct adreno_coresight_attr, attr)
ssize_t adreno_coresight_show_register(struct device *dev,
struct device_attribute *attr, char *buf)
{
struct adreno_coresight_device *adreno_csdev = dev_get_drvdata(dev);
struct adreno_coresight_attr *cattr = TO_ADRENO_CORESIGHT_ATTR(attr);
struct kgsl_device *device = adreno_csdev->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
unsigned int val = 0;
mutex_lock(&device->mutex);
/*
* Return the current value of the register if coresight is enabled,
* otherwise report 0
*/
if (!adreno_csdev->enabled)
goto out;
if (!adreno_active_count_get(adreno_dev)) {
kgsl_regread(device, cattr->reg->offset, &cattr->reg->value);
adreno_active_count_put(adreno_dev);
}
val = cattr->reg->value;
out:
mutex_unlock(&device->mutex);
return scnprintf(buf, PAGE_SIZE, "0x%X\n", val);
}
ssize_t adreno_coresight_store_register(struct device *dev,
struct device_attribute *attr, const char *buf, size_t size)
{
struct adreno_coresight_attr *cattr = TO_ADRENO_CORESIGHT_ATTR(attr);
struct adreno_coresight_device *adreno_csdev = dev_get_drvdata(dev);
struct kgsl_device *device = adreno_csdev->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
unsigned long val;
int ret;
ret = kstrtoul(buf, 0, &val);
if (ret)
return ret;
mutex_lock(&device->mutex);
/* Ignore writes while coresight is off */
if (!adreno_csdev->enabled)
goto out;
cattr->reg->value = val;
if (!adreno_active_count_get(adreno_dev)) {
kgsl_regwrite(device, cattr->reg->offset, cattr->reg->value);
adreno_active_count_put(adreno_dev);
}
out:
mutex_unlock(&device->mutex);
return size;
}
/*
* This is a generic function to disable coresight debug bus on Adreno
* devices. This function in turn calls the device specific function
* through the gpudev hook.
*/
static void adreno_coresight_disable(struct coresight_device *csdev,
struct perf_event *event)
{
struct adreno_coresight_device *adreno_csdev = dev_get_drvdata(&csdev->dev);
struct kgsl_device *device = adreno_csdev->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
const struct adreno_coresight *coresight = adreno_csdev->coresight;
int i;
mutex_lock(&device->mutex);
if (!adreno_csdev->enabled) {
mutex_unlock(&device->mutex);
return;
}
if (!adreno_active_count_get(adreno_dev)) {
for (i = 0; i < coresight->count; i++)
kgsl_regwrite(device,
coresight->registers[i].offset, 0);
adreno_active_count_put(adreno_dev);
}
adreno_csdev->enabled = false;
mutex_unlock(&device->mutex);
}
static void _adreno_coresight_get_and_clear(struct adreno_device *adreno_dev,
struct adreno_coresight_device *adreno_csdev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
const struct adreno_coresight *coresight = adreno_csdev->coresight;
int i;
if (IS_ERR_OR_NULL(adreno_csdev->dev) || !adreno_csdev->enabled)
return;
kgsl_pre_hwaccess(device);
/*
* Save the current value of each coresight register
* and then clear each register
*/
for (i = 0; i < coresight->count; i++) {
kgsl_regread(device, coresight->registers[i].offset,
&coresight->registers[i].value);
kgsl_regwrite(device, coresight->registers[i].offset, 0);
}
}
static void _adreno_coresight_set(struct adreno_device *adreno_dev,
struct adreno_coresight_device *adreno_csdev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
const struct adreno_coresight *coresight = adreno_csdev->coresight;
int i;
if (IS_ERR_OR_NULL(adreno_csdev->dev) || !adreno_csdev->enabled)
return;
for (i = 0; i < coresight->count; i++)
kgsl_regwrite(device, coresight->registers[i].offset,
coresight->registers[i].value);
}
/* Generic function to enable coresight debug bus on adreno devices */
static int adreno_coresight_enable(struct coresight_device *csdev,
struct perf_event *event, u32 mode)
{
struct adreno_coresight_device *adreno_csdev = dev_get_drvdata(&csdev->dev);
const struct adreno_coresight *coresight = adreno_csdev->coresight;
struct kgsl_device *device = adreno_csdev->device;
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int ret = 0;
mutex_lock(&device->mutex);
if (!adreno_csdev->enabled) {
int i;
adreno_csdev->enabled = true;
/* Reset all the debug registers to their default values */
for (i = 0; i < coresight->count; i++)
coresight->registers[i].value =
coresight->registers[i].initial;
ret = adreno_active_count_get(adreno_dev);
if (!ret) {
_adreno_coresight_set(adreno_dev, adreno_csdev);
adreno_active_count_put(adreno_dev);
}
}
mutex_unlock(&device->mutex);
return ret;
}
void adreno_coresight_stop(struct adreno_device *adreno_dev)
{
_adreno_coresight_get_and_clear(adreno_dev, &adreno_dev->gx_coresight);
_adreno_coresight_get_and_clear(adreno_dev, &adreno_dev->cx_coresight);
}
void adreno_coresight_start(struct adreno_device *adreno_dev)
{
_adreno_coresight_set(adreno_dev, &adreno_dev->gx_coresight);
_adreno_coresight_set(adreno_dev, &adreno_dev->cx_coresight);
}
static int adreno_coresight_trace_id(struct coresight_device *csdev)
{
struct adreno_coresight_device *adreno_csdev = dev_get_drvdata(&csdev->dev);
return adreno_csdev->atid;
}
static const struct coresight_ops_source adreno_coresight_source_ops = {
.trace_id = adreno_coresight_trace_id,
.enable = adreno_coresight_enable,
.disable = adreno_coresight_disable,
};
static const struct coresight_ops adreno_coresight_ops = {
.source_ops = &adreno_coresight_source_ops,
};
void adreno_coresight_remove(struct adreno_device *adreno_dev)
{
if (!IS_ERR_OR_NULL(adreno_dev->gx_coresight.dev))
coresight_unregister(adreno_dev->gx_coresight.dev);
if (!IS_ERR_OR_NULL(adreno_dev->cx_coresight.dev))
coresight_unregister(adreno_dev->cx_coresight.dev);
}
static int funnel_gfx_enable(struct coresight_device *csdev, int inport,
int outport)
{
struct kgsl_device *device = kgsl_get_device(0);
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int ret;
if (!device)
return -ENODEV;
mutex_lock(&device->mutex);
ret = adreno_active_count_get(adreno_dev);
if (ret)
goto err;
/* Now that GPU is up, Call into coresight driver to enable funnel */
ret = adreno_dev->funnel_gfx.funnel_ops->link_ops->enable(csdev, inport, outport);
adreno_active_count_put(adreno_dev);
err:
mutex_unlock(&device->mutex);
return ret;
}
static void funnel_gfx_disable(struct coresight_device *csdev, int inport,
int outport)
{
struct kgsl_device *device = kgsl_get_device(0);
struct adreno_device *adreno_dev = ADRENO_DEVICE(device);
int ret;
if (!device)
return;
mutex_lock(&device->mutex);
ret = adreno_active_count_get(adreno_dev);
if (ret)
goto err;
/* Now that GPU is up, Call into coresight driver to disable funnel */
adreno_dev->funnel_gfx.funnel_ops->link_ops->disable(csdev, inport, outport);
adreno_active_count_put(adreno_dev);
err:
mutex_unlock(&device->mutex);
return;
}
struct coresight_ops_link funnel_link_gfx_ops = {
.enable = funnel_gfx_enable,
.disable = funnel_gfx_disable,
};
struct coresight_ops funnel_gfx_ops = {
.link_ops = &funnel_link_gfx_ops,
};
static void adreno_coresight_dev_probe(struct kgsl_device *device,
const struct adreno_coresight *coresight,
struct adreno_coresight_device *adreno_csdev,
struct device_node *node)
{
struct platform_device *pdev = of_find_device_by_node(node);
struct coresight_desc desc;
u32 atid;
if (!pdev)
return;
if (of_property_read_u32(node, "coresight-atid", &atid))
return;
if (of_property_read_string(node, "coresight-name", &desc.name))
return;
desc.pdata = coresight_get_platform_data(&pdev->dev);
platform_device_put(pdev);
if (IS_ERR(desc.pdata))
return;
desc.type = CORESIGHT_DEV_TYPE_SOURCE;
desc.subtype.source_subtype = CORESIGHT_DEV_SUBTYPE_SOURCE_SOFTWARE;
desc.ops = &adreno_coresight_ops;
desc.dev = &device->pdev->dev;
desc.groups = coresight->groups;
adreno_csdev->atid = atid;
adreno_csdev->device = device;
adreno_csdev->dev = coresight_register(&desc);
adreno_csdev->coresight = coresight;
if (!IS_ERR_OR_NULL(adreno_csdev->dev))
dev_set_drvdata(&adreno_csdev->dev->dev, adreno_csdev);
}
void adreno_coresight_add_device(struct adreno_device *adreno_dev, const char *name,
const struct adreno_coresight *coresight,
struct adreno_coresight_device *adreno_csdev)
{
struct kgsl_device *device = KGSL_DEVICE(adreno_dev);
struct device_node *node = of_find_compatible_node(device->pdev->dev.of_node, NULL, name);
struct adreno_funnel_device *funnel_gfx = &adreno_dev->funnel_gfx;
if (!node)
return;
/* Set the funnel ops as graphics ops to bring GPU up before enabling funnel */
if (funnel_gfx !=NULL && funnel_gfx->funnel_csdev != NULL
&& funnel_gfx->funnel_csdev->ops == NULL)
funnel_gfx->funnel_csdev->ops = &funnel_gfx_ops;
adreno_coresight_dev_probe(device, coresight, adreno_csdev, node);
of_node_put(node);
}