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