| From ab2f33e35e35905a76204138143875251f3e1088 Mon Sep 17 00:00:00 2001 |
| From: Jonas Gorski <jonas.gorski@gmail.com> |
| Date: Fri, 24 Jun 2016 22:07:42 +0200 |
| Subject: [PATCH 01/13] pinctrl: add bcm63xx base code |
| |
| Setup directory and add a helper for bcm63xx pinctrl support. |
| |
| Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com> |
| --- |
| drivers/pinctrl/Kconfig | 1 + |
| drivers/pinctrl/Makefile | 1 + |
| drivers/pinctrl/bcm63xx/Kconfig | 3 + |
| drivers/pinctrl/bcm63xx/Makefile | 1 + |
| drivers/pinctrl/bcm63xx/pinctrl-bcm63xx.c | 142 ++++++++++++++++++++++++++++++ |
| drivers/pinctrl/bcm63xx/pinctrl-bcm63xx.h | 14 +++ |
| 7 files changed, 163 insertions(+) |
| create mode 100644 drivers/pinctrl/bcm63xx/Kconfig |
| create mode 100644 drivers/pinctrl/bcm63xx/Makefile |
| create mode 100644 drivers/pinctrl/bcm63xx/pinctrl-bcm63xx.c |
| create mode 100644 drivers/pinctrl/bcm63xx/pinctrl-bcm63xx.h |
| |
| --- a/drivers/pinctrl/Kconfig |
| +++ b/drivers/pinctrl/Kconfig |
| @@ -387,6 +387,7 @@ config PINCTRL_OCELOT |
| source "drivers/pinctrl/actions/Kconfig" |
| source "drivers/pinctrl/aspeed/Kconfig" |
| source "drivers/pinctrl/bcm/Kconfig" |
| +source "drivers/pinctrl/bcm63xx/Kconfig" |
| source "drivers/pinctrl/berlin/Kconfig" |
| source "drivers/pinctrl/freescale/Kconfig" |
| source "drivers/pinctrl/intel/Kconfig" |
| --- a/drivers/pinctrl/Makefile |
| +++ b/drivers/pinctrl/Makefile |
| @@ -50,6 +50,7 @@ obj-$(CONFIG_PINCTRL_OCELOT) += pinctrl- |
| obj-y += actions/ |
| obj-$(CONFIG_ARCH_ASPEED) += aspeed/ |
| obj-y += bcm/ |
| +obj-y += bcm63xx/ |
| obj-$(CONFIG_PINCTRL_BERLIN) += berlin/ |
| obj-y += freescale/ |
| obj-$(CONFIG_X86) += intel/ |
| --- /dev/null |
| +++ b/drivers/pinctrl/bcm63xx/Kconfig |
| @@ -0,0 +1,3 @@ |
| +config PINCTRL_BCM63XX |
| + bool |
| + select GPIO_GENERIC |
| --- /dev/null |
| +++ b/drivers/pinctrl/bcm63xx/Makefile |
| @@ -0,0 +1 @@ |
| +obj-$(CONFIG_PINCTRL_BCM63XX) += pinctrl-bcm63xx.o |
| --- /dev/null |
| +++ b/drivers/pinctrl/bcm63xx/pinctrl-bcm63xx.c |
| @@ -0,0 +1,155 @@ |
| +/* |
| + * This file is subject to the terms and conditions of the GNU General Public |
| + * License. See the file "COPYING" in the main directory of this archive |
| + * for more details. |
| + * |
| + * Copyright (C) 2016 Jonas Gorski <jonas.gorski@gmail.com> |
| + */ |
| + |
| +#include <linux/bitops.h> |
| +#include <linux/device.h> |
| +#include <linux/gpio/driver.h> |
| +#include <linux/of_irq.h> |
| + |
| +#include "pinctrl-bcm63xx.h" |
| +#include "../core.h" |
| + |
| +#define BANK_SIZE sizeof(u32) |
| +#define PINS_PER_BANK (BANK_SIZE * BITS_PER_BYTE) |
| + |
| +#ifdef CONFIG_OF |
| +static int bcm63xx_gpio_of_xlate(struct gpio_chip *gc, |
| + const struct of_phandle_args *gpiospec, |
| + u32 *flags) |
| +{ |
| + struct gpio_chip *base = gpiochip_get_data(gc); |
| + int pin = gpiospec->args[0]; |
| + |
| + if (gc != &base[pin / PINS_PER_BANK]) |
| + return -EINVAL; |
| + |
| + pin = pin % PINS_PER_BANK; |
| + |
| + if (pin >= gc->ngpio) |
| + return -EINVAL; |
| + |
| + if (flags) |
| + *flags = gpiospec->args[1]; |
| + |
| + return pin; |
| +} |
| +#endif |
| + |
| +static int bcm63xx_gpio_to_irq(struct gpio_chip *chip, unsigned gpio) |
| +{ |
| + struct gpio_chip *base = gpiochip_get_data(chip); |
| + char irq_name[7]; /* "gpioXX" */ |
| + |
| + /* FIXME: this is ugly */ |
| + sprintf(irq_name, "gpio%d", gpio + PINS_PER_BANK * (chip - base)); |
| + return of_irq_get_byname(chip->of_node, irq_name); |
| +} |
| + |
| +static int bcm63xx_setup_gpio(struct device *dev, struct gpio_chip *gc, |
| + void __iomem *dirout, void __iomem *data, |
| + size_t sz, int ngpio) |
| + |
| +{ |
| + int banks, chips, i, ret = -EINVAL; |
| + |
| + chips = DIV_ROUND_UP(ngpio, PINS_PER_BANK); |
| + banks = sz / BANK_SIZE; |
| + |
| + for (i = 0; i < chips; i++) { |
| + int offset, pins; |
| + int reg_offset; |
| + char *label; |
| + |
| + label = devm_kasprintf(dev, GFP_KERNEL, "bcm63xx-gpio.%i", i); |
| + if (!label) |
| + return -ENOMEM; |
| + |
| + offset = i * PINS_PER_BANK; |
| + pins = min_t(int, ngpio - offset, PINS_PER_BANK); |
| + |
| + /* the registers are treated like a huge big endian register */ |
| + reg_offset = (banks - i - 1) * BANK_SIZE; |
| + |
| + ret = bgpio_init(&gc[i], dev, BANK_SIZE, data + reg_offset, |
| + NULL, NULL, dirout + reg_offset, NULL, |
| + BGPIOF_BIG_ENDIAN_BYTE_ORDER); |
| + if (ret) |
| + return ret; |
| + |
| + gc[i].request = gpiochip_generic_request; |
| + gc[i].free = gpiochip_generic_free; |
| + |
| + if (of_get_property(dev->of_node, "interrupt-names", NULL)) |
| + gc[i].to_irq = bcm63xx_gpio_to_irq; |
| + |
| +#ifdef CONFIG_OF |
| + gc[i].of_gpio_n_cells = 2; |
| + gc[i].of_xlate = bcm63xx_gpio_of_xlate; |
| +#endif |
| + |
| + gc[i].label = label; |
| + gc[i].ngpio = pins; |
| + |
| + devm_gpiochip_add_data(dev, &gc[i], gc); |
| + } |
| + |
| + return 0; |
| +} |
| + |
| +static void bcm63xx_setup_pinranges(struct gpio_chip *gc, const char *name, |
| + int ngpio) |
| +{ |
| + int i, chips = DIV_ROUND_UP(ngpio, PINS_PER_BANK); |
| + |
| + for (i = 0; i < chips; i++) { |
| + int offset, pins; |
| + |
| + offset = i * PINS_PER_BANK; |
| + pins = min_t(int, ngpio - offset, PINS_PER_BANK); |
| + |
| + gpiochip_add_pin_range(&gc[i], name, 0, offset, pins); |
| + } |
| +} |
| + |
| +struct pinctrl_dev *bcm63xx_pinctrl_register(struct platform_device *pdev, |
| + struct pinctrl_desc *desc, |
| + void *priv, struct gpio_chip *gc, |
| + int ngpio) |
| +{ |
| + struct pinctrl_dev *pctldev; |
| + struct resource *res; |
| + void __iomem *dirout, *data; |
| + size_t sz; |
| + int ret; |
| + |
| + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dirout"); |
| + dirout = devm_ioremap_resource(&pdev->dev, res); |
| + if (IS_ERR(dirout)) |
| + return ERR_CAST(dirout); |
| + |
| + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat"); |
| + data = devm_ioremap_resource(&pdev->dev, res); |
| + if (IS_ERR(data)) |
| + return ERR_CAST(data); |
| + |
| + sz = resource_size(res); |
| + |
| + ret = bcm63xx_setup_gpio(&pdev->dev, gc, dirout, data, sz, ngpio); |
| + if (ret) |
| + return ERR_PTR(ret); |
| + |
| + pctldev = devm_pinctrl_register(&pdev->dev, desc, priv); |
| + if (IS_ERR(pctldev)) |
| + return pctldev; |
| + |
| + bcm63xx_setup_pinranges(gc, pinctrl_dev_get_devname(pctldev), ngpio); |
| + |
| + dev_info(&pdev->dev, "registered at mmio %p\n", dirout); |
| + |
| + return pctldev; |
| +} |
| --- /dev/null |
| +++ b/drivers/pinctrl/bcm63xx/pinctrl-bcm63xx.h |
| @@ -0,0 +1,14 @@ |
| +#ifndef __PINCTRL_BCM63XX |
| +#define __PINCTRL_BCM63XX |
| + |
| +#include <linux/kernel.h> |
| +#include <linux/gpio.h> |
| +#include <linux/pinctrl/pinctrl.h> |
| +#include <linux/platform_device.h> |
| + |
| +struct pinctrl_dev *bcm63xx_pinctrl_register(struct platform_device *pdev, |
| + struct pinctrl_desc *desc, |
| + void *priv, struct gpio_chip *gc, |
| + int ngpio); |
| + |
| +#endif |