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