Branch data Line data Source code
1 : : /** 2 : : * GPIO memory device driver 3 : : * 4 : : * Creates a chardev /dev/gpiomem which will provide user access to 5 : : * the BCM2835's GPIO registers when it is mmap()'d. 6 : : * No longer need root for user GPIO access, but without relaxing permissions 7 : : * on /dev/mem. 8 : : * 9 : : * Written by Luke Wren <luke@raspberrypi.org> 10 : : * Copyright (c) 2015, Raspberry Pi (Trading) Ltd. 11 : : * 12 : : * Redistribution and use in source and binary forms, with or without 13 : : * modification, are permitted provided that the following conditions 14 : : * are met: 15 : : * 1. Redistributions of source code must retain the above copyright 16 : : * notice, this list of conditions, and the following disclaimer, 17 : : * without modification. 18 : : * 2. Redistributions in binary form must reproduce the above copyright 19 : : * notice, this list of conditions and the following disclaimer in the 20 : : * documentation and/or other materials provided with the distribution. 21 : : * 3. The names of the above-listed copyright holders may not be used 22 : : * to endorse or promote products derived from this software without 23 : : * specific prior written permission. 24 : : * 25 : : * ALTERNATIVELY, this software may be distributed under the terms of the 26 : : * GNU General Public License ("GPL") version 2, as published by the Free 27 : : * Software Foundation. 28 : : * 29 : : * THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS 30 : : * IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, 31 : : * THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR 32 : : * PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR 33 : : * CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, 34 : : * EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, 35 : : * PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR 36 : : * PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF 37 : : * LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING 38 : : * NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 39 : : * SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 40 : : */ 41 : : 42 : : #include <linux/kernel.h> 43 : : #include <linux/module.h> 44 : : #include <linux/of.h> 45 : : #include <linux/platform_device.h> 46 : : #include <linux/mm.h> 47 : : #include <linux/slab.h> 48 : : #include <linux/cdev.h> 49 : : #include <linux/pagemap.h> 50 : : #include <linux/io.h> 51 : : 52 : : #define DEVICE_NAME "bcm2835-gpiomem" 53 : : #define DRIVER_NAME "gpiomem-bcm2835" 54 : : #define DEVICE_MINOR 0 55 : : 56 : : struct bcm2835_gpiomem_instance { 57 : : unsigned long gpio_regs_phys; 58 : : struct device *dev; 59 : : }; 60 : : 61 : : static struct cdev bcm2835_gpiomem_cdev; 62 : : static dev_t bcm2835_gpiomem_devid; 63 : : static struct class *bcm2835_gpiomem_class; 64 : : static struct device *bcm2835_gpiomem_dev; 65 : : static struct bcm2835_gpiomem_instance *inst; 66 : : 67 : : 68 : : /**************************************************************************** 69 : : * 70 : : * GPIO mem chardev file ops 71 : : * 72 : : ***************************************************************************/ 73 : : 74 : 0 : static int bcm2835_gpiomem_open(struct inode *inode, struct file *file) 75 : : { 76 : 0 : int dev = iminor(inode); 77 : : int ret = 0; 78 : : 79 : 0 : if (dev != DEVICE_MINOR) { 80 : 0 : dev_err(inst->dev, "Unknown minor device: %d", dev); 81 : : ret = -ENXIO; 82 : : } 83 : 0 : return ret; 84 : : } 85 : : 86 : 0 : static int bcm2835_gpiomem_release(struct inode *inode, struct file *file) 87 : : { 88 : 0 : int dev = iminor(inode); 89 : : int ret = 0; 90 : : 91 : 0 : if (dev != DEVICE_MINOR) { 92 : 0 : dev_err(inst->dev, "Unknown minor device %d", dev); 93 : : ret = -ENXIO; 94 : : } 95 : 0 : return ret; 96 : : } 97 : : 98 : : static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = { 99 : : #ifdef CONFIG_HAVE_IOREMAP_PROT 100 : : .access = generic_access_phys 101 : : #endif 102 : : }; 103 : : 104 : 0 : static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma) 105 : : { 106 : : /* Ignore what the user says - they're getting the GPIO regs 107 : : whether they like it or not! */ 108 : 0 : unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT; 109 : : 110 : 0 : vma->vm_page_prot = phys_mem_access_prot(file, gpio_page, 111 : : PAGE_SIZE, 112 : : vma->vm_page_prot); 113 : 0 : vma->vm_ops = &bcm2835_gpiomem_vm_ops; 114 : 0 : if (remap_pfn_range(vma, vma->vm_start, 115 : : gpio_page, 116 : : PAGE_SIZE, 117 : : vma->vm_page_prot)) { 118 : : return -EAGAIN; 119 : : } 120 : 0 : return 0; 121 : : } 122 : : 123 : : static const struct file_operations 124 : : bcm2835_gpiomem_fops = { 125 : : .owner = THIS_MODULE, 126 : : .open = bcm2835_gpiomem_open, 127 : : .release = bcm2835_gpiomem_release, 128 : : .mmap = bcm2835_gpiomem_mmap, 129 : : }; 130 : : 131 : : 132 : : /**************************************************************************** 133 : : * 134 : : * Probe and remove functions 135 : : * 136 : : ***************************************************************************/ 137 : : 138 : : 139 : 3 : static int bcm2835_gpiomem_probe(struct platform_device *pdev) 140 : : { 141 : : int err; 142 : : void *ptr_err; 143 : 3 : struct device *dev = &pdev->dev; 144 : : struct resource *ioresource; 145 : : 146 : : /* Allocate buffers and instance data */ 147 : : 148 : 3 : inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL); 149 : : 150 : 3 : if (!inst) { 151 : : err = -ENOMEM; 152 : : goto failed_inst_alloc; 153 : : } 154 : : 155 : 3 : inst->dev = dev; 156 : : 157 : 3 : ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0); 158 : 3 : if (ioresource) { 159 : 3 : inst->gpio_regs_phys = ioresource->start; 160 : : } else { 161 : 0 : dev_err(inst->dev, "failed to get IO resource"); 162 : : err = -ENOENT; 163 : 0 : goto failed_get_resource; 164 : : } 165 : : 166 : : /* Create character device entries */ 167 : : 168 : 3 : err = alloc_chrdev_region(&bcm2835_gpiomem_devid, 169 : : DEVICE_MINOR, 1, DEVICE_NAME); 170 : 3 : if (err != 0) { 171 : 0 : dev_err(inst->dev, "unable to allocate device number"); 172 : 0 : goto failed_alloc_chrdev; 173 : : } 174 : 3 : cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops); 175 : 3 : bcm2835_gpiomem_cdev.owner = THIS_MODULE; 176 : 3 : err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1); 177 : 3 : if (err != 0) { 178 : 0 : dev_err(inst->dev, "unable to register device"); 179 : 0 : goto failed_cdev_add; 180 : : } 181 : : 182 : : /* Create sysfs entries */ 183 : : 184 : 3 : bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME); 185 : : ptr_err = bcm2835_gpiomem_class; 186 : 3 : if (IS_ERR(ptr_err)) 187 : : goto failed_class_create; 188 : : 189 : 3 : bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL, 190 : : bcm2835_gpiomem_devid, NULL, 191 : : "gpiomem"); 192 : : ptr_err = bcm2835_gpiomem_dev; 193 : 3 : if (IS_ERR(ptr_err)) 194 : : goto failed_device_create; 195 : : 196 : 3 : dev_info(inst->dev, "Initialised: Registers at 0x%08lx", 197 : : inst->gpio_regs_phys); 198 : : 199 : 3 : return 0; 200 : : 201 : : failed_device_create: 202 : 0 : class_destroy(bcm2835_gpiomem_class); 203 : : failed_class_create: 204 : 0 : cdev_del(&bcm2835_gpiomem_cdev); 205 : : err = PTR_ERR(ptr_err); 206 : : failed_cdev_add: 207 : 0 : unregister_chrdev_region(bcm2835_gpiomem_devid, 1); 208 : : failed_alloc_chrdev: 209 : : failed_get_resource: 210 : 0 : kfree(inst); 211 : : failed_inst_alloc: 212 : 0 : dev_err(inst->dev, "could not load bcm2835_gpiomem"); 213 : 0 : return err; 214 : : } 215 : : 216 : 0 : static int bcm2835_gpiomem_remove(struct platform_device *pdev) 217 : : { 218 : 0 : struct device *dev = inst->dev; 219 : : 220 : 0 : kfree(inst); 221 : 0 : device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid); 222 : 0 : class_destroy(bcm2835_gpiomem_class); 223 : 0 : cdev_del(&bcm2835_gpiomem_cdev); 224 : 0 : unregister_chrdev_region(bcm2835_gpiomem_devid, 1); 225 : : 226 : 0 : dev_info(dev, "GPIO mem driver removed - OK"); 227 : 0 : return 0; 228 : : } 229 : : 230 : : /**************************************************************************** 231 : : * 232 : : * Register the driver with device tree 233 : : * 234 : : ***************************************************************************/ 235 : : 236 : : static const struct of_device_id bcm2835_gpiomem_of_match[] = { 237 : : {.compatible = "brcm,bcm2835-gpiomem",}, 238 : : { /* sentinel */ }, 239 : : }; 240 : : 241 : : MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match); 242 : : 243 : : static struct platform_driver bcm2835_gpiomem_driver = { 244 : : .probe = bcm2835_gpiomem_probe, 245 : : .remove = bcm2835_gpiomem_remove, 246 : : .driver = { 247 : : .name = DRIVER_NAME, 248 : : .owner = THIS_MODULE, 249 : : .of_match_table = bcm2835_gpiomem_of_match, 250 : : }, 251 : : }; 252 : : 253 : 3 : module_platform_driver(bcm2835_gpiomem_driver); 254 : : 255 : : MODULE_ALIAS("platform:gpiomem-bcm2835"); 256 : : MODULE_LICENSE("GPL"); 257 : : MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace"); 258 : : MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");