root/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.c

/* [<][>][^][v][top][bottom][index][help] */

DEFINITIONS

This source file includes following definitions.
  1. kpc_dma_lookup_device
  2. kpc_dma_add_device
  3. kpc_dma_del_device
  4. show_engine_regs
  5. kpc_dma_probe
  6. kpc_dma_remove
  7. kpc_dma_driver_init
  8. kpc_dma_driver_exit

   1 /* SPDX-License-Identifier: GPL-2.0+ */
   2 #include <linux/init.h>
   3 #include <linux/module.h>
   4 #include <linux/types.h>
   5 #include <asm/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 
  13 MODULE_LICENSE("GPL");
  14 MODULE_AUTHOR("Matt.Sickler@daktronics.com");
  15 
  16 #define KPC_DMA_CHAR_MAJOR    UNNAMED_MAJOR
  17 #define KPC_DMA_NUM_MINORS    BIT(MINORBITS)
  18 static DEFINE_MUTEX(kpc_dma_mtx);
  19 static int assigned_major_num;
  20 static LIST_HEAD(kpc_dma_list);
  21 
  22 /**********  kpc_dma_list list management  **********/
  23 struct 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         }
  33         c = NULL; // not-found case
  34 out:
  35         mutex_unlock(&kpc_dma_mtx);
  36         return c;
  37 }
  38 
  39 static void kpc_dma_add_device(struct kpc_dma_device *ldev)
  40 {
  41         mutex_lock(&kpc_dma_mtx);
  42         list_add(&ldev->list, &kpc_dma_list);
  43         mutex_unlock(&kpc_dma_mtx);
  44 }
  45 
  46 static void kpc_dma_del_device(struct kpc_dma_device *ldev)
  47 {
  48         mutex_lock(&kpc_dma_mtx);
  49         list_del(&ldev->list);
  50         mutex_unlock(&kpc_dma_mtx);
  51 }
  52 
  53 /**********  SysFS Attributes **********/
  54 static ssize_t  show_engine_regs(struct device *dev, struct device_attribute *attr, char *buf)
  55 {
  56         struct kpc_dma_device *ldev;
  57         struct platform_device *pldev = to_platform_device(dev);
  58 
  59         if (!pldev)
  60                 return 0;
  61         ldev = platform_get_drvdata(pldev);
  62         if (!ldev)
  63                 return 0;
  64 
  65         return scnprintf(buf, PAGE_SIZE,
  66                 "EngineControlStatus      = 0x%08x\n"
  67                 "RegNextDescPtr           = 0x%08x\n"
  68                 "RegSWDescPtr             = 0x%08x\n"
  69                 "RegCompletedDescPtr      = 0x%08x\n"
  70                 "desc_pool_first          = %p\n"
  71                 "desc_pool_last           = %p\n"
  72                 "desc_next                = %p\n"
  73                 "desc_completed           = %p\n",
  74                 readl(ldev->eng_regs + 1),
  75                 readl(ldev->eng_regs + 2),
  76                 readl(ldev->eng_regs + 3),
  77                 readl(ldev->eng_regs + 4),
  78                 ldev->desc_pool_first,
  79                 ldev->desc_pool_last,
  80                 ldev->desc_next,
  81                 ldev->desc_completed
  82         );
  83 }
  84 static DEVICE_ATTR(engine_regs, 0444, show_engine_regs, NULL);
  85 
  86 static const struct attribute *ndd_attr_list[] = {
  87         &dev_attr_engine_regs.attr,
  88         NULL,
  89 };
  90 
  91 static struct class *kpc_dma_class;
  92 
  93 /**********  Platform Driver Functions  **********/
  94 static
  95 int  kpc_dma_probe(struct platform_device *pldev)
  96 {
  97         struct resource *r = NULL;
  98         int rv = 0;
  99         dev_t dev;
 100 
 101         struct kpc_dma_device *ldev = kzalloc(sizeof(struct kpc_dma_device), GFP_KERNEL);
 102 
 103         if (!ldev) {
 104                 dev_err(&pldev->dev, "%s: unable to kzalloc space for kpc_dma_device\n", __func__);
 105                 rv = -ENOMEM;
 106                 goto err_rv;
 107         }
 108 
 109         INIT_LIST_HEAD(&ldev->list);
 110 
 111         ldev->pldev = pldev;
 112         platform_set_drvdata(pldev, ldev);
 113         atomic_set(&ldev->open_count, 1);
 114 
 115         mutex_init(&ldev->sem);
 116         lock_engine(ldev);
 117 
 118         // Get Engine regs resource
 119         r = platform_get_resource(pldev, IORESOURCE_MEM, 0);
 120         if (!r) {
 121                 dev_err(&ldev->pldev->dev, "%s: didn't get the engine regs resource!\n", __func__);
 122                 rv = -ENXIO;
 123                 goto err_kfree;
 124         }
 125         ldev->eng_regs = ioremap_nocache(r->start, resource_size(r));
 126         if (!ldev->eng_regs) {
 127                 dev_err(&ldev->pldev->dev, "%s: failed to ioremap engine regs!\n", __func__);
 128                 rv = -ENXIO;
 129                 goto err_kfree;
 130         }
 131 
 132         r = platform_get_resource(pldev, IORESOURCE_IRQ, 0);
 133         if (!r) {
 134                 dev_err(&ldev->pldev->dev, "%s: didn't get the IRQ resource!\n", __func__);
 135                 rv = -ENXIO;
 136                 goto err_kfree;
 137         }
 138         ldev->irq = r->start;
 139 
 140         // Setup miscdev struct
 141         dev = MKDEV(assigned_major_num, pldev->id);
 142         ldev->kpc_dma_dev = device_create(kpc_dma_class, &pldev->dev, dev, ldev, "kpc_dma%d", pldev->id);
 143         if (IS_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         // Setup the DMA engine
 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         // Setup the sysfs files
 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 
 176 static
 177 int  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 /**********  Driver Functions  **********/
 195 static 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 
 203 static
 204 int __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 
 230 fail_platdriver_register:
 231         class_destroy(kpc_dma_class);
 232 fail_class_create:
 233         __unregister_chrdev(KPC_DMA_CHAR_MAJOR, 0, KPC_DMA_NUM_MINORS, "kpc_dma");
 234 fail_chrdev_register:
 235         return err;
 236 }
 237 module_init(kpc_dma_driver_init);
 238 
 239 static
 240 void __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 }
 246 module_exit(kpc_dma_driver_exit);

/* [<][>][^][v][top][bottom][index][help] */