aboutsummaryrefslogtreecommitdiffstats
path: root/drivers/dma/qcom
diff options
context:
space:
mode:
authorSinan Kaya <okaya@codeaurora.org>2016-02-04 23:34:34 -0500
committerVinod Koul <vinod.koul@intel.com>2016-03-10 21:12:23 -0500
commit7f8f209fd6e09a07fd1820144452caba419cf2b4 (patch)
tree2f430ca5b6ffb903e7bf0232aebac3a1e921742b /drivers/dma/qcom
parent858de34c0a31b9438eeab2ae0465c20c68bf63f0 (diff)
dmaengine: add Qualcomm Technologies HIDMA management driver
The Qualcomm Technologies HIDMA device has been designed to support virtualization technology. The driver has been divided into two to follow the hardware design. 1. HIDMA Management driver 2. HIDMA Channel driver Each HIDMA HW consists of multiple channels. These channels share some set of common parameters. These parameters are initialized by the management driver during power up. Same management driver is used for monitoring the execution of the channels. Management driver can change the performance behavior dynamically such as bandwidth allocation and prioritization. The management driver is executed in host context and is the main management entity for all channels provided by the device. Signed-off-by: Sinan Kaya <okaya@codeaurora.org> Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com> Signed-off-by: Vinod Koul <vinod.koul@intel.com>
Diffstat (limited to 'drivers/dma/qcom')
-rw-r--r--drivers/dma/qcom/Kconfig11
-rw-r--r--drivers/dma/qcom/Makefile2
-rw-r--r--drivers/dma/qcom/hidma_mgmt.c302
-rw-r--r--drivers/dma/qcom/hidma_mgmt.h39
-rw-r--r--drivers/dma/qcom/hidma_mgmt_sys.c295
5 files changed, 649 insertions, 0 deletions
diff --git a/drivers/dma/qcom/Kconfig b/drivers/dma/qcom/Kconfig
index f17c27288808..c975b1167acf 100644
--- a/drivers/dma/qcom/Kconfig
+++ b/drivers/dma/qcom/Kconfig
@@ -6,3 +6,14 @@ config QCOM_BAM_DMA
6 ---help--- 6 ---help---
7 Enable support for the QCOM BAM DMA controller. This controller 7 Enable support for the QCOM BAM DMA controller. This controller
8 provides DMA capabilities for a variety of on-chip devices. 8 provides DMA capabilities for a variety of on-chip devices.
9
10config QCOM_HIDMA_MGMT
11 tristate "Qualcomm Technologies HIDMA Management support"
12 select DMA_ENGINE
13 help
14 Enable support for the Qualcomm Technologies HIDMA Management.
15 Each DMA device requires one management interface driver
16 for basic initialization before QCOM_HIDMA channel driver can
17 start managing the channels. In a virtualized environment,
18 the guest OS would run QCOM_HIDMA channel driver and the
19 host would run the QCOM_HIDMA_MGMT management driver.
diff --git a/drivers/dma/qcom/Makefile b/drivers/dma/qcom/Makefile
index f612ae303ee0..bfea6990229f 100644
--- a/drivers/dma/qcom/Makefile
+++ b/drivers/dma/qcom/Makefile
@@ -1 +1,3 @@
1obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o 1obj-$(CONFIG_QCOM_BAM_DMA) += bam_dma.o
2obj-$(CONFIG_QCOM_HIDMA_MGMT) += hdma_mgmt.o
3hdma_mgmt-objs := hidma_mgmt.o hidma_mgmt_sys.o
diff --git a/drivers/dma/qcom/hidma_mgmt.c b/drivers/dma/qcom/hidma_mgmt.c
new file mode 100644
index 000000000000..ef491b893f40
--- /dev/null
+++ b/drivers/dma/qcom/hidma_mgmt.c
@@ -0,0 +1,302 @@
1/*
2 * Qualcomm Technologies HIDMA DMA engine Management 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/dmaengine.h>
17#include <linux/acpi.h>
18#include <linux/of.h>
19#include <linux/property.h>
20#include <linux/interrupt.h>
21#include <linux/platform_device.h>
22#include <linux/module.h>
23#include <linux/uaccess.h>
24#include <linux/slab.h>
25#include <linux/pm_runtime.h>
26#include <linux/bitops.h>
27
28#include "hidma_mgmt.h"
29
30#define HIDMA_QOS_N_OFFSET 0x300
31#define HIDMA_CFG_OFFSET 0x400
32#define HIDMA_MAX_BUS_REQ_LEN_OFFSET 0x41C
33#define HIDMA_MAX_XACTIONS_OFFSET 0x420
34#define HIDMA_HW_VERSION_OFFSET 0x424
35#define HIDMA_CHRESET_TIMEOUT_OFFSET 0x418
36
37#define HIDMA_MAX_WR_XACTIONS_MASK GENMASK(4, 0)
38#define HIDMA_MAX_RD_XACTIONS_MASK GENMASK(4, 0)
39#define HIDMA_WEIGHT_MASK GENMASK(6, 0)
40#define HIDMA_MAX_BUS_REQ_LEN_MASK GENMASK(15, 0)
41#define HIDMA_CHRESET_TIMEOUT_MASK GENMASK(19, 0)
42
43#define HIDMA_MAX_WR_XACTIONS_BIT_POS 16
44#define HIDMA_MAX_BUS_WR_REQ_BIT_POS 16
45#define HIDMA_WRR_BIT_POS 8
46#define HIDMA_PRIORITY_BIT_POS 15
47
48#define HIDMA_AUTOSUSPEND_TIMEOUT 2000
49#define HIDMA_MAX_CHANNEL_WEIGHT 15
50
51int hidma_mgmt_setup(struct hidma_mgmt_dev *mgmtdev)
52{
53 unsigned int i;
54 u32 val;
55
56 if (!is_power_of_2(mgmtdev->max_write_request) ||
57 (mgmtdev->max_write_request < 128) ||
58 (mgmtdev->max_write_request > 1024)) {
59 dev_err(&mgmtdev->pdev->dev, "invalid write request %d\n",
60 mgmtdev->max_write_request);
61 return -EINVAL;
62 }
63
64 if (!is_power_of_2(mgmtdev->max_read_request) ||
65 (mgmtdev->max_read_request < 128) ||
66 (mgmtdev->max_read_request > 1024)) {
67 dev_err(&mgmtdev->pdev->dev, "invalid read request %d\n",
68 mgmtdev->max_read_request);
69 return -EINVAL;
70 }
71
72 if (mgmtdev->max_wr_xactions > HIDMA_MAX_WR_XACTIONS_MASK) {
73 dev_err(&mgmtdev->pdev->dev,
74 "max_wr_xactions cannot be bigger than %ld\n",
75 HIDMA_MAX_WR_XACTIONS_MASK);
76 return -EINVAL;
77 }
78
79 if (mgmtdev->max_rd_xactions > HIDMA_MAX_RD_XACTIONS_MASK) {
80 dev_err(&mgmtdev->pdev->dev,
81 "max_rd_xactions cannot be bigger than %ld\n",
82 HIDMA_MAX_RD_XACTIONS_MASK);
83 return -EINVAL;
84 }
85
86 for (i = 0; i < mgmtdev->dma_channels; i++) {
87 if (mgmtdev->priority[i] > 1) {
88 dev_err(&mgmtdev->pdev->dev,
89 "priority can be 0 or 1\n");
90 return -EINVAL;
91 }
92
93 if (mgmtdev->weight[i] > HIDMA_MAX_CHANNEL_WEIGHT) {
94 dev_err(&mgmtdev->pdev->dev,
95 "max value of weight can be %d.\n",
96 HIDMA_MAX_CHANNEL_WEIGHT);
97 return -EINVAL;
98 }
99
100 /* weight needs to be at least one */
101 if (mgmtdev->weight[i] == 0)
102 mgmtdev->weight[i] = 1;
103 }
104
105 pm_runtime_get_sync(&mgmtdev->pdev->dev);
106 val = readl(mgmtdev->virtaddr + HIDMA_MAX_BUS_REQ_LEN_OFFSET);
107 val &= ~(HIDMA_MAX_BUS_REQ_LEN_MASK << HIDMA_MAX_BUS_WR_REQ_BIT_POS);
108 val |= mgmtdev->max_write_request << HIDMA_MAX_BUS_WR_REQ_BIT_POS;
109 val &= ~HIDMA_MAX_BUS_REQ_LEN_MASK;
110 val |= mgmtdev->max_read_request;
111 writel(val, mgmtdev->virtaddr + HIDMA_MAX_BUS_REQ_LEN_OFFSET);
112
113 val = readl(mgmtdev->virtaddr + HIDMA_MAX_XACTIONS_OFFSET);
114 val &= ~(HIDMA_MAX_WR_XACTIONS_MASK << HIDMA_MAX_WR_XACTIONS_BIT_POS);
115 val |= mgmtdev->max_wr_xactions << HIDMA_MAX_WR_XACTIONS_BIT_POS;
116 val &= ~HIDMA_MAX_RD_XACTIONS_MASK;
117 val |= mgmtdev->max_rd_xactions;
118 writel(val, mgmtdev->virtaddr + HIDMA_MAX_XACTIONS_OFFSET);
119
120 mgmtdev->hw_version =
121 readl(mgmtdev->virtaddr + HIDMA_HW_VERSION_OFFSET);
122 mgmtdev->hw_version_major = (mgmtdev->hw_version >> 28) & 0xF;
123 mgmtdev->hw_version_minor = (mgmtdev->hw_version >> 16) & 0xF;
124
125 for (i = 0; i < mgmtdev->dma_channels; i++) {
126 u32 weight = mgmtdev->weight[i];
127 u32 priority = mgmtdev->priority[i];
128
129 val = readl(mgmtdev->virtaddr + HIDMA_QOS_N_OFFSET + (4 * i));
130 val &= ~(1 << HIDMA_PRIORITY_BIT_POS);
131 val |= (priority & 0x1) << HIDMA_PRIORITY_BIT_POS;
132 val &= ~(HIDMA_WEIGHT_MASK << HIDMA_WRR_BIT_POS);
133 val |= (weight & HIDMA_WEIGHT_MASK) << HIDMA_WRR_BIT_POS;
134 writel(val, mgmtdev->virtaddr + HIDMA_QOS_N_OFFSET + (4 * i));
135 }
136
137 val = readl(mgmtdev->virtaddr + HIDMA_CHRESET_TIMEOUT_OFFSET);
138 val &= ~HIDMA_CHRESET_TIMEOUT_MASK;
139 val |= mgmtdev->chreset_timeout_cycles & HIDMA_CHRESET_TIMEOUT_MASK;
140 writel(val, mgmtdev->virtaddr + HIDMA_CHRESET_TIMEOUT_OFFSET);
141
142 pm_runtime_mark_last_busy(&mgmtdev->pdev->dev);
143 pm_runtime_put_autosuspend(&mgmtdev->pdev->dev);
144 return 0;
145}
146EXPORT_SYMBOL_GPL(hidma_mgmt_setup);
147
148static int hidma_mgmt_probe(struct platform_device *pdev)
149{
150 struct hidma_mgmt_dev *mgmtdev;
151 struct resource *res;
152 void __iomem *virtaddr;
153 int irq;
154 int rc;
155 u32 val;
156
157 pm_runtime_set_autosuspend_delay(&pdev->dev, HIDMA_AUTOSUSPEND_TIMEOUT);
158 pm_runtime_use_autosuspend(&pdev->dev);
159 pm_runtime_set_active(&pdev->dev);
160 pm_runtime_enable(&pdev->dev);
161 pm_runtime_get_sync(&pdev->dev);
162
163 res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
164 virtaddr = devm_ioremap_resource(&pdev->dev, res);
165 if (IS_ERR(virtaddr)) {
166 rc = -ENOMEM;
167 goto out;
168 }
169
170 irq = platform_get_irq(pdev, 0);
171 if (irq < 0) {
172 dev_err(&pdev->dev, "irq resources not found\n");
173 rc = irq;
174 goto out;
175 }
176
177 mgmtdev = devm_kzalloc(&pdev->dev, sizeof(*mgmtdev), GFP_KERNEL);
178 if (!mgmtdev) {
179 rc = -ENOMEM;
180 goto out;
181 }
182
183 mgmtdev->pdev = pdev;
184 mgmtdev->addrsize = resource_size(res);
185 mgmtdev->virtaddr = virtaddr;
186
187 rc = device_property_read_u32(&pdev->dev, "dma-channels",
188 &mgmtdev->dma_channels);
189 if (rc) {
190 dev_err(&pdev->dev, "number of channels missing\n");
191 goto out;
192 }
193
194 rc = device_property_read_u32(&pdev->dev,
195 "channel-reset-timeout-cycles",
196 &mgmtdev->chreset_timeout_cycles);
197 if (rc) {
198 dev_err(&pdev->dev, "channel reset timeout missing\n");
199 goto out;
200 }
201
202 rc = device_property_read_u32(&pdev->dev, "max-write-burst-bytes",
203 &mgmtdev->max_write_request);
204 if (rc) {
205 dev_err(&pdev->dev, "max-write-burst-bytes missing\n");
206 goto out;
207 }
208
209 rc = device_property_read_u32(&pdev->dev, "max-read-burst-bytes",
210 &mgmtdev->max_read_request);
211 if (rc) {
212 dev_err(&pdev->dev, "max-read-burst-bytes missing\n");
213 goto out;
214 }
215
216 rc = device_property_read_u32(&pdev->dev, "max-write-transactions",
217 &mgmtdev->max_wr_xactions);
218 if (rc) {
219 dev_err(&pdev->dev, "max-write-transactions missing\n");
220 goto out;
221 }
222
223 rc = device_property_read_u32(&pdev->dev, "max-read-transactions",
224 &mgmtdev->max_rd_xactions);
225 if (rc) {
226 dev_err(&pdev->dev, "max-read-transactions missing\n");
227 goto out;
228 }
229
230 mgmtdev->priority = devm_kcalloc(&pdev->dev,
231 mgmtdev->dma_channels,
232 sizeof(*mgmtdev->priority),
233 GFP_KERNEL);
234 if (!mgmtdev->priority) {
235 rc = -ENOMEM;
236 goto out;
237 }
238
239 mgmtdev->weight = devm_kcalloc(&pdev->dev,
240 mgmtdev->dma_channels,
241 sizeof(*mgmtdev->weight), GFP_KERNEL);
242 if (!mgmtdev->weight) {
243 rc = -ENOMEM;
244 goto out;
245 }
246
247 rc = hidma_mgmt_setup(mgmtdev);
248 if (rc) {
249 dev_err(&pdev->dev, "setup failed\n");
250 goto out;
251 }
252
253 /* start the HW */
254 val = readl(mgmtdev->virtaddr + HIDMA_CFG_OFFSET);
255 val |= 1;
256 writel(val, mgmtdev->virtaddr + HIDMA_CFG_OFFSET);
257
258 rc = hidma_mgmt_init_sys(mgmtdev);
259 if (rc) {
260 dev_err(&pdev->dev, "sysfs setup failed\n");
261 goto out;
262 }
263
264 dev_info(&pdev->dev,
265 "HW rev: %d.%d @ %pa with %d physical channels\n",
266 mgmtdev->hw_version_major, mgmtdev->hw_version_minor,
267 &res->start, mgmtdev->dma_channels);
268
269 platform_set_drvdata(pdev, mgmtdev);
270 pm_runtime_mark_last_busy(&pdev->dev);
271 pm_runtime_put_autosuspend(&pdev->dev);
272 return 0;
273out:
274 pm_runtime_put_sync_suspend(&pdev->dev);
275 pm_runtime_disable(&pdev->dev);
276 return rc;
277}
278
279#if IS_ENABLED(CONFIG_ACPI)
280static const struct acpi_device_id hidma_mgmt_acpi_ids[] = {
281 {"QCOM8060"},
282 {},
283};
284#endif
285
286static const struct of_device_id hidma_mgmt_match[] = {
287 {.compatible = "qcom,hidma-mgmt-1.0",},
288 {},
289};
290MODULE_DEVICE_TABLE(of, hidma_mgmt_match);
291
292static struct platform_driver hidma_mgmt_driver = {
293 .probe = hidma_mgmt_probe,
294 .driver = {
295 .name = "hidma-mgmt",
296 .of_match_table = hidma_mgmt_match,
297 .acpi_match_table = ACPI_PTR(hidma_mgmt_acpi_ids),
298 },
299};
300
301module_platform_driver(hidma_mgmt_driver);
302MODULE_LICENSE("GPL v2");
diff --git a/drivers/dma/qcom/hidma_mgmt.h b/drivers/dma/qcom/hidma_mgmt.h
new file mode 100644
index 000000000000..f7daf33769f4
--- /dev/null
+++ b/drivers/dma/qcom/hidma_mgmt.h
@@ -0,0 +1,39 @@
1/*
2 * Qualcomm Technologies HIDMA Management common header
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
16struct hidma_mgmt_dev {
17 u8 hw_version_major;
18 u8 hw_version_minor;
19
20 u32 max_wr_xactions;
21 u32 max_rd_xactions;
22 u32 max_write_request;
23 u32 max_read_request;
24 u32 dma_channels;
25 u32 chreset_timeout_cycles;
26 u32 hw_version;
27 u32 *priority;
28 u32 *weight;
29
30 /* Hardware device constants */
31 void __iomem *virtaddr;
32 resource_size_t addrsize;
33
34 struct kobject **chroots;
35 struct platform_device *pdev;
36};
37
38int hidma_mgmt_init_sys(struct hidma_mgmt_dev *dev);
39int hidma_mgmt_setup(struct hidma_mgmt_dev *mgmtdev);
diff --git a/drivers/dma/qcom/hidma_mgmt_sys.c b/drivers/dma/qcom/hidma_mgmt_sys.c
new file mode 100644
index 000000000000..d61f1068a34b
--- /dev/null
+++ b/drivers/dma/qcom/hidma_mgmt_sys.c
@@ -0,0 +1,295 @@
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
21struct hidma_chan_attr {
22 struct hidma_mgmt_dev *mdev;
23 int index;
24 struct kobj_attribute attr;
25};
26
27struct 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) \
35static int get_##name(struct hidma_mgmt_dev *mdev) \
36{ \
37 return mdev->name; \
38} \
39static 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
55IMPLEMENT_GETSET(hw_version_major)
56IMPLEMENT_GETSET(hw_version_minor)
57IMPLEMENT_GETSET(max_wr_xactions)
58IMPLEMENT_GETSET(max_rd_xactions)
59IMPLEMENT_GETSET(max_write_request)
60IMPLEMENT_GETSET(max_read_request)
61IMPLEMENT_GETSET(dma_channels)
62IMPLEMENT_GETSET(chreset_timeout_cycles)
63
64static 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
80static 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
96static 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
107static 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
125static 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
150static 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
167static 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
195static 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
218static 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
244int 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}
295EXPORT_SYMBOL_GPL(hidma_mgmt_init_sys);