OpenWrt – Blame information for rev 4
?pathlinks?
Rev | Author | Line No. | Line |
---|---|---|---|
4 | office | 1 | From 1b6bee75a64ca43404e6bc8ff2fdbe8b1d0a0423 Mon Sep 17 00:00:00 2001 |
2 | From: popcornmix <popcornmix@gmail.com> |
||
3 | Date: Tue, 23 Feb 2016 19:56:04 +0000 |
||
4 | Subject: [PATCH] bcm2835-virtgpio: Virtual GPIO driver |
||
5 | |||
6 | Add a virtual GPIO driver that uses the firmware mailbox interface to |
||
7 | request that the VPU toggles LEDs. |
||
8 | --- |
||
9 | drivers/gpio/Kconfig | 6 + |
||
10 | drivers/gpio/Makefile | 1 + |
||
11 | drivers/gpio/gpio-bcm-virt.c | 214 +++++++++++++++++++++++++++++ |
||
12 | include/soc/bcm2835/raspberrypi-firmware.h | 1 + |
||
13 | 4 files changed, 222 insertions(+) |
||
14 | create mode 100644 drivers/gpio/gpio-bcm-virt.c |
||
15 | |||
16 | --- a/drivers/gpio/Kconfig |
||
17 | +++ b/drivers/gpio/Kconfig |
||
18 | @@ -134,6 +134,12 @@ config GPIO_BCM_KONA |
||
19 | help |
||
20 | Turn on GPIO support for Broadcom "Kona" chips. |
||
21 | |||
22 | +config GPIO_BCM_VIRT |
||
23 | + bool "Broadcom Virt GPIO" |
||
24 | + depends on OF_GPIO && RASPBERRYPI_FIRMWARE && (ARCH_BCM2835 || COMPILE_TEST) |
||
25 | + help |
||
26 | + Turn on virtual GPIO support for Broadcom BCM283X chips. |
||
27 | + |
||
28 | config GPIO_BRCMSTB |
||
29 | tristate "BRCMSTB GPIO support" |
||
30 | default y if (ARCH_BRCMSTB || BMIPS_GENERIC) |
||
31 | --- a/drivers/gpio/Makefile |
||
32 | +++ b/drivers/gpio/Makefile |
||
33 | @@ -31,6 +31,7 @@ obj-$(CONFIG_GPIO_ATH79) += gpio-ath79.o |
||
34 | obj-$(CONFIG_GPIO_ASPEED) += gpio-aspeed.o |
||
35 | obj-$(CONFIG_GPIO_AXP209) += gpio-axp209.o |
||
36 | obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o |
||
37 | +obj-$(CONFIG_GPIO_BCM_VIRT) += gpio-bcm-virt.o |
||
38 | obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o |
||
39 | obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o |
||
40 | obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o |
||
41 | --- /dev/null |
||
42 | +++ b/drivers/gpio/gpio-bcm-virt.c |
||
43 | @@ -0,0 +1,214 @@ |
||
44 | +/* |
||
45 | + * brcmvirt GPIO driver |
||
46 | + * |
||
47 | + * Copyright (C) 2012,2013 Dom Cobley <popcornmix@gmail.com> |
||
48 | + * Based on gpio-clps711x.c by Alexander Shiyan <shc_work@mail.ru> |
||
49 | + * |
||
50 | + * This program is free software; you can redistribute it and/or modify |
||
51 | + * it under the terms of the GNU General Public License as published by |
||
52 | + * the Free Software Foundation; either version 2 of the License, or |
||
53 | + * (at your option) any later version. |
||
54 | + */ |
||
55 | + |
||
56 | +#include <linux/err.h> |
||
57 | +#include <linux/gpio.h> |
||
58 | +#include <linux/module.h> |
||
59 | +#include <linux/platform_device.h> |
||
60 | +#include <linux/dma-mapping.h> |
||
61 | +#include <soc/bcm2835/raspberrypi-firmware.h> |
||
62 | + |
||
63 | +#define MODULE_NAME "brcmvirt-gpio" |
||
64 | +#define NUM_GPIO 2 |
||
65 | + |
||
66 | +struct brcmvirt_gpio { |
||
67 | + struct gpio_chip gc; |
||
68 | + u32 __iomem *ts_base; |
||
69 | + /* two packed 16-bit counts of enabled and disables |
||
70 | + Allows host to detect a brief enable that was missed */ |
||
71 | + u32 enables_disables[NUM_GPIO]; |
||
72 | + dma_addr_t bus_addr; |
||
73 | +}; |
||
74 | + |
||
75 | +static int brcmvirt_gpio_dir_in(struct gpio_chip *gc, unsigned off) |
||
76 | +{ |
||
77 | + struct brcmvirt_gpio *gpio; |
||
78 | + gpio = container_of(gc, struct brcmvirt_gpio, gc); |
||
79 | + return -EINVAL; |
||
80 | +} |
||
81 | + |
||
82 | +static int brcmvirt_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val) |
||
83 | +{ |
||
84 | + struct brcmvirt_gpio *gpio; |
||
85 | + gpio = container_of(gc, struct brcmvirt_gpio, gc); |
||
86 | + return 0; |
||
87 | +} |
||
88 | + |
||
89 | +static int brcmvirt_gpio_get(struct gpio_chip *gc, unsigned off) |
||
90 | +{ |
||
91 | + struct brcmvirt_gpio *gpio; |
||
92 | + unsigned v; |
||
93 | + gpio = container_of(gc, struct brcmvirt_gpio, gc); |
||
94 | + v = readl(gpio->ts_base + off); |
||
95 | + return (v >> off) & 1; |
||
96 | +} |
||
97 | + |
||
98 | +static void brcmvirt_gpio_set(struct gpio_chip *gc, unsigned off, int val) |
||
99 | +{ |
||
100 | + struct brcmvirt_gpio *gpio; |
||
101 | + u16 enables, disables; |
||
102 | + s16 diff; |
||
103 | + bool lit; |
||
104 | + gpio = container_of(gc, struct brcmvirt_gpio, gc); |
||
105 | + enables = gpio->enables_disables[off] >> 16; |
||
106 | + disables = gpio->enables_disables[off] >> 0; |
||
107 | + diff = (s16)(enables - disables); |
||
108 | + lit = diff > 0; |
||
109 | + if ((val && lit) || (!val && !lit)) |
||
110 | + return; |
||
111 | + if (val) |
||
112 | + enables++; |
||
113 | + else |
||
114 | + disables++; |
||
115 | + diff = (s16)(enables - disables); |
||
116 | + BUG_ON(diff != 0 && diff != 1); |
||
117 | + gpio->enables_disables[off] = (enables << 16) | (disables << 0); |
||
118 | + writel(gpio->enables_disables[off], gpio->ts_base + off); |
||
119 | +} |
||
120 | + |
||
121 | +static int brcmvirt_gpio_probe(struct platform_device *pdev) |
||
122 | +{ |
||
123 | + int err = 0; |
||
124 | + struct device *dev = &pdev->dev; |
||
125 | + struct device_node *np = dev->of_node; |
||
126 | + struct device_node *fw_node; |
||
127 | + struct rpi_firmware *fw; |
||
128 | + struct brcmvirt_gpio *ucb; |
||
129 | + u32 gpiovirtbuf; |
||
130 | + |
||
131 | + fw_node = of_parse_phandle(np, "firmware", 0); |
||
132 | + if (!fw_node) { |
||
133 | + dev_err(dev, "Missing firmware node\n"); |
||
134 | + return -ENOENT; |
||
135 | + } |
||
136 | + |
||
137 | + fw = rpi_firmware_get(fw_node); |
||
138 | + if (!fw) |
||
139 | + return -EPROBE_DEFER; |
||
140 | + |
||
141 | + ucb = devm_kzalloc(dev, sizeof *ucb, GFP_KERNEL); |
||
142 | + if (!ucb) { |
||
143 | + err = -EINVAL; |
||
144 | + goto out; |
||
145 | + } |
||
146 | + |
||
147 | + ucb->ts_base = dma_zalloc_coherent(dev, PAGE_SIZE, &ucb->bus_addr, GFP_KERNEL); |
||
148 | + if (!ucb->ts_base) { |
||
149 | + pr_err("[%s]: failed to dma_alloc_coherent(%ld)\n", |
||
150 | + __func__, PAGE_SIZE); |
||
151 | + err = -ENOMEM; |
||
152 | + goto out; |
||
153 | + } |
||
154 | + |
||
155 | + gpiovirtbuf = (u32)ucb->bus_addr; |
||
156 | + err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF, |
||
157 | + &gpiovirtbuf, sizeof(gpiovirtbuf)); |
||
158 | + |
||
159 | + if (err || gpiovirtbuf != 0) { |
||
160 | + dev_warn(dev, "Failed to set gpiovirtbuf, trying to get err:%x\n", err); |
||
161 | + dma_free_coherent(dev, PAGE_SIZE, ucb->ts_base, ucb->bus_addr); |
||
162 | + ucb->ts_base = 0; |
||
163 | + ucb->bus_addr = 0; |
||
164 | + } |
||
165 | + |
||
166 | + if (!ucb->ts_base) { |
||
167 | + err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF, |
||
168 | + &gpiovirtbuf, sizeof(gpiovirtbuf)); |
||
169 | + |
||
170 | + if (err) { |
||
171 | + dev_err(dev, "Failed to get gpiovirtbuf\n"); |
||
172 | + goto out; |
||
173 | + } |
||
174 | + |
||
175 | + if (!gpiovirtbuf) { |
||
176 | + dev_err(dev, "No virtgpio buffer\n"); |
||
177 | + err = -ENOENT; |
||
178 | + goto out; |
||
179 | + } |
||
180 | + |
||
181 | + // mmap the physical memory |
||
182 | + gpiovirtbuf &= ~0xc0000000; |
||
183 | + ucb->ts_base = ioremap(gpiovirtbuf, 4096); |
||
184 | + if (ucb->ts_base == NULL) { |
||
185 | + dev_err(dev, "Failed to map physical address\n"); |
||
186 | + err = -ENOENT; |
||
187 | + goto out; |
||
188 | + } |
||
189 | + ucb->bus_addr = 0; |
||
190 | + } |
||
191 | + ucb->gc.label = MODULE_NAME; |
||
192 | + ucb->gc.owner = THIS_MODULE; |
||
193 | + //ucb->gc.dev = dev; |
||
194 | + ucb->gc.of_node = np; |
||
195 | + ucb->gc.base = 100; |
||
196 | + ucb->gc.ngpio = NUM_GPIO; |
||
197 | + |
||
198 | + ucb->gc.direction_input = brcmvirt_gpio_dir_in; |
||
199 | + ucb->gc.direction_output = brcmvirt_gpio_dir_out; |
||
200 | + ucb->gc.get = brcmvirt_gpio_get; |
||
201 | + ucb->gc.set = brcmvirt_gpio_set; |
||
202 | + ucb->gc.can_sleep = true; |
||
203 | + |
||
204 | + err = gpiochip_add(&ucb->gc); |
||
205 | + if (err) |
||
206 | + goto out; |
||
207 | + |
||
208 | + platform_set_drvdata(pdev, ucb); |
||
209 | + |
||
210 | + return 0; |
||
211 | +out: |
||
212 | + if (ucb->bus_addr) { |
||
213 | + dma_free_coherent(dev, PAGE_SIZE, ucb->ts_base, ucb->bus_addr); |
||
214 | + ucb->bus_addr = 0; |
||
215 | + ucb->ts_base = NULL; |
||
216 | + } else if (ucb->ts_base) { |
||
217 | + iounmap(ucb->ts_base); |
||
218 | + ucb->ts_base = NULL; |
||
219 | + } |
||
220 | + return err; |
||
221 | +} |
||
222 | + |
||
223 | +static int brcmvirt_gpio_remove(struct platform_device *pdev) |
||
224 | +{ |
||
225 | + struct device *dev = &pdev->dev; |
||
226 | + int err = 0; |
||
227 | + struct brcmvirt_gpio *ucb = platform_get_drvdata(pdev); |
||
228 | + |
||
229 | + gpiochip_remove(&ucb->gc); |
||
230 | + if (ucb->bus_addr) |
||
231 | + dma_free_coherent(dev, PAGE_SIZE, ucb->ts_base, ucb->bus_addr); |
||
232 | + else if (ucb->ts_base) |
||
233 | + iounmap(ucb->ts_base); |
||
234 | + return err; |
||
235 | +} |
||
236 | + |
||
237 | +static const struct of_device_id __maybe_unused brcmvirt_gpio_ids[] = { |
||
238 | + { .compatible = "brcm,bcm2835-virtgpio" }, |
||
239 | + { } |
||
240 | +}; |
||
241 | +MODULE_DEVICE_TABLE(of, brcmvirt_gpio_ids); |
||
242 | + |
||
243 | +static struct platform_driver brcmvirt_gpio_driver = { |
||
244 | + .driver = { |
||
245 | + .name = MODULE_NAME, |
||
246 | + .owner = THIS_MODULE, |
||
247 | + .of_match_table = of_match_ptr(brcmvirt_gpio_ids), |
||
248 | + }, |
||
249 | + .probe = brcmvirt_gpio_probe, |
||
250 | + .remove = brcmvirt_gpio_remove, |
||
251 | +}; |
||
252 | +module_platform_driver(brcmvirt_gpio_driver); |
||
253 | + |
||
254 | +MODULE_LICENSE("GPL"); |
||
255 | +MODULE_AUTHOR("Dom Cobley <popcornmix@gmail.com>"); |
||
256 | +MODULE_DESCRIPTION("brcmvirt GPIO driver"); |
||
257 | +MODULE_ALIAS("platform:brcmvirt-gpio"); |
||
258 | --- a/include/soc/bcm2835/raspberrypi-firmware.h |
||
259 | +++ b/include/soc/bcm2835/raspberrypi-firmware.h |
||
260 | @@ -116,6 +116,7 @@ enum rpi_firmware_property_tag { |
||
261 | RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a, |
||
262 | RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b, |
||
263 | RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF = 0x0004801f, |
||
264 | + RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF = 0x00048020, |
||
265 | RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC = 0x0004800e, |
||
266 | RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT = 0x0004800f, |
||
267 |