Skip to content

Commit d8180d9

Browse files
yihsin-hungasus-leslieyu
authored andcommitted
Implement gpiomem for Debian. DEVMOD=0666
Signed-off-by: yi-hsin_hung <yi-hsin_hung@asus.com> Change-Id: Ibda4c6071d0469e13a28d55884b4ce47c5ea4279
1 parent d4c5ee9 commit d8180d9

7 files changed

Lines changed: 335 additions & 0 deletions

File tree

arch/arm/boot/dts/rk3288-tinker-board.dts

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -200,6 +200,12 @@
200200
regulator-boot-on;
201201
};
202202

203+
rk3288_gpiomem: rk3288-gpiomem {
204+
compatible = "rockchip,rk3288-gpiomem";
205+
reg = <0x0 0xff750000 0x0 0x1000>;
206+
status = "okay";
207+
};
208+
203209
xin32k: xin32k {
204210
compatible = "fixed-clock";
205211
clock-frequency = <32768>;

arch/arm/configs/rockchip_linux_defconfig

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,8 @@ CONFIG_DEFAULT_HOSTNAME="localhost"
33
CONFIG_BOARDINFO=y
44
CONFIG_SYSVIPC=y
55
CONFIG_POSIX_MQUEUE=y
6+
CONFIG_RK_CHAR_DRIVERS=y
7+
CONFIG_RK3288_DEVGPIOMEM=y
68
CONFIG_FHANDLE=y
79
CONFIG_NO_HZ=y
810
CONFIG_HIGH_RES_TIMERS=y

drivers/char/Kconfig

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44

55
menu "Character devices"
66

7+
source "drivers/char/rockchip/Kconfig"
78
source "drivers/tty/Kconfig"
89

910
config DEVMEM

drivers/char/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,3 +60,4 @@ js-rtc-y = rtc.o
6060

6161
obj-$(CONFIG_TILE_SROM) += tile-srom.o
6262
obj-$(CONFIG_XILLYBUS) += xillybus/
63+
obj-$(CONFIG_RK_CHAR_DRIVERS) += rockchip/

drivers/char/rockchip/Kconfig

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,16 @@
1+
#
2+
# Rockchip char driver config
3+
#
4+
5+
menuconfig RK_CHAR_DRIVERS
6+
bool "Rockchip Char Drivers"
7+
help
8+
Rockchip's char drivers
9+
10+
config RK3288_DEVGPIOMEM
11+
tristate "/dev/gpiomem rootless GPIO access via mmap() on the RK3288"
12+
default y
13+
help
14+
Provides users with root-free access to the GPIO registers
15+
on the 3288. Calling mmap(/dev/gpiomem) will map the GPIO
16+
register page to the user's pointer.

drivers/char/rockchip/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
obj-$(CONFIG_RK3288_DEVGPIOMEM)+= rk3288-gpiomem.o
Lines changed: 308 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,308 @@
1+
/**
2+
* GPIO memory device driver
3+
*
4+
* Creates a chardev /dev/gpiomem which will provide user access to
5+
* the rk3288'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+
/*
43+
* Ported to rk3288 from Jorg Wolff, 2017
44+
*/
45+
46+
#include <linux/kernel.h>
47+
#include <linux/module.h>
48+
#include <linux/of.h>
49+
#include <linux/platform_device.h>
50+
#include <linux/mm.h>
51+
#include <linux/slab.h>
52+
#include <linux/cdev.h>
53+
#include <linux/pagemap.h>
54+
#include <linux/io.h>
55+
56+
#define DEVICE_NAME "rk3288-gpiomem"
57+
#define DRIVER_NAME "gpiomem-rk3288"
58+
#define DEVICE_MINOR 0
59+
60+
struct rk3288_gpiomem_instance {
61+
unsigned long gpio_regs_phys;
62+
struct device *dev;
63+
};
64+
65+
static struct cdev rk3288_gpiomem_cdev;
66+
static dev_t rk3288_gpiomem_devid;
67+
static struct class *rk3288_gpiomem_class;
68+
static struct device *rk3288_gpiomem_dev;
69+
static struct rk3288_gpiomem_instance *inst;
70+
71+
72+
/****************************************************************************
73+
*
74+
* GPIO mem chardev file ops
75+
*
76+
***************************************************************************/
77+
78+
static int rk3288_gpiomem_open(struct inode *inode, struct file *file)
79+
{
80+
int dev = iminor(inode);
81+
int ret = 0;
82+
83+
if (dev != DEVICE_MINOR) {
84+
dev_err(inst->dev, "Unknown minor device: %d", dev);
85+
ret = -ENXIO;
86+
}
87+
return ret;
88+
}
89+
90+
static int rk3288_gpiomem_release(struct inode *inode, struct file *file)
91+
{
92+
int dev = iminor(inode);
93+
int ret = 0;
94+
95+
if (dev != DEVICE_MINOR) {
96+
dev_err(inst->dev, "Unknown minor device %d", dev);
97+
ret = -ENXIO;
98+
}
99+
return ret;
100+
}
101+
102+
static const struct vm_operations_struct rk3288_gpiomem_vm_ops = {
103+
#ifdef CONFIG_HAVE_IOREMAP_PROT
104+
.access = generic_access_phys
105+
#endif
106+
};
107+
108+
static int address_is_allowed(unsigned long pfn, unsigned long size)
109+
{
110+
unsigned long address = pfn << PAGE_SHIFT;
111+
112+
dev_info(inst->dev, "address_is_allowed.pfn: 0x%08lx", address);
113+
114+
switch(address) {
115+
116+
case 0xff750000:
117+
case 0xff760000:
118+
case 0xff780000:
119+
case 0xff790000:
120+
case 0xff7a0000:
121+
case 0xff7b0000:
122+
case 0xff7c0000:
123+
case 0xff7d0000:
124+
case 0xff7e0000:
125+
case 0xff7f0000:
126+
case 0xff7f2000:
127+
case 0xff770000:
128+
case 0xff730000:
129+
case 0xff680000:
130+
dev_info(inst->dev, "address_is_allowed.return 1");
131+
return 1;
132+
break;
133+
default :
134+
dev_info(inst->dev, "address_is_allowed.return 0");
135+
return 0;
136+
}
137+
}
138+
139+
static int rk3288_gpiomem_mmap(struct file *file, struct vm_area_struct *vma)
140+
{
141+
142+
size_t size;
143+
144+
size = vma->vm_end - vma->vm_start;
145+
146+
147+
if (!address_is_allowed(vma->vm_pgoff, size))
148+
return -EPERM;
149+
150+
vma->vm_page_prot = phys_mem_access_prot(file, vma->vm_pgoff,
151+
size,
152+
vma->vm_page_prot);
153+
154+
vma->vm_ops = &rk3288_gpiomem_vm_ops;
155+
156+
/* Remap-pfn-range will mark the range VM_IO */
157+
if (remap_pfn_range(vma,
158+
vma->vm_start,
159+
vma->vm_pgoff,
160+
size,
161+
vma->vm_page_prot)) {
162+
return -EAGAIN;
163+
}
164+
165+
return 0;
166+
}
167+
168+
static const struct file_operations
169+
rk3288_gpiomem_fops = {
170+
.owner = THIS_MODULE,
171+
.open = rk3288_gpiomem_open,
172+
.release = rk3288_gpiomem_release,
173+
.mmap = rk3288_gpiomem_mmap,
174+
};
175+
176+
static int rk3288_gpiomem_dev_uevent(struct device *dev, struct kobj_uevent_env *env)
177+
{
178+
add_uevent_var(env, "DEVMODE=%#o", 0666);
179+
return 0;
180+
}
181+
182+
/****************************************************************************
183+
*
184+
* Probe and remove functions
185+
*
186+
***************************************************************************/
187+
188+
189+
static int rk3288_gpiomem_probe(struct platform_device *pdev)
190+
{
191+
int err;
192+
void *ptr_err;
193+
struct device *dev = &pdev->dev;
194+
struct resource *ioresource;
195+
196+
/* Allocate buffers and instance data */
197+
198+
inst = kzalloc(sizeof(struct rk3288_gpiomem_instance), GFP_KERNEL);
199+
200+
if (!inst) {
201+
err = -ENOMEM;
202+
goto failed_inst_alloc;
203+
}
204+
205+
inst->dev = dev;
206+
207+
ioresource = platform_get_resource(pdev, IORESOURCE_MEM, 0);
208+
if (ioresource) {
209+
inst->gpio_regs_phys = ioresource->start;
210+
} else {
211+
dev_err(inst->dev, "failed to get IO resource");
212+
err = -ENOENT;
213+
goto failed_get_resource;
214+
}
215+
216+
/* Create character device entries */
217+
218+
err = alloc_chrdev_region(&rk3288_gpiomem_devid,
219+
DEVICE_MINOR, 1, DEVICE_NAME);
220+
if (err != 0) {
221+
dev_err(inst->dev, "unable to allocate device number");
222+
goto failed_alloc_chrdev;
223+
}
224+
cdev_init(&rk3288_gpiomem_cdev, &rk3288_gpiomem_fops);
225+
rk3288_gpiomem_cdev.owner = THIS_MODULE;
226+
err = cdev_add(&rk3288_gpiomem_cdev, rk3288_gpiomem_devid, 1);
227+
if (err != 0) {
228+
dev_err(inst->dev, "unable to register device");
229+
goto failed_cdev_add;
230+
}
231+
232+
/* Create sysfs entries */
233+
234+
rk3288_gpiomem_class = class_create(THIS_MODULE, DEVICE_NAME);
235+
ptr_err = rk3288_gpiomem_class;
236+
if (IS_ERR(ptr_err))
237+
goto failed_class_create;
238+
rk3288_gpiomem_class->dev_uevent = rk3288_gpiomem_dev_uevent;
239+
rk3288_gpiomem_dev = device_create(rk3288_gpiomem_class, NULL,
240+
rk3288_gpiomem_devid, NULL,
241+
"gpiomem");
242+
ptr_err = rk3288_gpiomem_dev;
243+
if (IS_ERR(ptr_err))
244+
goto failed_device_create;
245+
246+
dev_info(inst->dev, "Initialised: Registers at 0x%08lx",
247+
inst->gpio_regs_phys);
248+
249+
return 0;
250+
251+
failed_device_create:
252+
class_destroy(rk3288_gpiomem_class);
253+
failed_class_create:
254+
cdev_del(&rk3288_gpiomem_cdev);
255+
err = PTR_ERR(ptr_err);
256+
failed_cdev_add:
257+
unregister_chrdev_region(rk3288_gpiomem_devid, 1);
258+
failed_alloc_chrdev:
259+
failed_get_resource:
260+
kfree(inst);
261+
failed_inst_alloc:
262+
dev_err(inst->dev, "could not load rk3288_gpiomem");
263+
return err;
264+
}
265+
266+
static int rk3288_gpiomem_remove(struct platform_device *pdev)
267+
{
268+
struct device *dev = inst->dev;
269+
270+
kfree(inst);
271+
device_destroy(rk3288_gpiomem_class, rk3288_gpiomem_devid);
272+
class_destroy(rk3288_gpiomem_class);
273+
cdev_del(&rk3288_gpiomem_cdev);
274+
unregister_chrdev_region(rk3288_gpiomem_devid, 1);
275+
276+
dev_info(dev, "GPIO mem driver removed - OK");
277+
return 0;
278+
}
279+
280+
/****************************************************************************
281+
*
282+
* Register the driver with device tree
283+
*
284+
***************************************************************************/
285+
286+
static const struct of_device_id rk3288_gpiomem_of_match[] = {
287+
{.compatible = "rockchip,rk3288-gpiomem",},
288+
{ /* sentinel */ },
289+
};
290+
291+
MODULE_DEVICE_TABLE(of, rk3288_gpiomem_of_match);
292+
293+
static struct platform_driver rk3288_gpiomem_driver = {
294+
.probe = rk3288_gpiomem_probe,
295+
.remove = rk3288_gpiomem_remove,
296+
.driver = {
297+
.name = DRIVER_NAME,
298+
.owner = THIS_MODULE,
299+
.of_match_table = rk3288_gpiomem_of_match,
300+
},
301+
};
302+
303+
module_platform_driver(rk3288_gpiomem_driver);
304+
305+
MODULE_ALIAS("platform:gpiomem-rk3288");
306+
MODULE_LICENSE("GPL");
307+
MODULE_DESCRIPTION("gpiomem driver for accessing GPIO from userspace");
308+
MODULE_AUTHOR("Luke Wren <luke@raspberrypi.org>");

0 commit comments

Comments
 (0)