char: aml-gpiomem: Update to Khadas' multi-instance version
authorSeung-Woo Kim <sw0312.kim@samsung.com>
Tue, 1 Dec 2020 09:55:58 +0000 (18:55 +0900)
committerSeung-Woo Kim <sw0312.kim@samsung.com>
Wed, 2 Dec 2020 04:43:03 +0000 (13:43 +0900)
The Khadas VIM3/VIM3L has two aml-gpiomem nodes, so it needs multi
instance driver version. Update to Khadas' multi-instance version.

Signed-off-by: Seung-Woo Kim <sw0312.kim@samsung.com>
drivers/char/aml-gpiomem.c

index b0d1df7..5c71560 100644 (file)
@@ -1,18 +1,16 @@
-/*
- * linux/drivers/char/aml-gpiomem.c
- *
+/**
  * GPIO memory device driver
  *
  * Creates a chardev /dev/gpiomem which will provide user access to
- * the Meson g12 GPIO registers when it is mmap()'d.
+ * the Meson's GPIO registers when it is mmap()'d.
  * No longer need root for user GPIO access, but without relaxing permissions
  * on /dev/mem.
  *
- * Copyright (c) 2017 Hardkernel Co., Ltd.
+ * Copyright (c) 2019 Wesion Co., Ltd.
  *
- * This driver is based on bcm2835-gpiomem.c in Raspberrypi's linux kernel 4.4:
- *     Written by Luke Wren <luke@raspberrypi.org>
- *     Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
+ * This driver is based on bcm2835-gpiomem.c:
+ * Written by Luke Wren <luke@raspberrypi.org>
+ * Copyright (c) 2015, Raspberry Pi (Trading) Ltd.
  *
  * Redistribution and use in source and binary forms, with or without
  * modification, are permitted provided that the following conditions
 #include <linux/cdev.h>
 #include <linux/pagemap.h>
 #include <linux/io.h>
-#include <linux/of.h>
-#include <asm/io.h>
 
 #define DEVICE_NAME "aml-gpiomem"
 #define DRIVER_NAME "gpiomem-aml"
 #define DEVICE_MINOR 0
 
-struct regs_phys {
-       unsigned long start;
-       unsigned long end;
-};
+#define DEVICE_NUM_MAX 2
 
 struct aml_gpiomem_instance {
-       struct regs_phys gpio_regs_phys[32];
-       int gpio_area_count;
+       unsigned long gpio_regs_phys;
        struct device *dev;
+       char dev_name[32];
+       int major;
 };
 
 static struct cdev aml_gpiomem_cdev;
 static dev_t aml_gpiomem_devid;
 static struct class *aml_gpiomem_class;
 static struct device *aml_gpiomem_dev;
-static struct aml_gpiomem_instance *inst;
+//static struct aml_gpiomem_instance *inst;
+static struct aml_gpiomem_instance *gpiomem_instances[DEVICE_NUM_MAX];
+static int instance_num = 0;
+
+/****************************************************************************
+*
+*   GPIO mem chardev file ops
+*
+***************************************************************************/
 
 static int aml_gpiomem_open(struct inode *inode, struct file *file)
 {
        int dev = iminor(inode);
+       int major = imajor(inode);
        int ret = 0;
+       int i = 0;
+       struct aml_gpiomem_instance *inst = NULL;
 
-       dev_info(inst->dev, "gpiomem device opened.");
+       for (i=0; i<DEVICE_NUM_MAX; i++) {
+               if (major == gpiomem_instances[i]->major) {
+                       inst = gpiomem_instances[i];
+                       file->private_data = gpiomem_instances[i];
+               }
+       }
+
+       if (!inst)
+               return -ENXIO;
 
        if (dev != DEVICE_MINOR) {
                dev_err(inst->dev, "Unknown minor device: %d", dev);
@@ -95,6 +108,7 @@ static int aml_gpiomem_release(struct inode *inode, struct file *file)
 {
        int dev = iminor(inode);
        int ret = 0;
+       struct aml_gpiomem_instance *inst = file->private_data;
 
        if (dev != DEVICE_MINOR) {
                dev_err(inst->dev, "Unknown minor device %d", dev);
@@ -111,33 +125,21 @@ static const struct vm_operations_struct aml_gpiomem_vm_ops = {
 
 static int aml_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
 {
-       int gpio_area = 0;
-       unsigned long start = vma->vm_pgoff << PAGE_SHIFT;
-       unsigned long end   = start + vma->vm_end - vma->vm_start;
-
-       while (gpio_area < inst->gpio_area_count) {
-               if ((inst->gpio_regs_phys[gpio_area].start >= start) &&
-                   (inst->gpio_regs_phys[gpio_area].end   <= end))
-                       goto found;
-               gpio_area++;
-       }
-
-       return -EACCES;
-
-found:
-       vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
-                       vma->vm_end - vma->vm_start,
-                       vma->vm_page_prot);
-
+       /* Ignore what the user says - they're getting the GPIO regs
+          whether they like it or not! */
+       struct aml_gpiomem_instance *inst = file->private_data;
+       unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT;
+
+       vma->vm_page_prot = phys_mem_access_prot(file, gpio_page,
+                                                PAGE_SIZE,
+                                                vma->vm_page_prot);
        vma->vm_ops = &aml_gpiomem_vm_ops;
-
        if (remap_pfn_range(vma, vma->vm_start,
-                               vma->vm_pgoff,
-                               vma->vm_end - vma->vm_start,
-                               vma->vm_page_prot)) {
+                       gpio_page,
+                       PAGE_SIZE,
+                       vma->vm_page_prot)) {
                return -EAGAIN;
        }
-
        return 0;
 }
 
@@ -149,13 +151,23 @@ aml_gpiomem_fops = {
        .mmap = aml_gpiomem_mmap,
 };
 
+
+ /****************************************************************************
+*
+*   Probe and remove functions
+*
+***************************************************************************/
+
+
 static int aml_gpiomem_probe(struct platform_device *pdev)
 {
-       int err = 0;
+       int err;
+       void *ptr_err;
        struct device *dev = &pdev->dev;
-       struct device_node *np = dev->of_node;
-       struct resource *res = NULL;
-       int i = 0;
+       struct resource *ioresource;
+       const char *str;
+       char tmp[64];
+       struct aml_gpiomem_instance *inst = NULL;
 
        /* Allocate buffers and instance data */
        inst = kzalloc(sizeof(struct aml_gpiomem_instance), GFP_KERNEL);
@@ -166,33 +178,31 @@ static int aml_gpiomem_probe(struct platform_device *pdev)
        }
 
        inst->dev = dev;
-       inst->gpio_area_count = of_property_count_elems_of_size(np, "reg",
-                               sizeof(u32)) / 4;
 
-       if (inst->gpio_area_count > 32 || inst->gpio_area_count <= 0) {
-               dev_err(inst->dev, "failed to get gpio register area.");
-               err = -EINVAL;
-               goto failed_inst_alloc;
+       platform_set_drvdata(pdev, inst);
+
+       ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (ioresource) {
+               inst->gpio_regs_phys = ioresource->start;
+       } else {
+               dev_err(inst->dev, "failed to get IO resource");
+               err = -ENOENT;
+               goto failed_get_resource;
        }
 
-       dev_info(inst->dev, "Initialised: GPIO register area is %d",
-                       inst->gpio_area_count);
-
-       for (i = 0; i < inst->gpio_area_count; ++i) {
-               res = platform_get_resource(pdev, IORESOURCE_MEM, i);
-               if (res) {
-                       inst->gpio_regs_phys[i].start = res->start;
-                       inst->gpio_regs_phys[i].end   = res->end;
-               } else {
-                       dev_err(inst->dev, "failed to get IO resource area %d", i);
-                       err = -ENOENT;
-                       goto failed_get_resource;
-               }
+       err = of_property_read_string(dev->of_node, "dev_name", &str);
+       if (err) {
+               dev_err(inst->dev, "failed to get device name\n");
+               err = -ENOENT;
+               goto failed_get_device_name;
        }
+       strncpy(inst->dev_name, str, 32);
+       inst->dev_name[31] = '\0';
 
        /* Create character device entries */
+       sprintf(tmp, "aml-%s", inst->dev_name);
        err = alloc_chrdev_region(&aml_gpiomem_devid,
-                                 DEVICE_MINOR, 1, DEVICE_NAME);
+                                 DEVICE_MINOR, 1, tmp);
        if (err != 0) {
                dev_err(inst->dev, "unable to allocate device number");
                goto failed_alloc_chrdev;
@@ -206,25 +216,23 @@ static int aml_gpiomem_probe(struct platform_device *pdev)
        }
 
        /* Create sysfs entries */
-       aml_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
-       err = IS_ERR(aml_gpiomem_class);
-       if (err)
+       aml_gpiomem_class = class_create(THIS_MODULE, tmp);
+       ptr_err = aml_gpiomem_class;
+       if (IS_ERR(ptr_err))
                goto failed_class_create;
 
        aml_gpiomem_dev = device_create(aml_gpiomem_class, NULL,
                                        aml_gpiomem_devid, NULL,
-                                       "gpiomem");
-       err = IS_ERR(aml_gpiomem_dev);
-       if (err)
+                                       inst->dev_name);
+       ptr_err = aml_gpiomem_dev;
+       if (IS_ERR(ptr_err))
                goto failed_device_create;
 
-       for (i = 0; i < inst->gpio_area_count; ++i) {
-               dev_info(inst->dev,
-                       "Initialised: Registers at start:0x%08lx end:0x%08lx size:0x%08lx",
-                       inst->gpio_regs_phys[i].start,
-                       inst->gpio_regs_phys[i].end,
-                       inst->gpio_regs_phys[i].end - inst->gpio_regs_phys[i].start);
-       }
+       inst->major = MAJOR(aml_gpiomem_devid);
+       gpiomem_instances[instance_num++] = inst;
+
+       dev_info(inst->dev, "Initialised: Registers at 0x%08lx\n",
+               inst->gpio_regs_phys);
 
        return 0;
 
@@ -232,9 +240,11 @@ failed_device_create:
        class_destroy(aml_gpiomem_class);
 failed_class_create:
        cdev_del(&aml_gpiomem_cdev);
+       err = PTR_ERR(ptr_err);
 failed_cdev_add:
        unregister_chrdev_region(aml_gpiomem_devid, 1);
 failed_alloc_chrdev:
+failed_get_device_name:
 failed_get_resource:
        kfree(inst);
 failed_inst_alloc:
@@ -244,6 +254,7 @@ failed_inst_alloc:
 
 static int aml_gpiomem_remove(struct platform_device *pdev)
 {
+       struct aml_gpiomem_instance *inst = platform_get_drvdata(pdev);
        struct device *dev = inst->dev;
 
        kfree(inst);
@@ -256,25 +267,32 @@ static int aml_gpiomem_remove(struct platform_device *pdev)
        return 0;
 }
 
+ /****************************************************************************
+*
+*   Register the driver with device tree
+*
+***************************************************************************/
+
 static const struct of_device_id aml_gpiomem_of_match[] = {
        {.compatible = "amlogic, gpiomem",},
-       { },
+       { /* sentinel */ },
 };
+
 MODULE_DEVICE_TABLE(of, aml_gpiomem_of_match);
 
 static struct platform_driver aml_gpiomem_driver = {
-       .driver                 = {
-               .name           = DRIVER_NAME,
-               .owner          = THIS_MODULE,
-               .of_match_table = aml_gpiomem_of_match,
+       .probe = aml_gpiomem_probe,
+       .remove = aml_gpiomem_remove,
+       .driver = {
+               .name = DRIVER_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = aml_gpiomem_of_match,
        },
-       .probe                  = aml_gpiomem_probe,
-       .remove                 = aml_gpiomem_remove,
 };
 
 module_platform_driver(aml_gpiomem_driver);
 
 MODULE_ALIAS("platform:gpiomem-aml");
-MODULE_DESCRIPTION("AMLogic gpiomem driver for accessing GPIO from userspace");
-MODULE_AUTHOR("Brian Kim <brian.kim@hardkernel.com>");
 MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace");
+MODULE_AUTHOR("Nick Xie <nick@khadas.com>");