You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
227 lines
6.1 KiB
227 lines
6.1 KiB
7 years ago
|
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
|
||
|
@@ -258,6 +258,7 @@ config PINCTRL_ZYNQ
|
||
|
|
||
|
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
|
||
|
@@ -39,6 +39,7 @@ obj-$(CONFIG_PINCTRL_ZYNQ) += pinctrl-zy
|
||
|
|
||
|
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
|