Skip to content

Commit

Permalink
brcmvirt_gpio: Create coherent buffer and push to firmware
Browse files Browse the repository at this point in the history
  • Loading branch information
popcornmix committed Nov 14, 2016
1 parent b4cd401 commit c17d6ec
Show file tree
Hide file tree
Showing 2 changed files with 62 additions and 27 deletions.
88 changes: 61 additions & 27 deletions drivers/gpio/gpio-bcm-virt.c
Expand Up @@ -14,6 +14,7 @@
#include <linux/gpio.h>
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/dma-mapping.h>
#include <soc/bcm2835/raspberrypi-firmware.h>

#define MODULE_NAME "brcmvirt-gpio"
Expand All @@ -25,6 +26,7 @@ struct brcmvirt_gpio {
/* two packed 16-bit counts of enabled and disables
Allows host to detect a brief enable that was missed */
u32 enables_disables[NUM_GPIO];
dma_addr_t bus_addr;
};

static int brcmvirt_gpio_dir_in(struct gpio_chip *gc, unsigned off)
Expand Down Expand Up @@ -75,13 +77,13 @@ static void brcmvirt_gpio_set(struct gpio_chip *gc, unsigned off, int val)

static int brcmvirt_gpio_probe(struct platform_device *pdev)
{
int err = 0;
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) {
Expand All @@ -93,35 +95,56 @@ static int brcmvirt_gpio_probe(struct platform_device *pdev)
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;
goto out;
}

// 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->ts_base = dma_zalloc_coherent(NULL, PAGE_SIZE, &ucb->bus_addr, GFP_KERNEL);

This comment has been minimized.

Copy link
@notro

notro Nov 14, 2016

Contributor

Have you tested this?
If you don't pass in struct device, you should get a wrong bus address, now that we're MULTI_PLATFORM.

This comment has been minimized.

Copy link
@pelwell

pelwell Nov 14, 2016

Contributor

It does work. I've not looked at the returned value, but there are no error messages.

This comment has been minimized.

Copy link
@popcornmix

popcornmix Nov 14, 2016

Author Collaborator

Yes, it does work (or at least behaves as if it works). No error returned and LED flashes as expected.
However there is no reason not to pass in device, so I've pushed a fixup commit.

if (!ucb->ts_base) {
pr_err("[%s]: failed to dma_alloc_coherent(%ld)\n",
__func__, PAGE_SIZE);
err = -ENOMEM;
goto out;
}

gpiovirtbuf = (u32)ucb->bus_addr;
err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF,
&gpiovirtbuf, sizeof(gpiovirtbuf));

if (err || gpiovirtbuf != 0) {
dev_warn(dev, "Failed to set gpiovirtbuf, trying to get err:%x\n", err);
dma_free_coherent(NULL, PAGE_SIZE, ucb->ts_base, ucb->bus_addr);
ucb->ts_base = 0;
ucb->bus_addr = 0;
}

if (!ucb->ts_base) {
err = rpi_firmware_property(fw, RPI_FIRMWARE_FRAMEBUFFER_GET_GPIOVIRTBUF,
&gpiovirtbuf, sizeof(gpiovirtbuf));

if (err) {
dev_err(dev, "Failed to get gpiovirtbuf\n");
goto out;
}

if (!gpiovirtbuf) {
dev_err(dev, "No virtgpio buffer\n");
err = -ENOENT;
goto out;
}

// 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 out;
}
ucb->bus_addr = 0;
}
ucb->gc.label = MODULE_NAME;
ucb->gc.owner = THIS_MODULE;
//ucb->gc.dev = dev;
Expand All @@ -137,13 +160,21 @@ static int brcmvirt_gpio_probe(struct platform_device *pdev)

err = gpiochip_add(&ucb->gc);
if (err)
goto err;
goto out;

platform_set_drvdata(pdev, ucb);

err:
return 0;
out:
if (ucb->bus_addr) {
dma_free_coherent(NULL, PAGE_SIZE, ucb->ts_base, ucb->bus_addr);
ucb->bus_addr = 0;
ucb->ts_base = NULL;
} else if (ucb->ts_base) {
iounmap(ucb->ts_base);
ucb->ts_base = NULL;
}
return err;

}

static int brcmvirt_gpio_remove(struct platform_device *pdev)
Expand All @@ -152,7 +183,10 @@ static int brcmvirt_gpio_remove(struct platform_device *pdev)
struct brcmvirt_gpio *ucb = platform_get_drvdata(pdev);

gpiochip_remove(&ucb->gc);
iounmap(ucb->ts_base);
if (ucb->bus_addr)
dma_free_coherent(NULL, PAGE_SIZE, ucb->ts_base, ucb->bus_addr);
else if (ucb->ts_base)
iounmap(ucb->ts_base);
return err;
}

Expand Down
1 change: 1 addition & 0 deletions include/soc/bcm2835/raspberrypi-firmware.h
Expand Up @@ -116,6 +116,7 @@ enum rpi_firmware_property_tag {
RPI_FIRMWARE_FRAMEBUFFER_SET_OVERSCAN = 0x0004800a,
RPI_FIRMWARE_FRAMEBUFFER_SET_PALETTE = 0x0004800b,
RPI_FIRMWARE_FRAMEBUFFER_SET_TOUCHBUF = 0x0004801f,
RPI_FIRMWARE_FRAMEBUFFER_SET_GPIOVIRTBUF = 0x00048020,
RPI_FIRMWARE_FRAMEBUFFER_SET_VSYNC = 0x0004800e,
RPI_FIRMWARE_FRAMEBUFFER_SET_BACKLIGHT = 0x0004800f,

Expand Down

0 comments on commit c17d6ec

Please sign in to comment.