diff options
Diffstat (limited to 'drivers/pinctrl/freescale/pinctrl-memmap.c')
-rw-r--r-- | drivers/pinctrl/freescale/pinctrl-memmap.c | 234 |
1 files changed, 234 insertions, 0 deletions
diff --git a/drivers/pinctrl/freescale/pinctrl-memmap.c b/drivers/pinctrl/freescale/pinctrl-memmap.c new file mode 100644 index 000000000000..0492429ca822 --- /dev/null +++ b/drivers/pinctrl/freescale/pinctrl-memmap.c @@ -0,0 +1,234 @@ +/* + * Copyright (C) 2016 Freescale Semiconductor, Inc. + * Copyright 2017 NXP + * + * 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. + */ + +#include <linux/err.h> +#include <linux/init.h> +#include <linux/io.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_device.h> +#include <linux/pinctrl/machine.h> +#include <linux/pinctrl/pinconf.h> +#include <linux/pinctrl/pinctrl.h> +#include <linux/pinctrl/pinmux.h> +#include <linux/slab.h> + +#include "../core.h" +#include "pinctrl-imx.h" + +#define IMX_PAD_SION 0x40000000 /* set SION */ + +int imx_pmx_set_one_pin_mem(struct imx_pinctrl *ipctl, struct imx_pin *pin) +{ + const struct imx_pinctrl_soc_info *info = ipctl->info; + unsigned int pin_id = pin->pin; + struct imx_pin_reg *pin_reg; + struct imx_pin_memmap *pin_memmap; + pin_reg = &info->pin_regs[pin_id]; + pin_memmap = &pin->pin_conf.pin_memmap; + + if (pin_reg->mux_reg == -1) { + dev_err(ipctl->dev, "Pin(%s) does not support mux function\n", + info->pins[pin_id].name); + return 0; + } + + if (info->flags & SHARE_MUX_CONF_REG) { + u32 reg; + reg = readl(ipctl->base + pin_reg->mux_reg); + reg &= ~info->mux_mask; + reg |= (pin_memmap->mux_mode << info->mux_shift); + writel(reg, ipctl->base + pin_reg->mux_reg); + dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", + pin_reg->mux_reg, reg); + } else { + writel(pin_memmap->mux_mode, ipctl->base + pin_reg->mux_reg); + dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", + pin_reg->mux_reg, pin_memmap->mux_mode); + } + + /* + * If the select input value begins with 0xff, it's a quirky + * select input and the value should be interpreted as below. + * 31 23 15 7 0 + * | 0xff | shift | width | select | + * It's used to work around the problem that the select + * input for some pin is not implemented in the select + * input register but in some general purpose register. + * We encode the select input value, width and shift of + * the bit field into input_val cell of pin function ID + * in device tree, and then decode them here for setting + * up the select input bits in general purpose register. + */ + if (pin_memmap->input_val >> 24 == 0xff) { + u32 val = pin_memmap->input_val; + u8 select = val & 0xff; + u8 width = (val >> 8) & 0xff; + u8 shift = (val >> 16) & 0xff; + u32 mask = ((1 << width) - 1) << shift; + /* + * The input_reg[i] here is actually some IOMUXC general + * purpose register, not regular select input register. + */ + val = readl(ipctl->base + pin_memmap->input_reg); + val &= ~mask; + val |= select << shift; + writel(val, ipctl->base + pin_memmap->input_reg); + } else if (pin_memmap->input_reg) { + /* + * Regular select input register can never be at offset + * 0, and we only print register value for regular case. + */ + if (ipctl->input_sel_base) + writel(pin_memmap->input_val, ipctl->input_sel_base + + pin_memmap->input_reg); + else + writel(pin_memmap->input_val, ipctl->base + + pin_memmap->input_reg); + dev_dbg(ipctl->dev, + "==>select_input: offset 0x%x val 0x%x\n", + pin_memmap->input_reg, pin_memmap->input_val); + } + + return 0; +} + +int imx_pmx_backend_gpio_set_direction_mem(struct pinctrl_dev *pctldev, + struct pinctrl_gpio_range *range, unsigned offset, bool input) +{ + struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx_pinctrl_soc_info *info = ipctl->info; + const struct imx_pin_reg *pin_reg; + u32 reg; + + /* + * Only Vybrid and iMX ULP has the input/output buffer enable flags + * (IBE/OBE) They are part of the shared mux/conf register. + */ + if (!(info->flags & SHARE_MUX_CONF_REG)) + return 0; + + pin_reg = &info->pin_regs[offset]; + if (pin_reg->mux_reg == -1) + return -EINVAL; + + reg = readl(ipctl->base + pin_reg->mux_reg); + if (input) + reg = (reg & ~info->obe_bit) | info->ibe_bit; + else + reg = (reg & ~info->ibe_bit) | info->obe_bit; + writel(reg, ipctl->base + pin_reg->mux_reg); + + return 0; +} + +int imx_pinconf_backend_get_mem(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *config) +{ + struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx_pinctrl_soc_info *info = ipctl->info; + const struct imx_pin_reg *pin_reg = &info->pin_regs[pin_id]; + + if (pin_reg->conf_reg == -1) { + dev_err(info->dev, "Pin(%s) does not support config function\n", + info->pins[pin_id].name); + return -EINVAL; + } + + *config = readl(ipctl->base + pin_reg->conf_reg); + + if (info->flags & SHARE_MUX_CONF_REG) + *config &= ~info->mux_mask; + + return 0; +} + +int imx_pinconf_backend_set_mem(struct pinctrl_dev *pctldev, + unsigned pin_id, unsigned long *configs, + unsigned num_configs) +{ + struct imx_pinctrl *ipctl = pinctrl_dev_get_drvdata(pctldev); + const struct imx_pinctrl_soc_info *info = ipctl->info; + const struct imx_pin_reg *pin_reg = &info->pin_regs[pin_id]; + int i; + + if (pin_reg->conf_reg == -1) { + dev_err(info->dev, "Pin(%s) does not support config function\n", + info->pins[pin_id].name); + return -EINVAL; + } + + dev_dbg(ipctl->dev, "pinconf set pin %s\n", + info->pins[pin_id].name); + + for (i = 0; i < num_configs; i++) { + if (info->flags & SHARE_MUX_CONF_REG) { + u32 reg; + reg = readl(ipctl->base + pin_reg->conf_reg); + reg &= info->mux_mask; + reg |= configs[i]; + writel(reg, ipctl->base + pin_reg->conf_reg); + dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%x\n", + pin_reg->conf_reg, reg); + } else { + writel(configs[i], ipctl->base + pin_reg->conf_reg); + dev_dbg(ipctl->dev, "write: offset 0x%x val 0x%lx\n", + pin_reg->conf_reg, configs[i]); + } + } /* for each config */ + + return 0; +} + +int imx_pinctrl_parse_pin_mem(struct imx_pinctrl_soc_info *info, + unsigned int *grp_pin_id, struct imx_pin *pin, + const __be32 **list_p) +{ + struct imx_pin_memmap *pin_memmap = &pin->pin_conf.pin_memmap; + u32 mux_reg = be32_to_cpu(*((*list_p)++)); + u32 conf_reg; + u32 config; + unsigned int pin_id; + struct imx_pin_reg *pin_reg; + + if (info->flags & SHARE_MUX_CONF_REG) { + conf_reg = mux_reg; + } else { + conf_reg = be32_to_cpu(*((*list_p)++)); + if (!conf_reg) + conf_reg = -1; + } + + pin_id = (mux_reg != -1) ? mux_reg / 4 : conf_reg / 4; + pin_reg = &info->pin_regs[pin_id]; + pin->pin = pin_id; + *grp_pin_id = pin_id; + pin_reg->mux_reg = mux_reg; + pin_reg->conf_reg = conf_reg; + pin_memmap->input_reg = be32_to_cpu(*((*list_p)++)); + pin_memmap->mux_mode = be32_to_cpu(*((*list_p)++)); + pin_memmap->input_val = be32_to_cpu((*(*list_p)++)); + + /* SION bit is in mux register */ + config = be32_to_cpu(*((*list_p)++)); + if (config & IMX_PAD_SION) + pin_memmap->mux_mode |= IOMUXC_CONFIG_SION; + pin_memmap->config = config & ~IMX_PAD_SION; + + dev_dbg(info->dev, "%s: 0x%x 0x%08lx", info->pins[pin_id].name, + pin_memmap->mux_mode, pin_memmap->config); + + return 0; +} |