mirror of
https://mirrors.bfsu.edu.cn/git/linux.git
synced 2024-11-26 13:44:15 +08:00
97fb5e8d9b
Based on 1 normalized pattern(s): this program is free software you can redistribute it and or modify it under the terms of the gnu general public license version 2 and only version 2 as published by the free software foundation this program is distributed in the hope that 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 extracted by the scancode license scanner the SPDX license identifier GPL-2.0-only has been chosen to replace the boilerplate/reference in 294 file(s). Signed-off-by: Thomas Gleixner <tglx@linutronix.de> Reviewed-by: Allison Randal <allison@lohutok.net> Reviewed-by: Alexios Zavras <alexios.zavras@intel.com> Cc: linux-spdx@vger.kernel.org Link: https://lkml.kernel.org/r/20190529141900.825281744@linutronix.de Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
286 lines
6.6 KiB
C
286 lines
6.6 KiB
C
// SPDX-License-Identifier: GPL-2.0-only
|
|
/*
|
|
* Qualcomm Technologies HIDMA Management SYS interface
|
|
*
|
|
* Copyright (c) 2015, The Linux Foundation. All rights reserved.
|
|
*/
|
|
|
|
#include <linux/sysfs.h>
|
|
#include <linux/platform_device.h>
|
|
|
|
#include "hidma_mgmt.h"
|
|
|
|
struct hidma_chan_attr {
|
|
struct hidma_mgmt_dev *mdev;
|
|
int index;
|
|
struct kobj_attribute attr;
|
|
};
|
|
|
|
struct hidma_mgmt_fileinfo {
|
|
char *name;
|
|
int mode;
|
|
int (*get)(struct hidma_mgmt_dev *mdev);
|
|
int (*set)(struct hidma_mgmt_dev *mdev, u64 val);
|
|
};
|
|
|
|
#define IMPLEMENT_GETSET(name) \
|
|
static int get_##name(struct hidma_mgmt_dev *mdev) \
|
|
{ \
|
|
return mdev->name; \
|
|
} \
|
|
static int set_##name(struct hidma_mgmt_dev *mdev, u64 val) \
|
|
{ \
|
|
u64 tmp; \
|
|
int rc; \
|
|
\
|
|
tmp = mdev->name; \
|
|
mdev->name = val; \
|
|
rc = hidma_mgmt_setup(mdev); \
|
|
if (rc) \
|
|
mdev->name = tmp; \
|
|
return rc; \
|
|
}
|
|
|
|
#define DECLARE_ATTRIBUTE(name, mode) \
|
|
{#name, mode, get_##name, set_##name}
|
|
|
|
IMPLEMENT_GETSET(hw_version_major)
|
|
IMPLEMENT_GETSET(hw_version_minor)
|
|
IMPLEMENT_GETSET(max_wr_xactions)
|
|
IMPLEMENT_GETSET(max_rd_xactions)
|
|
IMPLEMENT_GETSET(max_write_request)
|
|
IMPLEMENT_GETSET(max_read_request)
|
|
IMPLEMENT_GETSET(dma_channels)
|
|
IMPLEMENT_GETSET(chreset_timeout_cycles)
|
|
|
|
static int set_priority(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
|
|
{
|
|
u64 tmp;
|
|
int rc;
|
|
|
|
if (i >= mdev->dma_channels)
|
|
return -EINVAL;
|
|
|
|
tmp = mdev->priority[i];
|
|
mdev->priority[i] = val;
|
|
rc = hidma_mgmt_setup(mdev);
|
|
if (rc)
|
|
mdev->priority[i] = tmp;
|
|
return rc;
|
|
}
|
|
|
|
static int set_weight(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)
|
|
{
|
|
u64 tmp;
|
|
int rc;
|
|
|
|
if (i >= mdev->dma_channels)
|
|
return -EINVAL;
|
|
|
|
tmp = mdev->weight[i];
|
|
mdev->weight[i] = val;
|
|
rc = hidma_mgmt_setup(mdev);
|
|
if (rc)
|
|
mdev->weight[i] = tmp;
|
|
return rc;
|
|
}
|
|
|
|
static struct hidma_mgmt_fileinfo hidma_mgmt_files[] = {
|
|
DECLARE_ATTRIBUTE(hw_version_major, S_IRUGO),
|
|
DECLARE_ATTRIBUTE(hw_version_minor, S_IRUGO),
|
|
DECLARE_ATTRIBUTE(dma_channels, S_IRUGO),
|
|
DECLARE_ATTRIBUTE(chreset_timeout_cycles, S_IRUGO),
|
|
DECLARE_ATTRIBUTE(max_wr_xactions, S_IRUGO),
|
|
DECLARE_ATTRIBUTE(max_rd_xactions, S_IRUGO),
|
|
DECLARE_ATTRIBUTE(max_write_request, S_IRUGO),
|
|
DECLARE_ATTRIBUTE(max_read_request, S_IRUGO),
|
|
};
|
|
|
|
static ssize_t show_values(struct device *dev, struct device_attribute *attr,
|
|
char *buf)
|
|
{
|
|
struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
|
|
unsigned int i;
|
|
|
|
buf[0] = 0;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
|
|
if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
|
|
sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev));
|
|
break;
|
|
}
|
|
}
|
|
return strlen(buf);
|
|
}
|
|
|
|
static ssize_t set_values(struct device *dev, struct device_attribute *attr,
|
|
const char *buf, size_t count)
|
|
{
|
|
struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);
|
|
unsigned long tmp;
|
|
unsigned int i;
|
|
int rc;
|
|
|
|
rc = kstrtoul(buf, 0, &tmp);
|
|
if (rc)
|
|
return rc;
|
|
|
|
for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
|
|
if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {
|
|
rc = hidma_mgmt_files[i].set(mdev, tmp);
|
|
if (rc)
|
|
return rc;
|
|
|
|
break;
|
|
}
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static ssize_t show_values_channel(struct kobject *kobj,
|
|
struct kobj_attribute *attr, char *buf)
|
|
{
|
|
struct hidma_chan_attr *chattr;
|
|
struct hidma_mgmt_dev *mdev;
|
|
|
|
buf[0] = 0;
|
|
chattr = container_of(attr, struct hidma_chan_attr, attr);
|
|
mdev = chattr->mdev;
|
|
if (strcmp(attr->attr.name, "priority") == 0)
|
|
sprintf(buf, "%d\n", mdev->priority[chattr->index]);
|
|
else if (strcmp(attr->attr.name, "weight") == 0)
|
|
sprintf(buf, "%d\n", mdev->weight[chattr->index]);
|
|
|
|
return strlen(buf);
|
|
}
|
|
|
|
static ssize_t set_values_channel(struct kobject *kobj,
|
|
struct kobj_attribute *attr, const char *buf,
|
|
size_t count)
|
|
{
|
|
struct hidma_chan_attr *chattr;
|
|
struct hidma_mgmt_dev *mdev;
|
|
unsigned long tmp;
|
|
int rc;
|
|
|
|
chattr = container_of(attr, struct hidma_chan_attr, attr);
|
|
mdev = chattr->mdev;
|
|
|
|
rc = kstrtoul(buf, 0, &tmp);
|
|
if (rc)
|
|
return rc;
|
|
|
|
if (strcmp(attr->attr.name, "priority") == 0) {
|
|
rc = set_priority(mdev, chattr->index, tmp);
|
|
if (rc)
|
|
return rc;
|
|
} else if (strcmp(attr->attr.name, "weight") == 0) {
|
|
rc = set_weight(mdev, chattr->index, tmp);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
return count;
|
|
}
|
|
|
|
static int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode)
|
|
{
|
|
struct device_attribute *attrs;
|
|
char *name_copy;
|
|
|
|
attrs = devm_kmalloc(&dev->pdev->dev,
|
|
sizeof(struct device_attribute), GFP_KERNEL);
|
|
if (!attrs)
|
|
return -ENOMEM;
|
|
|
|
name_copy = devm_kstrdup(&dev->pdev->dev, name, GFP_KERNEL);
|
|
if (!name_copy)
|
|
return -ENOMEM;
|
|
|
|
attrs->attr.name = name_copy;
|
|
attrs->attr.mode = mode;
|
|
attrs->show = show_values;
|
|
attrs->store = set_values;
|
|
sysfs_attr_init(&attrs->attr);
|
|
|
|
return device_create_file(&dev->pdev->dev, attrs);
|
|
}
|
|
|
|
static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,
|
|
int mode, int index,
|
|
struct kobject *parent)
|
|
{
|
|
struct hidma_chan_attr *chattr;
|
|
char *name_copy;
|
|
|
|
chattr = devm_kmalloc(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL);
|
|
if (!chattr)
|
|
return -ENOMEM;
|
|
|
|
name_copy = devm_kstrdup(&mdev->pdev->dev, name, GFP_KERNEL);
|
|
if (!name_copy)
|
|
return -ENOMEM;
|
|
|
|
chattr->mdev = mdev;
|
|
chattr->index = index;
|
|
chattr->attr.attr.name = name_copy;
|
|
chattr->attr.attr.mode = mode;
|
|
chattr->attr.show = show_values_channel;
|
|
chattr->attr.store = set_values_channel;
|
|
sysfs_attr_init(&chattr->attr.attr);
|
|
|
|
return sysfs_create_file(parent, &chattr->attr.attr);
|
|
}
|
|
|
|
int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)
|
|
{
|
|
unsigned int i;
|
|
int rc;
|
|
int required;
|
|
struct kobject *chanops;
|
|
|
|
required = sizeof(*mdev->chroots) * mdev->dma_channels;
|
|
mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL);
|
|
if (!mdev->chroots)
|
|
return -ENOMEM;
|
|
|
|
chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);
|
|
if (!chanops)
|
|
return -ENOMEM;
|
|
|
|
/* create each channel directory here */
|
|
for (i = 0; i < mdev->dma_channels; i++) {
|
|
char name[20];
|
|
|
|
snprintf(name, sizeof(name), "chan%d", i);
|
|
mdev->chroots[i] = kobject_create_and_add(name, chanops);
|
|
if (!mdev->chroots[i])
|
|
return -ENOMEM;
|
|
}
|
|
|
|
/* populate common parameters */
|
|
for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {
|
|
rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name,
|
|
hidma_mgmt_files[i].mode);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
/* populate parameters that are per channel */
|
|
for (i = 0; i < mdev->dma_channels; i++) {
|
|
rc = create_sysfs_entry_channel(mdev, "priority",
|
|
(S_IRUGO | S_IWUGO), i,
|
|
mdev->chroots[i]);
|
|
if (rc)
|
|
return rc;
|
|
|
|
rc = create_sysfs_entry_channel(mdev, "weight",
|
|
(S_IRUGO | S_IWUGO), i,
|
|
mdev->chroots[i]);
|
|
if (rc)
|
|
return rc;
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);
|