[PATCH 09/11] pinctrl: add rockchip pinctrl and gpio drivers

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



Signed-off-by: Beniamino Galvani <b.galvani@xxxxxxxxx>
---
 arch/arm/Kconfig                   |    2 +
 drivers/pinctrl/Kconfig            |    7 +
 drivers/pinctrl/Makefile           |    1 +
 drivers/pinctrl/pinctrl-rockchip.c |  560 ++++++++++++++++++++++++++++++++++++
 4 files changed, 570 insertions(+)
 create mode 100644 drivers/pinctrl/pinctrl-rockchip.c

diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig
index 580faa0..991a411 100644
--- a/arch/arm/Kconfig
+++ b/arch/arm/Kconfig
@@ -132,6 +132,8 @@ config ARCH_ROCKCHIP
 	select COMMON_CLK
 	select CLKDEV_LOOKUP
 	select COMMON_CLK_OF_PROVIDER
+	select GPIOLIB
+	select PINCTRL_ROCKCHIP
 
 config ARCH_SOCFPGA
 	bool "Altera SOCFPGA cyclone5"
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 18adbf7..b9b66f9 100644
--- a/drivers/pinctrl/Kconfig
+++ b/drivers/pinctrl/Kconfig
@@ -24,6 +24,13 @@ config PINCTRL_IMX_IOMUX_V3
 	help
 	  This iomux controller is found on i.MX25,35,51,53,6.
 
+config PINCTRL_ROCKCHIP
+	select PINCTRL
+	select GPIO_GENERIC
+	bool
+	help
+	  The pinmux controller found on Rockchip SoCs.
+
 config PINCTRL_SINGLE
 	select PINCTRL
 	bool "pinctrl single"
diff --git a/drivers/pinctrl/Makefile b/drivers/pinctrl/Makefile
index af705ea..3978c56 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -2,6 +2,7 @@ obj-$(CONFIG_PINCTRL)	+= pinctrl.o
 obj-$(CONFIG_PINCTRL_IMX_IOMUX_V1) += imx-iomux-v1.o
 obj-$(CONFIG_PINCTRL_IMX_IOMUX_V2) += imx-iomux-v2.o
 obj-$(CONFIG_PINCTRL_IMX_IOMUX_V3) += imx-iomux-v3.o
+obj-$(CONFIG_PINCTRL_ROCKCHIP) += pinctrl-rockchip.o
 obj-$(CONFIG_PINCTRL_SINGLE) += pinctrl-single.o
 obj-$(CONFIG_PINCTRL_TEGRA20) += pinctrl-tegra20.o
 obj-$(CONFIG_PINCTRL_TEGRA30) += pinctrl-tegra30.o
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
new file mode 100644
index 0000000..a71fed3
--- /dev/null
+++ b/drivers/pinctrl/pinctrl-rockchip.c
@@ -0,0 +1,560 @@
+/*
+ * Rockchip pinctrl and gpio driver for Barebox
+ *
+ * Copyright (C) 2014 Beniamino Galvani <b.galvani@xxxxxxxxx>
+ *
+ * Based on Linux pinctrl-rockchip:
+ *   Copyright (C) 2013 MundoReader S.L.
+ *   Copyright (C) 2012 Samsung Electronics Co., Ltd.
+ *   Copyright (C) 2012 Linaro Ltd
+ *   Copyright (C) 2011-2012 Jean-Christophe PLAGNIOL-VILLARD
+ *
+ * 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.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <common.h>
+#include <gpio.h>
+#include <init.h>
+#include <malloc.h>
+#include <of.h>
+#include <of_address.h>
+#include <pinctrl.h>
+
+#include <linux/basic_mmio_gpio.h>
+#include <linux/clk.h>
+#include <linux/err.h>
+
+enum rockchip_pinctrl_type {
+	RK2928,
+	RK3066B,
+	RK3188,
+};
+
+enum rockchip_pin_bank_type {
+	COMMON_BANK,
+	RK3188_BANK0,
+};
+
+struct rockchip_pin_bank {
+	void __iomem			*reg_base;
+	void __iomem			*reg_pull;
+	struct clk			*clk;
+	u32				pin_base;
+	u8				nr_pins;
+	char				*name;
+	u8				bank_num;
+	enum rockchip_pin_bank_type	bank_type;
+	bool				valid;
+	struct device_node		*of_node;
+	struct rockchip_pinctrl		*drvdata;
+	struct bgpio_chip		bgpio_chip;
+};
+
+#define PIN_BANK(id, pins, label)			\
+	{						\
+		.bank_num	= id,			\
+		.nr_pins	= pins,			\
+		.name		= label,		\
+	}
+
+struct rockchip_pin_ctrl {
+	struct rockchip_pin_bank	*pin_banks;
+	u32				nr_banks;
+	u32				nr_pins;
+	char				*label;
+	enum rockchip_pinctrl_type	type;
+	int				mux_offset;
+	void	(*pull_calc_reg)(struct rockchip_pin_bank *bank, int pin_num,
+				 void __iomem **reg, u8 *bit);
+};
+
+struct rockchip_pinctrl {
+	void __iomem			*reg_base;
+	void __iomem			*reg_pull;
+	struct pinctrl_device		pctl_dev;
+	struct rockchip_pin_ctrl	*ctrl;
+};
+
+enum {
+	RK_BIAS_DISABLE = 0,
+	RK_BIAS_PULL_UP,
+	RK_BIAS_PULL_DOWN,
+	RK_BIAS_BUS_HOLD,
+};
+
+/* GPIO registers */
+enum {
+	RK_GPIO_SWPORT_DR	= 0x00,
+	RK_GPIO_SWPORT_DDR	= 0x04,
+	RK_GPIO_EXT_PORT	= 0x50,
+};
+
+static int rockchip_gpiolib_register(struct device_d *dev,
+				     struct rockchip_pinctrl *info)
+{
+	struct rockchip_pin_ctrl *ctrl = info->ctrl;
+	struct rockchip_pin_bank *bank = ctrl->pin_banks;
+	void __iomem *reg_base;
+	int ret;
+	int i;
+
+	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+		if (!bank->valid) {
+			dev_warn(dev, "bank %s is not valid\n", bank->name);
+			continue;
+		}
+
+		reg_base = bank->reg_base;
+
+		ret = bgpio_init(&bank->bgpio_chip, dev, 4,
+				 reg_base + RK_GPIO_EXT_PORT,
+				 reg_base + RK_GPIO_SWPORT_DR, NULL,
+				 reg_base + RK_GPIO_SWPORT_DDR, NULL, 0);
+		if (ret)
+			goto fail;
+
+		bank->bgpio_chip.gc.ngpio = bank->nr_pins;
+		ret = gpiochip_add(&bank->bgpio_chip.gc);
+		if (ret) {
+			dev_err(dev, "failed to register gpio_chip %s, error code: %d\n",
+				bank->name, ret);
+			goto fail;
+		}
+
+	}
+
+	return 0;
+fail:
+	for (--i, --bank; i >= 0; --i, --bank) {
+		if (!bank->valid)
+			continue;
+
+		gpiochip_remove(&bank->bgpio_chip.gc);
+	}
+	return ret;
+}
+
+static struct rockchip_pinctrl *to_rockchip_pinctrl(struct pinctrl_device *pdev)
+{
+	return container_of(pdev, struct rockchip_pinctrl, pctl_dev);
+}
+
+static struct rockchip_pin_bank *bank_num_to_bank(struct rockchip_pinctrl *info,
+						  unsigned num)
+{
+	struct rockchip_pin_bank *b = info->ctrl->pin_banks;
+	int i;
+
+	for (i = 0; i < info->ctrl->nr_banks; i++, b++) {
+		if (b->bank_num == num)
+			return b;
+	}
+
+	return ERR_PTR(-EINVAL);
+}
+
+static int parse_bias_config(struct device_node *np)
+{
+	u32 val;
+
+	if (of_property_read_u32(np, "bias-pull-up", &val) != -EINVAL)
+		return RK_BIAS_PULL_UP;
+	else if (of_property_read_u32(np, "bias-pull-down", &val) != -EINVAL)
+		return RK_BIAS_PULL_DOWN;
+	else if (of_property_read_u32(np, "bias-bus-hold", &val) != -EINVAL)
+		return RK_BIAS_BUS_HOLD;
+	else
+		return RK_BIAS_DISABLE;
+}
+
+
+#define RK2928_PULL_OFFSET		0x118
+#define RK2928_PULL_PINS_PER_REG	16
+#define RK2928_PULL_BANK_STRIDE		8
+
+static void rk2928_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, void __iomem **reg,
+					 u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	*reg = info->reg_base + RK2928_PULL_OFFSET;
+	*reg += bank->bank_num * RK2928_PULL_BANK_STRIDE;
+	*reg += (pin_num / RK2928_PULL_PINS_PER_REG) * 4;
+
+	*bit = pin_num % RK2928_PULL_PINS_PER_REG;
+};
+
+#define RK3188_PULL_BITS_PER_PIN	2
+#define RK3188_PULL_PINS_PER_REG	8
+#define RK3188_PULL_BANK_STRIDE		16
+
+static void rk3188_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank,
+					 int pin_num, void __iomem **reg,
+					 u8 *bit)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+
+	/* The first 12 pins of the first bank are located elsewhere */
+	if (bank->bank_type == RK3188_BANK0 && pin_num < 12) {
+		*reg = bank->reg_pull +
+				((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+		*bit = pin_num % RK3188_PULL_PINS_PER_REG;
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	} else {
+		*reg = info->reg_pull - 4;
+		*reg += bank->bank_num * RK3188_PULL_BANK_STRIDE;
+		*reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4);
+
+		/*
+		 * The bits in these registers have an inverse ordering
+		 * with the lowest pin being in bits 15:14 and the highest
+		 * pin in bits 1:0
+		 */
+		*bit = 7 - (pin_num % RK3188_PULL_PINS_PER_REG);
+		*bit *= RK3188_PULL_BITS_PER_PIN;
+	}
+}
+
+static int rockchip_pinctrl_set_func(struct rockchip_pin_bank *bank, int pin,
+				     int mux)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	void __iomem *reg = info->reg_base + info->ctrl->mux_offset;
+	u8 bit;
+	u32 data;
+
+	/* get basic quadruple of mux registers and the correct reg inside */
+	reg += bank->bank_num * 0x10;
+	reg += (pin / 8) * 4;
+	bit = (pin % 8) * 2;
+
+	data = 3 << (bit + 16);
+	data |= (mux & 3) << bit;
+	writel(data, reg);
+
+	return 0;
+}
+
+static int rockchip_pinctrl_set_pull(struct rockchip_pin_bank *bank,
+				     int pin_num, int pull)
+{
+	struct rockchip_pinctrl *info = bank->drvdata;
+	struct rockchip_pin_ctrl *ctrl = info->ctrl;
+	void __iomem *reg;
+	u8 bit;
+	u32 data;
+
+	dev_dbg(info->pctl_dev.dev, "setting pull of GPIO%d-%d to %d\n",
+		 bank->bank_num, pin_num, pull);
+
+	/* rk3066b doesn't support any pulls */
+	if (ctrl->type == RK3066B)
+		return pull ? -EINVAL : 0;
+
+	ctrl->pull_calc_reg(bank, pin_num, &reg, &bit);
+
+	switch (ctrl->type) {
+	case RK2928:
+		data = BIT(bit + 16);
+		if (pull == RK_BIAS_DISABLE)
+			data |= BIT(bit);
+		writel(data, reg);
+		break;
+	case RK3188:
+		data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16);
+		data |= pull << bit;
+		writel(data, reg);
+		break;
+	default:
+		dev_err(info->pctl_dev.dev, "unsupported pinctrl type\n");
+		return -EINVAL;
+	}
+
+	return 0;
+}
+
+static int rockchip_pinctrl_set_state(struct pinctrl_device *pdev,
+				      struct device_node *np)
+{
+	struct rockchip_pinctrl *info = to_rockchip_pinctrl(pdev);
+	const __be32 *list;
+	int i, size;
+	int bank_num, pin_num, func;
+
+	/*
+	 * the binding format is rockchip,pins = <bank pin mux CONFIG>,
+	 * do sanity check and calculate pins number
+	 */
+	list = of_get_property(np, "rockchip,pins", &size);
+	size /= sizeof(*list);
+
+	if (!size || size % 4) {
+		dev_err(pdev->dev, "wrong pins number or pins and configs should be by 4\n");
+		return -EINVAL;
+	}
+
+	for (i = 0; i < size; i += 4) {
+		const __be32 *phandle;
+		struct device_node *np_config;
+		struct rockchip_pin_bank *bank;
+
+		bank_num = be32_to_cpu(*list++);
+		pin_num = be32_to_cpu(*list++);
+		func = be32_to_cpu(*list++);
+		phandle = list++;
+
+		if (!phandle)
+			return -EINVAL;
+
+		np_config = of_find_node_by_phandle(be32_to_cpup(phandle));
+		bank = bank_num_to_bank(info, bank_num);
+		rockchip_pinctrl_set_func(bank, pin_num, func);
+		rockchip_pinctrl_set_pull(bank, pin_num,
+					  parse_bias_config(np_config));
+	}
+
+	return 0;
+}
+
+static struct pinctrl_ops rockchip_pinctrl_ops = {
+	.set_state = rockchip_pinctrl_set_state,
+};
+
+static int rockchip_get_bank_data(struct rockchip_pin_bank *bank,
+				  struct device_d *dev)
+{
+	struct resource node_res, *res;
+
+	if (of_address_to_resource(bank->of_node, 0, &node_res)) {
+		dev_err(dev, "cannot find IO resource for bank\n");
+		return -ENOENT;
+	}
+
+	res = request_iomem_region(dev_name(dev), node_res.start, node_res.end);
+	if (!res) {
+		dev_err(dev, "cannot request iomem region %08x\n",
+			node_res.start);
+		return -ENOENT;
+	}
+
+	bank->reg_base = (void __iomem *)res->start;
+
+	/*
+	 * special case, where parts of the pull setting-registers are
+	 * part of the PMU register space
+	 */
+	if (of_device_is_compatible(bank->of_node,
+				    "rockchip,rk3188-gpio-bank0")) {
+		bank->bank_type = RK3188_BANK0;
+
+		if (of_address_to_resource(bank->of_node, 1, &node_res)) {
+			dev_err(dev, "cannot find IO resource for bank\n");
+			return -ENOENT;
+		}
+
+		res = request_iomem_region(dev_name(dev), node_res.start,
+					   node_res.end);
+		if (!res) {
+			dev_err(dev, "cannot request iomem region %08x\n",
+				node_res.start);
+			return -ENOENT;
+		}
+
+		bank->reg_pull = (void __iomem *)res->start;
+	} else {
+		bank->bank_type = COMMON_BANK;
+	}
+
+	bank->clk = of_clk_get(bank->of_node, 0);
+	if (IS_ERR(bank->clk))
+		return PTR_ERR(bank->clk);
+
+	return clk_enable(bank->clk);
+}
+
+static struct of_device_id rockchip_pinctrl_dt_match[];
+
+static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data(
+	struct rockchip_pinctrl *d, struct device_d *dev)
+{
+	const struct of_device_id *match;
+	struct device_node *node = dev->device_node;
+	struct device_node *np;
+	struct rockchip_pin_ctrl *ctrl;
+	struct rockchip_pin_bank *bank;
+	char *name;
+	int i;
+
+	match = of_match_node(rockchip_pinctrl_dt_match, node);
+	ctrl = (struct rockchip_pin_ctrl *)match->data;
+
+	for_each_child_of_node(node, np) {
+		if (!of_find_property(np, "gpio-controller", NULL))
+			continue;
+
+		bank = ctrl->pin_banks;
+		for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+			name = bank->name;
+			if (!strncmp(name, np->name, strlen(name))) {
+				bank->of_node = np;
+				if (!rockchip_get_bank_data(bank, dev))
+					bank->valid = true;
+
+				break;
+			}
+		}
+	}
+
+	bank = ctrl->pin_banks;
+	for (i = 0; i < ctrl->nr_banks; ++i, ++bank) {
+		bank->drvdata = d;
+		bank->pin_base = ctrl->nr_pins;
+		ctrl->nr_pins += bank->nr_pins;
+	}
+
+	return ctrl;
+}
+
+static int rockchip_pinctrl_probe(struct device_d *dev)
+{
+	struct rockchip_pinctrl *info;
+	struct rockchip_pin_ctrl *ctrl;
+	int ret;
+
+	info = xzalloc(sizeof(struct rockchip_pinctrl));
+	if (!info)
+		return -ENOMEM;
+
+	ctrl = rockchip_pinctrl_get_soc_data(info, dev);
+	if (!ctrl) {
+		dev_err(dev, "driver data not available\n");
+		return -EINVAL;
+	}
+	info->ctrl = ctrl;
+
+	info->reg_base = dev_request_mem_region(dev, 0);
+	if (!info->reg_base) {
+		dev_err(dev, "Could not get reg_base region\n");
+		return -ENODEV;
+	}
+
+	/* The RK3188 has its pull registers in a separate place */
+	if (ctrl->type == RK3188) {
+		info->reg_pull = dev_request_mem_region(dev, 1);
+		if (!info->reg_pull) {
+			dev_err(dev, "Could not get reg_pull region\n");
+			return -ENODEV;
+		}
+	}
+
+	info->pctl_dev.dev = dev;
+	info->pctl_dev.ops = &rockchip_pinctrl_ops;
+
+	ret = rockchip_gpiolib_register(dev, info);
+	if (ret)
+		return ret;
+
+	ret = pinctrl_register(&info->pctl_dev);
+	if (ret)
+		return ret;
+
+	return 0;
+}
+
+static struct rockchip_pin_bank rk2928_pin_banks[] = {
+	PIN_BANK(0, 32, "gpio0"),
+	PIN_BANK(1, 32, "gpio1"),
+	PIN_BANK(2, 32, "gpio2"),
+	PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk2928_pin_ctrl = {
+	.pin_banks	= rk2928_pin_banks,
+	.nr_banks	= ARRAY_SIZE(rk2928_pin_banks),
+	.type		= RK2928,
+	.mux_offset	= 0xa8,
+	.pull_calc_reg	= rk2928_calc_pull_reg_and_bit,
+};
+
+static struct rockchip_pin_bank rk3066a_pin_banks[] = {
+	PIN_BANK(0, 32, "gpio0"),
+	PIN_BANK(1, 32, "gpio1"),
+	PIN_BANK(2, 32, "gpio2"),
+	PIN_BANK(3, 32, "gpio3"),
+	PIN_BANK(4, 32, "gpio4"),
+	PIN_BANK(6, 16, "gpio6"),
+};
+
+static struct rockchip_pin_ctrl rk3066a_pin_ctrl = {
+	.pin_banks	= rk3066a_pin_banks,
+	.nr_banks	= ARRAY_SIZE(rk3066a_pin_banks),
+	.type		= RK2928,
+	.mux_offset	= 0xa8,
+	.pull_calc_reg	= rk2928_calc_pull_reg_and_bit,
+};
+
+static struct rockchip_pin_bank rk3066b_pin_banks[] = {
+	PIN_BANK(0, 32, "gpio0"),
+	PIN_BANK(1, 32, "gpio1"),
+	PIN_BANK(2, 32, "gpio2"),
+	PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk3066b_pin_ctrl = {
+	.pin_banks	= rk3066b_pin_banks,
+	.nr_banks	= ARRAY_SIZE(rk3066b_pin_banks),
+	.type		= RK3066B,
+	.mux_offset	= 0x60,
+};
+
+static struct rockchip_pin_bank rk3188_pin_banks[] = {
+	PIN_BANK(0, 32, "gpio0"),
+	PIN_BANK(1, 32, "gpio1"),
+	PIN_BANK(2, 32, "gpio2"),
+	PIN_BANK(3, 32, "gpio3"),
+};
+
+static struct rockchip_pin_ctrl rk3188_pin_ctrl = {
+	.pin_banks	= rk3188_pin_banks,
+	.nr_banks	= ARRAY_SIZE(rk3188_pin_banks),
+	.type		= RK3188,
+	.mux_offset	= 0x60,
+	.pull_calc_reg	= rk3188_calc_pull_reg_and_bit,
+};
+
+static struct of_device_id rockchip_pinctrl_dt_match[] = {
+	{
+		.compatible = "rockchip,rk2928-pinctrl",
+		.data = (long)&rk2928_pin_ctrl,
+	},
+	{
+		.compatible = "rockchip,rk3066a-pinctrl",
+		.data = (long)&rk3066a_pin_ctrl,
+	},
+	{
+		.compatible = "rockchip,rk3066b-pinctrl",
+		.data = (long)&rk3066b_pin_ctrl,
+	},
+	{
+		.compatible = "rockchip,rk3188-pinctrl",
+		.data = (long)&rk3188_pin_ctrl,
+	}, {
+		/* sentinel */
+	}
+};
+
+static struct driver_d rockchip_pinctrl_driver = {
+	.name = "rockchip-pinctrl",
+	.probe = rockchip_pinctrl_probe,
+	.of_compatible = DRV_OF_COMPAT(rockchip_pinctrl_dt_match),
+};
+
+console_platform_driver(rockchip_pinctrl_driver);
-- 
1.7.10.4


_______________________________________________
barebox mailing list
barebox@xxxxxxxxxxxxxxxxxxx
http://lists.infradead.org/mailman/listinfo/barebox




[Index of Archives]     [Linux Embedded]     [Linux USB Devel]     [Linux Audio Users]     [Yosemite News]     [Linux Kernel]     [Linux SCSI]     [XFree86]

  Powered by Linux