Skip to content

Commit 1be0d57

Browse files
committed
Merge pull request #1112 from Wren6991/gpiomem
Add /dev/gpiomem device for rootless user GPIO access
2 parents 1820cd0 + 020873a commit 1be0d57

File tree

5 files changed

+289
-0
lines changed

5 files changed

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

0 commit comments

Comments
 (0)