This is an internal 32-bit input and 32-bit output port to the FPGA logic. Instantiate this in the device tree as: gpio3: gpio@ff706010 { #address-cells = <1>; #size-cells = <0>; compatible = "altr,fpgamgr-gpio"; reg = <0xff706010 0x8>; status = "okay"; portd: gpio-controller@0 { compatible = "altr,fpgamgr-gpio-output"; gpio-controller; #gpio-cells = <2>; reg = <0>; }; porte: gpio-controller@1 { compatible = "altr,fpgamgr-gpio-input"; gpio-controller; #gpio-cells = <2>; reg = <1>; }; }; Signed-off-by: Bernd Edlinger <bernd.edlinger@xxxxxxxxxx> --- drivers/gpio/Kconfig | 6 ++ drivers/gpio/Makefile | 1 + drivers/gpio/gpio-fpgamgr.c | 256 ++++++++++++++++++++++++++++++++++++++++++++ 3 files changed, 263 insertions(+) create mode 100644 drivers/gpio/gpio-fpgamgr.c diff --git a/drivers/gpio/Kconfig b/drivers/gpio/Kconfig index 3388d54..e0c5a35 100644 --- a/drivers/gpio/Kconfig +++ b/drivers/gpio/Kconfig @@ -192,6 +192,12 @@ config GPIO_EXAR Selecting this option will enable handling of GPIO pins present on Exar XR17V352/354/358 chips. +config GPIO_FPGAMGR + tristate "Altera FPGAMGR GPIO" + depends on OF_GPIO + help + Say yes here to support the Altera FPGAMGR GPIO device. + config GPIO_GE_FPGA bool "GE FPGA based GPIO" depends on GE_FPGA diff --git a/drivers/gpio/Makefile b/drivers/gpio/Makefile index aeb70e9d..88a32ab 100644 --- a/drivers/gpio/Makefile +++ b/drivers/gpio/Makefile @@ -50,6 +50,7 @@ obj-$(CONFIG_GPIO_ETRAXFS) += gpio-etraxfs.o obj-$(CONFIG_GPIO_EXAR) += gpio-exar.o obj-$(CONFIG_GPIO_F7188X) += gpio-f7188x.o obj-$(CONFIG_GPIO_FTGPIO010) += gpio-ftgpio010.o +obj-$(CONFIG_GPIO_FPGAMGR) += gpio-fpgamgr.o obj-$(CONFIG_GPIO_GE_FPGA) += gpio-ge.o obj-$(CONFIG_GPIO_GPIO_MM) += gpio-gpio-mm.o obj-$(CONFIG_GPIO_GRGPIO) += gpio-grgpio.o diff --git a/drivers/gpio/gpio-fpgamgr.c b/drivers/gpio/gpio-fpgamgr.c new file mode 100644 index 0000000..8aa86e7 --- /dev/null +++ b/drivers/gpio/gpio-fpgamgr.c @@ -0,0 +1,256 @@ +/* + * Copyright (c) 2015 Softing Industrial Automation GmbH + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + * + */ +#include <linux/gpio/driver.h> +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/platform_device.h> +#include <linux/spinlock.h> +#include <linux/slab.h> + +struct fpgamgr_port_property { + struct device_node *node; + const char *name; + unsigned int idx; +}; + +struct fpgamgr_platform_data { + struct fpgamgr_port_property *properties; + unsigned int nports; +}; + +struct fpgamgr_gpio_port { + struct gpio_chip bgc; + bool is_registered; + struct fpgamgr_gpio *gpio; + unsigned int idx; +}; + +struct fpgamgr_gpio { + struct device *dev; + void __iomem *regs; + struct fpgamgr_gpio_port *ports; + unsigned int nr_ports; +}; + +static int fpgamgr_gpio_add_port(struct fpgamgr_gpio *gpio, + struct fpgamgr_port_property *pp, + unsigned int offs) +{ + struct fpgamgr_gpio_port *port; + void __iomem *dat; + int err; + + port = &gpio->ports[offs]; + port->gpio = gpio; + port->idx = pp->idx; + + dat = gpio->regs + (pp->idx * 4); + + err = bgpio_init(&port->bgc, gpio->dev, 4, dat, NULL, NULL, + NULL, NULL, 0); + if (err) { + dev_err(gpio->dev, "failed to init gpio chip for %s\n", + pp->name); + return err; + } + +#ifdef CONFIG_OF_GPIO + port->bgc.of_node = pp->node; +#endif + + err = gpiochip_add(&port->bgc); + if (err) + dev_err(gpio->dev, "failed to register gpiochip for %s\n", + pp->name); + else + port->is_registered = true; + + return err; +} + +static void fpgamgr_gpio_unregister(struct fpgamgr_gpio *gpio) +{ + unsigned int m; + + for (m = 0; m < gpio->nr_ports; ++m) + if (gpio->ports[m].is_registered) + gpiochip_remove(&gpio->ports[m].bgc); +} + +static struct fpgamgr_platform_data * +fpgamgr_gpio_get_pdata_of(struct device *dev) +{ + struct device_node *node, *port_np; + struct fpgamgr_platform_data *pdata; + struct fpgamgr_port_property *pp; + int nports; + int i; + + node = dev->of_node; + if (!IS_ENABLED(CONFIG_OF_GPIO) || !node) + return ERR_PTR(-ENODEV); + + nports = of_get_child_count(node); + if (nports == 0) + return ERR_PTR(-ENODEV); + + pdata = kzalloc(sizeof(*pdata), GFP_KERNEL); + if (!pdata) + return ERR_PTR(-ENOMEM); + + pdata->properties = kcalloc(nports, sizeof(*pp), GFP_KERNEL); + if (!pdata->properties) { + kfree(pdata); + return ERR_PTR(-ENOMEM); + } + + pdata->nports = nports; + + i = 0; + for_each_child_of_node(node, port_np) { + pp = &pdata->properties[i++]; + pp->node = port_np; + + if (of_property_read_u32(port_np, "reg", &pp->idx) || + pp->idx > 1) { + dev_err(dev, "missing/invalid port index for %s\n", + port_np->full_name); + kfree(pdata->properties); + kfree(pdata); + return ERR_PTR(-EINVAL); + } + + pp->name = port_np->full_name; + } + + return pdata; +} + +static inline void fpgamgr_free_pdata_of(struct fpgamgr_platform_data *pdata) +{ + if (!IS_ENABLED(CONFIG_OF_GPIO) || !pdata) + return; + + kfree(pdata->properties); + kfree(pdata); +} + +static int fpgamgr_gpio_probe(struct platform_device *pdev) +{ + unsigned int i; + struct resource *res; + struct fpgamgr_gpio *gpio; + int err; + struct device *dev = &pdev->dev; + struct fpgamgr_platform_data *pdata = dev_get_platdata(dev); + bool is_pdata_alloc = !pdata; + + if (is_pdata_alloc) { + pdata = fpgamgr_gpio_get_pdata_of(dev); + if (IS_ERR(pdata)) + return PTR_ERR(pdata); + } + + if (!pdata->nports) { + err = -ENODEV; + goto out_err; + } + + gpio = devm_kzalloc(&pdev->dev, sizeof(*gpio), GFP_KERNEL); + if (!gpio) { + err = -ENOMEM; + goto out_err; + } + gpio->dev = &pdev->dev; + gpio->nr_ports = pdata->nports; + + gpio->ports = devm_kcalloc(&pdev->dev, gpio->nr_ports, + sizeof(*gpio->ports), GFP_KERNEL); + if (!gpio->ports) { + err = -ENOMEM; + goto out_err; + } + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + gpio->regs = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(gpio->regs)) { + err = PTR_ERR(gpio->regs); + goto out_err; + } + + for (i = 0; i < gpio->nr_ports; i++) { + err = fpgamgr_gpio_add_port(gpio, &pdata->properties[i], i); + if (err) + goto out_unregister; + } + platform_set_drvdata(pdev, gpio); + + goto out_err; + +out_unregister: + fpgamgr_gpio_unregister(gpio); + +out_err: + if (is_pdata_alloc) + fpgamgr_free_pdata_of(pdata); + + return err; +} + +static int fpgamgr_gpio_remove(struct platform_device *pdev) +{ + struct fpgamgr_gpio *gpio = platform_get_drvdata(pdev); + + fpgamgr_gpio_unregister(gpio); + + return 0; +} + +static const struct of_device_id fpgamgr_of_match[] = { + { .compatible = "altr,fpgamgr-gpio" }, + { /* Sentinel */ } +}; +MODULE_DEVICE_TABLE(of, fpgamgr_of_match); + +#ifdef CONFIG_PM_SLEEP +static int fpgamgr_gpio_suspend(struct device *dev) +{ + return 0; +} + +static int fpgamgr_gpio_resume(struct device *dev) +{ + return 0; +} +#endif + +static SIMPLE_DEV_PM_OPS(fpgamgr_gpio_pm_ops, fpgamgr_gpio_suspend, + fpgamgr_gpio_resume); + +static struct platform_driver fpgamgr_gpio_driver = { + .driver = { + .name = "gpio-fpgamgr", + .owner = THIS_MODULE, + .pm = &fpgamgr_gpio_pm_ops, + .of_match_table = of_match_ptr(fpgamgr_of_match), + }, + .probe = fpgamgr_gpio_probe, + .remove = fpgamgr_gpio_remove, +}; + +module_platform_driver(fpgamgr_gpio_driver); + +MODULE_LICENSE("GPL"); +MODULE_AUTHOR("Bernd Edlinger"); +MODULE_DESCRIPTION("Altera fpgamgr GPIO driver"); -- 2.7.4 ��.n��������+%������w��{.n�����{�� b���ܨ}���Ơz�j:+v�����w����ޙ��&�)ߡ�a����z�ޗ���ݢj��w�f