286 lines · c
1// SPDX-License-Identifier: GPL-2.0-only2/*3 * Qualcomm Technologies HIDMA Management SYS interface4 *5 * Copyright (c) 2015, The Linux Foundation. All rights reserved.6 */7 8#include <linux/sysfs.h>9#include <linux/platform_device.h>10 11#include "hidma_mgmt.h"12 13struct hidma_chan_attr {14 struct hidma_mgmt_dev *mdev;15 int index;16 struct kobj_attribute attr;17};18 19struct hidma_mgmt_fileinfo {20 char *name;21 int mode;22 int (*get)(struct hidma_mgmt_dev *mdev);23 int (*set)(struct hidma_mgmt_dev *mdev, u64 val);24};25 26#define IMPLEMENT_GETSET(name) \27static int get_##name(struct hidma_mgmt_dev *mdev) \28{ \29 return mdev->name; \30} \31static int set_##name(struct hidma_mgmt_dev *mdev, u64 val) \32{ \33 u64 tmp; \34 int rc; \35 \36 tmp = mdev->name; \37 mdev->name = val; \38 rc = hidma_mgmt_setup(mdev); \39 if (rc) \40 mdev->name = tmp; \41 return rc; \42}43 44#define DECLARE_ATTRIBUTE(name, mode) \45 {#name, mode, get_##name, set_##name}46 47IMPLEMENT_GETSET(hw_version_major)48IMPLEMENT_GETSET(hw_version_minor)49IMPLEMENT_GETSET(max_wr_xactions)50IMPLEMENT_GETSET(max_rd_xactions)51IMPLEMENT_GETSET(max_write_request)52IMPLEMENT_GETSET(max_read_request)53IMPLEMENT_GETSET(dma_channels)54IMPLEMENT_GETSET(chreset_timeout_cycles)55 56static int set_priority(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)57{58 u64 tmp;59 int rc;60 61 if (i >= mdev->dma_channels)62 return -EINVAL;63 64 tmp = mdev->priority[i];65 mdev->priority[i] = val;66 rc = hidma_mgmt_setup(mdev);67 if (rc)68 mdev->priority[i] = tmp;69 return rc;70}71 72static int set_weight(struct hidma_mgmt_dev *mdev, unsigned int i, u64 val)73{74 u64 tmp;75 int rc;76 77 if (i >= mdev->dma_channels)78 return -EINVAL;79 80 tmp = mdev->weight[i];81 mdev->weight[i] = val;82 rc = hidma_mgmt_setup(mdev);83 if (rc)84 mdev->weight[i] = tmp;85 return rc;86}87 88static struct hidma_mgmt_fileinfo hidma_mgmt_files[] = {89 DECLARE_ATTRIBUTE(hw_version_major, S_IRUGO),90 DECLARE_ATTRIBUTE(hw_version_minor, S_IRUGO),91 DECLARE_ATTRIBUTE(dma_channels, S_IRUGO),92 DECLARE_ATTRIBUTE(chreset_timeout_cycles, S_IRUGO),93 DECLARE_ATTRIBUTE(max_wr_xactions, S_IRUGO),94 DECLARE_ATTRIBUTE(max_rd_xactions, S_IRUGO),95 DECLARE_ATTRIBUTE(max_write_request, S_IRUGO),96 DECLARE_ATTRIBUTE(max_read_request, S_IRUGO),97};98 99static ssize_t show_values(struct device *dev, struct device_attribute *attr,100 char *buf)101{102 struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);103 unsigned int i;104 105 buf[0] = 0;106 107 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {108 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {109 sprintf(buf, "%d\n", hidma_mgmt_files[i].get(mdev));110 break;111 }112 }113 return strlen(buf);114}115 116static ssize_t set_values(struct device *dev, struct device_attribute *attr,117 const char *buf, size_t count)118{119 struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev);120 unsigned long tmp;121 unsigned int i;122 int rc;123 124 rc = kstrtoul(buf, 0, &tmp);125 if (rc)126 return rc;127 128 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {129 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) {130 rc = hidma_mgmt_files[i].set(mdev, tmp);131 if (rc)132 return rc;133 134 break;135 }136 }137 return count;138}139 140static ssize_t show_values_channel(struct kobject *kobj,141 struct kobj_attribute *attr, char *buf)142{143 struct hidma_chan_attr *chattr;144 struct hidma_mgmt_dev *mdev;145 146 buf[0] = 0;147 chattr = container_of(attr, struct hidma_chan_attr, attr);148 mdev = chattr->mdev;149 if (strcmp(attr->attr.name, "priority") == 0)150 sprintf(buf, "%d\n", mdev->priority[chattr->index]);151 else if (strcmp(attr->attr.name, "weight") == 0)152 sprintf(buf, "%d\n", mdev->weight[chattr->index]);153 154 return strlen(buf);155}156 157static ssize_t set_values_channel(struct kobject *kobj,158 struct kobj_attribute *attr, const char *buf,159 size_t count)160{161 struct hidma_chan_attr *chattr;162 struct hidma_mgmt_dev *mdev;163 unsigned long tmp;164 int rc;165 166 chattr = container_of(attr, struct hidma_chan_attr, attr);167 mdev = chattr->mdev;168 169 rc = kstrtoul(buf, 0, &tmp);170 if (rc)171 return rc;172 173 if (strcmp(attr->attr.name, "priority") == 0) {174 rc = set_priority(mdev, chattr->index, tmp);175 if (rc)176 return rc;177 } else if (strcmp(attr->attr.name, "weight") == 0) {178 rc = set_weight(mdev, chattr->index, tmp);179 if (rc)180 return rc;181 }182 return count;183}184 185static int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode)186{187 struct device_attribute *attrs;188 char *name_copy;189 190 attrs = devm_kmalloc(&dev->pdev->dev,191 sizeof(struct device_attribute), GFP_KERNEL);192 if (!attrs)193 return -ENOMEM;194 195 name_copy = devm_kstrdup(&dev->pdev->dev, name, GFP_KERNEL);196 if (!name_copy)197 return -ENOMEM;198 199 attrs->attr.name = name_copy;200 attrs->attr.mode = mode;201 attrs->show = show_values;202 attrs->store = set_values;203 sysfs_attr_init(&attrs->attr);204 205 return device_create_file(&dev->pdev->dev, attrs);206}207 208static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name,209 int mode, int index,210 struct kobject *parent)211{212 struct hidma_chan_attr *chattr;213 char *name_copy;214 215 chattr = devm_kmalloc(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL);216 if (!chattr)217 return -ENOMEM;218 219 name_copy = devm_kstrdup(&mdev->pdev->dev, name, GFP_KERNEL);220 if (!name_copy)221 return -ENOMEM;222 223 chattr->mdev = mdev;224 chattr->index = index;225 chattr->attr.attr.name = name_copy;226 chattr->attr.attr.mode = mode;227 chattr->attr.show = show_values_channel;228 chattr->attr.store = set_values_channel;229 sysfs_attr_init(&chattr->attr.attr);230 231 return sysfs_create_file(parent, &chattr->attr.attr);232}233 234int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev)235{236 unsigned int i;237 int rc;238 int required;239 struct kobject *chanops;240 241 required = sizeof(*mdev->chroots) * mdev->dma_channels;242 mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL);243 if (!mdev->chroots)244 return -ENOMEM;245 246 chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj);247 if (!chanops)248 return -ENOMEM;249 250 /* create each channel directory here */251 for (i = 0; i < mdev->dma_channels; i++) {252 char name[20];253 254 snprintf(name, sizeof(name), "chan%d", i);255 mdev->chroots[i] = kobject_create_and_add(name, chanops);256 if (!mdev->chroots[i])257 return -ENOMEM;258 }259 260 /* populate common parameters */261 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) {262 rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name,263 hidma_mgmt_files[i].mode);264 if (rc)265 return rc;266 }267 268 /* populate parameters that are per channel */269 for (i = 0; i < mdev->dma_channels; i++) {270 rc = create_sysfs_entry_channel(mdev, "priority",271 (S_IRUGO | S_IWUGO), i,272 mdev->chroots[i]);273 if (rc)274 return rc;275 276 rc = create_sysfs_entry_channel(mdev, "weight",277 (S_IRUGO | S_IWUGO), i,278 mdev->chroots[i]);279 if (rc)280 return rc;281 }282 283 return 0;284}285EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);286