-/*
- * 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);
{
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);
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;
}
.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);
}
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;
}
/* 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;
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:
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);
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>");