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