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 <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/**********  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        c = NULL; // not-found case
  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/**********  SysFS Attributes **********/
  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/**********  Platform Driver Functions  **********/
  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        // Get Engine regs resource
 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        // Setup miscdev struct
 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        // 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