brcm2708: add linux 4.19 support
[oweals/openwrt.git] / target / linux / brcm2708 / patches-4.19 / 950-0090-bcm2835-virtgpio-Virtual-GPIO-driver.patch
1 From ae7c0b0955e96a7231ad4b6d909124fa7f7713e8 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/703] 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 @@ -151,6 +151,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_RASPBERRYPI_EXP)     += gpio-raspberrypi-exp.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");