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