Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
brcmvirt_gpio: Create coherent buffer and push to firmware
- Loading branch information
1 parent
b4cd401
commit c17d6ec
Showing
2 changed files
with
62 additions
and
27 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -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" | ||
|
@@ -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) | ||
|
@@ -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) { | ||
|
@@ -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.
Sorry, something went wrong.
This comment has been minimized.
Sorry, something went wrong.
pelwell
Contributor
|
||
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; | ||
|
@@ -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) | ||
|
@@ -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; | ||
} | ||
|
||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
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.