diff --git a/arch/arm/Kconfig b/arch/arm/Kconfig index 580faa063..991a4111e 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 7390971ea..906e33af8 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 b3b0fa9c5..9a5d0ba97 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 000000000..a71fed325 --- /dev/null +++ b/drivers/pinctrl/pinctrl-rockchip.c @@ -0,0 +1,560 @@ +/* + * Rockchip pinctrl and gpio driver for Barebox + * + * Copyright (C) 2014 Beniamino Galvani + * + * 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 +#include +#include +#include +#include +#include +#include + +#include +#include +#include + +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, ®, &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 = , + * 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);