Commit 355c9b757e1110151c78ea82fd834e6cb09bdb96

Authored by Georgii Staroselskii
Committed by Bin Meng
1 parent 224742a390

x86: tangier: pinmux: add API to configure protected pins

This API is going to be used to configure some pins that are protected
for simple modification.

It's not a comprehensive pinctrl driver but can be turned into one
when we need this in the future. Now it is planned to be used only
in one place. So that's why I decided not to pollute the codebase with a
full-blown pinctrl-merrifield nobody will use.

This driver reads corresponding fields in DT and configures pins
accordingly.

The "protected" flag is used to distinguish configuration of SCU-owned
pins from the ordinary ones.

The code has been adapted from Linux work done by Andy Shevchenko
in pinctrl-merrfifield.c

Signed-off-by: Georgii Staroselskii <georgii.staroselskii@emlid.com>
Reviewed-by: Andy Shevchenko <andy.shevchenko@gmail.com>
Reviewed-by: Bin Meng <bmeng.cn@gmail.com>
[bmeng: fix build warning]
Signed-off-by: Bin Meng <bmeng.cn@gmail.com>

Showing 2 changed files with 195 additions and 1 deletions Side-by-side Diff

arch/x86/cpu/tangier/Makefile
... ... @@ -2,6 +2,6 @@
2 2 #
3 3 # Copyright (c) 2017 Intel Corporation
4 4  
5   -obj-y += car.o tangier.o sdram.o sysreset.o
  5 +obj-y += car.o tangier.o sdram.o sysreset.o pinmux.o
6 6 obj-$(CONFIG_GENERATE_ACPI_TABLE) += acpi.o
arch/x86/cpu/tangier/pinmux.c
  1 +// SPDX-License-Identifier: GPL-2.0+
  2 +/*
  3 + * Copyright (c) 2018 Emlid Limited
  4 + */
  5 +
  6 +#include <common.h>
  7 +#include <dm.h>
  8 +#include <dm/pinctrl.h>
  9 +#include <dm/read.h>
  10 +#include <regmap.h>
  11 +#include <syscon.h>
  12 +#include <asm/cpu.h>
  13 +#include <asm/scu.h>
  14 +#include <linux/io.h>
  15 +
  16 +#define BUFCFG_OFFSET 0x100
  17 +
  18 +#define MRFLD_FAMILY_LEN 0x400
  19 +
  20 +/* These are taken from Linux kernel */
  21 +#define MRFLD_PINMODE_MASK 0x07
  22 +
  23 +#define pin_to_bufno(f, p) ((p) - (f)->pin_base)
  24 +
  25 +struct mrfld_family {
  26 + unsigned int family_number;
  27 + unsigned int pin_base;
  28 + size_t npins;
  29 + void __iomem *regs;
  30 +};
  31 +
  32 +#define MRFLD_FAMILY(b, s, e) \
  33 + { \
  34 + .family_number = (b), \
  35 + .pin_base = (s), \
  36 + .npins = (e) - (s) + 1, \
  37 + }
  38 +
  39 +/* Now we only support I2C family of pins */
  40 +static struct mrfld_family mrfld_families[] = {
  41 + MRFLD_FAMILY(7, 101, 114),
  42 +};
  43 +
  44 +struct mrfld_pinctrl {
  45 + const struct mrfld_family *families;
  46 + size_t nfamilies;
  47 +};
  48 +
  49 +static const struct mrfld_family *
  50 +mrfld_get_family(struct mrfld_pinctrl *mp, unsigned int pin)
  51 +{
  52 + const struct mrfld_family *family;
  53 + unsigned int i;
  54 +
  55 + for (i = 0; i < mp->nfamilies; i++) {
  56 + family = &mp->families[i];
  57 + if (pin >= family->pin_base &&
  58 + pin < family->pin_base + family->npins)
  59 + return family;
  60 + }
  61 +
  62 + pr_err("failed to find family for pin %u\n", pin);
  63 + return NULL;
  64 +}
  65 +
  66 +static void __iomem *
  67 +mrfld_get_bufcfg(struct mrfld_pinctrl *pinctrl, unsigned int pin)
  68 +{
  69 + const struct mrfld_family *family;
  70 + unsigned int bufno;
  71 +
  72 + family = mrfld_get_family(pinctrl, pin);
  73 + if (!family)
  74 + return NULL;
  75 +
  76 + bufno = pin_to_bufno(family, pin);
  77 +
  78 + return family->regs + BUFCFG_OFFSET + bufno * 4;
  79 +}
  80 +
  81 +static void
  82 +mrfld_setup_families(void *base_addr,
  83 + struct mrfld_family *families, unsigned int nfam)
  84 +{
  85 + for (int i = 0; i < nfam; i++) {
  86 + struct mrfld_family *family = &families[i];
  87 +
  88 + family->regs = base_addr +
  89 + family->family_number * MRFLD_FAMILY_LEN;
  90 + }
  91 +}
  92 +
  93 +static int mrfld_pinconfig_protected(unsigned int pin, u32 mask, u32 bits)
  94 +{
  95 + struct mrfld_pinctrl *pinctrl;
  96 + struct udevice *dev;
  97 + void __iomem *bufcfg;
  98 + u32 v, value;
  99 + int ret;
  100 +
  101 + ret = syscon_get_by_driver_data(X86_SYSCON_PINCONF, &dev);
  102 + if (ret)
  103 + return ret;
  104 +
  105 + pinctrl = dev_get_priv(dev);
  106 +
  107 + bufcfg = mrfld_get_bufcfg(pinctrl, pin);
  108 + if (!bufcfg)
  109 + return -EINVAL;
  110 +
  111 + value = readl(bufcfg);
  112 +
  113 + v = (value & ~mask) | (bits & mask);
  114 +
  115 + debug("scu: v: 0x%x p: 0x%x bits: %d, mask: %d bufcfg: 0x%p\n",
  116 + v, (u32)bufcfg, bits, mask, bufcfg);
  117 +
  118 + ret = scu_ipc_raw_command(IPCMSG_INDIRECT_WRITE, 0, &v, 4,
  119 + NULL, 0, (u32)bufcfg, 0);
  120 + if (ret)
  121 + pr_err("Failed to set mode via SCU for pin %u (%d)\n",
  122 + pin, ret);
  123 +
  124 + return ret;
  125 +}
  126 +
  127 +static int mrfld_pinctrl_cfg_pin(ofnode pin_node)
  128 +{
  129 + bool is_protected;
  130 + int pad_offset;
  131 + int mode;
  132 + u32 mask;
  133 + int ret;
  134 +
  135 + /* For now we only support just protected Family of pins */
  136 + is_protected = ofnode_read_bool(pin_node, "protected");
  137 + if (!is_protected)
  138 + return -ENOTSUPP;
  139 +
  140 + pad_offset = ofnode_read_s32_default(pin_node, "pad-offset", -1);
  141 + if (pad_offset == -1)
  142 + return -EINVAL;
  143 +
  144 + mode = ofnode_read_s32_default(pin_node, "mode-func", -1);
  145 + if (mode == -1)
  146 + return -EINVAL;
  147 +
  148 + mask = MRFLD_PINMODE_MASK;
  149 +
  150 + /* We don't support modes not in range 0..7 */
  151 + if (mode & ~mask)
  152 + return -ENOTSUPP;
  153 +
  154 + ret = mrfld_pinconfig_protected(pad_offset, mask, mode);
  155 +
  156 + return ret;
  157 +}
  158 +
  159 +static int tangier_pinctrl_probe(struct udevice *dev)
  160 +{
  161 + void *base_addr = syscon_get_first_range(X86_SYSCON_PINCONF);
  162 + struct mrfld_pinctrl *pinctrl = dev_get_priv(dev);
  163 + ofnode pin_node;
  164 + int ret;
  165 +
  166 + mrfld_setup_families(base_addr, mrfld_families,
  167 + ARRAY_SIZE(mrfld_families));
  168 +
  169 + pinctrl->families = mrfld_families;
  170 + pinctrl->nfamilies = ARRAY_SIZE(mrfld_families);
  171 +
  172 + ofnode_for_each_subnode(pin_node, dev_ofnode(dev)) {
  173 + ret = mrfld_pinctrl_cfg_pin(pin_node);
  174 + if (ret) {
  175 + pr_err("%s: invalid configuration for the pin %ld\n",
  176 + __func__, pin_node.of_offset);
  177 + }
  178 + }
  179 +
  180 + return 0;
  181 +}
  182 +
  183 +static const struct udevice_id tangier_pinctrl_match[] = {
  184 + { .compatible = "intel,pinctrl-tangier", .data = X86_SYSCON_PINCONF },
  185 + { /* sentinel */ }
  186 +};
  187 +
  188 +U_BOOT_DRIVER(tangier_pinctrl) = {
  189 + .name = "tangier_pinctrl",
  190 + .id = UCLASS_SYSCON,
  191 + .of_match = tangier_pinctrl_match,
  192 + .probe = tangier_pinctrl_probe,
  193 + .priv_auto_alloc_size = sizeof(struct mrfld_pinctrl),
  194 +};