summaryrefslogtreecommitdiffstats
path: root/drivers/pinctrl
diff options
context:
space:
mode:
authorBeniamino Galvani <b.galvani@gmail.com>2014-04-27 11:30:42 +0200
committerSascha Hauer <s.hauer@pengutronix.de>2014-04-29 08:15:41 +0200
commitb28e8d7e61c7faf57aa2db3b3c0ec681e7ff05d6 (patch)
tree18aa08f8d7c7d70f93562ba9970c86477c3bdaae /drivers/pinctrl
parentaf4c8a0128c9b240367a0f36270d53d99be57cfc (diff)
downloadbarebox-b28e8d7e61c7faf57aa2db3b3c0ec681e7ff05d6.tar.gz
barebox-b28e8d7e61c7faf57aa2db3b3c0ec681e7ff05d6.tar.xz
pinctrl: add rockchip pinctrl and gpio drivers
Signed-off-by: Beniamino Galvani <b.galvani@gmail.com> Signed-off-by: Sascha Hauer <s.hauer@pengutronix.de>
Diffstat (limited to 'drivers/pinctrl')
-rw-r--r--drivers/pinctrl/Kconfig7
-rw-r--r--drivers/pinctrl/Makefile1
-rw-r--r--drivers/pinctrl/pinctrl-rockchip.c560
3 files changed, 568 insertions, 0 deletions
diff --git a/drivers/pinctrl/Kconfig b/drivers/pinctrl/Kconfig
index 7390971ea3..906e33af8f 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 b3b0fa9c58..9a5d0ba97e 100644
--- a/drivers/pinctrl/Makefile
+++ b/drivers/pinctrl/Makefile
@@ -2,5 +2,6 @@ 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
diff --git a/drivers/pinctrl/pinctrl-rockchip.c b/drivers/pinctrl/pinctrl-rockchip.c
new file mode 100644
index 0000000000..a71fed3259
--- /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@gmail.com>
+ *
+ * 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);