From fba2e30e3511cbc94249468490843723268a0c39 Mon Sep 17 00:00:00 2001 From: popcornmix Date: Tue, 23 Feb 2016 19:56:04 +0000 Subject: [PATCH] bcm2835-virtgpio: Virtual GPIO driver Add a virtual GPIO driver that uses the firmware mailbox interface to request that the VPU toggles LEDs. --- arch/arm/configs/bcm2709_defconfig | 1 + drivers/gpio/Kconfig | 6 + drivers/gpio/Makefile | 1 + drivers/gpio/gpio-bcm-virt.c | 180 +++++++++++++++++++++++++++++ include/soc/bcm2835/raspberrypi-firmware.h | 1 + 5 files changed, 189 insertions(+) create mode 100644 drivers/gpio/gpio-bcm-virt.c --- a/arch/arm/configs/bcm2709_defconfig +++ b/arch/arm/configs/bcm2709_defconfig @@ -607,6 +607,7 @@ CONFIG_PPS=m CONFIG_PPS_CLIENT_LDISC=m CONFIG_PPS_CLIENT_GPIO=m CONFIG_GPIO_SYSFS=y +CONFIG_GPIO_BCM_VIRT=y CONFIG_GPIO_ARIZONA=m CONFIG_GPIO_STMPE=y CONFIG_W1=m --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -133,6 +133,12 @@ config GPIO_BCM_KONA help Turn on GPIO support for Broadcom "Kona" chips. +config GPIO_BCM_VIRT + bool "Broadcom Virt GPIO" + depends on OF_GPIO && RASPBERRYPI_FIRMWARE && (ARCH_BCM2835 || ARCH_BCM2708 || ARCH_BCM2709 || COMPILE_TEST) + help + Turn on virtual GPIO support for Broadcom BCM283X chips. + config GPIO_BRCMSTB tristate "BRCMSTB GPIO support" default y if ARCH_BRCMSTB --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -24,6 +24,7 @@ obj-$(CONFIG_GPIO_AMDPT) += gpio-amdpt.o obj-$(CONFIG_GPIO_ARIZONA) += gpio-arizona.o obj-$(CONFIG_ATH79) += gpio-ath79.o obj-$(CONFIG_GPIO_BCM_KONA) += gpio-bcm-kona.o +obj-$(CONFIG_GPIO_BCM_VIRT) += gpio-bcm-virt.o obj-$(CONFIG_GPIO_BRCMSTB) += gpio-brcmstb.o obj-$(CONFIG_GPIO_BT8XX) += gpio-bt8xx.o obj-$(CONFIG_GPIO_CLPS711X) += gpio-clps711x.o --- /dev/null +++ b/drivers/gpio/gpio-bcm-virt.c @@ -0,0 +1,180 @@ +/* + * brcmvirt GPIO driver + * + * Copyright (C) 2012,2013 Dom Cobley + * Based on gpio-clps711x.c by Alexander Shiyan + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + */ + +#include +#include +#include +#include +#include +#include + +#define MODULE_NAME "brcmvirt-gpio" +#define NUM_GPIO 2 + +struct brcmvirt_gpio { + struct gpio_chip gc; + u32 __iomem *ts_base; + /* two packed 16-bit counts of enabled and disables + Allows host to detect a brief enable that was missed */ + u32 enables_disables[NUM_GPIO]; +}; + +static int brcmvirt_gpio_dir_in(struct gpio_chip *gc, unsigned off) +{ + struct brcmvirt_gpio *gpio; + gpio = container_of(gc, struct brcmvirt_gpio, gc); + return -EINVAL; +} + +static int brcmvirt_gpio_dir_out(struct gpio_chip *gc, unsigned off, int val) +{ + struct brcmvirt_gpio *gpio; + gpio = container_of(gc, struct brcmvirt_gpio, gc); + return 0; +} + +static int brcmvirt_gpio_get(struct gpio_chip *gc, unsigned off) +{ + struct brcmvirt_gpio *gpio; + unsigned v; + gpio = container_of(gc, struct brcmvirt_gpio, gc); + v = readl(gpio->ts_base + off); + return (v >> off) & 1; +} + +static void brcmvirt_gpio_set(struct gpio_chip *gc, unsigned off, int val) +{ + struct brcmvirt_gpio *gpio; + u16 enables, disables; + s16 diff; + bool lit; + gpio = container_of(gc, struct brcmvirt_gpio, gc); + enables = gpio->enables_disables[off] >> 16; + disables = gpio->enables_disables[off] >> 0; + diff = (s16)(enables - disables); + lit = diff > 0; + if ((val && lit) || (!val && !lit)) + return; + if (val) + enables++; + else + disables++; + diff = (s16)(enables - disables); + BUG_ON(diff != 0 && diff != 1); + gpio->enables_disables[off] = (enables << 16) | (disables << 0); + writel(gpio->enables_disables[off], gpio->ts_base + off); +} + +static int brcmvirt_gpio_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *fw_node; + struct rpi_firmware *fw; + struct brcmvirt_gpio *ucb; + u32 gpiovirtbuf; + int err = 0; + + fw_node = of_parse_phandle(np, "firmware", 0); + if (!fw_node) { + dev_err(dev, "Missing firmware node\n"); + return -ENOENT; + } + + fw = rpi_firmware_get(fw_node); + if (!fw) + return -EPROBE_DEFER; + + err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF, + &gpiovirtbuf, sizeof(gpiovirtbuf)); + + if (err) { + dev_err(dev, "Failed to get gpiovirtbuf\n"); + goto err; + } + + if (!gpiovirtbuf) { + dev_err(dev, "No virtgpio buffer\n"); + err = -ENOENT; + goto err; + } + + ucb = devm_kzalloc(dev, sizeof *ucb, GFP_KERNEL); + if (!ucb) { + err = -EINVAL; + goto err; + } + + // mmap the physical memory + gpiovirtbuf &= ~0xc0000000; + ucb->ts_base = ioremap(gpiovirtbuf, 4096); + if (ucb->ts_base == NULL) { + dev_err(dev, "Failed to map physical address\n"); + err = -ENOENT; + goto err; + } + + ucb->gc.label = MODULE_NAME; + ucb->gc.owner = THIS_MODULE; + ucb->gc.dev = dev; + ucb->gc.of_node = np; + ucb->gc.base = 100; + ucb->gc.ngpio = NUM_GPIO; + + ucb->gc.direction_input = brcmvirt_gpio_dir_in; + ucb->gc.direction_output = brcmvirt_gpio_dir_out; + ucb->gc.get = brcmvirt_gpio_get; + ucb->gc.set = brcmvirt_gpio_set; + ucb->gc.can_sleep = true; + + err = gpiochip_add(&ucb->gc); + if (err) + goto err; + + platform_set_drvdata(pdev, ucb); + +err: + return err; + +} + +static int brcmvirt_gpio_remove(struct platform_device *pdev) +{ + int err = 0; + struct brcmvirt_gpio *ucb = platform_get_drvdata(pdev); + + gpiochip_remove(&ucb->gc); + iounmap(ucb->ts_base); + return err; +} + +static const struct of_device_id __maybe_unused brcmvirt_gpio_ids[] = { + { .compatible = "brcm,bcm2835-virtgpio" }, + { } +}; +MODULE_DEVICE_TABLE(of, brcmvirt_gpio_ids); + +static struct platform_driver brcmvirt_gpio_driver = { + .driver = { + .name = MODULE_NAME, + .owner = THIS_MODULE, + .of_match_table = of_match_ptr(brcmvirt_gpio_ids), + }, + .probe = brcmvirt_gpio_probe, + .remove = brcmvirt_gpio_remove, +}; +module_platform_driver(brcmvirt_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Dom Cobley "); +MODULE_DESCRIPTION("brcmvirt GPIO driver"); +MODULE_ALIAS("platform:brcmvirt-gpio"); --- a/include/soc/bcm2835/raspberrypi-firmware.h +++ b/include/soc/bcm2835/raspberrypi-firmware.h @@ -93,6 +93,7 @@ enum rpi_firmware_property_tag { RPI_FIRMWARE_FRAMEBUFFER_GET_OVERSCAN = 0x0004000a, RPI_FIRMWARE_FRAMEBUFFER_GET_PALETTE = 0x0004000b, RPI_FIRMWARE_FRAMEBUFFER_GET_TOUCHBUF = 0x0004000f, + RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF = 0x00040010, RPI_FIRMWARE_FRAMEBUFFER_RELEASE = 0x00048001, RPI_FIRMWARE_FRAMEBUFFER_TEST_PHYSICAL_WIDTH_HEIGHT = 0x00044003, RPI_FIRMWARE_FRAMEBUFFER_TEST_VIRTUAL_WIDTH_HEIGHT = 0x00044004,