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 13 struct hidma_chan_attr { 14 struct hidma_mgmt_dev *mdev; 15 int index; 16 struct kobj_attribute attr; 17 }; 18 19 struct 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) \ 27 static int get_##name(struct hidma_mgmt_dev *mdev) \ 28 { \ 29 return mdev->name; \ 30 } \ 31 static 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 47 IMPLEMENT_GETSET(hw_version_major) 48 IMPLEMENT_GETSET(hw_version_minor) 49 IMPLEMENT_GETSET(max_wr_xactions) 50 IMPLEMENT_GETSET(max_rd_xactions) 51 IMPLEMENT_GETSET(max_write_request) 52 IMPLEMENT_GETSET(max_read_request) 53 IMPLEMENT_GETSET(dma_channels) 54 IMPLEMENT_GETSET(chreset_timeout_cycles) 55 56 static 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 72 static 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 88 static 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 99 static 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 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) { 106 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) 107 return sysfs_emit(buf, "%d\n", 108 hidma_mgmt_files[i].get(mdev)); 109 } 110 return 0; 111 } 112 113 static ssize_t set_values(struct device *dev, struct device_attribute *attr, 114 const char *buf, size_t count) 115 { 116 struct hidma_mgmt_dev *mdev = dev_get_drvdata(dev); 117 unsigned long tmp; 118 unsigned int i; 119 int rc; 120 121 rc = kstrtoul(buf, 0, &tmp); 122 if (rc) 123 return rc; 124 125 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) { 126 if (strcmp(attr->attr.name, hidma_mgmt_files[i].name) == 0) { 127 rc = hidma_mgmt_files[i].set(mdev, tmp); 128 if (rc) 129 return rc; 130 131 break; 132 } 133 } 134 return count; 135 } 136 137 static ssize_t show_values_channel(struct kobject *kobj, 138 struct kobj_attribute *attr, char *buf) 139 { 140 struct hidma_chan_attr *chattr; 141 struct hidma_mgmt_dev *mdev; 142 143 chattr = container_of(attr, struct hidma_chan_attr, attr); 144 mdev = chattr->mdev; 145 146 if (strcmp(attr->attr.name, "priority") == 0) 147 return sysfs_emit(buf, "%d\n", mdev->priority[chattr->index]); 148 else if (strcmp(attr->attr.name, "weight") == 0) 149 return sysfs_emit(buf, "%d\n", mdev->weight[chattr->index]); 150 151 return 0; 152 } 153 154 static ssize_t set_values_channel(struct kobject *kobj, 155 struct kobj_attribute *attr, const char *buf, 156 size_t count) 157 { 158 struct hidma_chan_attr *chattr; 159 struct hidma_mgmt_dev *mdev; 160 unsigned long tmp; 161 int rc; 162 163 chattr = container_of(attr, struct hidma_chan_attr, attr); 164 mdev = chattr->mdev; 165 166 rc = kstrtoul(buf, 0, &tmp); 167 if (rc) 168 return rc; 169 170 if (strcmp(attr->attr.name, "priority") == 0) { 171 rc = set_priority(mdev, chattr->index, tmp); 172 if (rc) 173 return rc; 174 } else if (strcmp(attr->attr.name, "weight") == 0) { 175 rc = set_weight(mdev, chattr->index, tmp); 176 if (rc) 177 return rc; 178 } 179 return count; 180 } 181 182 static int create_sysfs_entry(struct hidma_mgmt_dev *dev, char *name, int mode) 183 { 184 struct device_attribute *attrs; 185 char *name_copy; 186 187 attrs = devm_kmalloc(&dev->pdev->dev, 188 sizeof(struct device_attribute), GFP_KERNEL); 189 if (!attrs) 190 return -ENOMEM; 191 192 name_copy = devm_kstrdup(&dev->pdev->dev, name, GFP_KERNEL); 193 if (!name_copy) 194 return -ENOMEM; 195 196 attrs->attr.name = name_copy; 197 attrs->attr.mode = mode; 198 attrs->show = show_values; 199 attrs->store = set_values; 200 sysfs_attr_init(&attrs->attr); 201 202 return device_create_file(&dev->pdev->dev, attrs); 203 } 204 205 static int create_sysfs_entry_channel(struct hidma_mgmt_dev *mdev, char *name, 206 int mode, int index, 207 struct kobject *parent) 208 { 209 struct hidma_chan_attr *chattr; 210 char *name_copy; 211 212 chattr = devm_kmalloc(&mdev->pdev->dev, sizeof(*chattr), GFP_KERNEL); 213 if (!chattr) 214 return -ENOMEM; 215 216 name_copy = devm_kstrdup(&mdev->pdev->dev, name, GFP_KERNEL); 217 if (!name_copy) 218 return -ENOMEM; 219 220 chattr->mdev = mdev; 221 chattr->index = index; 222 chattr->attr.attr.name = name_copy; 223 chattr->attr.attr.mode = mode; 224 chattr->attr.show = show_values_channel; 225 chattr->attr.store = set_values_channel; 226 sysfs_attr_init(&chattr->attr.attr); 227 228 return sysfs_create_file(parent, &chattr->attr.attr); 229 } 230 231 int hidma_mgmt_init_sys(struct hidma_mgmt_dev *mdev) 232 { 233 unsigned int i; 234 int rc; 235 int required; 236 struct kobject *chanops; 237 238 required = sizeof(*mdev->chroots) * mdev->dma_channels; 239 mdev->chroots = devm_kmalloc(&mdev->pdev->dev, required, GFP_KERNEL); 240 if (!mdev->chroots) 241 return -ENOMEM; 242 243 chanops = kobject_create_and_add("chanops", &mdev->pdev->dev.kobj); 244 if (!chanops) 245 return -ENOMEM; 246 247 /* create each channel directory here */ 248 for (i = 0; i < mdev->dma_channels; i++) { 249 char name[20]; 250 251 snprintf(name, sizeof(name), "chan%d", i); 252 mdev->chroots[i] = kobject_create_and_add(name, chanops); 253 if (!mdev->chroots[i]) 254 return -ENOMEM; 255 } 256 257 /* populate common parameters */ 258 for (i = 0; i < ARRAY_SIZE(hidma_mgmt_files); i++) { 259 rc = create_sysfs_entry(mdev, hidma_mgmt_files[i].name, 260 hidma_mgmt_files[i].mode); 261 if (rc) 262 return rc; 263 } 264 265 /* populate parameters that are per channel */ 266 for (i = 0; i < mdev->dma_channels; i++) { 267 rc = create_sysfs_entry_channel(mdev, "priority", 268 (S_IRUGO | S_IWUGO), i, 269 mdev->chroots[i]); 270 if (rc) 271 return rc; 272 273 rc = create_sysfs_entry_channel(mdev, "weight", 274 (S_IRUGO | S_IWUGO), i, 275 mdev->chroots[i]); 276 if (rc) 277 return rc; 278 } 279 280 return 0; 281 } 282 EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys); 283