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 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 116 static 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 140 static 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 157 static 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 185 static 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 208 static 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 234 int 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 } 285 EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys); 286