uboot/drivers/pinctrl/nxp/pinctrl-imx.c
<<
>>
Prefs
   1// SPDX-License-Identifier: GPL-2.0+
   2/*
   3 * Copyright (C) 2016 Peng Fan <van.freenix@gmail.com>
   4 */
   5
   6#include <common.h>
   7#include <mapmem.h>
   8#include <linux/io.h>
   9#include <linux/err.h>
  10#include <dm.h>
  11#include <dm/pinctrl.h>
  12
  13#include "pinctrl-imx.h"
  14
  15DECLARE_GLOBAL_DATA_PTR;
  16
  17static int imx_pinctrl_set_state(struct udevice *dev, struct udevice *config)
  18{
  19        struct imx_pinctrl_priv *priv = dev_get_priv(dev);
  20        struct imx_pinctrl_soc_info *info = priv->info;
  21        int node = dev_of_offset(config);
  22        const struct fdt_property *prop;
  23        u32 *pin_data;
  24        int npins, size, pin_size;
  25        int mux_reg, conf_reg, input_reg, input_val, mux_mode, config_val;
  26        u32 mux_shift = info->mux_mask ? ffs(info->mux_mask) - 1 : 0;
  27        int i, j = 0;
  28
  29        dev_dbg(dev, "%s: %s\n", __func__, config->name);
  30
  31        if (info->flags & SHARE_MUX_CONF_REG)
  32                pin_size = SHARE_FSL_PIN_SIZE;
  33        else
  34                pin_size = FSL_PIN_SIZE;
  35
  36        prop = fdt_getprop(gd->fdt_blob, node, "fsl,pins", &size);
  37        if (!prop) {
  38                dev_err(dev, "No fsl,pins property in node %s\n", config->name);
  39                return -EINVAL;
  40        }
  41
  42        if (!size || size % pin_size) {
  43                dev_err(dev, "Invalid fsl,pins property in node %s\n",
  44                        config->name);
  45                return -EINVAL;
  46        }
  47
  48        pin_data = devm_kzalloc(dev, size, 0);
  49        if (!pin_data)
  50                return -ENOMEM;
  51
  52        if (fdtdec_get_int_array(gd->fdt_blob, node, "fsl,pins",
  53                                 pin_data, size >> 2)) {
  54                dev_err(dev, "Error reading pin data.\n");
  55                devm_kfree(dev, pin_data);
  56                return -EINVAL;
  57        }
  58
  59        npins = size / pin_size;
  60
  61        /*
  62         * Refer to linux documentation for details:
  63         * Documentation/devicetree/bindings/pinctrl/fsl,imx-pinctrl.txt
  64         */
  65        for (i = 0; i < npins; i++) {
  66                mux_reg = pin_data[j++];
  67
  68                if (!(info->flags & ZERO_OFFSET_VALID) && !mux_reg)
  69                        mux_reg = -1;
  70
  71                if (info->flags & SHARE_MUX_CONF_REG) {
  72                        conf_reg = mux_reg;
  73                } else {
  74                        conf_reg = pin_data[j++];
  75                        if (!(info->flags & ZERO_OFFSET_VALID) && !conf_reg)
  76                                conf_reg = -1;
  77                }
  78
  79                if ((mux_reg == -1) || (conf_reg == -1)) {
  80                        dev_err(dev, "Error mux_reg or conf_reg\n");
  81                        devm_kfree(dev, pin_data);
  82                        return -EINVAL;
  83                }
  84
  85                input_reg = pin_data[j++];
  86                mux_mode = pin_data[j++];
  87                input_val = pin_data[j++];
  88                config_val = pin_data[j++];
  89
  90                dev_dbg(dev, "mux_reg 0x%x, conf_reg 0x%x, input_reg 0x%x, "
  91                        "mux_mode 0x%x, input_val 0x%x, config_val 0x%x\n",
  92                        mux_reg, conf_reg, input_reg, mux_mode, input_val,
  93                        config_val);
  94
  95                if (config_val & IMX_PAD_SION)
  96                        mux_mode |= IOMUXC_CONFIG_SION;
  97
  98                config_val &= ~IMX_PAD_SION;
  99
 100                /* Set Mux */
 101                if (info->flags & SHARE_MUX_CONF_REG) {
 102                        clrsetbits_le32(info->base + mux_reg, info->mux_mask,
 103                                        mux_mode << mux_shift);
 104                } else {
 105                        writel(mux_mode, info->base + mux_reg);
 106                }
 107
 108                dev_dbg(dev, "write mux: offset 0x%x val 0x%x\n", mux_reg,
 109                        mux_mode);
 110
 111                /*
 112                 * Set select input
 113                 *
 114                 * If the select input value begins with 0xff, it's a quirky
 115                 * select input and the value should be interpreted as below.
 116                 *     31     23      15      7        0
 117                 *     | 0xff | shift | width | select |
 118                 * It's used to work around the problem that the select
 119                 * input for some pin is not implemented in the select
 120                 * input register but in some general purpose register.
 121                 * We encode the select input value, width and shift of
 122                 * the bit field into input_val cell of pin function ID
 123                 * in device tree, and then decode them here for setting
 124                 * up the select input bits in general purpose register.
 125                 */
 126
 127                if (input_val >> 24 == 0xff) {
 128                        u32 val = input_val;
 129                        u8 select = val & 0xff;
 130                        u8 width = (val >> 8) & 0xff;
 131                        u8 shift = (val >> 16) & 0xff;
 132                        u32 mask = ((1 << width) - 1) << shift;
 133                        /*
 134                         * The input_reg[i] here is actually some IOMUXC general
 135                         * purpose register, not regular select input register.
 136                         */
 137                        val = readl(info->base + input_reg);
 138                        val &= ~mask;
 139                        val |= select << shift;
 140                        writel(val, info->base + input_reg);
 141                } else if (input_reg) {
 142                        /*
 143                         * Regular select input register can never be at offset
 144                         * 0, and we only print register value for regular case.
 145                         */
 146                        if (info->input_sel_base)
 147                                writel(input_val, info->input_sel_base +
 148                                       input_reg);
 149                        else
 150                                writel(input_val, info->base + input_reg);
 151
 152                        dev_dbg(dev, "select_input: offset 0x%x val 0x%x\n",
 153                                input_reg, input_val);
 154                }
 155
 156                /* Set config */
 157                if (!(config_val & IMX_NO_PAD_CTL)) {
 158                        if (info->flags & SHARE_MUX_CONF_REG) {
 159                                clrsetbits_le32(info->base + conf_reg,
 160                                                ~info->mux_mask, config_val);
 161                        } else {
 162                                writel(config_val, info->base + conf_reg);
 163                        }
 164
 165                        dev_dbg(dev, "write config: offset 0x%x val 0x%x\n",
 166                                conf_reg, config_val);
 167                }
 168        }
 169
 170        devm_kfree(dev, pin_data);
 171
 172        return 0;
 173}
 174
 175const struct pinctrl_ops imx_pinctrl_ops  = {
 176        .set_state = imx_pinctrl_set_state,
 177};
 178
 179int imx_pinctrl_probe(struct udevice *dev,
 180                      struct imx_pinctrl_soc_info *info)
 181{
 182        struct imx_pinctrl_priv *priv = dev_get_priv(dev);
 183        int node = dev_of_offset(dev), ret;
 184        struct fdtdec_phandle_args arg;
 185        fdt_addr_t addr;
 186        fdt_size_t size;
 187
 188        if (!info) {
 189                dev_err(dev, "wrong pinctrl info\n");
 190                return -EINVAL;
 191        }
 192
 193        priv->dev = dev;
 194        priv->info = info;
 195
 196        addr = fdtdec_get_addr_size(gd->fdt_blob, dev_of_offset(dev), "reg",
 197                                    &size);
 198
 199        if (addr == FDT_ADDR_T_NONE)
 200                return -EINVAL;
 201
 202        info->base = map_sysmem(addr, size);
 203        if (!info->base)
 204                return -ENOMEM;
 205        priv->info = info;
 206
 207        info->mux_mask = fdtdec_get_int(gd->fdt_blob, node, "fsl,mux_mask", 0);
 208        /*
 209         * Refer to linux documentation for details:
 210         * Documentation/devicetree/bindings/pinctrl/fsl,imx7d-pinctrl.txt
 211         */
 212        if (fdtdec_get_bool(gd->fdt_blob, node, "fsl,input-sel")) {
 213                ret = fdtdec_parse_phandle_with_args(gd->fdt_blob,
 214                                                     node, "fsl,input-sel",
 215                                                     NULL, 0, 0, &arg);
 216                if (ret) {
 217                        dev_err(dev, "iomuxc fsl,input-sel property not found\n");
 218                        return -EINVAL;
 219                }
 220
 221                addr = fdtdec_get_addr_size(gd->fdt_blob, arg.node, "reg",
 222                                            &size);
 223                if (addr == FDT_ADDR_T_NONE)
 224                        return -EINVAL;
 225
 226                info->input_sel_base = map_sysmem(addr, size);
 227                if (!info->input_sel_base)
 228                        return -ENOMEM;
 229        }
 230
 231        dev_dbg(dev, "initialized IMX pinctrl driver\n");
 232
 233        return 0;
 234}
 235
 236int imx_pinctrl_remove(struct udevice *dev)
 237{
 238        struct imx_pinctrl_priv *priv = dev_get_priv(dev);
 239        struct imx_pinctrl_soc_info *info = priv->info;
 240
 241        if (info->input_sel_base)
 242                unmap_sysmem(info->input_sel_base);
 243        if (info->base)
 244                unmap_sysmem(info->base);
 245
 246        return 0;
 247}
 248