1
2#include <linux/init.h>
3#include <linux/module.h>
4#include <linux/types.h>
5#include <linux/io.h>
6#include <linux/export.h>
7#include <linux/slab.h>
8#include <linux/platform_device.h>
9#include <linux/fs.h>
10#include <linux/rwsem.h>
11#include "kpc_dma_driver.h"
12
13MODULE_LICENSE("GPL");
14MODULE_AUTHOR("Matt.Sickler@daktronics.com");
15
16#define KPC_DMA_CHAR_MAJOR UNNAMED_MAJOR
17#define KPC_DMA_NUM_MINORS BIT(MINORBITS)
18static DEFINE_MUTEX(kpc_dma_mtx);
19static int assigned_major_num;
20static LIST_HEAD(kpc_dma_list);
21
22
23struct kpc_dma_device *kpc_dma_lookup_device(int minor)
24{
25 struct kpc_dma_device *c;
26
27 mutex_lock(&kpc_dma_mtx);
28 list_for_each_entry(c, &kpc_dma_list, list) {
29 if (c->pldev->id == minor)
30 goto out;
31 }
32 c = NULL;
33out:
34 mutex_unlock(&kpc_dma_mtx);
35 return c;
36}
37
38static void kpc_dma_add_device(struct kpc_dma_device *ldev)
39{
40 mutex_lock(&kpc_dma_mtx);
41 list_add(&ldev->list, &kpc_dma_list);
42 mutex_unlock(&kpc_dma_mtx);
43}
44
45static void kpc_dma_del_device(struct kpc_dma_device *ldev)
46{
47 mutex_lock(&kpc_dma_mtx);
48 list_del(&ldev->list);
49 mutex_unlock(&kpc_dma_mtx);
50}
51
52
53static ssize_t show_engine_regs(struct device *dev, struct device_attribute *attr, char *buf)
54{
55 struct kpc_dma_device *ldev;
56 struct platform_device *pldev = to_platform_device(dev);
57
58 if (!pldev)
59 return 0;
60 ldev = platform_get_drvdata(pldev);
61 if (!ldev)
62 return 0;
63
64 return scnprintf(buf, PAGE_SIZE,
65 "EngineControlStatus = 0x%08x\n"
66 "RegNextDescPtr = 0x%08x\n"
67 "RegSWDescPtr = 0x%08x\n"
68 "RegCompletedDescPtr = 0x%08x\n"
69 "desc_pool_first = %p\n"
70 "desc_pool_last = %p\n"
71 "desc_next = %p\n"
72 "desc_completed = %p\n",
73 readl(ldev->eng_regs + 1),
74 readl(ldev->eng_regs + 2),
75 readl(ldev->eng_regs + 3),
76 readl(ldev->eng_regs + 4),
77 ldev->desc_pool_first,
78 ldev->desc_pool_last,
79 ldev->desc_next,
80 ldev->desc_completed
81 );
82}
83static DEVICE_ATTR(engine_regs, 0444, show_engine_regs, NULL);
84
85static const struct attribute *ndd_attr_list[] = {
86 &dev_attr_engine_regs.attr,
87 NULL,
88};
89
90static struct class *kpc_dma_class;
91
92
93static
94int kpc_dma_probe(struct platform_device *pldev)
95{
96 struct resource *r = NULL;
97 int rv = 0;
98 dev_t dev;
99
100 struct kpc_dma_device *ldev = kzalloc(sizeof(*ldev), GFP_KERNEL);
101
102 if (!ldev) {
103 dev_err(&pldev->dev, "%s: unable to kzalloc space for kpc_dma_device\n", __func__);
104 rv = -ENOMEM;
105 goto err_rv;
106 }
107
108 INIT_LIST_HEAD(&ldev->list);
109
110 ldev->pldev = pldev;
111 platform_set_drvdata(pldev, ldev);
112 atomic_set(&ldev->open_count, 1);
113
114 mutex_init(&ldev->sem);
115 lock_engine(ldev);
116
117
118 r = platform_get_resource(pldev, IORESOURCE_MEM, 0);
119 if (!r) {
120 dev_err(&ldev->pldev->dev, "%s: didn't get the engine regs resource!\n", __func__);
121 rv = -ENXIO;
122 goto err_kfree;
123 }
124 ldev->eng_regs = ioremap(r->start, resource_size(r));
125 if (!ldev->eng_regs) {
126 dev_err(&ldev->pldev->dev, "%s: failed to ioremap engine regs!\n", __func__);
127 rv = -ENXIO;
128 goto err_kfree;
129 }
130
131 r = platform_get_resource(pldev, IORESOURCE_IRQ, 0);
132 if (!r) {
133 dev_err(&ldev->pldev->dev, "%s: didn't get the IRQ resource!\n", __func__);
134 rv = -ENXIO;
135 goto err_kfree;
136 }
137 ldev->irq = r->start;
138
139
140 dev = MKDEV(assigned_major_num, pldev->id);
141 ldev->kpc_dma_dev = device_create(kpc_dma_class, &pldev->dev, dev, ldev, "kpc_dma%d", pldev->id);
142 if (IS_ERR(ldev->kpc_dma_dev)) {
143 rv = PTR_ERR(ldev->kpc_dma_dev);
144 dev_err(&ldev->pldev->dev, "%s: device_create failed: %d\n", __func__, rv);
145 goto err_kfree;
146 }
147
148
149 rv = setup_dma_engine(ldev, 30);
150 if (rv) {
151 dev_err(&ldev->pldev->dev, "%s: failed to setup_dma_engine: %d\n", __func__, rv);
152 goto err_misc_dereg;
153 }
154
155
156 rv = sysfs_create_files(&(ldev->pldev->dev.kobj), ndd_attr_list);
157 if (rv) {
158 dev_err(&ldev->pldev->dev, "%s: Failed to add sysfs files: %d\n", __func__, rv);
159 goto err_destroy_eng;
160 }
161
162 kpc_dma_add_device(ldev);
163
164 return 0;
165
166 err_destroy_eng:
167 destroy_dma_engine(ldev);
168 err_misc_dereg:
169 device_destroy(kpc_dma_class, dev);
170 err_kfree:
171 kfree(ldev);
172 err_rv:
173 return rv;
174}
175
176static
177int kpc_dma_remove(struct platform_device *pldev)
178{
179 struct kpc_dma_device *ldev = platform_get_drvdata(pldev);
180
181 if (!ldev)
182 return -ENXIO;
183
184 lock_engine(ldev);
185 sysfs_remove_files(&(ldev->pldev->dev.kobj), ndd_attr_list);
186 destroy_dma_engine(ldev);
187 kpc_dma_del_device(ldev);
188 device_destroy(kpc_dma_class, MKDEV(assigned_major_num, ldev->pldev->id));
189 kfree(ldev);
190
191 return 0;
192}
193
194
195static struct platform_driver kpc_dma_plat_driver_i = {
196 .probe = kpc_dma_probe,
197 .remove = kpc_dma_remove,
198 .driver = {
199 .name = KP_DRIVER_NAME_DMA_CONTROLLER,
200 },
201};
202
203static
204int __init kpc_dma_driver_init(void)
205{
206 int err;
207
208 err = __register_chrdev(KPC_DMA_CHAR_MAJOR, 0, KPC_DMA_NUM_MINORS, "kpc_dma", &kpc_dma_fops);
209 if (err < 0) {
210 pr_err("Can't allocate a major number (%d) for kpc_dma (err = %d)\n", KPC_DMA_CHAR_MAJOR, err);
211 goto fail_chrdev_register;
212 }
213 assigned_major_num = err;
214
215 kpc_dma_class = class_create(THIS_MODULE, "kpc_dma");
216 err = PTR_ERR(kpc_dma_class);
217 if (IS_ERR(kpc_dma_class)) {
218 pr_err("Can't create class kpc_dma (err = %d)\n", err);
219 goto fail_class_create;
220 }
221
222 err = platform_driver_register(&kpc_dma_plat_driver_i);
223 if (err) {
224 pr_err("Can't register platform driver for kpc_dma (err = %d)\n", err);
225 goto fail_platdriver_register;
226 }
227
228 return err;
229
230fail_platdriver_register:
231 class_destroy(kpc_dma_class);
232fail_class_create:
233 __unregister_chrdev(KPC_DMA_CHAR_MAJOR, 0, KPC_DMA_NUM_MINORS, "kpc_dma");
234fail_chrdev_register:
235 return err;
236}
237module_init(kpc_dma_driver_init);
238
239static
240void __exit kpc_dma_driver_exit(void)
241{
242 platform_driver_unregister(&kpc_dma_plat_driver_i);
243 class_destroy(kpc_dma_class);
244 __unregister_chrdev(KPC_DMA_CHAR_MAJOR, 0, KPC_DMA_NUM_MINORS, "kpc_dma");
245}
246module_exit(kpc_dma_driver_exit);
247