From 45444cb631555e2dc16b95d779b10aa075c7482e Mon Sep 17 00:00:00 2001
From: Jonas Gorski <jonas.gorski@gmail.com>
Date: Fri, 24 Jun 2016 22:14:13 +0200
Subject: [PATCH 05/16] pinctrl: add a pincontrol driver for BCM6348

Add a pincotrol driver for BCM6348. BCM6348 allow muxing five groups of
up to ten gpios into fourteen potential functions. It does not allow
muxing individual pins. Some functions require more than one group to be
muxed to the same function.

Signed-off-by: Jonas Gorski <jonas.gorski@gmail.com>
---
 drivers/pinctrl/bcm63xx/Kconfig           |   7 +
 drivers/pinctrl/bcm63xx/Makefile          |   1 +
 drivers/pinctrl/bcm63xx/pinctrl-bcm6348.c | 391 ++++++++++++++++++++++++++++++
 3 files changed, 399 insertions(+)
 create mode 100644 drivers/pinctrl/bcm63xx/pinctrl-bcm6348.c

--- a/drivers/pinctrl/bcm63xx/Kconfig
+++ b/drivers/pinctrl/bcm63xx/Kconfig
@@ -8,3 +8,10 @@ config PINCTRL_BCM6328
 	select PINCONF
 	select PINCTRL_BCM63XX
 	select GENERIC_PINCONF
+
+config PINCTRL_BCM6348
+	bool "BCM6348 pincontrol driver" if COMPILE_TEST
+	select PINMUX
+	select PINCONF
+	select PINCTRL_BCM63XX
+	select GENERIC_PINCONF
--- a/drivers/pinctrl/bcm63xx/Makefile
+++ b/drivers/pinctrl/bcm63xx/Makefile
@@ -1,2 +1,3 @@
 obj-$(CONFIG_PINCTRL_BCM63XX)	+= pinctrl-bcm63xx.o
 obj-$(CONFIG_PINCTRL_BCM6328)	+= pinctrl-bcm6328.o
+obj-$(CONFIG_PINCTRL_BCM6348)	+= pinctrl-bcm6348.o
--- /dev/null
+++ b/drivers/pinctrl/bcm63xx/pinctrl-bcm6348.c
@@ -0,0 +1,370 @@
+/*
+ * 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/kernel.h>
+#include <linux/spinlock.h>
+#include <linux/bitops.h>
+#include <linux/gpio.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+
+#include <linux/pinctrl/machine.h>
+#include <linux/pinctrl/pinconf.h>
+#include <linux/pinctrl/pinconf-generic.h>
+#include <linux/pinctrl/pinmux.h>
+
+#include "../core.h"
+#include "../pinctrl-utils.h"
+
+#include "pinctrl-bcm63xx.h"
+
+#define BCM6348_NGPIO		37
+
+#define MAX_GROUP		4
+#define PINS_PER_GROUP		8
+#define PIN_TO_GROUP(pin)	(MAX_GROUP - ((pin) / PINS_PER_GROUP))
+#define GROUP_SHIFT(pin)	(PIN_TO_GROUP(pin) * 4)
+#define GROUP_MASK(pin)		(0xf << GROUP_SHIFT(pin))
+
+struct bcm6348_pingroup {
+	const char *name;
+	const unsigned * const pins;
+	const unsigned num_pins;
+};
+
+struct bcm6348_function {
+	const char *name;
+	const char * const *groups;
+	const unsigned num_groups;
+	unsigned int value;
+};
+
+struct bcm6348_pinctrl {
+	struct pinctrl_dev *pctldev;
+	struct pinctrl_desc desc;
+
+	void __iomem *mode;
+
+	/* register access lock */
+	spinlock_t lock;
+
+	struct gpio_chip gpio[2];
+};
+
+#define BCM6348_PIN(a, b, group)		\
+	{					\
+		.number = a,			\
+		.name = b,			\
+		.drv_data = (void *)(group),	\
+	}
+
+static const struct pinctrl_pin_desc bcm6348_pins[] = {
+	BCM6348_PIN(0, "gpio0", 4),
+	BCM6348_PIN(1, "gpio1", 4),
+	BCM6348_PIN(2, "gpio2", 4),
+	BCM6348_PIN(3, "gpio3", 4),
+	BCM6348_PIN(4, "gpio4", 4),
+	BCM6348_PIN(5, "gpio5", 4),
+	BCM6348_PIN(6, "gpio6", 4),
+	BCM6348_PIN(7, "gpio7", 4),
+	BCM6348_PIN(8, "gpio8", 3),
+	BCM6348_PIN(9, "gpio9", 3),
+	BCM6348_PIN(10, "gpio10", 3),
+	BCM6348_PIN(11, "gpio11", 3),
+	BCM6348_PIN(12, "gpio12", 3),
+	BCM6348_PIN(13, "gpio13", 3),
+	BCM6348_PIN(14, "gpio14", 3),
+	BCM6348_PIN(15, "gpio15", 3),
+	BCM6348_PIN(16, "gpio16", 2),
+	BCM6348_PIN(17, "gpio17", 2),
+	BCM6348_PIN(18, "gpio18", 2),
+	BCM6348_PIN(19, "gpio19", 2),
+	BCM6348_PIN(20, "gpio20", 2),
+	BCM6348_PIN(21, "gpio21", 2),
+	BCM6348_PIN(22, "gpio22", 1),
+	BCM6348_PIN(23, "gpio23", 1),
+	BCM6348_PIN(24, "gpio24", 1),
+	BCM6348_PIN(25, "gpio25", 1),
+	BCM6348_PIN(26, "gpio26", 1),
+	BCM6348_PIN(27, "gpio27", 1),
+	BCM6348_PIN(28, "gpio28", 1),
+	BCM6348_PIN(29, "gpio29", 1),
+	BCM6348_PIN(30, "gpio30", 1),
+	BCM6348_PIN(31, "gpio31", 1),
+	BCM6348_PIN(32, "gpio32", 0),
+	BCM6348_PIN(33, "gpio33", 0),
+	BCM6348_PIN(34, "gpio34", 0),
+	BCM6348_PIN(35, "gpio35", 0),
+	BCM6348_PIN(36, "gpio36", 0),
+};
+
+enum bcm6348_muxes {
+	BCM6348_MUX_GPIO = 0,
+	BCM6348_MUX_EXT_EPHY,
+	BCM6348_MUX_MII_SNOOP,
+	BCM6348_MUX_LEGACY_LED,
+	BCM6348_MUX_MII_PCCARD,
+	BCM6348_MUX_PCI,
+	BCM6348_MUX_SPI_MASTER_UART,
+	BCM6348_MUX_EXT_MII,
+	BCM6348_MUX_UTOPIA,
+	BCM6348_MUX_DIAG,
+};
+
+static unsigned group0_pins[] = {
+	32, 33, 34, 35, 36,
+};
+
+static unsigned group1_pins[] = {
+	22, 23, 24, 25, 26, 27, 28, 29, 30, 31,
+};
+
+static unsigned group2_pins[] = {
+	16, 17, 18, 19, 20, 21,
+};
+
+static unsigned group3_pins[] = {
+	8, 9, 10, 11, 12, 13, 14, 15,
+};
+
+static unsigned group4_pins[] = {
+	0, 1, 2, 3, 4, 5, 6, 7,
+};
+
+#define BCM6348_GROUP(n)				\
+	{						\
+		.name = #n,				\
+		.pins = n##_pins,			\
+		.num_pins = ARRAY_SIZE(n##_pins),	\
+	} \
+
+static struct bcm6348_pingroup bcm6348_groups[] = {
+	BCM6348_GROUP(group0),
+	BCM6348_GROUP(group1),
+	BCM6348_GROUP(group2),
+	BCM6348_GROUP(group3),
+	BCM6348_GROUP(group4),
+};
+
+static const char * const ext_mii_groups[] = {
+	"group0",
+	"group3",
+};
+
+static const char * const ext_ephy_groups[] = {
+	"group1",
+	"group4"
+};
+
+static const char * const mii_snoop_groups[] = {
+	"group1",
+	"group4",
+};
+
+static const char * const legacy_led_groups[] = {
+	"group4",
+};
+
+static const char * const mii_pccard_groups[] = {
+	"group1",
+};
+
+static const char * const pci_groups[] = {
+	"group2",
+};
+
+static const char * const spi_master_uart_groups[] = {
+	"group1",
+};
+
+static const char * const utopia_groups[] = {
+	"group1",
+	"group3",
+	"group4",
+};
+
+static const char * const diag_groups[] = {
+	"group0",
+	"group1",
+	"group2",
+	"group3",
+	"group4",
+};
+
+#define BCM6348_FUN(n, f)				\
+	{						\
+		.name = #n,				\
+		.groups = n##_groups,			\
+		.num_groups = ARRAY_SIZE(n##_groups),	\
+		.value = BCM6348_MUX_##f,		\
+	}
+
+static const struct bcm6348_function bcm6348_funcs[] = {
+	BCM6348_FUN(ext_mii, EXT_MII),
+	BCM6348_FUN(ext_ephy, EXT_EPHY),
+	BCM6348_FUN(mii_snoop, MII_SNOOP),
+	BCM6348_FUN(legacy_led, LEGACY_LED),
+	BCM6348_FUN(mii_pccard, MII_PCCARD),
+	BCM6348_FUN(pci, PCI),
+	BCM6348_FUN(spi_master_uart, SPI_MASTER_UART),
+	BCM6348_FUN(utopia, UTOPIA),
+	BCM6348_FUN(diag, DIAG),
+};
+
+static int bcm6348_pinctrl_get_group_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(bcm6348_groups);
+}
+
+static const char *bcm6348_pinctrl_get_group_name(struct pinctrl_dev *pctldev,
+						  unsigned group)
+{
+	return bcm6348_groups[group].name;
+}
+
+static int bcm6348_pinctrl_get_group_pins(struct pinctrl_dev *pctldev,
+					  unsigned group, const unsigned **pins,
+					  unsigned *num_pins)
+{
+	*pins = bcm6348_groups[group].pins;
+	*num_pins = bcm6348_groups[group].num_pins;
+
+	return 0;
+}
+
+static int bcm6348_pinctrl_get_func_count(struct pinctrl_dev *pctldev)
+{
+	return ARRAY_SIZE(bcm6348_funcs);
+}
+
+static const char *bcm6348_pinctrl_get_func_name(struct pinctrl_dev *pctldev,
+						 unsigned selector)
+{
+	return bcm6348_funcs[selector].name;
+}
+
+static int bcm6348_pinctrl_get_groups(struct pinctrl_dev *pctldev,
+				      unsigned selector,
+				      const char * const **groups,
+				      unsigned * const num_groups)
+{
+	*groups = bcm6348_funcs[selector].groups;
+	*num_groups = bcm6348_funcs[selector].num_groups;
+
+	return 0;
+}
+
+static void bcm6348_rmw_mux(struct bcm6348_pinctrl *pctl, u32 mask, u32 val)
+{
+	unsigned long flags;
+	u32 reg;
+
+	spin_lock_irqsave(&pctl->lock, flags);
+
+	reg = __raw_readl(pctl->mode);
+	reg &= ~mask;
+	reg |= val & mask;
+	__raw_writel(reg, pctl->mode);
+
+	spin_unlock_irqrestore(&pctl->lock, flags);
+}
+
+static int bcm6348_pinctrl_set_mux(struct pinctrl_dev *pctldev,
+				   unsigned selector, unsigned group)
+{
+	struct bcm6348_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+	const struct bcm6348_pingroup *grp = &bcm6348_groups[group];
+	const struct bcm6348_function *f = &bcm6348_funcs[selector];
+	u32 mask, val;
+
+	/*
+	 * pins n..(n+7) share the same group, so we only need to look at
+	 * the first pin.
+	 */
+	mask = GROUP_MASK(grp->pins[0]);
+	val = f->value << GROUP_SHIFT(grp->pins[0]);
+
+	bcm6348_rmw_mux(pctl, mask, val);
+
+	return 0;
+}
+
+static struct pinctrl_ops bcm6348_pctl_ops = {
+	.get_groups_count	= bcm6348_pinctrl_get_group_count,
+	.get_group_name		= bcm6348_pinctrl_get_group_name,
+	.get_group_pins		= bcm6348_pinctrl_get_group_pins,
+#ifdef CONFIG_OF
+	.dt_node_to_map		= pinconf_generic_dt_node_to_map_pin,
+	.dt_free_map		= pinctrl_utils_free_map,
+#endif
+};
+
+static struct pinmux_ops bcm6348_pmx_ops = {
+	.get_functions_count	= bcm6348_pinctrl_get_func_count,
+	.get_function_name	= bcm6348_pinctrl_get_func_name,
+	.get_function_groups	= bcm6348_pinctrl_get_groups,
+	.set_mux		= bcm6348_pinctrl_set_mux,
+	.strict			= false,
+};
+
+static int bcm6348_pinctrl_probe(struct platform_device *pdev)
+{
+	struct bcm6348_pinctrl *pctl;
+	struct resource *res;
+	void __iomem *mode;
+
+	res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mode");
+	mode = devm_ioremap_resource(&pdev->dev, res);
+	if (IS_ERR(mode))
+		return PTR_ERR(mode);
+
+	pctl = devm_kzalloc(&pdev->dev, sizeof(*pctl), GFP_KERNEL);
+	if (!pctl)
+		return -ENOMEM;
+
+	spin_lock_init(&pctl->lock);
+
+	pctl->mode = mode;
+
+	/* disable all muxes by default */
+	__raw_writel(0, pctl->mode);
+
+	pctl->desc.name = dev_name(&pdev->dev);
+	pctl->desc.owner = THIS_MODULE;
+	pctl->desc.pctlops = &bcm6348_pctl_ops;
+	pctl->desc.pmxops = &bcm6348_pmx_ops;
+
+	pctl->desc.npins = ARRAY_SIZE(bcm6348_pins);
+	pctl->desc.pins = bcm6348_pins;
+
+	platform_set_drvdata(pdev, pctl);
+
+	pctl->pctldev = bcm63xx_pinctrl_register(pdev, &pctl->desc, pctl,
+						 pctl->gpio, BCM6348_NGPIO);
+	if (IS_ERR(pctl->pctldev))
+		return PTR_ERR(pctl->pctldev);
+
+	return 0;
+}
+
+static const struct of_device_id bcm6348_pinctrl_match[] = {
+	{ .compatible = "brcm,bcm6348-pinctrl", },
+	{ },
+};
+
+static struct platform_driver bcm6348_pinctrl_driver = {
+	.probe = bcm6348_pinctrl_probe,
+	.driver = {
+		.name = "bcm6348-pinctrl",
+		.of_match_table = bcm6348_pinctrl_match,
+	},
+};
+
+builtin_platform_driver(bcm6348_pinctrl_driver);
