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 : 207 : static int bcm2835_gpiomem_probe(struct platform_device *pdev)
140 : : {
141 : : int err;
142 : : void *ptr_err;
143 : 207 : struct device *dev = &pdev->dev;
144 : : struct resource *ioresource;
145 : :
146 : : /* Allocate buffers and instance data */
147 : :
148 : 207 : inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL);
149 : :
150 [ + - ]: 207 : if (!inst) {
151 : : err = -ENOMEM;
152 : : goto failed_inst_alloc;
153 : : }
154 : :
155 : 207 : inst->dev = dev;
156 : :
157 : 207 : ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
158 [ + - ]: 207 : if (ioresource) {
159 : 207 : 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 : 207 : err = alloc_chrdev_region(&bcm2835_gpiomem_devid,
169 : : DEVICE_MINOR, 1, DEVICE_NAME);
170 [ - + ]: 207 : if (err != 0) {
171 : 0 : dev_err(inst->dev, "unable to allocate device number");
172 : 0 : goto failed_alloc_chrdev;
173 : : }
174 : 207 : cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops);
175 : 207 : bcm2835_gpiomem_cdev.owner = THIS_MODULE;
176 : 207 : err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1);
177 [ - + ]: 207 : 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 : 207 : bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
185 : : ptr_err = bcm2835_gpiomem_class;
186 [ + - ]: 207 : if (IS_ERR(ptr_err))
187 : : goto failed_class_create;
188 : :
189 : 207 : bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL,
190 : : bcm2835_gpiomem_devid, NULL,
191 : : "gpiomem");
192 : : ptr_err = bcm2835_gpiomem_dev;
193 [ + - ]: 207 : if (IS_ERR(ptr_err))
194 : : goto failed_device_create;
195 : :
196 : 207 : dev_info(inst->dev, "Initialised: Registers at 0x%08lx",
197 : : inst->gpio_regs_phys);
198 : :
199 : 207 : 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 : 207 : 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>");
|