linux/drivers/dma/qcom/hidma_mgmt_sys.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0-only
   2/*
   3 * Qualcomm Technologies HIDMA Management SYS interface
   4 *
   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