linux/drivers/staging/kpc2000/kpc_dma/kpc_dma_driver.c
<<
>>
Prefs
   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
  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/**********  kpc_dma_list list management  **********/
  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        }
  33        c = NULL; // not-found case
  34out:
  35        mutex_unlock(&kpc_dma_mtx);
  36        return c;
  37}
  38
  39static 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
  46static 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 **********/
  54static 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}
  84static DEVICE_ATTR(engine_regs, 0444, show_engine_regs, NULL);
  85
  86static const struct attribute *ndd_attr_list[] = {
  87        &dev_attr_engine_regs.attr,
  88        NULL,
  89};
  90
  91static struct class *kpc_dma_class;
  92
  93/**********  Platform Driver Functions  **********/
  94static
  95int  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
 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/**********  Driver Functions  **********/
 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