From: Huang, Tao Date: Wed, 11 Nov 2015 07:12:54 +0000 (+0800) Subject: rk: rename pinctrl-rk3368.c to pinctrl-rk.c X-Git-Tag: firefly_0821_release~3613 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=2bfcbd9d8232f8a8a9a8aaac8d83afb4a38716a6;p=firefly-linux-kernel-4.4.55.git rk: rename pinctrl-rk3368.c to pinctrl-rk.c Signed-off-by: Huang, Tao --- diff --git a/drivers/pinctrl/pinctrl-rk.c b/drivers/pinctrl/pinctrl-rk.c new file mode 100755 index 000000000000..9712b1d12e72 --- /dev/null +++ b/drivers/pinctrl/pinctrl-rk.c @@ -0,0 +1,2136 @@ +/* + * Pinctrl driver for Rockchip SoCs + * + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner + * + * With some ideas taken from pinctrl-samsung: + * Copyright (c) 2012 Samsung Electronics Co., Ltd. + * http://www.samsung.com + * Copyright (c) 2012 Linaro Ltd + * http://www.linaro.org + * + * and pinctrl-at91: + * 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 +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include "core.h" +#include "pinconf.h" + + +#if 0 +#define pinctrl_dbg(dev, format, arg...) \ + dev_printk(KERN_INFO , dev , format , ## arg) +#else + #define pinctrl_dbg(dev, format, arg...) +#endif + + +/* GPIO control registers */ +#define GPIO_SWPORT_DR 0x00 +#define GPIO_SWPORT_DDR 0x04 +#define GPIO_INTEN 0x30 +#define GPIO_INTMASK 0x34 +#define GPIO_INTTYPE_LEVEL 0x38 +#define GPIO_INT_POLARITY 0x3c +#define GPIO_INT_STATUS 0x40 +#define GPIO_INT_RAWSTATUS 0x44 +#define GPIO_DEBOUNCE 0x48 +#define GPIO_PORTS_EOI 0x4c +#define GPIO_EXT_PORT 0x50 +#define GPIO_LS_SYNC 0x60 + +enum rockchip_pinctrl_type { + RK2928, + RK3066B, + RK3188, + RK3228, + RK3288, + RK3368, +}; + +/** + * Encode variants of iomux registers into a type variable + */ +#define IOMUX_GPIO_ONLY BIT(0) +#define IOMUX_WIDTH_4BIT BIT(1) +#define IOMUX_SOURCE_PMU BIT(2) +#define IOMUX_UNROUTED BIT(3) + +/** + * @type: iomux variant using IOMUX_* constants + * @offset: if initialized to -1 it will be autocalculated, by specifying + * an initial offset value the relevant source offset can be reset + * to a new value for autocalculating the following iomux registers. + */ +struct rockchip_iomux { + int type; + int offset; +}; + +/** + * @reg_base: register base of the gpio bank + * @reg_pull: optional separate register for additional pull settings + * @clk: clock of the gpio bank + * @irq: interrupt of the gpio bank + * @pin_base: first pin number + * @nr_pins: number of pins in this bank + * @name: name of the bank + * @bank_num: number of the bank, to account for holes + * @iomux: array describing the 4 iomux sources of the bank + * @valid: are all necessary informations present + * @of_node: dt node of this bank + * @drvdata: common pinctrl basedata + * @domain: irqdomain of the gpio bank + * @gpio_chip: gpiolib chip + * @grange: gpio range + * @slock: spinlock for the gpio bank + */ +struct rockchip_pin_bank { + void __iomem *reg_base; + struct regmap *regmap_pull; + struct clk *clk; + int irq; + u32 pin_base; + u8 nr_pins; + char *name; + u8 bank_num; + struct rockchip_iomux iomux[4]; + bool valid; + struct device_node *of_node; + struct rockchip_pinctrl *drvdata; + struct irq_domain *domain; + struct gpio_chip gpio_chip; + struct pinctrl_gpio_range grange; + /*spinlock for the gpio bank*/ + spinlock_t slock; + u32 toggle_edge_mode; + u32 suspend_wakeup; + u32 saved_wakeup; +}; + +#define PIN_BANK(id, pins, label) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + { .offset = -1 }, \ + }, \ + } + +#define PIN_BANK_IOMUX_FLAGS(id, pins, label, iom0, iom1, iom2, iom3) \ + { \ + .bank_num = id, \ + .nr_pins = pins, \ + .name = label, \ + .iomux = { \ + { .type = iom0, .offset = -1 }, \ + { .type = iom1, .offset = -1 }, \ + { .type = iom2, .offset = -1 }, \ + { .type = iom3, .offset = -1 }, \ + }, \ + } + +/** + */ +struct rockchip_pin_ctrl { + struct rockchip_pin_bank *pin_banks; + u32 nr_banks; + u32 nr_pins; + char *label; + enum rockchip_pinctrl_type type; + int grf_mux_offset; + int pmu_mux_offset; + + void (*pull_calc_reg)(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit); +}; + +struct rockchip_pin_config { + unsigned int func; + unsigned long *configs; + unsigned int nconfigs; +}; + +/** + * struct rockchip_pin_group: represent group of pins of a pinmux function. + * @name: name of the pin group, used to lookup the group. + * @pins: the pins included in this group. + * @npins: number of pins included in this group. + * @func: the mux function number to be programmed when selected. + * @configs: the config values to be set for each pin + * @nconfigs: number of configs for each pin + */ +struct rockchip_pin_group { + const char *name; + unsigned int npins; + unsigned int *pins; + struct rockchip_pin_config *data; +}; + +/** + * struct rockchip_pmx_func: represent a pin function. + * @name: name of the pin function, used to lookup the function. + * @groups: one or more names of pin groups that provide this function. + * @num_groups: number of groups included in @groups. + */ +struct rockchip_pmx_func { + const char *name; + const char **groups; + u8 ngroups; +}; + +struct rockchip_pinctrl { + struct regmap *regmap_base; + int reg_size; + struct regmap *regmap_pull; + struct regmap *regmap_pmu; + struct device *dev; + struct rockchip_pin_ctrl *ctrl; + struct pinctrl_desc pctl; + struct pinctrl_dev *pctl_dev; + struct rockchip_pin_group *groups; + unsigned int ngroups; + struct rockchip_pmx_func *functions; + unsigned int nfunctions; +}; + +static struct regmap_config rockchip_regmap_config = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, +}; +static struct rockchip_pinctrl *g_info; + +static inline struct rockchip_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) +{ + return container_of(gc, struct rockchip_pin_bank, gpio_chip); +} + +static const inline struct rockchip_pin_group *pinctrl_name_to_group( + const struct rockchip_pinctrl *info, + const char *name) +{ + int i; + + for (i = 0; i < info->ngroups; i++) { + if (!strcmp(info->groups[i].name, name)) + return &info->groups[i]; + } + + return NULL; +} + +/* + * given a pin number that is local to a pin controller, find out the pin bank + * and the register base of the pin bank. + */ +static struct rockchip_pin_bank *pin_to_bank(struct rockchip_pinctrl *info, + unsigned pin) +{ + struct rockchip_pin_bank *b = info->ctrl->pin_banks; + + while (pin >= (b->pin_base + b->nr_pins)) + b++; + + return b; +} + +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); +} + +/* + * Pinctrl_ops handling + */ + +static int rockchip_get_groups_count(struct pinctrl_dev *pctldev) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->ngroups; +} + +static const char *rockchip_get_group_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->groups[selector].name; +} + +static int rockchip_get_group_pins(struct pinctrl_dev *pctldev, + unsigned selector, const unsigned **pins, + unsigned *npins) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + if (selector >= info->ngroups) + return -EINVAL; + + *pins = info->groups[selector].pins; + *npins = info->groups[selector].npins; + + return 0; +} + +static int rockchip_dt_node_to_map(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, unsigned *num_maps) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const struct rockchip_pin_group *grp; + struct pinctrl_map *new_map; + struct device_node *parent; + int map_num = 1; + int i; + + /* + * first find the group of this node and check if we need to create + * config maps for pins + */ + grp = pinctrl_name_to_group(info, np->name); + if (!grp) { + dev_err(info->dev, "unable to find group for node %s\n", + np->name); + return -EINVAL; + } + + map_num += grp->npins; + new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num, + GFP_KERNEL); + if (!new_map) + return -ENOMEM; + + *map = new_map; + *num_maps = map_num; + + /* create mux map */ + parent = of_get_parent(np); + if (!parent) { + devm_kfree(pctldev->dev, new_map); + return -EINVAL; + } + new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; + new_map[0].data.mux.function = parent->name; + new_map[0].data.mux.group = np->name; + of_node_put(parent); + + /* create config map */ + new_map++; + for (i = 0; i < grp->npins; i++) { + new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN; + new_map[i].data.configs.group_or_pin = + pin_get_name(pctldev, grp->pins[i]); + new_map[i].data.configs.configs = grp->data[i].configs; + new_map[i].data.configs.num_configs = grp->data[i].nconfigs; + } + + pinctrl_dbg(pctldev->dev, "maps: function %s group %s num %d\n", + (*map)->data.mux.function, (*map)->data.mux.group, map_num); + + return 0; +} + +static void rockchip_dt_free_map(struct pinctrl_dev *pctldev, + struct pinctrl_map *map, unsigned num_maps) +{ +} + +static const struct pinctrl_ops rockchip_pctrl_ops = { + .get_groups_count = rockchip_get_groups_count, + .get_group_name = rockchip_get_group_name, + .get_group_pins = rockchip_get_group_pins, + .dt_node_to_map = rockchip_dt_node_to_map, + .dt_free_map = rockchip_dt_free_map, +}; + +/* + * Hardware access + */ + +static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin) +{ + struct rockchip_pinctrl *info = bank->drvdata; + int iomux_num = (pin / 8); + struct regmap *regmap; + unsigned int val; + int reg, ret, mask; + u8 bit; + + if (iomux_num > 3) + return -EINVAL; + + if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) { + dev_err(info->dev, "pin %d is unrouted\n", pin); + return -EINVAL; + } + + if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) + return RK_FUNC_GPIO; + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? info->regmap_pmu : info->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mask = (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) ? 0xf : 0x3; + reg = bank->iomux[iomux_num].offset; + if (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) { + if ((pin % 8) >= 4) + reg += 0x4; + bit = (pin % 4) * 4; + } else { + bit = (pin % 8) * 2; + } + + ret = regmap_read(regmap, reg, &val); + if (ret) + return ret; + + return ((val >> bit) & mask); +} + +/* + * Set a new mux function for a pin. + * + * The register is divided into the upper and lower 16 bit. When changing + * a value, the previous register value is not read and changed. Instead + * it seems the changed bits are marked in the upper 16 bit, while the + * changed value gets set in the same offset in the lower 16 bit. + * All pin settings seem to be 2 bit wide in both the upper and lower + * parts. + * @bank: pin bank to change + * @pin: pin to change + * @mux: new mux function to set + */ +static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) +{ + struct rockchip_pinctrl *info = bank->drvdata; + int iomux_num = (pin / 8); + struct regmap *regmap; + int reg, ret, mask; + unsigned long flags; + u8 bit; + u32 data, rmask; + + if (iomux_num > 3) + return -EINVAL; + + if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) { + dev_err(info->dev, "pin %d is unrouted\n", pin); + return -EINVAL; + } + + if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) { + if (mux != RK_FUNC_GPIO) { + dev_err(info->dev, + "pin %d only supports a gpio mux\n", pin); + return -ENOTSUPP; + } else { + return 0; + } + } + + pinctrl_dbg(info->dev, "setting mux of GPIO%d-%d to %d\n", + bank->bank_num, pin, mux); + + regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) + ? info->regmap_pmu : info->regmap_base; + + /* get basic quadrupel of mux registers and the correct reg inside */ + mask = (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) ? 0xf : 0x3; + reg = bank->iomux[iomux_num].offset; + if (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) { + if ((pin % 8) >= 4) + reg += 0x4; + bit = (pin % 4) * 4; + } else { + bit = (pin % 8) * 2; + } + + spin_lock_irqsave(&bank->slock, flags); + + data = (mask << (bit + 16)); + rmask = data | (data >> 16); + data |= (mux & mask) << bit; + ret = regmap_update_bits(regmap, reg, rmask, data); + + spin_unlock_irqrestore(&bank->slock, flags); + + return ret; +} + +#define RK3188_PULL_BITS_PER_PIN 2 +#define RK3188_PULL_PINS_PER_REG 8 +#define RK3188_PULL_BANK_STRIDE 16 +#define RK3188_PULL_PMU_OFFSET 0x64 + +#define RK3228_PULL_PMU_OFFSET 0x100 +#define RK3228_PULL_OFFSET 0x110 + +#define RK3288_PULL_OFFSET 0x140 +#define RK3368_PULL_PMU_OFFSET 0x10 +#define RK3368_PULL_OFFSET 0x100 + +static void rk3288_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + + /* The first 24 pins of the first bank are located in PMU */ + if (bank->bank_num == 0) { + if (ctrl->type == RK3228) { + *regmap = info->regmap_base; + *reg = RK3228_PULL_PMU_OFFSET; + } else if (ctrl->type == RK3288) { + *regmap = info->regmap_pmu; + *reg = RK3188_PULL_PMU_OFFSET; + } else if (ctrl->type == RK3368) { + *regmap = info->regmap_pmu; + *reg = RK3368_PULL_PMU_OFFSET; + } + + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + *bit = pin_num % RK3188_PULL_PINS_PER_REG; + *bit *= RK3188_PULL_BITS_PER_PIN; + } else { + *regmap = info->regmap_base; + if (ctrl->type == RK3228) + *reg = RK3228_PULL_OFFSET; + else if (ctrl->type == RK3288) + *reg = RK3288_PULL_OFFSET; + else if (ctrl->type == RK3368) + *reg = RK3368_PULL_OFFSET; + + /* correct the offset, as we're starting with the 2nd bank */ + *reg -= 0x10; + *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; + *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); + + *bit = (pin_num % RK3188_PULL_PINS_PER_REG); + *bit *= RK3188_PULL_BITS_PER_PIN; + } +} + +#define RK3228_DRV_PMU_OFFSET 0x200 +#define RK3228_DRV_GRF_OFFSET 0x210 + +#define RK3288_DRV_PMU_OFFSET 0x70 +#define RK3288_DRV_GRF_OFFSET 0x1c0 +#define RK3288_DRV_BITS_PER_PIN 2 +#define RK3288_DRV_PINS_PER_REG 8 +#define RK3288_DRV_BANK_STRIDE 16 +static int rk3288_drv_list[] = { 2, 4, 8, 12 }; + +#define RK3368_DRV_PMU_OFFSET 0x20 +#define RK3368_DRV_GRF_OFFSET 0x200 + +static void rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, + int pin_num, struct regmap **regmap, + int *reg, u8 *bit) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + + /* The first 24 pins of the first bank are located in PMU */ + if (bank->bank_num == 0) { + if (ctrl->type == RK3228) { + *regmap = info->regmap_base; + *reg = RK3228_DRV_PMU_OFFSET; + } else if (ctrl->type == RK3288) { + *regmap = info->regmap_pmu; + *reg = RK3288_DRV_PMU_OFFSET; + } else if (ctrl->type == RK3368) { + *regmap = info->regmap_pmu; + *reg = RK3368_DRV_PMU_OFFSET; + } + + *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4); + *bit = pin_num % RK3288_DRV_PINS_PER_REG; + *bit *= RK3288_DRV_BITS_PER_PIN; + } else { + *regmap = info->regmap_base; + if (ctrl->type == RK3228) + *reg = RK3228_DRV_GRF_OFFSET; + else if (ctrl->type == RK3288) + *reg = RK3288_DRV_GRF_OFFSET; + else if (ctrl->type == RK3368) + *reg = RK3368_DRV_GRF_OFFSET; + + /* correct the offset, as we're starting with the 2nd bank */ + *reg -= 0x10; + *reg += bank->bank_num * RK3288_DRV_BANK_STRIDE; + *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4); + + *bit = (pin_num % RK3288_DRV_PINS_PER_REG); + *bit *= RK3288_DRV_BITS_PER_PIN; + } +} + +static int rk3288_get_drive(struct rockchip_pin_bank *bank, int pin_num) +{ + struct regmap *regmap; + int reg, ret; + u32 data; + u8 bit; + + rk3288_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + ret = regmap_read(regmap, reg, &data); + if (ret) + return ret; + + data >>= bit; + data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1; + + return rk3288_drv_list[data]; +} + +static int rk3288_set_drive(struct rockchip_pin_bank *bank, int pin_num, + int strength) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct regmap *regmap; + unsigned long flags; + int reg, ret, i; + u32 data, rmask; + u8 bit; + + rk3288_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); + + ret = -EINVAL; + for (i = 0; i < ARRAY_SIZE(rk3288_drv_list); i++) { + if (rk3288_drv_list[i] == strength) { + ret = i; + break; + } + } + + if (ret < 0) { + dev_err(info->dev, "unsupported driver strength %d\n", + strength); + return ret; + } + + spin_lock_irqsave(&bank->slock, flags); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + data |= (ret << bit); + + ret = regmap_update_bits(regmap, reg, rmask, data); + spin_unlock_irqrestore(&bank->slock, flags); + + return ret; +} + +static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) +{ + struct rockchip_pinctrl *info = bank->drvdata; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct regmap *regmap; + int reg, ret; + u8 bit; + u32 data; + + /* rk3066b does support any pulls */ + if (ctrl->type == RK3066B) + return PIN_CONFIG_BIAS_DISABLE; + + ctrl->pull_calc_reg(bank, pin_num, ®map, ®, &bit); + + ret = regmap_read(regmap, reg, &data); + if (ret) + return ret; + + switch (ctrl->type) { + case RK2928: + return !(data & BIT(bit)) + ? PIN_CONFIG_BIAS_PULL_PIN_DEFAULT + : PIN_CONFIG_BIAS_DISABLE; + case RK3188: + case RK3228: + case RK3288: + case RK3368: + data >>= bit; + data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1; + + switch (data) { + case 0: + return PIN_CONFIG_BIAS_DISABLE; + case 1: + return PIN_CONFIG_BIAS_PULL_UP; + case 2: + return PIN_CONFIG_BIAS_PULL_DOWN; + case 3: + return PIN_CONFIG_BIAS_BUS_HOLD; + } + + dev_err(info->dev, "unknown pull setting\n"); + return -EIO; + default: + dev_err(info->dev, "unsupported pinctrl type\n"); + return -EINVAL; + }; +} + +static int rockchip_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; + struct regmap *regmap; + int reg, ret; + unsigned long flags; + u8 bit; + u32 data, rmask; + + pinctrl_dbg(info->dev, "setting pull of GPIO%d-%d to %d\n", + bank->bank_num, pin_num, pull); + + /* rk3066b does support any pulls */ + if (ctrl->type == RK3066B) + return pull ? -EINVAL : 0; + + ctrl->pull_calc_reg(bank, pin_num, ®map, ®, &bit); + + switch (ctrl->type) { + case RK2928: + spin_lock_irqsave(&bank->slock, flags); + + data = BIT(bit + 16); + if (pull == PIN_CONFIG_BIAS_DISABLE) + data |= BIT(bit); + ret = regmap_write(regmap, reg, data); + + spin_unlock_irqrestore(&bank->slock, flags); + break; + case RK3188: + case RK3228: + case RK3288: + case RK3368: + spin_lock_irqsave(&bank->slock, flags); + + /* enable the write to the equivalent lower bits */ + data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16); + rmask = data | (data >> 16); + + switch (pull) { + case PIN_CONFIG_BIAS_DISABLE: + break; + case PIN_CONFIG_BIAS_PULL_UP: + data |= (1 << bit); + break; + case PIN_CONFIG_BIAS_PULL_DOWN: + data |= (2 << bit); + break; + case PIN_CONFIG_BIAS_BUS_HOLD: + data |= (3 << bit); + break; + default: + spin_unlock_irqrestore(&bank->slock, flags); + dev_err(info->dev, "unsupported pull setting %d\n", + pull); + return -EINVAL; + } + + ret = regmap_update_bits(regmap, reg, rmask, data); + + spin_unlock_irqrestore(&bank->slock, flags); + break; + default: + dev_err(info->dev, "unsupported pinctrl type\n"); + return -EINVAL; + } + + return ret; +} + +/* + * Pinmux_ops handling + */ + +static int rockchip_pmx_get_funcs_count(struct pinctrl_dev *pctldev) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->nfunctions; +} + +static const char *rockchip_pmx_get_func_name(struct pinctrl_dev *pctldev, + unsigned selector) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + return info->functions[selector].name; +} + +static int rockchip_pmx_get_groups(struct pinctrl_dev *pctldev, + unsigned selector, + const char * const **groups, + unsigned * const num_groups) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + + *groups = info->functions[selector].groups; + *num_groups = info->functions[selector].ngroups; + + return 0; +} + +static int rockchip_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, + unsigned group) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins = info->groups[group].pins; + const struct rockchip_pin_config *data = info->groups[group].data; + struct rockchip_pin_bank *bank; + int cnt, ret = 0; + + pinctrl_dbg(info->dev, "enable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + /* + * for each pin in the pin group selected, program the correspoding pin + * pin function number in the config register. + */ + for (cnt = 0; cnt < info->groups[group].npins; cnt++) { + bank = pin_to_bank(info, pins[cnt]); + ret = rockchip_set_mux(bank, pins[cnt] - bank->pin_base, + data[cnt].func); + if (ret) + break; + } + + if (ret) { + /* revert the already done pin settings */ + for (cnt--; cnt >= 0; cnt--) + rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0); + + return ret; + } + + return 0; +} + +static void rockchip_pmx_disable(struct pinctrl_dev *pctldev, + unsigned selector, unsigned group) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + const unsigned int *pins = info->groups[group].pins; + struct rockchip_pin_bank *bank; + int cnt; + + pinctrl_dbg(info->dev, "disable function %s group %s\n", + info->functions[selector].name, info->groups[group].name); + + for (cnt = 0; cnt < info->groups[group].npins; cnt++) { + bank = pin_to_bank(info, pins[cnt]); + rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0); + } +} + +/* + * The calls to gpio_direction_output() and gpio_direction_input() + * leads to this function call (via the pinctrl_gpio_direction_{input|output}() + * function called from the gpiolib interface). + */ +static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, + unsigned offset, bool input) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank; + struct gpio_chip *chip; + int pin, ret; + u32 data; + + chip = range->gc; + bank = gc_to_pin_bank(chip); + pin = offset - chip->base; + + dev_dbg(info->dev, "gpio_direction for pin %u as %s-%d to %s\n", + offset, range->name, pin, input ? "input" : "output"); + + ret = rockchip_set_mux(bank, pin, RK_FUNC_GPIO); + if (ret < 0) + return ret; + + data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); + /* set bit to 1 for output, 0 for input */ + if (!input) + data |= BIT(pin); + else + data &= ~BIT(pin); + writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); + + return 0; +} + +static const struct pinmux_ops rockchip_pmx_ops = { + .get_functions_count = rockchip_pmx_get_funcs_count, + .get_function_name = rockchip_pmx_get_func_name, + .get_function_groups = rockchip_pmx_get_groups, + .enable = rockchip_pmx_enable, + .disable = rockchip_pmx_disable, + .gpio_set_direction = rockchip_pmx_gpio_set_direction, +}; + +/* + * Pinconf_ops handling + */ + +static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, + enum pin_config_param pull) +{ + switch (ctrl->type) { + case RK2928: + return (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT || + pull == PIN_CONFIG_BIAS_DISABLE); + case RK3066B: + return pull ? false : true; + case RK3188: + case RK3228: + case RK3288: + case RK3368: + return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT); + } + + return false; +} + +static int rockchip_gpio_direction_output( + struct gpio_chip *gc, unsigned offset, int value); +static int rockchip_gpio_direction_input( + struct gpio_chip *gc, unsigned offset); +static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset); + +/* set the pin config settings for a specified pin */ +static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long configs) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank = pin_to_bank(info, pin); + enum pin_config_param param; + u16 arg; + int rc; + + param = pinconf_to_config_param(configs); + arg = pinconf_to_config_argument(configs); + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + rc = rockchip_set_pull(bank, pin - bank->pin_base, + param); + if (rc) + return rc; + break; + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_BUS_HOLD: + if (!rockchip_pinconf_pull_valid(info->ctrl, param)) + return -ENOTSUPP; + + if (!arg) + return -EINVAL; + + rc = rockchip_set_pull(bank, pin - bank->pin_base, + param); + if (rc) + return rc; + break; + case PIN_CONFIG_OUTPUT: + rc = rockchip_gpio_direction_output( + &bank->gpio_chip, + pin - bank->pin_base, + arg); + if (rc) + return rc; + break; + + case PIN_CONFIG_INPUT_ENABLE: + if (arg == 1) { + rc = rockchip_gpio_direction_input( + &bank->gpio_chip, pin - bank->pin_base); + if (rc) + return rc; + } + break; + + case PIN_CONFIG_DRIVE_STRENGTH: + /* rk3228 rk3288 rk3368 is the first + with per-pin drive-strength */ + if ((RK3228 != info->ctrl->type) && + (RK3288 != info->ctrl->type) && + (RK3368 != info->ctrl->type)) + return -ENOTSUPP; + + rc = rk3288_set_drive(bank, pin - bank->pin_base, arg); + if (rc < 0) + return rc; + break; + default: + return -ENOTSUPP; + break; + } + + return 0; +} + +/* get the pin config settings for a specified pin */ +static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, + unsigned long *config) +{ + struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); + struct rockchip_pin_bank *bank = pin_to_bank(info, pin); + enum pin_config_param param = pinconf_to_config_param(*config); + u16 arg; + int rc; + + switch (param) { + case PIN_CONFIG_BIAS_DISABLE: + if (rockchip_get_pull(bank, pin - bank->pin_base) != param) + return -EINVAL; + + arg = 0; + break; + case PIN_CONFIG_BIAS_PULL_UP: + case PIN_CONFIG_BIAS_PULL_DOWN: + case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: + case PIN_CONFIG_BIAS_BUS_HOLD: + if (!rockchip_pinconf_pull_valid(info->ctrl, param)) + return -ENOTSUPP; + + if (rockchip_get_pull(bank, pin - bank->pin_base) != param) + return -EINVAL; + + arg = 1; + break; + case PIN_CONFIG_OUTPUT: + rc = rockchip_get_mux(bank, pin - bank->pin_base); + if (rc != RK_FUNC_GPIO) + return -EINVAL; + + rc = rockchip_gpio_get(&bank->gpio_chip, pin - bank->pin_base); + if (rc < 0) + return rc; + + arg = rc ? 1 : 0; + break; + case PIN_CONFIG_DRIVE_STRENGTH: + /* rk3228 rk3288 RK3368 is the first with per-pin + * drive-strength + */ + if ((RK3228 != info->ctrl->type) && + (RK3288 != info->ctrl->type) && + (RK3368 != info->ctrl->type)) + return -ENOTSUPP; + + rc = rk3288_get_drive(bank, pin - bank->pin_base); + if (rc < 0) + return rc; + + arg = rc; + break; + default: + return -ENOTSUPP; + break; + } + + *config = pinconf_to_config_packed(param, arg); + + return 0; +} + +static const struct pinconf_ops rockchip_pinconf_ops = { + .pin_config_get = rockchip_pinconf_get, + .pin_config_set = rockchip_pinconf_set, + .is_generic = true, +}; + +static const struct of_device_id rockchip_bank_match[] = { + { .compatible = "rockchip,gpio-bank" }, + { .compatible = "rockchip,rk3188-gpio-bank0" }, + {}, +}; + +static void rockchip_pinctrl_child_count(struct rockchip_pinctrl *info, + struct device_node *np) +{ + struct device_node *child; + + for_each_child_of_node(np, child) { + if (of_match_node(rockchip_bank_match, child)) + continue; + + info->nfunctions++; + info->ngroups += of_get_child_count(child); + } +} + +static int rockchip_pinctrl_parse_groups(struct device_node *np, + struct rockchip_pin_group *grp, + struct rockchip_pinctrl *info, + u32 index) +{ + struct rockchip_pin_bank *bank; + int size; + const __be32 *list; + int num; + int i, j; + int ret; + + dev_dbg(info->dev, "group(%d): %s\n", index, np->name); + + /* Initialise group */ + grp->name = np->name; + + /* + * the binding format is rockchip,pins = , + * do sanity check and calculate pins number + */ + list = of_get_property(np, "rockchip,pins", &size); + /* we do not check return since it's safe node passed down */ + size /= sizeof(*list); + if (!size || size % 4) { + dev_err(info->dev, + "wrong pins number or pins and configs should be by 4\n"); + return -EINVAL; + } + + grp->npins = size / 4; + + grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), + GFP_KERNEL); + grp->data = devm_kzalloc(info->dev, grp->npins * + sizeof(struct rockchip_pin_config), + GFP_KERNEL); + if (!grp->pins || !grp->data) + return -ENOMEM; + + for (i = 0, j = 0; i < size; i += 4, j++) { + const __be32 *phandle; + struct device_node *np_config; + + num = be32_to_cpu(*list++); + bank = bank_num_to_bank(info, num); + if (IS_ERR(bank)) + return PTR_ERR(bank); + + grp->pins[j] = bank->pin_base + be32_to_cpu(*list++); + grp->data[j].func = be32_to_cpu(*list++); + + phandle = list++; + if (!phandle) + return -EINVAL; + + np_config = of_find_node_by_phandle(be32_to_cpup(phandle)); + ret = pinconf_generic_parse_dt_config(np_config, + &grp->data[j].configs, + &grp->data[j].nconfigs); + if (ret) + return ret; + } + + return 0; +} + +static int rockchip_pinctrl_parse_functions(struct device_node *np, + struct rockchip_pinctrl *info, + u32 index) +{ + struct device_node *child; + struct rockchip_pmx_func *func; + struct rockchip_pin_group *grp; + int ret; + static u32 grp_index; + u32 i = 0; + + dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name); + + func = &info->functions[index]; + + /* Initialise function */ + func->name = np->name; + func->ngroups = of_get_child_count(np); + if (func->ngroups <= 0) + return 0; + + func->groups = devm_kzalloc(info->dev, + func->ngroups * sizeof(char *), GFP_KERNEL); + if (!func->groups) + return -ENOMEM; + + for_each_child_of_node(np, child) { + func->groups[i] = child->name; + grp = &info->groups[grp_index++]; + ret = rockchip_pinctrl_parse_groups(child, grp, info, i++); + if (ret) + return ret; + } + + return 0; +} + +static int rockchip_pinctrl_parse_dt(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct device *dev = &pdev->dev; + struct device_node *np = dev->of_node; + struct device_node *child; + int ret; + int i; + + rockchip_pinctrl_child_count(info, np); + + dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); + dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); + + info->functions = devm_kzalloc(dev, info->nfunctions * + sizeof(struct rockchip_pmx_func), + GFP_KERNEL); + if (!info->functions) { + dev_err(dev, "failed to allocate memory for function list\n"); + return -EINVAL; + } + + info->groups = devm_kzalloc(dev, info->ngroups * + sizeof(struct rockchip_pin_group), + GFP_KERNEL); + if (!info->groups) { + dev_err(dev, "failed allocate memory for ping group list\n"); + return -EINVAL; + } + + i = 0; + + for_each_child_of_node(np, child) { + if (of_match_node(rockchip_bank_match, child)) + continue; + + ret = rockchip_pinctrl_parse_functions(child, info, i++); + if (ret) { + dev_err(&pdev->dev, "failed to parse function\n"); + return ret; + } + } + + return 0; +} + +static int rockchip_pinctrl_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct pinctrl_desc *ctrldesc = &info->pctl; + struct pinctrl_pin_desc *pindesc, *pdesc; + struct rockchip_pin_bank *pin_bank; + int pin, bank, ret; + int k; + + ctrldesc->name = "rockchip-pinctrl"; + ctrldesc->owner = THIS_MODULE; + ctrldesc->pctlops = &rockchip_pctrl_ops; + ctrldesc->pmxops = &rockchip_pmx_ops; + ctrldesc->confops = &rockchip_pinconf_ops; + + pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) * + info->ctrl->nr_pins, GFP_KERNEL); + if (!pindesc) { + dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n"); + return -ENOMEM; + } + ctrldesc->pins = pindesc; + ctrldesc->npins = info->ctrl->nr_pins; + + pdesc = pindesc; + for (bank = 0 , k = 0; bank < info->ctrl->nr_banks; bank++) { + pin_bank = &info->ctrl->pin_banks[bank]; + for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) { + pdesc->number = k; + pdesc->name = kasprintf(GFP_KERNEL, "%s-%d", + pin_bank->name, pin); + pdesc++; + } + } + + info->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, info); + if (!info->pctl_dev) { + dev_err(&pdev->dev, "could not register pinctrl driver\n"); + return -EINVAL; + } + + for (bank = 0; bank < info->ctrl->nr_banks; ++bank) { + pin_bank = &info->ctrl->pin_banks[bank]; + pin_bank->grange.name = pin_bank->name; + pin_bank->grange.id = bank; + pin_bank->grange.pin_base = pin_bank->pin_base; + pin_bank->grange.base = pin_bank->gpio_chip.base; + pin_bank->grange.npins = pin_bank->gpio_chip.ngpio; + pin_bank->grange.gc = &pin_bank->gpio_chip; + pinctrl_add_gpio_range(info->pctl_dev, &pin_bank->grange); + } + + ret = rockchip_pinctrl_parse_dt(pdev, info); + if (ret) { + pinctrl_unregister(info->pctl_dev); + return ret; + } + + return 0; +} + +/* + * GPIO handling + */ + +static int rockchip_gpio_request(struct gpio_chip *chip, unsigned offset) +{ + return pinctrl_request_gpio(chip->base + offset); +} + +static void rockchip_gpio_free(struct gpio_chip *chip, unsigned offset) +{ + pinctrl_free_gpio(chip->base + offset); +} + +static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR; + unsigned long flags; + u32 data; + + spin_lock_irqsave(&bank->slock, flags); + + data = readl(reg); + data &= ~BIT(offset); + if (value) + data |= BIT(offset); + writel(data, reg); + + spin_unlock_irqrestore(&bank->slock, flags); +} + +/* + * Returns the level of the pin for input direction and setting of the DR + * register for output gpios. + */ +static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + u32 data; + + data = readl(bank->reg_base + GPIO_EXT_PORT); + data >>= offset; + data &= 1; + return data; +} + +/* + * gpiolib gpio_direction_input callback function. The setting of the pin + * mux function as 'gpio input' will be handled by the pinctrl susbsystem + * interface. + */ +static int rockchip_gpio_direction_input(struct gpio_chip *gc, unsigned offset) +{ + return pinctrl_gpio_direction_input(gc->base + offset); +} + +/* + * gpiolib gpio_direction_output callback function. The setting of the pin + * mux function as 'gpio output' will be handled by the pinctrl susbsystem + * interface. + */ +static int rockchip_gpio_direction_output(struct gpio_chip *gc, + unsigned offset, int value) +{ + rockchip_gpio_set(gc, offset, value); + return pinctrl_gpio_direction_output(gc->base + offset); +} + +/* + * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin + * and a virtual IRQ, if not already present. + */ +static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned offset) +{ + struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); + unsigned int virq; + + if (!bank->domain) + return -ENXIO; + + virq = irq_create_mapping(bank->domain, offset); + + return (virq) ? : -ENXIO; +} + +static const struct gpio_chip rockchip_gpiolib_chip = { + .request = rockchip_gpio_request, + .free = rockchip_gpio_free, + .set = rockchip_gpio_set, + .get = rockchip_gpio_get, + .direction_input = rockchip_gpio_direction_input, + .direction_output = rockchip_gpio_direction_output, + .to_irq = rockchip_gpio_to_irq, + .owner = THIS_MODULE, +}; + +/* + * Interrupt handling + */ + +static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) +{ + struct irq_chip *chip = irq_get_chip(irq); + struct rockchip_pin_bank *bank = irq_get_handler_data(irq); + u32 polarity = 0, data = 0; + u32 pend; + bool edge_changed = false; + + pinctrl_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name); + + chained_irq_enter(chip, desc); + + pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS); + + if (bank->toggle_edge_mode) { + polarity = readl_relaxed(bank->reg_base + + GPIO_INT_POLARITY); + data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT); + } + + while (pend) { + unsigned int virq; + + irq = __ffs(pend); + pend &= ~BIT(irq); + virq = irq_linear_revmap(bank->domain, irq); + + if (!virq) { + dev_err(bank->drvdata->dev, "unmapped irq %d\n", irq); + continue; + } + + pinctrl_dbg(bank->drvdata->dev, "handling irq %d\n", irq); + + /* + * Triggering IRQ on both rising and falling edge + * needs manual intervention. + */ + if (bank->toggle_edge_mode & BIT(irq)) { + if (data & BIT(irq)) + polarity &= ~BIT(irq); + else + polarity |= BIT(irq); + + edge_changed = true; + } + + generic_handle_irq(virq); + } + + if (bank->toggle_edge_mode && edge_changed) { + /* Interrupt params should only be set with ints disabled */ + data = readl_relaxed(bank->reg_base + GPIO_INTEN); + writel_relaxed(0, bank->reg_base + GPIO_INTEN); + writel(polarity, bank->reg_base + GPIO_INT_POLARITY); + writel(data, bank->reg_base + GPIO_INTEN); + } + + chained_irq_exit(chip, desc); +} + +static int rockchip_gpio_irq_set_type(struct irq_data *d, unsigned int type) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + u32 mask = BIT(d->hwirq); + u32 polarity; + u32 level; + u32 data; + int ret; + unsigned long flags; + + /* make sure the pin is configured as gpio input */ + ret = rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO); + if (ret < 0) + return ret; + + data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); + data &= ~mask; + writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); + + if (type & IRQ_TYPE_EDGE_BOTH) + __irq_set_handler_locked(d->irq, handle_edge_irq); + else + __irq_set_handler_locked(d->irq, handle_level_irq); + + spin_lock_irqsave(&bank->slock, flags); + + level = readl_relaxed(bank->reg_base + GPIO_INTTYPE_LEVEL); + polarity = readl_relaxed(bank->reg_base + GPIO_INT_POLARITY); + + switch (type) { + case IRQ_TYPE_EDGE_BOTH: + bank->toggle_edge_mode |= mask; + level |= mask; + + /* + * Determine gpio state. If 1 next interrupt should be falling + * otherwise rising. + */ + data = readl(bank->reg_base + GPIO_EXT_PORT); + if (data & mask) + polarity &= ~mask; + else + polarity |= mask; + break; + case IRQ_TYPE_EDGE_RISING: + bank->toggle_edge_mode &= ~mask; + level |= mask; + polarity |= mask; + break; + case IRQ_TYPE_EDGE_FALLING: + bank->toggle_edge_mode &= ~mask; + level |= mask; + polarity &= ~mask; + break; + case IRQ_TYPE_LEVEL_HIGH: + bank->toggle_edge_mode &= ~mask; + level &= ~mask; + polarity |= mask; + break; + case IRQ_TYPE_LEVEL_LOW: + bank->toggle_edge_mode &= ~mask; + level &= ~mask; + polarity &= ~mask; + break; + default: + return -EINVAL; + } + + writel_relaxed(level, bank->reg_base + GPIO_INTTYPE_LEVEL); + writel_relaxed(polarity, bank->reg_base + GPIO_INT_POLARITY); + + spin_unlock_irqrestore(&bank->slock, flags); + + return 0; +} + +static inline void rockchip_gpio_bit_op(void __iomem *reg_base + , unsigned int offset, u32 bit, unsigned char flag) +{ + u32 val = __raw_readl(reg_base + offset); + + if (flag) + val |= BIT(bit); + else + val &= ~BIT(bit); + + __raw_writel(val, reg_base + offset); +} + +static inline unsigned gpio_to_bit(struct rockchip_pin_bank *bank, + unsigned gpio) +{ + while (gpio >= (bank->pin_base + bank->nr_pins)) + bank++; + + return gpio - bank->pin_base; +} + +static inline unsigned offset_to_bit(unsigned offset) +{ + return 1u << offset; +} + +static void GPIOEnableIntr(void __iomem *reg_base, unsigned int bit) +{ + rockchip_gpio_bit_op(reg_base, GPIO_INTEN, bit, 1); +} + +static void GPIODisableIntr(void __iomem *reg_base, unsigned int bit) +{ + rockchip_gpio_bit_op(reg_base, GPIO_INTEN, bit, 0); +} + +static void GPIOAckIntr(void __iomem *reg_base, unsigned int bit) +{ + rockchip_gpio_bit_op(reg_base, GPIO_PORTS_EOI, bit, 1); +} + +static int rockchip_gpio_irq_set_wake(struct irq_data *d, unsigned int on) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + u32 bit = d->hwirq; + unsigned long flags; + + spin_lock_irqsave(&bank->slock, flags); + + if (on) + bank->suspend_wakeup |= BIT(bit); + else + bank->suspend_wakeup &= ~BIT(bit); + spin_unlock_irqrestore(&bank->slock, flags); + + return 0; +} + +static void rockchip_gpio_irq_unmask(struct irq_data *d) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + u32 bit = d->hwirq; + unsigned long flags; + + spin_lock_irqsave(&bank->slock, flags); + GPIOEnableIntr(bank->reg_base, bit); + spin_unlock_irqrestore(&bank->slock, flags); +} + +static void rockchip_gpio_irq_mask(struct irq_data *d) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + u32 bit = d->hwirq; + unsigned long flags; + + spin_lock_irqsave(&bank->slock, flags); + GPIODisableIntr(bank->reg_base, bit); + spin_unlock_irqrestore(&bank->slock, flags); +} + +static void rockchip_gpio_irq_ack(struct irq_data *d) +{ + struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); + u32 bit = d->hwirq; + + GPIOAckIntr(bank->reg_base, bit); +} + +static struct irq_chip rockchip_gpio_irq_chip = { + .name = "ROCKCHIP_GPIO_CHIP", + .irq_ack = rockchip_gpio_irq_ack, + .irq_disable = rockchip_gpio_irq_mask, + .irq_mask = rockchip_gpio_irq_mask, + .irq_unmask = rockchip_gpio_irq_unmask, + .irq_set_type = rockchip_gpio_irq_set_type, + .irq_set_wake = rockchip_gpio_irq_set_wake, +}; + +static int rockchip_gpio_irq_map(struct irq_domain *d, unsigned int irq, + irq_hw_number_t hwirq) +{ + struct rockchip_pin_bank *bank = d->host_data; + struct irq_data *irq_data = irq_get_irq_data(irq); + + if (!bank) { + dev_err(bank->drvdata->dev, "%s:bank=0x%p,irq=%d\n", + __func__, bank, irq); + return -EINVAL; + } + + irq_set_chip_and_handler(irq, &rockchip_gpio_irq_chip, + handle_level_irq); + irq_set_chip_data(irq, bank); + set_irq_flags(irq, IRQF_VALID); + + irq_data->hwirq = hwirq; + irq_data->irq = irq; + + pinctrl_dbg(bank->drvdata->dev, "%s:irq = %d, hwirq =%ld\n", + __func__, irq, hwirq); + return 0; +} + +static const struct irq_domain_ops rockchip_gpio_irq_ops = { + .map = rockchip_gpio_irq_map, + .xlate = irq_domain_xlate_twocell, +}; + +static int rockchip_interrupts_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) { + dev_warn(&pdev->dev, "bank %s is not valid\n", + bank->name); + continue; + } + + __raw_writel(0, bank->reg_base + GPIO_INTEN); + + bank->drvdata = info; + bank->domain = irq_domain_add_linear(bank->of_node, 32, + &rockchip_gpio_irq_ops, bank); + if (!bank->domain) { + dev_warn(&pdev->dev, "could not initialize irq domain for bank %s\n", + bank->name); + continue; + } + + irq_set_handler_data(bank->irq, bank); + irq_set_chained_handler(bank->irq, rockchip_irq_demux); + } + + return 0; +} + +static int rockchip_gpiolib_register(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + struct gpio_chip *gc; + int ret; + int i; + + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) { + dev_warn(&pdev->dev, "bank %s is not valid\n", + bank->name); + continue; + } + + bank->gpio_chip = rockchip_gpiolib_chip; + + gc = &bank->gpio_chip; + gc->base = bank->pin_base; + gc->ngpio = bank->nr_pins; + gc->dev = &pdev->dev; + gc->of_node = bank->of_node; + gc->label = bank->name; + + ret = gpiochip_add(gc); + if (ret) { + dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n", + gc->label, ret); + goto fail; + } + } + + rockchip_interrupts_register(pdev, info); + + return 0; + +fail: + for (--i, --bank; i >= 0; --i, --bank) { + if (!bank->valid) + continue; + + if (gpiochip_remove(&bank->gpio_chip)) + dev_err(&pdev->dev, "gpio chip %s remove failed\n", + bank->gpio_chip.label); + } + return ret; +} + +static int rockchip_gpiolib_unregister(struct platform_device *pdev, + struct rockchip_pinctrl *info) +{ + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + int ret = 0; + int i; + + for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank) { + if (!bank->valid) + continue; + + ret = gpiochip_remove(&bank->gpio_chip); + } + + if (ret) + dev_err(&pdev->dev, "gpio chip remove failed\n"); + + return ret; +} + +static int rockchip_get_bank_data(struct rockchip_pin_bank *bank, + struct rockchip_pinctrl *info) +{ + struct resource res; + void __iomem *base; + + if (of_address_to_resource(bank->of_node, 0, &res)) { + dev_err(info->dev, "cannot find IO resource for bank\n"); + return -ENOENT; + } + + bank->reg_base = devm_ioremap_resource(info->dev, &res); + if (IS_ERR(bank->reg_base)) + return PTR_ERR(bank->reg_base); + + /* + * 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")) { + struct device_node *node; + + node = of_parse_phandle(bank->of_node->parent, + "rockchip,pmugrf", 0); + if (!node) { + if (of_address_to_resource(bank->of_node, 1, &res)) { + dev_err(info->dev, "cannot find IO resource for bank\n"); + return -ENOENT; + } + + base = devm_ioremap_resource(info->dev, &res); + if (IS_ERR(base)) + return PTR_ERR(base); + rockchip_regmap_config.max_register = + resource_size(&res) - 4; + rockchip_regmap_config.name = + "rockchip,rk3188-gpio-bank0-pull"; + bank->regmap_pull = devm_regmap_init_mmio(info->dev, + base, + &rockchip_regmap_config); + } + } + + bank->irq = irq_of_parse_and_map(bank->of_node, 0); + + bank->clk = of_clk_get(bank->of_node, 0); + if (IS_ERR(bank->clk)) + return PTR_ERR(bank->clk); + + return clk_prepare_enable(bank->clk); +} + +static const struct of_device_id rockchip_pinctrl_dt_match[]; + +/* retrieve the soc specific data */ +static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( + struct rockchip_pinctrl *d, + struct platform_device *pdev) +{ + const struct of_device_id *match; + struct device_node *node = pdev->dev.of_node; + struct device_node *np; + struct rockchip_pin_ctrl *ctrl; + struct rockchip_pin_bank *bank; + int grf_offs, pmu_offs, i, j; + + 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) { + if (!strcmp(bank->name, np->name)) { + bank->of_node = np; + + if (!rockchip_get_bank_data(bank, d)) + bank->valid = true; + + break; + } + } + } + + grf_offs = ctrl->grf_mux_offset; + pmu_offs = ctrl->pmu_mux_offset; + bank = ctrl->pin_banks; + for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { + int bank_pins = 0; + + spin_lock_init(&bank->slock); + bank->drvdata = d; + bank->pin_base = ctrl->nr_pins; + ctrl->nr_pins += bank->nr_pins; + + /* calculate iomux offsets */ + for (j = 0; j < 4; j++) { + struct rockchip_iomux *iom = &bank->iomux[j]; + int inc; + + if (bank_pins >= bank->nr_pins) + break; + + /* preset offset value, set new start value */ + if (iom->offset >= 0) { + if (iom->type & IOMUX_SOURCE_PMU) + pmu_offs = iom->offset; + else + grf_offs = iom->offset; + } else { /* set current offset */ + iom->offset = (iom->type & IOMUX_SOURCE_PMU) ? + pmu_offs : grf_offs; + } + + pinctrl_dbg(d->dev, "bank %d, iomux %d has offset 0x%x\n", + i, j, iom->offset); + + /* + * Increase offset according to iomux width. + * 4bit iomux'es are spread over two registers. + */ + inc = (iom->type & IOMUX_WIDTH_4BIT) ? 8 : 4; + if (iom->type & IOMUX_SOURCE_PMU) + pmu_offs += inc; + else + grf_offs += inc; + + bank_pins += 8; + } + } + + return ctrl; +} + +#ifdef CONFIG_PM +static int rockchip_pinctrl_suspend(void) +{ + struct rockchip_pinctrl *info = g_info; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + int n; + + for (n = 0; n < ctrl->nr_banks; n++) { + bank->saved_wakeup = __raw_readl(bank->reg_base + GPIO_INTEN); + __raw_writel(bank->suspend_wakeup, bank->reg_base + GPIO_INTEN); + + if (!bank->suspend_wakeup) + clk_disable_unprepare(bank->clk); + bank++; + } + + return 0; +} + +static void rockchip_pinctrl_resume(void) +{ + struct rockchip_pinctrl *info = g_info; + struct rockchip_pin_ctrl *ctrl = info->ctrl; + struct rockchip_pin_bank *bank = ctrl->pin_banks; + int n; + u32 isr; + + for (n = 0; n < ctrl->nr_banks; n++) { + if (!bank->suspend_wakeup) + clk_prepare_enable(bank->clk); + + /* keep enable for resume irq */ + isr = __raw_readl(bank->reg_base + GPIO_INT_STATUS); + __raw_writel(bank->saved_wakeup + | (bank->suspend_wakeup & isr) + , bank->reg_base + GPIO_INTEN); + bank++; + } +} + +static struct syscore_ops rockchip_gpio_syscore_ops = { + .suspend = rockchip_pinctrl_suspend, + .resume = rockchip_pinctrl_resume, +}; +#endif + +static int rockchip_pinctrl_probe(struct platform_device *pdev) +{ + struct rockchip_pinctrl *info; + struct device *dev = &pdev->dev; + struct rockchip_pin_ctrl *ctrl; + struct device_node *np = pdev->dev.of_node, *node; + struct resource *res; + void __iomem *base; + int ret; + + if (!dev->of_node) { + dev_err(dev, "device tree node not found\n"); + return -ENODEV; + } + + info = devm_kzalloc(dev, sizeof(struct rockchip_pinctrl), GFP_KERNEL); + if (!info) + return -ENOMEM; + + info->dev = dev; + + ctrl = rockchip_pinctrl_get_soc_data(info, pdev); + if (!ctrl) { + dev_err(dev, "driver data not available\n"); + return -EINVAL; + } + info->ctrl = ctrl; + g_info = info; + + node = of_parse_phandle(np, "rockchip,grf", 0); + if (node) { + info->regmap_base = syscon_node_to_regmap(node); + if (IS_ERR(info->regmap_base)) + return PTR_ERR(info->regmap_base); + } else { + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + rockchip_regmap_config.max_register = resource_size(res) - 4; + rockchip_regmap_config.name = "rockchip,pinctrl"; + info->regmap_base = devm_regmap_init_mmio(&pdev->dev, base, + &rockchip_regmap_config); + + /* to check for the old dt-bindings */ + info->reg_size = resource_size(res); + + /* Honor the old binding, with pull registers as 2nd resource */ + if (ctrl->type == RK3188 && info->reg_size < 0x200) { + res = platform_get_resource(pdev, IORESOURCE_MEM, 1); + base = devm_ioremap_resource(&pdev->dev, res); + if (IS_ERR(base)) + return PTR_ERR(base); + + rockchip_regmap_config.max_register = + resource_size(res) - 4; + rockchip_regmap_config.name = "rockchip,pinctrl-pull"; + info->regmap_pull = devm_regmap_init_mmio(&pdev->dev, + base, + &rockchip_regmap_config); + } + } + + /* try to find the optional reference to the pmu syscon */ + node = of_parse_phandle(np, "rockchip,pmugrf", 0); + if (node) { + info->regmap_pmu = syscon_node_to_regmap(node); + if (IS_ERR(info->regmap_pmu)) + return PTR_ERR(info->regmap_pmu); + } + + ret = rockchip_gpiolib_register(pdev, info); + if (ret) + return ret; + + ret = rockchip_pinctrl_register(pdev, info); + if (ret) { + rockchip_gpiolib_unregister(pdev, info); + return ret; + } + + platform_set_drvdata(pdev, info); +#ifdef CONFIG_PM + register_syscore_ops(&rockchip_gpio_syscore_ops); +#endif + + return 0; +} + +static struct rockchip_pin_bank rk3228_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, 0), +}; + +static struct rockchip_pin_ctrl rk3228_pin_ctrl = { + .pin_banks = rk3228_pin_banks, + .nr_banks = ARRAY_SIZE(rk3228_pin_banks), + .label = "RK3228-GPIO", + .type = RK3228, + .grf_mux_offset = 0x0, + .pmu_mux_offset = 0x0, + .pull_calc_reg = rk3288_calc_pull_reg_and_bit, +}; +static struct rockchip_pin_bank rk3368_pin_banks[] = { + PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU, + IOMUX_SOURCE_PMU), + PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, 0, 0, 0), + PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, 0), +}; + +static struct rockchip_pin_ctrl rk3368_pin_ctrl = { + .pin_banks = rk3368_pin_banks, + .nr_banks = ARRAY_SIZE(rk3368_pin_banks), + .label = "RK3368-GPIO", + .type = RK3368, + .grf_mux_offset = 0x0, + .pmu_mux_offset = 0x0, + .pull_calc_reg = rk3288_calc_pull_reg_and_bit, +}; + +static const struct of_device_id rockchip_pinctrl_dt_match[] = { + { .compatible = "rockchip,rk3228-pinctrl", + .data = (void *)&rk3228_pin_ctrl }, + { .compatible = "rockchip,rk3368-pinctrl", + .data = (void *)&rk3368_pin_ctrl }, + {}, +}; +MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match); + +static struct platform_driver rockchip_pinctrl_driver = { + .probe = rockchip_pinctrl_probe, + .driver = { + .name = "rk3368-pinctrl", + .owner = THIS_MODULE, + .of_match_table = rockchip_pinctrl_dt_match, + }, +}; + +static int __init rockchip_pinctrl_drv_register(void) +{ + return platform_driver_register(&rockchip_pinctrl_driver); +} +postcore_initcall(rockchip_pinctrl_drv_register); + +MODULE_AUTHOR("Heiko Stuebner "); +MODULE_DESCRIPTION("Rockchip pinctrl driver"); +MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/pinctrl-rk3368.c b/drivers/pinctrl/pinctrl-rk3368.c deleted file mode 100755 index 9712b1d12e72..000000000000 --- a/drivers/pinctrl/pinctrl-rk3368.c +++ /dev/null @@ -1,2136 +0,0 @@ -/* - * Pinctrl driver for Rockchip SoCs - * - * Copyright (c) 2013 MundoReader S.L. - * Author: Heiko Stuebner - * - * With some ideas taken from pinctrl-samsung: - * Copyright (c) 2012 Samsung Electronics Co., Ltd. - * http://www.samsung.com - * Copyright (c) 2012 Linaro Ltd - * http://www.linaro.org - * - * and pinctrl-at91: - * 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 -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include "core.h" -#include "pinconf.h" - - -#if 0 -#define pinctrl_dbg(dev, format, arg...) \ - dev_printk(KERN_INFO , dev , format , ## arg) -#else - #define pinctrl_dbg(dev, format, arg...) -#endif - - -/* GPIO control registers */ -#define GPIO_SWPORT_DR 0x00 -#define GPIO_SWPORT_DDR 0x04 -#define GPIO_INTEN 0x30 -#define GPIO_INTMASK 0x34 -#define GPIO_INTTYPE_LEVEL 0x38 -#define GPIO_INT_POLARITY 0x3c -#define GPIO_INT_STATUS 0x40 -#define GPIO_INT_RAWSTATUS 0x44 -#define GPIO_DEBOUNCE 0x48 -#define GPIO_PORTS_EOI 0x4c -#define GPIO_EXT_PORT 0x50 -#define GPIO_LS_SYNC 0x60 - -enum rockchip_pinctrl_type { - RK2928, - RK3066B, - RK3188, - RK3228, - RK3288, - RK3368, -}; - -/** - * Encode variants of iomux registers into a type variable - */ -#define IOMUX_GPIO_ONLY BIT(0) -#define IOMUX_WIDTH_4BIT BIT(1) -#define IOMUX_SOURCE_PMU BIT(2) -#define IOMUX_UNROUTED BIT(3) - -/** - * @type: iomux variant using IOMUX_* constants - * @offset: if initialized to -1 it will be autocalculated, by specifying - * an initial offset value the relevant source offset can be reset - * to a new value for autocalculating the following iomux registers. - */ -struct rockchip_iomux { - int type; - int offset; -}; - -/** - * @reg_base: register base of the gpio bank - * @reg_pull: optional separate register for additional pull settings - * @clk: clock of the gpio bank - * @irq: interrupt of the gpio bank - * @pin_base: first pin number - * @nr_pins: number of pins in this bank - * @name: name of the bank - * @bank_num: number of the bank, to account for holes - * @iomux: array describing the 4 iomux sources of the bank - * @valid: are all necessary informations present - * @of_node: dt node of this bank - * @drvdata: common pinctrl basedata - * @domain: irqdomain of the gpio bank - * @gpio_chip: gpiolib chip - * @grange: gpio range - * @slock: spinlock for the gpio bank - */ -struct rockchip_pin_bank { - void __iomem *reg_base; - struct regmap *regmap_pull; - struct clk *clk; - int irq; - u32 pin_base; - u8 nr_pins; - char *name; - u8 bank_num; - struct rockchip_iomux iomux[4]; - bool valid; - struct device_node *of_node; - struct rockchip_pinctrl *drvdata; - struct irq_domain *domain; - struct gpio_chip gpio_chip; - struct pinctrl_gpio_range grange; - /*spinlock for the gpio bank*/ - spinlock_t slock; - u32 toggle_edge_mode; - u32 suspend_wakeup; - u32 saved_wakeup; -}; - -#define PIN_BANK(id, pins, label) \ - { \ - .bank_num = id, \ - .nr_pins = pins, \ - .name = label, \ - .iomux = { \ - { .offset = -1 }, \ - { .offset = -1 }, \ - { .offset = -1 }, \ - { .offset = -1 }, \ - }, \ - } - -#define PIN_BANK_IOMUX_FLAGS(id, pins, label, iom0, iom1, iom2, iom3) \ - { \ - .bank_num = id, \ - .nr_pins = pins, \ - .name = label, \ - .iomux = { \ - { .type = iom0, .offset = -1 }, \ - { .type = iom1, .offset = -1 }, \ - { .type = iom2, .offset = -1 }, \ - { .type = iom3, .offset = -1 }, \ - }, \ - } - -/** - */ -struct rockchip_pin_ctrl { - struct rockchip_pin_bank *pin_banks; - u32 nr_banks; - u32 nr_pins; - char *label; - enum rockchip_pinctrl_type type; - int grf_mux_offset; - int pmu_mux_offset; - - void (*pull_calc_reg)(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit); -}; - -struct rockchip_pin_config { - unsigned int func; - unsigned long *configs; - unsigned int nconfigs; -}; - -/** - * struct rockchip_pin_group: represent group of pins of a pinmux function. - * @name: name of the pin group, used to lookup the group. - * @pins: the pins included in this group. - * @npins: number of pins included in this group. - * @func: the mux function number to be programmed when selected. - * @configs: the config values to be set for each pin - * @nconfigs: number of configs for each pin - */ -struct rockchip_pin_group { - const char *name; - unsigned int npins; - unsigned int *pins; - struct rockchip_pin_config *data; -}; - -/** - * struct rockchip_pmx_func: represent a pin function. - * @name: name of the pin function, used to lookup the function. - * @groups: one or more names of pin groups that provide this function. - * @num_groups: number of groups included in @groups. - */ -struct rockchip_pmx_func { - const char *name; - const char **groups; - u8 ngroups; -}; - -struct rockchip_pinctrl { - struct regmap *regmap_base; - int reg_size; - struct regmap *regmap_pull; - struct regmap *regmap_pmu; - struct device *dev; - struct rockchip_pin_ctrl *ctrl; - struct pinctrl_desc pctl; - struct pinctrl_dev *pctl_dev; - struct rockchip_pin_group *groups; - unsigned int ngroups; - struct rockchip_pmx_func *functions; - unsigned int nfunctions; -}; - -static struct regmap_config rockchip_regmap_config = { - .reg_bits = 32, - .val_bits = 32, - .reg_stride = 4, -}; -static struct rockchip_pinctrl *g_info; - -static inline struct rockchip_pin_bank *gc_to_pin_bank(struct gpio_chip *gc) -{ - return container_of(gc, struct rockchip_pin_bank, gpio_chip); -} - -static const inline struct rockchip_pin_group *pinctrl_name_to_group( - const struct rockchip_pinctrl *info, - const char *name) -{ - int i; - - for (i = 0; i < info->ngroups; i++) { - if (!strcmp(info->groups[i].name, name)) - return &info->groups[i]; - } - - return NULL; -} - -/* - * given a pin number that is local to a pin controller, find out the pin bank - * and the register base of the pin bank. - */ -static struct rockchip_pin_bank *pin_to_bank(struct rockchip_pinctrl *info, - unsigned pin) -{ - struct rockchip_pin_bank *b = info->ctrl->pin_banks; - - while (pin >= (b->pin_base + b->nr_pins)) - b++; - - return b; -} - -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); -} - -/* - * Pinctrl_ops handling - */ - -static int rockchip_get_groups_count(struct pinctrl_dev *pctldev) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - - return info->ngroups; -} - -static const char *rockchip_get_group_name(struct pinctrl_dev *pctldev, - unsigned selector) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - - return info->groups[selector].name; -} - -static int rockchip_get_group_pins(struct pinctrl_dev *pctldev, - unsigned selector, const unsigned **pins, - unsigned *npins) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - - if (selector >= info->ngroups) - return -EINVAL; - - *pins = info->groups[selector].pins; - *npins = info->groups[selector].npins; - - return 0; -} - -static int rockchip_dt_node_to_map(struct pinctrl_dev *pctldev, - struct device_node *np, - struct pinctrl_map **map, unsigned *num_maps) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - const struct rockchip_pin_group *grp; - struct pinctrl_map *new_map; - struct device_node *parent; - int map_num = 1; - int i; - - /* - * first find the group of this node and check if we need to create - * config maps for pins - */ - grp = pinctrl_name_to_group(info, np->name); - if (!grp) { - dev_err(info->dev, "unable to find group for node %s\n", - np->name); - return -EINVAL; - } - - map_num += grp->npins; - new_map = devm_kzalloc(pctldev->dev, sizeof(*new_map) * map_num, - GFP_KERNEL); - if (!new_map) - return -ENOMEM; - - *map = new_map; - *num_maps = map_num; - - /* create mux map */ - parent = of_get_parent(np); - if (!parent) { - devm_kfree(pctldev->dev, new_map); - return -EINVAL; - } - new_map[0].type = PIN_MAP_TYPE_MUX_GROUP; - new_map[0].data.mux.function = parent->name; - new_map[0].data.mux.group = np->name; - of_node_put(parent); - - /* create config map */ - new_map++; - for (i = 0; i < grp->npins; i++) { - new_map[i].type = PIN_MAP_TYPE_CONFIGS_PIN; - new_map[i].data.configs.group_or_pin = - pin_get_name(pctldev, grp->pins[i]); - new_map[i].data.configs.configs = grp->data[i].configs; - new_map[i].data.configs.num_configs = grp->data[i].nconfigs; - } - - pinctrl_dbg(pctldev->dev, "maps: function %s group %s num %d\n", - (*map)->data.mux.function, (*map)->data.mux.group, map_num); - - return 0; -} - -static void rockchip_dt_free_map(struct pinctrl_dev *pctldev, - struct pinctrl_map *map, unsigned num_maps) -{ -} - -static const struct pinctrl_ops rockchip_pctrl_ops = { - .get_groups_count = rockchip_get_groups_count, - .get_group_name = rockchip_get_group_name, - .get_group_pins = rockchip_get_group_pins, - .dt_node_to_map = rockchip_dt_node_to_map, - .dt_free_map = rockchip_dt_free_map, -}; - -/* - * Hardware access - */ - -static int rockchip_get_mux(struct rockchip_pin_bank *bank, int pin) -{ - struct rockchip_pinctrl *info = bank->drvdata; - int iomux_num = (pin / 8); - struct regmap *regmap; - unsigned int val; - int reg, ret, mask; - u8 bit; - - if (iomux_num > 3) - return -EINVAL; - - if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) { - dev_err(info->dev, "pin %d is unrouted\n", pin); - return -EINVAL; - } - - if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) - return RK_FUNC_GPIO; - - regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) - ? info->regmap_pmu : info->regmap_base; - - /* get basic quadrupel of mux registers and the correct reg inside */ - mask = (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) ? 0xf : 0x3; - reg = bank->iomux[iomux_num].offset; - if (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) { - if ((pin % 8) >= 4) - reg += 0x4; - bit = (pin % 4) * 4; - } else { - bit = (pin % 8) * 2; - } - - ret = regmap_read(regmap, reg, &val); - if (ret) - return ret; - - return ((val >> bit) & mask); -} - -/* - * Set a new mux function for a pin. - * - * The register is divided into the upper and lower 16 bit. When changing - * a value, the previous register value is not read and changed. Instead - * it seems the changed bits are marked in the upper 16 bit, while the - * changed value gets set in the same offset in the lower 16 bit. - * All pin settings seem to be 2 bit wide in both the upper and lower - * parts. - * @bank: pin bank to change - * @pin: pin to change - * @mux: new mux function to set - */ -static int rockchip_set_mux(struct rockchip_pin_bank *bank, int pin, int mux) -{ - struct rockchip_pinctrl *info = bank->drvdata; - int iomux_num = (pin / 8); - struct regmap *regmap; - int reg, ret, mask; - unsigned long flags; - u8 bit; - u32 data, rmask; - - if (iomux_num > 3) - return -EINVAL; - - if (bank->iomux[iomux_num].type & IOMUX_UNROUTED) { - dev_err(info->dev, "pin %d is unrouted\n", pin); - return -EINVAL; - } - - if (bank->iomux[iomux_num].type & IOMUX_GPIO_ONLY) { - if (mux != RK_FUNC_GPIO) { - dev_err(info->dev, - "pin %d only supports a gpio mux\n", pin); - return -ENOTSUPP; - } else { - return 0; - } - } - - pinctrl_dbg(info->dev, "setting mux of GPIO%d-%d to %d\n", - bank->bank_num, pin, mux); - - regmap = (bank->iomux[iomux_num].type & IOMUX_SOURCE_PMU) - ? info->regmap_pmu : info->regmap_base; - - /* get basic quadrupel of mux registers and the correct reg inside */ - mask = (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) ? 0xf : 0x3; - reg = bank->iomux[iomux_num].offset; - if (bank->iomux[iomux_num].type & IOMUX_WIDTH_4BIT) { - if ((pin % 8) >= 4) - reg += 0x4; - bit = (pin % 4) * 4; - } else { - bit = (pin % 8) * 2; - } - - spin_lock_irqsave(&bank->slock, flags); - - data = (mask << (bit + 16)); - rmask = data | (data >> 16); - data |= (mux & mask) << bit; - ret = regmap_update_bits(regmap, reg, rmask, data); - - spin_unlock_irqrestore(&bank->slock, flags); - - return ret; -} - -#define RK3188_PULL_BITS_PER_PIN 2 -#define RK3188_PULL_PINS_PER_REG 8 -#define RK3188_PULL_BANK_STRIDE 16 -#define RK3188_PULL_PMU_OFFSET 0x64 - -#define RK3228_PULL_PMU_OFFSET 0x100 -#define RK3228_PULL_OFFSET 0x110 - -#define RK3288_PULL_OFFSET 0x140 -#define RK3368_PULL_PMU_OFFSET 0x10 -#define RK3368_PULL_OFFSET 0x100 - -static void rk3288_calc_pull_reg_and_bit(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit) -{ - struct rockchip_pinctrl *info = bank->drvdata; - struct rockchip_pin_ctrl *ctrl = info->ctrl; - - /* The first 24 pins of the first bank are located in PMU */ - if (bank->bank_num == 0) { - if (ctrl->type == RK3228) { - *regmap = info->regmap_base; - *reg = RK3228_PULL_PMU_OFFSET; - } else if (ctrl->type == RK3288) { - *regmap = info->regmap_pmu; - *reg = RK3188_PULL_PMU_OFFSET; - } else if (ctrl->type == RK3368) { - *regmap = info->regmap_pmu; - *reg = RK3368_PULL_PMU_OFFSET; - } - - *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); - *bit = pin_num % RK3188_PULL_PINS_PER_REG; - *bit *= RK3188_PULL_BITS_PER_PIN; - } else { - *regmap = info->regmap_base; - if (ctrl->type == RK3228) - *reg = RK3228_PULL_OFFSET; - else if (ctrl->type == RK3288) - *reg = RK3288_PULL_OFFSET; - else if (ctrl->type == RK3368) - *reg = RK3368_PULL_OFFSET; - - /* correct the offset, as we're starting with the 2nd bank */ - *reg -= 0x10; - *reg += bank->bank_num * RK3188_PULL_BANK_STRIDE; - *reg += ((pin_num / RK3188_PULL_PINS_PER_REG) * 4); - - *bit = (pin_num % RK3188_PULL_PINS_PER_REG); - *bit *= RK3188_PULL_BITS_PER_PIN; - } -} - -#define RK3228_DRV_PMU_OFFSET 0x200 -#define RK3228_DRV_GRF_OFFSET 0x210 - -#define RK3288_DRV_PMU_OFFSET 0x70 -#define RK3288_DRV_GRF_OFFSET 0x1c0 -#define RK3288_DRV_BITS_PER_PIN 2 -#define RK3288_DRV_PINS_PER_REG 8 -#define RK3288_DRV_BANK_STRIDE 16 -static int rk3288_drv_list[] = { 2, 4, 8, 12 }; - -#define RK3368_DRV_PMU_OFFSET 0x20 -#define RK3368_DRV_GRF_OFFSET 0x200 - -static void rk3288_calc_drv_reg_and_bit(struct rockchip_pin_bank *bank, - int pin_num, struct regmap **regmap, - int *reg, u8 *bit) -{ - struct rockchip_pinctrl *info = bank->drvdata; - struct rockchip_pin_ctrl *ctrl = info->ctrl; - - /* The first 24 pins of the first bank are located in PMU */ - if (bank->bank_num == 0) { - if (ctrl->type == RK3228) { - *regmap = info->regmap_base; - *reg = RK3228_DRV_PMU_OFFSET; - } else if (ctrl->type == RK3288) { - *regmap = info->regmap_pmu; - *reg = RK3288_DRV_PMU_OFFSET; - } else if (ctrl->type == RK3368) { - *regmap = info->regmap_pmu; - *reg = RK3368_DRV_PMU_OFFSET; - } - - *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4); - *bit = pin_num % RK3288_DRV_PINS_PER_REG; - *bit *= RK3288_DRV_BITS_PER_PIN; - } else { - *regmap = info->regmap_base; - if (ctrl->type == RK3228) - *reg = RK3228_DRV_GRF_OFFSET; - else if (ctrl->type == RK3288) - *reg = RK3288_DRV_GRF_OFFSET; - else if (ctrl->type == RK3368) - *reg = RK3368_DRV_GRF_OFFSET; - - /* correct the offset, as we're starting with the 2nd bank */ - *reg -= 0x10; - *reg += bank->bank_num * RK3288_DRV_BANK_STRIDE; - *reg += ((pin_num / RK3288_DRV_PINS_PER_REG) * 4); - - *bit = (pin_num % RK3288_DRV_PINS_PER_REG); - *bit *= RK3288_DRV_BITS_PER_PIN; - } -} - -static int rk3288_get_drive(struct rockchip_pin_bank *bank, int pin_num) -{ - struct regmap *regmap; - int reg, ret; - u32 data; - u8 bit; - - rk3288_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); - - ret = regmap_read(regmap, reg, &data); - if (ret) - return ret; - - data >>= bit; - data &= (1 << RK3288_DRV_BITS_PER_PIN) - 1; - - return rk3288_drv_list[data]; -} - -static int rk3288_set_drive(struct rockchip_pin_bank *bank, int pin_num, - int strength) -{ - struct rockchip_pinctrl *info = bank->drvdata; - struct regmap *regmap; - unsigned long flags; - int reg, ret, i; - u32 data, rmask; - u8 bit; - - rk3288_calc_drv_reg_and_bit(bank, pin_num, ®map, ®, &bit); - - ret = -EINVAL; - for (i = 0; i < ARRAY_SIZE(rk3288_drv_list); i++) { - if (rk3288_drv_list[i] == strength) { - ret = i; - break; - } - } - - if (ret < 0) { - dev_err(info->dev, "unsupported driver strength %d\n", - strength); - return ret; - } - - spin_lock_irqsave(&bank->slock, flags); - - /* enable the write to the equivalent lower bits */ - data = ((1 << RK3288_DRV_BITS_PER_PIN) - 1) << (bit + 16); - rmask = data | (data >> 16); - data |= (ret << bit); - - ret = regmap_update_bits(regmap, reg, rmask, data); - spin_unlock_irqrestore(&bank->slock, flags); - - return ret; -} - -static int rockchip_get_pull(struct rockchip_pin_bank *bank, int pin_num) -{ - struct rockchip_pinctrl *info = bank->drvdata; - struct rockchip_pin_ctrl *ctrl = info->ctrl; - struct regmap *regmap; - int reg, ret; - u8 bit; - u32 data; - - /* rk3066b does support any pulls */ - if (ctrl->type == RK3066B) - return PIN_CONFIG_BIAS_DISABLE; - - ctrl->pull_calc_reg(bank, pin_num, ®map, ®, &bit); - - ret = regmap_read(regmap, reg, &data); - if (ret) - return ret; - - switch (ctrl->type) { - case RK2928: - return !(data & BIT(bit)) - ? PIN_CONFIG_BIAS_PULL_PIN_DEFAULT - : PIN_CONFIG_BIAS_DISABLE; - case RK3188: - case RK3228: - case RK3288: - case RK3368: - data >>= bit; - data &= (1 << RK3188_PULL_BITS_PER_PIN) - 1; - - switch (data) { - case 0: - return PIN_CONFIG_BIAS_DISABLE; - case 1: - return PIN_CONFIG_BIAS_PULL_UP; - case 2: - return PIN_CONFIG_BIAS_PULL_DOWN; - case 3: - return PIN_CONFIG_BIAS_BUS_HOLD; - } - - dev_err(info->dev, "unknown pull setting\n"); - return -EIO; - default: - dev_err(info->dev, "unsupported pinctrl type\n"); - return -EINVAL; - }; -} - -static int rockchip_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; - struct regmap *regmap; - int reg, ret; - unsigned long flags; - u8 bit; - u32 data, rmask; - - pinctrl_dbg(info->dev, "setting pull of GPIO%d-%d to %d\n", - bank->bank_num, pin_num, pull); - - /* rk3066b does support any pulls */ - if (ctrl->type == RK3066B) - return pull ? -EINVAL : 0; - - ctrl->pull_calc_reg(bank, pin_num, ®map, ®, &bit); - - switch (ctrl->type) { - case RK2928: - spin_lock_irqsave(&bank->slock, flags); - - data = BIT(bit + 16); - if (pull == PIN_CONFIG_BIAS_DISABLE) - data |= BIT(bit); - ret = regmap_write(regmap, reg, data); - - spin_unlock_irqrestore(&bank->slock, flags); - break; - case RK3188: - case RK3228: - case RK3288: - case RK3368: - spin_lock_irqsave(&bank->slock, flags); - - /* enable the write to the equivalent lower bits */ - data = ((1 << RK3188_PULL_BITS_PER_PIN) - 1) << (bit + 16); - rmask = data | (data >> 16); - - switch (pull) { - case PIN_CONFIG_BIAS_DISABLE: - break; - case PIN_CONFIG_BIAS_PULL_UP: - data |= (1 << bit); - break; - case PIN_CONFIG_BIAS_PULL_DOWN: - data |= (2 << bit); - break; - case PIN_CONFIG_BIAS_BUS_HOLD: - data |= (3 << bit); - break; - default: - spin_unlock_irqrestore(&bank->slock, flags); - dev_err(info->dev, "unsupported pull setting %d\n", - pull); - return -EINVAL; - } - - ret = regmap_update_bits(regmap, reg, rmask, data); - - spin_unlock_irqrestore(&bank->slock, flags); - break; - default: - dev_err(info->dev, "unsupported pinctrl type\n"); - return -EINVAL; - } - - return ret; -} - -/* - * Pinmux_ops handling - */ - -static int rockchip_pmx_get_funcs_count(struct pinctrl_dev *pctldev) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - - return info->nfunctions; -} - -static const char *rockchip_pmx_get_func_name(struct pinctrl_dev *pctldev, - unsigned selector) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - - return info->functions[selector].name; -} - -static int rockchip_pmx_get_groups(struct pinctrl_dev *pctldev, - unsigned selector, - const char * const **groups, - unsigned * const num_groups) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - - *groups = info->functions[selector].groups; - *num_groups = info->functions[selector].ngroups; - - return 0; -} - -static int rockchip_pmx_enable(struct pinctrl_dev *pctldev, unsigned selector, - unsigned group) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - const unsigned int *pins = info->groups[group].pins; - const struct rockchip_pin_config *data = info->groups[group].data; - struct rockchip_pin_bank *bank; - int cnt, ret = 0; - - pinctrl_dbg(info->dev, "enable function %s group %s\n", - info->functions[selector].name, info->groups[group].name); - - /* - * for each pin in the pin group selected, program the correspoding pin - * pin function number in the config register. - */ - for (cnt = 0; cnt < info->groups[group].npins; cnt++) { - bank = pin_to_bank(info, pins[cnt]); - ret = rockchip_set_mux(bank, pins[cnt] - bank->pin_base, - data[cnt].func); - if (ret) - break; - } - - if (ret) { - /* revert the already done pin settings */ - for (cnt--; cnt >= 0; cnt--) - rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0); - - return ret; - } - - return 0; -} - -static void rockchip_pmx_disable(struct pinctrl_dev *pctldev, - unsigned selector, unsigned group) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - const unsigned int *pins = info->groups[group].pins; - struct rockchip_pin_bank *bank; - int cnt; - - pinctrl_dbg(info->dev, "disable function %s group %s\n", - info->functions[selector].name, info->groups[group].name); - - for (cnt = 0; cnt < info->groups[group].npins; cnt++) { - bank = pin_to_bank(info, pins[cnt]); - rockchip_set_mux(bank, pins[cnt] - bank->pin_base, 0); - } -} - -/* - * The calls to gpio_direction_output() and gpio_direction_input() - * leads to this function call (via the pinctrl_gpio_direction_{input|output}() - * function called from the gpiolib interface). - */ -static int rockchip_pmx_gpio_set_direction(struct pinctrl_dev *pctldev, - struct pinctrl_gpio_range *range, - unsigned offset, bool input) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - struct rockchip_pin_bank *bank; - struct gpio_chip *chip; - int pin, ret; - u32 data; - - chip = range->gc; - bank = gc_to_pin_bank(chip); - pin = offset - chip->base; - - dev_dbg(info->dev, "gpio_direction for pin %u as %s-%d to %s\n", - offset, range->name, pin, input ? "input" : "output"); - - ret = rockchip_set_mux(bank, pin, RK_FUNC_GPIO); - if (ret < 0) - return ret; - - data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); - /* set bit to 1 for output, 0 for input */ - if (!input) - data |= BIT(pin); - else - data &= ~BIT(pin); - writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); - - return 0; -} - -static const struct pinmux_ops rockchip_pmx_ops = { - .get_functions_count = rockchip_pmx_get_funcs_count, - .get_function_name = rockchip_pmx_get_func_name, - .get_function_groups = rockchip_pmx_get_groups, - .enable = rockchip_pmx_enable, - .disable = rockchip_pmx_disable, - .gpio_set_direction = rockchip_pmx_gpio_set_direction, -}; - -/* - * Pinconf_ops handling - */ - -static bool rockchip_pinconf_pull_valid(struct rockchip_pin_ctrl *ctrl, - enum pin_config_param pull) -{ - switch (ctrl->type) { - case RK2928: - return (pull == PIN_CONFIG_BIAS_PULL_PIN_DEFAULT || - pull == PIN_CONFIG_BIAS_DISABLE); - case RK3066B: - return pull ? false : true; - case RK3188: - case RK3228: - case RK3288: - case RK3368: - return (pull != PIN_CONFIG_BIAS_PULL_PIN_DEFAULT); - } - - return false; -} - -static int rockchip_gpio_direction_output( - struct gpio_chip *gc, unsigned offset, int value); -static int rockchip_gpio_direction_input( - struct gpio_chip *gc, unsigned offset); -static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset); - -/* set the pin config settings for a specified pin */ -static int rockchip_pinconf_set(struct pinctrl_dev *pctldev, unsigned int pin, - unsigned long configs) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - struct rockchip_pin_bank *bank = pin_to_bank(info, pin); - enum pin_config_param param; - u16 arg; - int rc; - - param = pinconf_to_config_param(configs); - arg = pinconf_to_config_argument(configs); - - switch (param) { - case PIN_CONFIG_BIAS_DISABLE: - rc = rockchip_set_pull(bank, pin - bank->pin_base, - param); - if (rc) - return rc; - break; - case PIN_CONFIG_BIAS_PULL_UP: - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: - case PIN_CONFIG_BIAS_BUS_HOLD: - if (!rockchip_pinconf_pull_valid(info->ctrl, param)) - return -ENOTSUPP; - - if (!arg) - return -EINVAL; - - rc = rockchip_set_pull(bank, pin - bank->pin_base, - param); - if (rc) - return rc; - break; - case PIN_CONFIG_OUTPUT: - rc = rockchip_gpio_direction_output( - &bank->gpio_chip, - pin - bank->pin_base, - arg); - if (rc) - return rc; - break; - - case PIN_CONFIG_INPUT_ENABLE: - if (arg == 1) { - rc = rockchip_gpio_direction_input( - &bank->gpio_chip, pin - bank->pin_base); - if (rc) - return rc; - } - break; - - case PIN_CONFIG_DRIVE_STRENGTH: - /* rk3228 rk3288 rk3368 is the first - with per-pin drive-strength */ - if ((RK3228 != info->ctrl->type) && - (RK3288 != info->ctrl->type) && - (RK3368 != info->ctrl->type)) - return -ENOTSUPP; - - rc = rk3288_set_drive(bank, pin - bank->pin_base, arg); - if (rc < 0) - return rc; - break; - default: - return -ENOTSUPP; - break; - } - - return 0; -} - -/* get the pin config settings for a specified pin */ -static int rockchip_pinconf_get(struct pinctrl_dev *pctldev, unsigned int pin, - unsigned long *config) -{ - struct rockchip_pinctrl *info = pinctrl_dev_get_drvdata(pctldev); - struct rockchip_pin_bank *bank = pin_to_bank(info, pin); - enum pin_config_param param = pinconf_to_config_param(*config); - u16 arg; - int rc; - - switch (param) { - case PIN_CONFIG_BIAS_DISABLE: - if (rockchip_get_pull(bank, pin - bank->pin_base) != param) - return -EINVAL; - - arg = 0; - break; - case PIN_CONFIG_BIAS_PULL_UP: - case PIN_CONFIG_BIAS_PULL_DOWN: - case PIN_CONFIG_BIAS_PULL_PIN_DEFAULT: - case PIN_CONFIG_BIAS_BUS_HOLD: - if (!rockchip_pinconf_pull_valid(info->ctrl, param)) - return -ENOTSUPP; - - if (rockchip_get_pull(bank, pin - bank->pin_base) != param) - return -EINVAL; - - arg = 1; - break; - case PIN_CONFIG_OUTPUT: - rc = rockchip_get_mux(bank, pin - bank->pin_base); - if (rc != RK_FUNC_GPIO) - return -EINVAL; - - rc = rockchip_gpio_get(&bank->gpio_chip, pin - bank->pin_base); - if (rc < 0) - return rc; - - arg = rc ? 1 : 0; - break; - case PIN_CONFIG_DRIVE_STRENGTH: - /* rk3228 rk3288 RK3368 is the first with per-pin - * drive-strength - */ - if ((RK3228 != info->ctrl->type) && - (RK3288 != info->ctrl->type) && - (RK3368 != info->ctrl->type)) - return -ENOTSUPP; - - rc = rk3288_get_drive(bank, pin - bank->pin_base); - if (rc < 0) - return rc; - - arg = rc; - break; - default: - return -ENOTSUPP; - break; - } - - *config = pinconf_to_config_packed(param, arg); - - return 0; -} - -static const struct pinconf_ops rockchip_pinconf_ops = { - .pin_config_get = rockchip_pinconf_get, - .pin_config_set = rockchip_pinconf_set, - .is_generic = true, -}; - -static const struct of_device_id rockchip_bank_match[] = { - { .compatible = "rockchip,gpio-bank" }, - { .compatible = "rockchip,rk3188-gpio-bank0" }, - {}, -}; - -static void rockchip_pinctrl_child_count(struct rockchip_pinctrl *info, - struct device_node *np) -{ - struct device_node *child; - - for_each_child_of_node(np, child) { - if (of_match_node(rockchip_bank_match, child)) - continue; - - info->nfunctions++; - info->ngroups += of_get_child_count(child); - } -} - -static int rockchip_pinctrl_parse_groups(struct device_node *np, - struct rockchip_pin_group *grp, - struct rockchip_pinctrl *info, - u32 index) -{ - struct rockchip_pin_bank *bank; - int size; - const __be32 *list; - int num; - int i, j; - int ret; - - dev_dbg(info->dev, "group(%d): %s\n", index, np->name); - - /* Initialise group */ - grp->name = np->name; - - /* - * the binding format is rockchip,pins = , - * do sanity check and calculate pins number - */ - list = of_get_property(np, "rockchip,pins", &size); - /* we do not check return since it's safe node passed down */ - size /= sizeof(*list); - if (!size || size % 4) { - dev_err(info->dev, - "wrong pins number or pins and configs should be by 4\n"); - return -EINVAL; - } - - grp->npins = size / 4; - - grp->pins = devm_kzalloc(info->dev, grp->npins * sizeof(unsigned int), - GFP_KERNEL); - grp->data = devm_kzalloc(info->dev, grp->npins * - sizeof(struct rockchip_pin_config), - GFP_KERNEL); - if (!grp->pins || !grp->data) - return -ENOMEM; - - for (i = 0, j = 0; i < size; i += 4, j++) { - const __be32 *phandle; - struct device_node *np_config; - - num = be32_to_cpu(*list++); - bank = bank_num_to_bank(info, num); - if (IS_ERR(bank)) - return PTR_ERR(bank); - - grp->pins[j] = bank->pin_base + be32_to_cpu(*list++); - grp->data[j].func = be32_to_cpu(*list++); - - phandle = list++; - if (!phandle) - return -EINVAL; - - np_config = of_find_node_by_phandle(be32_to_cpup(phandle)); - ret = pinconf_generic_parse_dt_config(np_config, - &grp->data[j].configs, - &grp->data[j].nconfigs); - if (ret) - return ret; - } - - return 0; -} - -static int rockchip_pinctrl_parse_functions(struct device_node *np, - struct rockchip_pinctrl *info, - u32 index) -{ - struct device_node *child; - struct rockchip_pmx_func *func; - struct rockchip_pin_group *grp; - int ret; - static u32 grp_index; - u32 i = 0; - - dev_dbg(info->dev, "parse function(%d): %s\n", index, np->name); - - func = &info->functions[index]; - - /* Initialise function */ - func->name = np->name; - func->ngroups = of_get_child_count(np); - if (func->ngroups <= 0) - return 0; - - func->groups = devm_kzalloc(info->dev, - func->ngroups * sizeof(char *), GFP_KERNEL); - if (!func->groups) - return -ENOMEM; - - for_each_child_of_node(np, child) { - func->groups[i] = child->name; - grp = &info->groups[grp_index++]; - ret = rockchip_pinctrl_parse_groups(child, grp, info, i++); - if (ret) - return ret; - } - - return 0; -} - -static int rockchip_pinctrl_parse_dt(struct platform_device *pdev, - struct rockchip_pinctrl *info) -{ - struct device *dev = &pdev->dev; - struct device_node *np = dev->of_node; - struct device_node *child; - int ret; - int i; - - rockchip_pinctrl_child_count(info, np); - - dev_dbg(&pdev->dev, "nfunctions = %d\n", info->nfunctions); - dev_dbg(&pdev->dev, "ngroups = %d\n", info->ngroups); - - info->functions = devm_kzalloc(dev, info->nfunctions * - sizeof(struct rockchip_pmx_func), - GFP_KERNEL); - if (!info->functions) { - dev_err(dev, "failed to allocate memory for function list\n"); - return -EINVAL; - } - - info->groups = devm_kzalloc(dev, info->ngroups * - sizeof(struct rockchip_pin_group), - GFP_KERNEL); - if (!info->groups) { - dev_err(dev, "failed allocate memory for ping group list\n"); - return -EINVAL; - } - - i = 0; - - for_each_child_of_node(np, child) { - if (of_match_node(rockchip_bank_match, child)) - continue; - - ret = rockchip_pinctrl_parse_functions(child, info, i++); - if (ret) { - dev_err(&pdev->dev, "failed to parse function\n"); - return ret; - } - } - - return 0; -} - -static int rockchip_pinctrl_register(struct platform_device *pdev, - struct rockchip_pinctrl *info) -{ - struct pinctrl_desc *ctrldesc = &info->pctl; - struct pinctrl_pin_desc *pindesc, *pdesc; - struct rockchip_pin_bank *pin_bank; - int pin, bank, ret; - int k; - - ctrldesc->name = "rockchip-pinctrl"; - ctrldesc->owner = THIS_MODULE; - ctrldesc->pctlops = &rockchip_pctrl_ops; - ctrldesc->pmxops = &rockchip_pmx_ops; - ctrldesc->confops = &rockchip_pinconf_ops; - - pindesc = devm_kzalloc(&pdev->dev, sizeof(*pindesc) * - info->ctrl->nr_pins, GFP_KERNEL); - if (!pindesc) { - dev_err(&pdev->dev, "mem alloc for pin descriptors failed\n"); - return -ENOMEM; - } - ctrldesc->pins = pindesc; - ctrldesc->npins = info->ctrl->nr_pins; - - pdesc = pindesc; - for (bank = 0 , k = 0; bank < info->ctrl->nr_banks; bank++) { - pin_bank = &info->ctrl->pin_banks[bank]; - for (pin = 0; pin < pin_bank->nr_pins; pin++, k++) { - pdesc->number = k; - pdesc->name = kasprintf(GFP_KERNEL, "%s-%d", - pin_bank->name, pin); - pdesc++; - } - } - - info->pctl_dev = pinctrl_register(ctrldesc, &pdev->dev, info); - if (!info->pctl_dev) { - dev_err(&pdev->dev, "could not register pinctrl driver\n"); - return -EINVAL; - } - - for (bank = 0; bank < info->ctrl->nr_banks; ++bank) { - pin_bank = &info->ctrl->pin_banks[bank]; - pin_bank->grange.name = pin_bank->name; - pin_bank->grange.id = bank; - pin_bank->grange.pin_base = pin_bank->pin_base; - pin_bank->grange.base = pin_bank->gpio_chip.base; - pin_bank->grange.npins = pin_bank->gpio_chip.ngpio; - pin_bank->grange.gc = &pin_bank->gpio_chip; - pinctrl_add_gpio_range(info->pctl_dev, &pin_bank->grange); - } - - ret = rockchip_pinctrl_parse_dt(pdev, info); - if (ret) { - pinctrl_unregister(info->pctl_dev); - return ret; - } - - return 0; -} - -/* - * GPIO handling - */ - -static int rockchip_gpio_request(struct gpio_chip *chip, unsigned offset) -{ - return pinctrl_request_gpio(chip->base + offset); -} - -static void rockchip_gpio_free(struct gpio_chip *chip, unsigned offset) -{ - pinctrl_free_gpio(chip->base + offset); -} - -static void rockchip_gpio_set(struct gpio_chip *gc, unsigned offset, int value) -{ - struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); - void __iomem *reg = bank->reg_base + GPIO_SWPORT_DR; - unsigned long flags; - u32 data; - - spin_lock_irqsave(&bank->slock, flags); - - data = readl(reg); - data &= ~BIT(offset); - if (value) - data |= BIT(offset); - writel(data, reg); - - spin_unlock_irqrestore(&bank->slock, flags); -} - -/* - * Returns the level of the pin for input direction and setting of the DR - * register for output gpios. - */ -static int rockchip_gpio_get(struct gpio_chip *gc, unsigned offset) -{ - struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); - u32 data; - - data = readl(bank->reg_base + GPIO_EXT_PORT); - data >>= offset; - data &= 1; - return data; -} - -/* - * gpiolib gpio_direction_input callback function. The setting of the pin - * mux function as 'gpio input' will be handled by the pinctrl susbsystem - * interface. - */ -static int rockchip_gpio_direction_input(struct gpio_chip *gc, unsigned offset) -{ - return pinctrl_gpio_direction_input(gc->base + offset); -} - -/* - * gpiolib gpio_direction_output callback function. The setting of the pin - * mux function as 'gpio output' will be handled by the pinctrl susbsystem - * interface. - */ -static int rockchip_gpio_direction_output(struct gpio_chip *gc, - unsigned offset, int value) -{ - rockchip_gpio_set(gc, offset, value); - return pinctrl_gpio_direction_output(gc->base + offset); -} - -/* - * gpiolib gpio_to_irq callback function. Creates a mapping between a GPIO pin - * and a virtual IRQ, if not already present. - */ -static int rockchip_gpio_to_irq(struct gpio_chip *gc, unsigned offset) -{ - struct rockchip_pin_bank *bank = gc_to_pin_bank(gc); - unsigned int virq; - - if (!bank->domain) - return -ENXIO; - - virq = irq_create_mapping(bank->domain, offset); - - return (virq) ? : -ENXIO; -} - -static const struct gpio_chip rockchip_gpiolib_chip = { - .request = rockchip_gpio_request, - .free = rockchip_gpio_free, - .set = rockchip_gpio_set, - .get = rockchip_gpio_get, - .direction_input = rockchip_gpio_direction_input, - .direction_output = rockchip_gpio_direction_output, - .to_irq = rockchip_gpio_to_irq, - .owner = THIS_MODULE, -}; - -/* - * Interrupt handling - */ - -static void rockchip_irq_demux(unsigned int irq, struct irq_desc *desc) -{ - struct irq_chip *chip = irq_get_chip(irq); - struct rockchip_pin_bank *bank = irq_get_handler_data(irq); - u32 polarity = 0, data = 0; - u32 pend; - bool edge_changed = false; - - pinctrl_dbg(bank->drvdata->dev, "got irq for bank %s\n", bank->name); - - chained_irq_enter(chip, desc); - - pend = readl_relaxed(bank->reg_base + GPIO_INT_STATUS); - - if (bank->toggle_edge_mode) { - polarity = readl_relaxed(bank->reg_base + - GPIO_INT_POLARITY); - data = readl_relaxed(bank->reg_base + GPIO_EXT_PORT); - } - - while (pend) { - unsigned int virq; - - irq = __ffs(pend); - pend &= ~BIT(irq); - virq = irq_linear_revmap(bank->domain, irq); - - if (!virq) { - dev_err(bank->drvdata->dev, "unmapped irq %d\n", irq); - continue; - } - - pinctrl_dbg(bank->drvdata->dev, "handling irq %d\n", irq); - - /* - * Triggering IRQ on both rising and falling edge - * needs manual intervention. - */ - if (bank->toggle_edge_mode & BIT(irq)) { - if (data & BIT(irq)) - polarity &= ~BIT(irq); - else - polarity |= BIT(irq); - - edge_changed = true; - } - - generic_handle_irq(virq); - } - - if (bank->toggle_edge_mode && edge_changed) { - /* Interrupt params should only be set with ints disabled */ - data = readl_relaxed(bank->reg_base + GPIO_INTEN); - writel_relaxed(0, bank->reg_base + GPIO_INTEN); - writel(polarity, bank->reg_base + GPIO_INT_POLARITY); - writel(data, bank->reg_base + GPIO_INTEN); - } - - chained_irq_exit(chip, desc); -} - -static int rockchip_gpio_irq_set_type(struct irq_data *d, unsigned int type) -{ - struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); - u32 mask = BIT(d->hwirq); - u32 polarity; - u32 level; - u32 data; - int ret; - unsigned long flags; - - /* make sure the pin is configured as gpio input */ - ret = rockchip_set_mux(bank, d->hwirq, RK_FUNC_GPIO); - if (ret < 0) - return ret; - - data = readl_relaxed(bank->reg_base + GPIO_SWPORT_DDR); - data &= ~mask; - writel_relaxed(data, bank->reg_base + GPIO_SWPORT_DDR); - - if (type & IRQ_TYPE_EDGE_BOTH) - __irq_set_handler_locked(d->irq, handle_edge_irq); - else - __irq_set_handler_locked(d->irq, handle_level_irq); - - spin_lock_irqsave(&bank->slock, flags); - - level = readl_relaxed(bank->reg_base + GPIO_INTTYPE_LEVEL); - polarity = readl_relaxed(bank->reg_base + GPIO_INT_POLARITY); - - switch (type) { - case IRQ_TYPE_EDGE_BOTH: - bank->toggle_edge_mode |= mask; - level |= mask; - - /* - * Determine gpio state. If 1 next interrupt should be falling - * otherwise rising. - */ - data = readl(bank->reg_base + GPIO_EXT_PORT); - if (data & mask) - polarity &= ~mask; - else - polarity |= mask; - break; - case IRQ_TYPE_EDGE_RISING: - bank->toggle_edge_mode &= ~mask; - level |= mask; - polarity |= mask; - break; - case IRQ_TYPE_EDGE_FALLING: - bank->toggle_edge_mode &= ~mask; - level |= mask; - polarity &= ~mask; - break; - case IRQ_TYPE_LEVEL_HIGH: - bank->toggle_edge_mode &= ~mask; - level &= ~mask; - polarity |= mask; - break; - case IRQ_TYPE_LEVEL_LOW: - bank->toggle_edge_mode &= ~mask; - level &= ~mask; - polarity &= ~mask; - break; - default: - return -EINVAL; - } - - writel_relaxed(level, bank->reg_base + GPIO_INTTYPE_LEVEL); - writel_relaxed(polarity, bank->reg_base + GPIO_INT_POLARITY); - - spin_unlock_irqrestore(&bank->slock, flags); - - return 0; -} - -static inline void rockchip_gpio_bit_op(void __iomem *reg_base - , unsigned int offset, u32 bit, unsigned char flag) -{ - u32 val = __raw_readl(reg_base + offset); - - if (flag) - val |= BIT(bit); - else - val &= ~BIT(bit); - - __raw_writel(val, reg_base + offset); -} - -static inline unsigned gpio_to_bit(struct rockchip_pin_bank *bank, - unsigned gpio) -{ - while (gpio >= (bank->pin_base + bank->nr_pins)) - bank++; - - return gpio - bank->pin_base; -} - -static inline unsigned offset_to_bit(unsigned offset) -{ - return 1u << offset; -} - -static void GPIOEnableIntr(void __iomem *reg_base, unsigned int bit) -{ - rockchip_gpio_bit_op(reg_base, GPIO_INTEN, bit, 1); -} - -static void GPIODisableIntr(void __iomem *reg_base, unsigned int bit) -{ - rockchip_gpio_bit_op(reg_base, GPIO_INTEN, bit, 0); -} - -static void GPIOAckIntr(void __iomem *reg_base, unsigned int bit) -{ - rockchip_gpio_bit_op(reg_base, GPIO_PORTS_EOI, bit, 1); -} - -static int rockchip_gpio_irq_set_wake(struct irq_data *d, unsigned int on) -{ - struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); - u32 bit = d->hwirq; - unsigned long flags; - - spin_lock_irqsave(&bank->slock, flags); - - if (on) - bank->suspend_wakeup |= BIT(bit); - else - bank->suspend_wakeup &= ~BIT(bit); - spin_unlock_irqrestore(&bank->slock, flags); - - return 0; -} - -static void rockchip_gpio_irq_unmask(struct irq_data *d) -{ - struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); - u32 bit = d->hwirq; - unsigned long flags; - - spin_lock_irqsave(&bank->slock, flags); - GPIOEnableIntr(bank->reg_base, bit); - spin_unlock_irqrestore(&bank->slock, flags); -} - -static void rockchip_gpio_irq_mask(struct irq_data *d) -{ - struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); - u32 bit = d->hwirq; - unsigned long flags; - - spin_lock_irqsave(&bank->slock, flags); - GPIODisableIntr(bank->reg_base, bit); - spin_unlock_irqrestore(&bank->slock, flags); -} - -static void rockchip_gpio_irq_ack(struct irq_data *d) -{ - struct rockchip_pin_bank *bank = irq_data_get_irq_chip_data(d); - u32 bit = d->hwirq; - - GPIOAckIntr(bank->reg_base, bit); -} - -static struct irq_chip rockchip_gpio_irq_chip = { - .name = "ROCKCHIP_GPIO_CHIP", - .irq_ack = rockchip_gpio_irq_ack, - .irq_disable = rockchip_gpio_irq_mask, - .irq_mask = rockchip_gpio_irq_mask, - .irq_unmask = rockchip_gpio_irq_unmask, - .irq_set_type = rockchip_gpio_irq_set_type, - .irq_set_wake = rockchip_gpio_irq_set_wake, -}; - -static int rockchip_gpio_irq_map(struct irq_domain *d, unsigned int irq, - irq_hw_number_t hwirq) -{ - struct rockchip_pin_bank *bank = d->host_data; - struct irq_data *irq_data = irq_get_irq_data(irq); - - if (!bank) { - dev_err(bank->drvdata->dev, "%s:bank=0x%p,irq=%d\n", - __func__, bank, irq); - return -EINVAL; - } - - irq_set_chip_and_handler(irq, &rockchip_gpio_irq_chip, - handle_level_irq); - irq_set_chip_data(irq, bank); - set_irq_flags(irq, IRQF_VALID); - - irq_data->hwirq = hwirq; - irq_data->irq = irq; - - pinctrl_dbg(bank->drvdata->dev, "%s:irq = %d, hwirq =%ld\n", - __func__, irq, hwirq); - return 0; -} - -static const struct irq_domain_ops rockchip_gpio_irq_ops = { - .map = rockchip_gpio_irq_map, - .xlate = irq_domain_xlate_twocell, -}; - -static int rockchip_interrupts_register(struct platform_device *pdev, - struct rockchip_pinctrl *info) -{ - struct rockchip_pin_ctrl *ctrl = info->ctrl; - struct rockchip_pin_bank *bank = ctrl->pin_banks; - int i; - - for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { - if (!bank->valid) { - dev_warn(&pdev->dev, "bank %s is not valid\n", - bank->name); - continue; - } - - __raw_writel(0, bank->reg_base + GPIO_INTEN); - - bank->drvdata = info; - bank->domain = irq_domain_add_linear(bank->of_node, 32, - &rockchip_gpio_irq_ops, bank); - if (!bank->domain) { - dev_warn(&pdev->dev, "could not initialize irq domain for bank %s\n", - bank->name); - continue; - } - - irq_set_handler_data(bank->irq, bank); - irq_set_chained_handler(bank->irq, rockchip_irq_demux); - } - - return 0; -} - -static int rockchip_gpiolib_register(struct platform_device *pdev, - struct rockchip_pinctrl *info) -{ - struct rockchip_pin_ctrl *ctrl = info->ctrl; - struct rockchip_pin_bank *bank = ctrl->pin_banks; - struct gpio_chip *gc; - int ret; - int i; - - for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { - if (!bank->valid) { - dev_warn(&pdev->dev, "bank %s is not valid\n", - bank->name); - continue; - } - - bank->gpio_chip = rockchip_gpiolib_chip; - - gc = &bank->gpio_chip; - gc->base = bank->pin_base; - gc->ngpio = bank->nr_pins; - gc->dev = &pdev->dev; - gc->of_node = bank->of_node; - gc->label = bank->name; - - ret = gpiochip_add(gc); - if (ret) { - dev_err(&pdev->dev, "failed to register gpio_chip %s, error code: %d\n", - gc->label, ret); - goto fail; - } - } - - rockchip_interrupts_register(pdev, info); - - return 0; - -fail: - for (--i, --bank; i >= 0; --i, --bank) { - if (!bank->valid) - continue; - - if (gpiochip_remove(&bank->gpio_chip)) - dev_err(&pdev->dev, "gpio chip %s remove failed\n", - bank->gpio_chip.label); - } - return ret; -} - -static int rockchip_gpiolib_unregister(struct platform_device *pdev, - struct rockchip_pinctrl *info) -{ - struct rockchip_pin_ctrl *ctrl = info->ctrl; - struct rockchip_pin_bank *bank = ctrl->pin_banks; - int ret = 0; - int i; - - for (i = 0; !ret && i < ctrl->nr_banks; ++i, ++bank) { - if (!bank->valid) - continue; - - ret = gpiochip_remove(&bank->gpio_chip); - } - - if (ret) - dev_err(&pdev->dev, "gpio chip remove failed\n"); - - return ret; -} - -static int rockchip_get_bank_data(struct rockchip_pin_bank *bank, - struct rockchip_pinctrl *info) -{ - struct resource res; - void __iomem *base; - - if (of_address_to_resource(bank->of_node, 0, &res)) { - dev_err(info->dev, "cannot find IO resource for bank\n"); - return -ENOENT; - } - - bank->reg_base = devm_ioremap_resource(info->dev, &res); - if (IS_ERR(bank->reg_base)) - return PTR_ERR(bank->reg_base); - - /* - * 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")) { - struct device_node *node; - - node = of_parse_phandle(bank->of_node->parent, - "rockchip,pmugrf", 0); - if (!node) { - if (of_address_to_resource(bank->of_node, 1, &res)) { - dev_err(info->dev, "cannot find IO resource for bank\n"); - return -ENOENT; - } - - base = devm_ioremap_resource(info->dev, &res); - if (IS_ERR(base)) - return PTR_ERR(base); - rockchip_regmap_config.max_register = - resource_size(&res) - 4; - rockchip_regmap_config.name = - "rockchip,rk3188-gpio-bank0-pull"; - bank->regmap_pull = devm_regmap_init_mmio(info->dev, - base, - &rockchip_regmap_config); - } - } - - bank->irq = irq_of_parse_and_map(bank->of_node, 0); - - bank->clk = of_clk_get(bank->of_node, 0); - if (IS_ERR(bank->clk)) - return PTR_ERR(bank->clk); - - return clk_prepare_enable(bank->clk); -} - -static const struct of_device_id rockchip_pinctrl_dt_match[]; - -/* retrieve the soc specific data */ -static struct rockchip_pin_ctrl *rockchip_pinctrl_get_soc_data( - struct rockchip_pinctrl *d, - struct platform_device *pdev) -{ - const struct of_device_id *match; - struct device_node *node = pdev->dev.of_node; - struct device_node *np; - struct rockchip_pin_ctrl *ctrl; - struct rockchip_pin_bank *bank; - int grf_offs, pmu_offs, i, j; - - 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) { - if (!strcmp(bank->name, np->name)) { - bank->of_node = np; - - if (!rockchip_get_bank_data(bank, d)) - bank->valid = true; - - break; - } - } - } - - grf_offs = ctrl->grf_mux_offset; - pmu_offs = ctrl->pmu_mux_offset; - bank = ctrl->pin_banks; - for (i = 0; i < ctrl->nr_banks; ++i, ++bank) { - int bank_pins = 0; - - spin_lock_init(&bank->slock); - bank->drvdata = d; - bank->pin_base = ctrl->nr_pins; - ctrl->nr_pins += bank->nr_pins; - - /* calculate iomux offsets */ - for (j = 0; j < 4; j++) { - struct rockchip_iomux *iom = &bank->iomux[j]; - int inc; - - if (bank_pins >= bank->nr_pins) - break; - - /* preset offset value, set new start value */ - if (iom->offset >= 0) { - if (iom->type & IOMUX_SOURCE_PMU) - pmu_offs = iom->offset; - else - grf_offs = iom->offset; - } else { /* set current offset */ - iom->offset = (iom->type & IOMUX_SOURCE_PMU) ? - pmu_offs : grf_offs; - } - - pinctrl_dbg(d->dev, "bank %d, iomux %d has offset 0x%x\n", - i, j, iom->offset); - - /* - * Increase offset according to iomux width. - * 4bit iomux'es are spread over two registers. - */ - inc = (iom->type & IOMUX_WIDTH_4BIT) ? 8 : 4; - if (iom->type & IOMUX_SOURCE_PMU) - pmu_offs += inc; - else - grf_offs += inc; - - bank_pins += 8; - } - } - - return ctrl; -} - -#ifdef CONFIG_PM -static int rockchip_pinctrl_suspend(void) -{ - struct rockchip_pinctrl *info = g_info; - struct rockchip_pin_ctrl *ctrl = info->ctrl; - struct rockchip_pin_bank *bank = ctrl->pin_banks; - int n; - - for (n = 0; n < ctrl->nr_banks; n++) { - bank->saved_wakeup = __raw_readl(bank->reg_base + GPIO_INTEN); - __raw_writel(bank->suspend_wakeup, bank->reg_base + GPIO_INTEN); - - if (!bank->suspend_wakeup) - clk_disable_unprepare(bank->clk); - bank++; - } - - return 0; -} - -static void rockchip_pinctrl_resume(void) -{ - struct rockchip_pinctrl *info = g_info; - struct rockchip_pin_ctrl *ctrl = info->ctrl; - struct rockchip_pin_bank *bank = ctrl->pin_banks; - int n; - u32 isr; - - for (n = 0; n < ctrl->nr_banks; n++) { - if (!bank->suspend_wakeup) - clk_prepare_enable(bank->clk); - - /* keep enable for resume irq */ - isr = __raw_readl(bank->reg_base + GPIO_INT_STATUS); - __raw_writel(bank->saved_wakeup - | (bank->suspend_wakeup & isr) - , bank->reg_base + GPIO_INTEN); - bank++; - } -} - -static struct syscore_ops rockchip_gpio_syscore_ops = { - .suspend = rockchip_pinctrl_suspend, - .resume = rockchip_pinctrl_resume, -}; -#endif - -static int rockchip_pinctrl_probe(struct platform_device *pdev) -{ - struct rockchip_pinctrl *info; - struct device *dev = &pdev->dev; - struct rockchip_pin_ctrl *ctrl; - struct device_node *np = pdev->dev.of_node, *node; - struct resource *res; - void __iomem *base; - int ret; - - if (!dev->of_node) { - dev_err(dev, "device tree node not found\n"); - return -ENODEV; - } - - info = devm_kzalloc(dev, sizeof(struct rockchip_pinctrl), GFP_KERNEL); - if (!info) - return -ENOMEM; - - info->dev = dev; - - ctrl = rockchip_pinctrl_get_soc_data(info, pdev); - if (!ctrl) { - dev_err(dev, "driver data not available\n"); - return -EINVAL; - } - info->ctrl = ctrl; - g_info = info; - - node = of_parse_phandle(np, "rockchip,grf", 0); - if (node) { - info->regmap_base = syscon_node_to_regmap(node); - if (IS_ERR(info->regmap_base)) - return PTR_ERR(info->regmap_base); - } else { - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - rockchip_regmap_config.max_register = resource_size(res) - 4; - rockchip_regmap_config.name = "rockchip,pinctrl"; - info->regmap_base = devm_regmap_init_mmio(&pdev->dev, base, - &rockchip_regmap_config); - - /* to check for the old dt-bindings */ - info->reg_size = resource_size(res); - - /* Honor the old binding, with pull registers as 2nd resource */ - if (ctrl->type == RK3188 && info->reg_size < 0x200) { - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); - base = devm_ioremap_resource(&pdev->dev, res); - if (IS_ERR(base)) - return PTR_ERR(base); - - rockchip_regmap_config.max_register = - resource_size(res) - 4; - rockchip_regmap_config.name = "rockchip,pinctrl-pull"; - info->regmap_pull = devm_regmap_init_mmio(&pdev->dev, - base, - &rockchip_regmap_config); - } - } - - /* try to find the optional reference to the pmu syscon */ - node = of_parse_phandle(np, "rockchip,pmugrf", 0); - if (node) { - info->regmap_pmu = syscon_node_to_regmap(node); - if (IS_ERR(info->regmap_pmu)) - return PTR_ERR(info->regmap_pmu); - } - - ret = rockchip_gpiolib_register(pdev, info); - if (ret) - return ret; - - ret = rockchip_pinctrl_register(pdev, info); - if (ret) { - rockchip_gpiolib_unregister(pdev, info); - return ret; - } - - platform_set_drvdata(pdev, info); -#ifdef CONFIG_PM - register_syscore_ops(&rockchip_gpio_syscore_ops); -#endif - - return 0; -} - -static struct rockchip_pin_bank rk3228_pin_banks[] = { - PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", 0, 0, 0, 0), - PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", 0, 0, 0, 0), - PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, 0, 0, 0), - PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, 0), -}; - -static struct rockchip_pin_ctrl rk3228_pin_ctrl = { - .pin_banks = rk3228_pin_banks, - .nr_banks = ARRAY_SIZE(rk3228_pin_banks), - .label = "RK3228-GPIO", - .type = RK3228, - .grf_mux_offset = 0x0, - .pmu_mux_offset = 0x0, - .pull_calc_reg = rk3288_calc_pull_reg_and_bit, -}; -static struct rockchip_pin_bank rk3368_pin_banks[] = { - PIN_BANK_IOMUX_FLAGS(0, 32, "gpio0", IOMUX_SOURCE_PMU, - IOMUX_SOURCE_PMU, - IOMUX_SOURCE_PMU, - IOMUX_SOURCE_PMU), - PIN_BANK_IOMUX_FLAGS(1, 32, "gpio1", 0, 0, 0, 0), - PIN_BANK_IOMUX_FLAGS(2, 32, "gpio2", 0, 0, 0, 0), - PIN_BANK_IOMUX_FLAGS(3, 32, "gpio3", 0, 0, 0, 0), -}; - -static struct rockchip_pin_ctrl rk3368_pin_ctrl = { - .pin_banks = rk3368_pin_banks, - .nr_banks = ARRAY_SIZE(rk3368_pin_banks), - .label = "RK3368-GPIO", - .type = RK3368, - .grf_mux_offset = 0x0, - .pmu_mux_offset = 0x0, - .pull_calc_reg = rk3288_calc_pull_reg_and_bit, -}; - -static const struct of_device_id rockchip_pinctrl_dt_match[] = { - { .compatible = "rockchip,rk3228-pinctrl", - .data = (void *)&rk3228_pin_ctrl }, - { .compatible = "rockchip,rk3368-pinctrl", - .data = (void *)&rk3368_pin_ctrl }, - {}, -}; -MODULE_DEVICE_TABLE(of, rockchip_pinctrl_dt_match); - -static struct platform_driver rockchip_pinctrl_driver = { - .probe = rockchip_pinctrl_probe, - .driver = { - .name = "rk3368-pinctrl", - .owner = THIS_MODULE, - .of_match_table = rockchip_pinctrl_dt_match, - }, -}; - -static int __init rockchip_pinctrl_drv_register(void) -{ - return platform_driver_register(&rockchip_pinctrl_driver); -} -postcore_initcall(rockchip_pinctrl_drv_register); - -MODULE_AUTHOR("Heiko Stuebner "); -MODULE_DESCRIPTION("Rockchip pinctrl driver"); -MODULE_LICENSE("GPL v2"); diff --git a/include/dt-bindings/pinctrl/rk.h b/include/dt-bindings/pinctrl/rk.h new file mode 100644 index 000000000000..976424b3fbdc --- /dev/null +++ b/include/dt-bindings/pinctrl/rk.h @@ -0,0 +1,185 @@ +/* + * Header providing constants for Rockchip pinctrl bindings. + * + * Copyright (c) 2013 MundoReader S.L. + * Author: Heiko Stuebner + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation; either version 2 of the License, or + * (at your option) any later version. + * + * 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. + */ + +#ifndef __DT_BINDINGS_ROCKCHIP_PINCTRL_H__ +#define __DT_BINDINGS_ROCKCHIP_PINCTRL_H__ + +#define RK_GPIO0 0 +#define RK_GPIO1 1 +#define RK_GPIO2 2 +#define RK_GPIO3 3 +#define RK_GPIO4 4 +#define RK_GPIO5 5 +#define RK_GPIO6 6 +#define RK_GPIO7 7 +#define RK_GPIO8 8 + + +#define RK_FUNC_GPIO 0 +#define RK_FUNC_1 1 +#define RK_FUNC_2 2 +#define RK_FUNC_3 3 +#define RK_FUNC_4 4 +#define RK_FUNC_5 5 +#define RK_FUNC_6 6 +#define RK_FUNC_7 7 + + + +/*special virtual pin for vcc domain setting*/ +#define VIRTUAL_PIN_FOR_AP0_VCC 0xfA00 +#define VIRTUAL_PIN_FOR_AP1_VCC 0xfA10 +#define VIRTUAL_PIN_FOR_CIF_VCC 0xfA20 +#define VIRTUAL_PIN_FOR_FLASH_VCC 0xfA30 +#define VIRTUAL_PIN_FOR_VCCIO0_VCC 0xfA40 +#define VIRTUAL_PIN_FOR_VCCIO1_VCC 0xfA50 +#define VIRTUAL_PIN_FOR_LCDC0_VCC 0xfA60 +#define VIRTUAL_PIN_FOR_LCDC1_VCC 0xfA70 + + +#define RK32_VIRTUAL_PIN_FOR_LCDC_VCC 0xfA00 +#define RK32_VIRTUAL_PIN_FOR_DVP_VCC 0xfA10 +#define RK32_VIRTUAL_PIN_FOR_FLASH0_VCC 0xfA20 +#define RK32_VIRTUAL_PIN_FOR_FLASH1_VCC 0xfA30 +#define RK32_VIRTUAL_PIN_FOR_WIFI_VCC 0xfA40 +#define RK32_VIRTUAL_PIN_FOR_BB_VCC 0xfA50 +#define RK32_VIRTUAL_PIN_FOR_AUDIO_VCC 0xfA60 +#define RK32_VIRTUAL_PIN_FOR_SDCARD_VCC 0xfA70 + +#define RK32_VIRTUAL_PIN_FOR_GPIO30_VCC 0xfB00 +#define RK32_VIRTUAL_PIN_FOR_GPIO1830_VCC 0xfB10 + + +#define TYPE_PULL_REG 0x01 +#define TYPE_VOL_REG 0x02 +#define TYPE_DRV_REG 0x03 +#define TYPE_TRI_REG 0x04 + +#define RK2928_PULL_OFFSET 0x118 +#define RK2928_PULL_PINS_PER_REG 16 +#define RK2928_PULL_BANK_STRIDE 8 + +#define RK3188_PULL_BITS_PER_PIN 2 +#define RK3188_PULL_PINS_PER_REG 8 +#define RK3188_PULL_BANK_STRIDE 16 + +#define RK3036_PULL_BITS_PER_PIN 1 +#define RK3036_PULL_PINS_PER_REG 16 +#define RK3036_PULL_BANK_STRIDE 8 + +#define RK312X_PULL_BITS_PER_PIN 1 +#define RK312X_PULL_PINS_PER_REG 16 +#define RK312X_PULL_BANK_STRIDE 8 + + +/*warning:don not chang the following value*/ +#define VALUE_PULL_NORMAL 0 +#define VALUE_PULL_UP 1 +#define VALUE_PULL_DOWN 2 +#define VALUE_PULL_KEEP 3 +#define VALUE_PULL_DISABLE 4 //don't set and keep pull default +#define VALUE_PULL_DEFAULT 4 //don't set and keep pull default + + +//for rk2928,rk3036 +#define VALUE_PULL_UPDOWN_DISABLE 0 +#define VALUE_PULL_UPDOWN_ENABLE 1 + +#define VALUE_VOL_DEFAULT 0 +#define VALUE_VOL_3V3 0 +#define VALUE_VOL_1V8 1 + +#define VALUE_DRV_DEFAULT 0 +#define VALUE_DRV_2MA 0 +#define VALUE_DRV_4MA 1 +#define VALUE_DRV_8MA 2 +#define VALUE_DRV_12MA 3 + +#define VALUE_TRI_DEFAULT 0 +#define VALUE_TRI_FALSE 0 +#define VALUE_TRI_TRUE 1 + + +/* + * pin config bit field definitions + * + * pull-up: 1..0 (2) + * voltage: 3..2 (2) + * drive: 5..4 (2) + * trisiate: 7..6 (2) + * + * MSB of each field is presence bit for the config. + */ +#define PULL_SHIFT 0 +#define PULL_PRESENT (1 << 2) +#define VOL_SHIFT 3 +#define VOL_PRESENT (1 << 5) +#define DRV_SHIFT 6 +#define DRV_PRESENT (1 << 8) +#define TRI_SHIFT 9 +#define TRI_PRESENT (1 << 11) + +#define CONFIG_TO_PULL(c) ((c) >> PULL_SHIFT & 0x3) +#define CONFIG_TO_VOL(c) ((c) >> VOL_SHIFT & 0x3) +#define CONFIG_TO_DRV(c) ((c) >> DRV_SHIFT & 0x3) +#define CONFIG_TO_TRI(c) ((c) >> TRI_SHIFT & 0x3) + + +#define MAX_NUM_CONFIGS 4 +#define POS_PULL 0 +#define POS_VOL 1 +#define POS_DRV 2 +#define POS_TRI 3 + + +#define GPIO_A0 0 +#define GPIO_A1 1 +#define GPIO_A2 2 +#define GPIO_A3 3 +#define GPIO_A4 4 +#define GPIO_A5 5 +#define GPIO_A6 6 +#define GPIO_A7 7 +#define GPIO_B0 8 +#define GPIO_B1 9 +#define GPIO_B2 10 +#define GPIO_B3 11 +#define GPIO_B4 12 +#define GPIO_B5 13 +#define GPIO_B6 14 +#define GPIO_B7 15 +#define GPIO_C0 16 +#define GPIO_C1 17 +#define GPIO_C2 18 +#define GPIO_C3 19 +#define GPIO_C4 20 +#define GPIO_C5 21 +#define GPIO_C6 22 +#define GPIO_C7 23 +#define GPIO_D0 24 +#define GPIO_D1 25 +#define GPIO_D2 26 +#define GPIO_D3 27 +#define GPIO_D4 28 +#define GPIO_D5 29 +#define GPIO_D6 30 +#define GPIO_D7 31 + +#define FUNC_TO_GPIO(m) ((m) & 0xfff0) + + +#endif diff --git a/include/dt-bindings/pinctrl/rockchip.h b/include/dt-bindings/pinctrl/rockchip.h deleted file mode 100644 index 976424b3fbdc..000000000000 --- a/include/dt-bindings/pinctrl/rockchip.h +++ /dev/null @@ -1,185 +0,0 @@ -/* - * Header providing constants for Rockchip pinctrl bindings. - * - * Copyright (c) 2013 MundoReader S.L. - * Author: Heiko Stuebner - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation; either version 2 of the License, or - * (at your option) any later version. - * - * 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. - */ - -#ifndef __DT_BINDINGS_ROCKCHIP_PINCTRL_H__ -#define __DT_BINDINGS_ROCKCHIP_PINCTRL_H__ - -#define RK_GPIO0 0 -#define RK_GPIO1 1 -#define RK_GPIO2 2 -#define RK_GPIO3 3 -#define RK_GPIO4 4 -#define RK_GPIO5 5 -#define RK_GPIO6 6 -#define RK_GPIO7 7 -#define RK_GPIO8 8 - - -#define RK_FUNC_GPIO 0 -#define RK_FUNC_1 1 -#define RK_FUNC_2 2 -#define RK_FUNC_3 3 -#define RK_FUNC_4 4 -#define RK_FUNC_5 5 -#define RK_FUNC_6 6 -#define RK_FUNC_7 7 - - - -/*special virtual pin for vcc domain setting*/ -#define VIRTUAL_PIN_FOR_AP0_VCC 0xfA00 -#define VIRTUAL_PIN_FOR_AP1_VCC 0xfA10 -#define VIRTUAL_PIN_FOR_CIF_VCC 0xfA20 -#define VIRTUAL_PIN_FOR_FLASH_VCC 0xfA30 -#define VIRTUAL_PIN_FOR_VCCIO0_VCC 0xfA40 -#define VIRTUAL_PIN_FOR_VCCIO1_VCC 0xfA50 -#define VIRTUAL_PIN_FOR_LCDC0_VCC 0xfA60 -#define VIRTUAL_PIN_FOR_LCDC1_VCC 0xfA70 - - -#define RK32_VIRTUAL_PIN_FOR_LCDC_VCC 0xfA00 -#define RK32_VIRTUAL_PIN_FOR_DVP_VCC 0xfA10 -#define RK32_VIRTUAL_PIN_FOR_FLASH0_VCC 0xfA20 -#define RK32_VIRTUAL_PIN_FOR_FLASH1_VCC 0xfA30 -#define RK32_VIRTUAL_PIN_FOR_WIFI_VCC 0xfA40 -#define RK32_VIRTUAL_PIN_FOR_BB_VCC 0xfA50 -#define RK32_VIRTUAL_PIN_FOR_AUDIO_VCC 0xfA60 -#define RK32_VIRTUAL_PIN_FOR_SDCARD_VCC 0xfA70 - -#define RK32_VIRTUAL_PIN_FOR_GPIO30_VCC 0xfB00 -#define RK32_VIRTUAL_PIN_FOR_GPIO1830_VCC 0xfB10 - - -#define TYPE_PULL_REG 0x01 -#define TYPE_VOL_REG 0x02 -#define TYPE_DRV_REG 0x03 -#define TYPE_TRI_REG 0x04 - -#define RK2928_PULL_OFFSET 0x118 -#define RK2928_PULL_PINS_PER_REG 16 -#define RK2928_PULL_BANK_STRIDE 8 - -#define RK3188_PULL_BITS_PER_PIN 2 -#define RK3188_PULL_PINS_PER_REG 8 -#define RK3188_PULL_BANK_STRIDE 16 - -#define RK3036_PULL_BITS_PER_PIN 1 -#define RK3036_PULL_PINS_PER_REG 16 -#define RK3036_PULL_BANK_STRIDE 8 - -#define RK312X_PULL_BITS_PER_PIN 1 -#define RK312X_PULL_PINS_PER_REG 16 -#define RK312X_PULL_BANK_STRIDE 8 - - -/*warning:don not chang the following value*/ -#define VALUE_PULL_NORMAL 0 -#define VALUE_PULL_UP 1 -#define VALUE_PULL_DOWN 2 -#define VALUE_PULL_KEEP 3 -#define VALUE_PULL_DISABLE 4 //don't set and keep pull default -#define VALUE_PULL_DEFAULT 4 //don't set and keep pull default - - -//for rk2928,rk3036 -#define VALUE_PULL_UPDOWN_DISABLE 0 -#define VALUE_PULL_UPDOWN_ENABLE 1 - -#define VALUE_VOL_DEFAULT 0 -#define VALUE_VOL_3V3 0 -#define VALUE_VOL_1V8 1 - -#define VALUE_DRV_DEFAULT 0 -#define VALUE_DRV_2MA 0 -#define VALUE_DRV_4MA 1 -#define VALUE_DRV_8MA 2 -#define VALUE_DRV_12MA 3 - -#define VALUE_TRI_DEFAULT 0 -#define VALUE_TRI_FALSE 0 -#define VALUE_TRI_TRUE 1 - - -/* - * pin config bit field definitions - * - * pull-up: 1..0 (2) - * voltage: 3..2 (2) - * drive: 5..4 (2) - * trisiate: 7..6 (2) - * - * MSB of each field is presence bit for the config. - */ -#define PULL_SHIFT 0 -#define PULL_PRESENT (1 << 2) -#define VOL_SHIFT 3 -#define VOL_PRESENT (1 << 5) -#define DRV_SHIFT 6 -#define DRV_PRESENT (1 << 8) -#define TRI_SHIFT 9 -#define TRI_PRESENT (1 << 11) - -#define CONFIG_TO_PULL(c) ((c) >> PULL_SHIFT & 0x3) -#define CONFIG_TO_VOL(c) ((c) >> VOL_SHIFT & 0x3) -#define CONFIG_TO_DRV(c) ((c) >> DRV_SHIFT & 0x3) -#define CONFIG_TO_TRI(c) ((c) >> TRI_SHIFT & 0x3) - - -#define MAX_NUM_CONFIGS 4 -#define POS_PULL 0 -#define POS_VOL 1 -#define POS_DRV 2 -#define POS_TRI 3 - - -#define GPIO_A0 0 -#define GPIO_A1 1 -#define GPIO_A2 2 -#define GPIO_A3 3 -#define GPIO_A4 4 -#define GPIO_A5 5 -#define GPIO_A6 6 -#define GPIO_A7 7 -#define GPIO_B0 8 -#define GPIO_B1 9 -#define GPIO_B2 10 -#define GPIO_B3 11 -#define GPIO_B4 12 -#define GPIO_B5 13 -#define GPIO_B6 14 -#define GPIO_B7 15 -#define GPIO_C0 16 -#define GPIO_C1 17 -#define GPIO_C2 18 -#define GPIO_C3 19 -#define GPIO_C4 20 -#define GPIO_C5 21 -#define GPIO_C6 22 -#define GPIO_C7 23 -#define GPIO_D0 24 -#define GPIO_D1 25 -#define GPIO_D2 26 -#define GPIO_D3 27 -#define GPIO_D4 28 -#define GPIO_D5 29 -#define GPIO_D6 30 -#define GPIO_D7 31 - -#define FUNC_TO_GPIO(m) ((m) & 0xfff0) - - -#endif