Skip to content

Commit 67b9651

Browse files
committed
Add /dev/gpiomem device for rootless user GPIO access
Signed-off-by: Luke Wren <[email protected]>
1 parent 5925037 commit 67b9651

File tree

5 files changed

+295
-0
lines changed

5 files changed

+295
-0
lines changed

arch/arm/boot/dts/bcm2708.dtsi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,5 +15,11 @@
1515
arm-pmu {
1616
compatible = "arm,arm1176-pmu";
1717
};
18+
19+
gpiomem {
20+
compatible = "brcm,bcm2835-gpiomem";
21+
reg = <0x7e200000 0x1000>;
22+
status = "okay";
23+
};
1824
};
1925
};

arch/arm/boot/dts/bcm2709.dtsi

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,12 @@
1616
compatible = "arm,cortex-a7-pmu";
1717
interrupts = <3 9>;
1818
};
19+
20+
gpiomem {
21+
compatible = "brcm,bcm2835-gpiomem";
22+
reg = <0x7e200000 0x1000>;
23+
status = "okay";
24+
};
1925
};
2026

2127
timer {

drivers/char/broadcom/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,12 @@ config BCM_VC_SM
3838
help
3939
Support for the VC shared memory on the Broadcom reference
4040
design. Uses the VCHIQ stack.
41+
42+
config BCM2835_DEVGPIOMEM
43+
tristate "/dev/gpiomem rootless GPIO access via mmap() on the BCM2835"
44+
default m
45+
help
46+
Provides users with root-free access to the GPIO registers
47+
on the 2835. Calling mmap(/dev/gpiomem) will map the GPIO
48+
register page to the user's pointer.
49+

drivers/char/broadcom/Makefile

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,3 +2,6 @@ obj-$(CONFIG_BCM_VC_CMA) += vc_cma/
22
obj-$(CONFIG_BCM2708_VCMEM) += vc_mem.o
33
obj-$(CONFIG_BCM_VCIO) += vcio.o
44
obj-$(CONFIG_BCM_VC_SM) += vc_sm/
5+
6+
obj-$(CONFIG_BCM2835_DEVGPIOMEM)+= bcm2835-gpiomem.o
7+
Lines changed: 271 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,271 @@
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 <[email protected]>
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+
static int bcm2835_gpiomem_open(struct inode *inode, struct file *file)
75+
{
76+
int dev = iminor(inode);
77+
int ret = 0;
78+
79+
dev_info(inst->dev, "gpiomem device opened.");
80+
81+
if (dev != DEVICE_MINOR) {
82+
dev_err(inst->dev, " Unknown minor device: %d",
83+
dev);
84+
ret = -ENXIO;
85+
goto out;
86+
}
87+
out:
88+
return ret;
89+
}
90+
91+
static int bcm2835_gpiomem_release(struct inode *inode, struct file *file)
92+
{
93+
int dev = iminor(inode);
94+
int ret = 0;
95+
96+
if (dev != DEVICE_MINOR) {
97+
dev_err(inst->dev, "Unknown minor device %d", dev);
98+
ret = -ENXIO;
99+
goto out;
100+
}
101+
102+
out:
103+
return ret;
104+
}
105+
106+
static const struct vm_operations_struct bcm2835_gpiomem_vm_ops = {
107+
#ifdef CONFIG_HAVE_IOREMAP_PROT
108+
.access = generic_access_phys
109+
#endif
110+
};
111+
112+
static int bcm2835_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
113+
{
114+
/* Ignore what the user says - they're getting the GPIO regs
115+
whether they like it or not! */
116+
unsigned long gpio_page = inst->gpio_regs_phys >> PAGE_SHIFT;
117+
118+
vma->vm_page_prot = phys_mem_access_prot(file, gpio_page,
119+
PAGE_SIZE,
120+
vma->vm_page_prot);
121+
vma->vm_ops = &bcm2835_gpiomem_vm_ops;
122+
if (remap_pfn_range(vma, vma->vm_start,
123+
gpio_page,
124+
PAGE_SIZE,
125+
vma->vm_page_prot)) {
126+
return -EAGAIN;
127+
}
128+
return 0;
129+
}
130+
131+
static const struct file_operations
132+
bcm2835_gpiomem_fops = {
133+
.owner = THIS_MODULE,
134+
.open = bcm2835_gpiomem_open,
135+
.release = bcm2835_gpiomem_release,
136+
.mmap = bcm2835_gpiomem_mmap,
137+
};
138+
139+
140+
/****************************************************************************
141+
*
142+
* Probe and remove functions
143+
*
144+
***************************************************************************/
145+
146+
147+
static int bcm2835_gpiomem_probe(struct platform_device *pdev)
148+
{
149+
int err;
150+
void *ptr_err;
151+
struct device *dev = &pdev->dev;
152+
struct device_node *node = dev->of_node;
153+
struct resource *ioresource;
154+
155+
/* Allocate buffers and instance data */
156+
157+
inst = kzalloc(sizeof(struct bcm2835_gpiomem_instance), GFP_KERNEL);
158+
159+
if (!inst) {
160+
err = -ENOMEM;
161+
goto failed_inst_alloc;
162+
}
163+
164+
inst->dev = dev;
165+
166+
/* Create character device entries */
167+
168+
err = alloc_chrdev_region(&bcm2835_gpiomem_devid,
169+
DEVICE_MINOR, 1, DEVICE_NAME);
170+
if (err != 0) {
171+
dev_err(inst->dev, "unable to allocate device number");
172+
goto failed_alloc_chrdev;
173+
}
174+
cdev_init(&bcm2835_gpiomem_cdev, &bcm2835_gpiomem_fops);
175+
bcm2835_gpiomem_cdev.owner = THIS_MODULE;
176+
err = cdev_add(&bcm2835_gpiomem_cdev, bcm2835_gpiomem_devid, 1);
177+
if (err != 0) {
178+
dev_err(inst->dev, "unable to register device");
179+
goto failed_cdev_add;
180+
}
181+
182+
/* Create sysfs entries */
183+
184+
bcm2835_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
185+
ptr_err = bcm2835_gpiomem_class;
186+
if (IS_ERR(ptr_err))
187+
goto failed_class_create;
188+
189+
bcm2835_gpiomem_dev = device_create(bcm2835_gpiomem_class, NULL,
190+
bcm2835_gpiomem_devid, NULL,
191+
"gpiomem");
192+
ptr_err = bcm2835_gpiomem_dev;
193+
if (IS_ERR(ptr_err))
194+
goto failed_device_create;
195+
196+
/* Get address from device tree if available (*_resource() correctly
197+
converts the bus address in device tree to a physical address),
198+
or use hardcoded offset + BCM2708_PERI_BASE if not.
199+
(In spite of its name 2708 actually seems to have the correct
200+
mach-dependent value on 2709 etc, as it is defined in
201+
mach-bcm270x/platform.h) */
202+
203+
if (node) {
204+
ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
205+
inst->gpio_regs_phys = ioresource->start;
206+
} else {
207+
inst->gpio_regs_phys = BCM2708_PERI_BASE + 0x00200000ul;
208+
}
209+
210+
dev_info(inst->dev, "Initialised: Registers at 0x%08x",
211+
inst->gpio_regs_phys);
212+
213+
return 0;
214+
215+
failed_device_create:
216+
class_destroy(bcm2835_gpiomem_class);
217+
failed_class_create:
218+
cdev_del(&bcm2835_gpiomem_cdev);
219+
err = PTR_ERR(ptr_err);
220+
failed_cdev_add:
221+
unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
222+
failed_alloc_chrdev:
223+
kfree(inst);
224+
failed_inst_alloc:
225+
dev_err(inst->dev, "could not load bcm2835_gpiomem");
226+
return err;
227+
}
228+
229+
static int bcm2835_gpiomem_remove(struct platform_device *pdev)
230+
{
231+
struct device *dev = inst->dev;
232+
233+
kfree(inst);
234+
device_destroy(bcm2835_gpiomem_class, bcm2835_gpiomem_devid);
235+
class_destroy(bcm2835_gpiomem_class);
236+
cdev_del(&bcm2835_gpiomem_cdev);
237+
unregister_chrdev_region(bcm2835_gpiomem_devid, 1);
238+
239+
dev_info(dev, "GPIO mem driver removed - OK");
240+
return 0;
241+
}
242+
243+
/****************************************************************************
244+
*
245+
* Register the driver with device tree
246+
*
247+
***************************************************************************/
248+
249+
static const struct of_device_id bcm2835_gpiomem_of_match[] = {
250+
{.compatible = "brcm,bcm2835-gpiomem",},
251+
{ /* sentinel */ },
252+
};
253+
254+
MODULE_DEVICE_TABLE(of, bcm2835_gpiomem_of_match);
255+
256+
static struct platform_driver bcm2835_gpiomem_driver = {
257+
.probe = bcm2835_gpiomem_probe,
258+
.remove = bcm2835_gpiomem_remove,
259+
.driver = {
260+
.name = DRIVER_NAME,
261+
.owner = THIS_MODULE,
262+
.of_match_table = bcm2835_gpiomem_of_match,
263+
},
264+
};
265+
266+
module_platform_driver(bcm2835_gpiomem_driver);
267+
268+
MODULE_ALIAS("platform:gpiomem-bcm2835");
269+
MODULE_LICENSE("GPL");
270+
MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace");
271+
MODULE_AUTHOR("Luke Wren <[email protected]>");

0 commit comments

Comments
 (0)