linux/drivers/i3c/master/mipi-i3c-hci/ext_caps.c
<<
>>
Prefs
   1// SPDX-License-Identifier: BSD-3-Clause
   2/*
   3 * Copyright (c) 2020, MIPI Alliance, Inc.
   4 *
   5 * Author: Nicolas Pitre <npitre@baylibre.com>
   6 */
   7
   8#include <linux/bitfield.h>
   9#include <linux/device.h>
  10#include <linux/errno.h>
  11#include <linux/i3c/master.h>
  12#include <linux/kernel.h>
  13#include <linux/io.h>
  14
  15#include "hci.h"
  16#include "ext_caps.h"
  17#include "xfer_mode_rate.h"
  18
  19
  20/* Extended Capability Header */
  21#define CAP_HEADER_LENGTH               GENMASK(23, 8)
  22#define CAP_HEADER_ID                   GENMASK(7, 0)
  23
  24static int hci_extcap_hardware_id(struct i3c_hci *hci, void __iomem *base)
  25{
  26        hci->vendor_mipi_id     = readl(base + 0x04);
  27        hci->vendor_version_id  = readl(base + 0x08);
  28        hci->vendor_product_id  = readl(base + 0x0c);
  29
  30        dev_info(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id);
  31        dev_info(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id);
  32        dev_info(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id);
  33
  34        /* ought to go in a table if this grows too much */
  35        switch (hci->vendor_mipi_id) {
  36        case MIPI_VENDOR_NXP:
  37                hci->quirks |= HCI_QUIRK_RAW_CCC;
  38                DBG("raw CCC quirks set");
  39                break;
  40        }
  41
  42        return 0;
  43}
  44
  45static int hci_extcap_master_config(struct i3c_hci *hci, void __iomem *base)
  46{
  47        u32 master_config = readl(base + 0x04);
  48        unsigned int operation_mode = FIELD_GET(GENMASK(5, 4), master_config);
  49        static const char * const functionality[] = {
  50                "(unknown)", "master only", "target only",
  51                "primary/secondary master" };
  52        dev_info(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]);
  53        if (operation_mode & 0x1)
  54                return 0;
  55        dev_err(&hci->master.dev, "only master mode is currently supported\n");
  56        return -EOPNOTSUPP;
  57}
  58
  59static int hci_extcap_multi_bus(struct i3c_hci *hci, void __iomem *base)
  60{
  61        u32 bus_instance = readl(base + 0x04);
  62        unsigned int count = FIELD_GET(GENMASK(3, 0), bus_instance);
  63
  64        dev_info(&hci->master.dev, "%d bus instances\n", count);
  65        return 0;
  66}
  67
  68static int hci_extcap_xfer_modes(struct i3c_hci *hci, void __iomem *base)
  69{
  70        u32 header = readl(base);
  71        u32 entries = FIELD_GET(CAP_HEADER_LENGTH, header) - 1;
  72        unsigned int index;
  73
  74        dev_info(&hci->master.dev, "transfer mode table has %d entries\n",
  75                 entries);
  76        base += 4;  /* skip header */
  77        for (index = 0; index < entries; index++) {
  78                u32 mode_entry = readl(base);
  79
  80                DBG("mode %d: 0x%08x", index, mode_entry);
  81                /* TODO: will be needed when I3C core does more than SDR */
  82                base += 4;
  83        }
  84
  85        return 0;
  86}
  87
  88static int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base)
  89{
  90        u32 header = readl(base);
  91        u32 entries = FIELD_GET(CAP_HEADER_LENGTH, header) - 1;
  92        u32 rate_entry;
  93        unsigned int index, rate, rate_id, mode_id;
  94
  95        base += 4;  /* skip header */
  96
  97        dev_info(&hci->master.dev, "available data rates:\n");
  98        for (index = 0; index < entries; index++) {
  99                rate_entry = readl(base);
 100                DBG("entry %d: 0x%08x", index, rate_entry);
 101                rate = FIELD_GET(XFERRATE_ACTUAL_RATE_KHZ, rate_entry);
 102                rate_id = FIELD_GET(XFERRATE_RATE_ID, rate_entry);
 103                mode_id = FIELD_GET(XFERRATE_MODE_ID, rate_entry);
 104                dev_info(&hci->master.dev, "rate %d for %s = %d kHz\n",
 105                         rate_id,
 106                         mode_id == XFERRATE_MODE_I3C ? "I3C" :
 107                         mode_id == XFERRATE_MODE_I2C ? "I2C" :
 108                         "unknown mode",
 109                         rate);
 110                base += 4;
 111        }
 112
 113        return 0;
 114}
 115
 116static int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base)
 117{
 118        u32 autocmd_ext_caps = readl(base + 0x04);
 119        unsigned int max_count = FIELD_GET(GENMASK(3, 0), autocmd_ext_caps);
 120        u32 autocmd_ext_config = readl(base + 0x08);
 121        unsigned int count = FIELD_GET(GENMASK(3, 0), autocmd_ext_config);
 122
 123        dev_info(&hci->master.dev, "%d/%d active auto-command entries\n",
 124                 count, max_count);
 125        /* remember auto-command register location for later use */
 126        hci->AUTOCMD_regs = base;
 127        return 0;
 128}
 129
 130static int hci_extcap_debug(struct i3c_hci *hci, void __iomem *base)
 131{
 132        dev_info(&hci->master.dev, "debug registers present\n");
 133        hci->DEBUG_regs = base;
 134        return 0;
 135}
 136
 137static int hci_extcap_scheduled_cmd(struct i3c_hci *hci, void __iomem *base)
 138{
 139        dev_info(&hci->master.dev, "scheduled commands available\n");
 140        /* hci->schedcmd_regs = base; */
 141        return 0;
 142}
 143
 144static int hci_extcap_non_curr_master(struct i3c_hci *hci, void __iomem *base)
 145{
 146        dev_info(&hci->master.dev, "Non-Current Master support available\n");
 147        /* hci->NCM_regs = base; */
 148        return 0;
 149}
 150
 151static int hci_extcap_ccc_resp_conf(struct i3c_hci *hci, void __iomem *base)
 152{
 153        dev_info(&hci->master.dev, "CCC Response Configuration available\n");
 154        return 0;
 155}
 156
 157static int hci_extcap_global_DAT(struct i3c_hci *hci, void __iomem *base)
 158{
 159        dev_info(&hci->master.dev, "Global DAT available\n");
 160        return 0;
 161}
 162
 163static int hci_extcap_multilane(struct i3c_hci *hci, void __iomem *base)
 164{
 165        dev_info(&hci->master.dev, "Master Multi-Lane support available\n");
 166        return 0;
 167}
 168
 169static int hci_extcap_ncm_multilane(struct i3c_hci *hci, void __iomem *base)
 170{
 171        dev_info(&hci->master.dev, "NCM Multi-Lane support available\n");
 172        return 0;
 173}
 174
 175struct hci_ext_caps {
 176        u8  id;
 177        u16 min_length;
 178        int (*parser)(struct i3c_hci *hci, void __iomem *base);
 179};
 180
 181#define EXT_CAP(_id, _highest_mandatory_reg_offset, _parser) \
 182        { .id = (_id), .parser = (_parser), \
 183          .min_length = (_highest_mandatory_reg_offset)/4 + 1 }
 184
 185static const struct hci_ext_caps ext_capabilities[] = {
 186        EXT_CAP(0x01, 0x0c, hci_extcap_hardware_id),
 187        EXT_CAP(0x02, 0x04, hci_extcap_master_config),
 188        EXT_CAP(0x03, 0x04, hci_extcap_multi_bus),
 189        EXT_CAP(0x04, 0x24, hci_extcap_xfer_modes),
 190        EXT_CAP(0x05, 0x08, hci_extcap_auto_command),
 191        EXT_CAP(0x08, 0x40, hci_extcap_xfer_rates),
 192        EXT_CAP(0x0c, 0x10, hci_extcap_debug),
 193        EXT_CAP(0x0d, 0x0c, hci_extcap_scheduled_cmd),
 194        EXT_CAP(0x0e, 0x80, hci_extcap_non_curr_master), /* TODO confirm size */
 195        EXT_CAP(0x0f, 0x04, hci_extcap_ccc_resp_conf),
 196        EXT_CAP(0x10, 0x08, hci_extcap_global_DAT),
 197        EXT_CAP(0x9d, 0x04,     hci_extcap_multilane),
 198        EXT_CAP(0x9e, 0x04, hci_extcap_ncm_multilane),
 199};
 200
 201static int hci_extcap_vendor_NXP(struct i3c_hci *hci, void __iomem *base)
 202{
 203        hci->vendor_data = (__force void *)base;
 204        dev_info(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1*4));
 205        /* reset the FPGA */
 206        writel(0xdeadbeef, base + 1*4);
 207        return 0;
 208}
 209
 210struct hci_ext_cap_vendor_specific {
 211        u32 vendor;
 212        u8  cap;
 213        u16 min_length;
 214        int (*parser)(struct i3c_hci *hci, void __iomem *base);
 215};
 216
 217#define EXT_CAP_VENDOR(_vendor, _cap, _highest_mandatory_reg_offset) \
 218        { .vendor = (MIPI_VENDOR_##_vendor), .cap = (_cap), \
 219          .parser = (hci_extcap_vendor_##_vendor), \
 220          .min_length = (_highest_mandatory_reg_offset)/4 + 1 }
 221
 222static const struct hci_ext_cap_vendor_specific vendor_ext_caps[] = {
 223        EXT_CAP_VENDOR(NXP, 0xc0, 0x20),
 224};
 225
 226static int hci_extcap_vendor_specific(struct i3c_hci *hci, void __iomem *base,
 227                                      u32 cap_id, u32 cap_length)
 228{
 229        const struct hci_ext_cap_vendor_specific *vendor_cap_entry;
 230        int i;
 231
 232        vendor_cap_entry = NULL;
 233        for (i = 0; i < ARRAY_SIZE(vendor_ext_caps); i++) {
 234                if (vendor_ext_caps[i].vendor == hci->vendor_mipi_id &&
 235                    vendor_ext_caps[i].cap == cap_id) {
 236                        vendor_cap_entry = &vendor_ext_caps[i];
 237                        break;
 238                }
 239        }
 240
 241        if (!vendor_cap_entry) {
 242                dev_notice(&hci->master.dev,
 243                           "unknown ext_cap 0x%02x for vendor 0x%02x\n",
 244                           cap_id, hci->vendor_mipi_id);
 245                return 0;
 246        }
 247        if (cap_length < vendor_cap_entry->min_length) {
 248                dev_err(&hci->master.dev,
 249                        "ext_cap 0x%02x has size %d (expecting >= %d)\n",
 250                        cap_id, cap_length, vendor_cap_entry->min_length);
 251                return -EINVAL;
 252        }
 253        return vendor_cap_entry->parser(hci, base);
 254}
 255
 256int i3c_hci_parse_ext_caps(struct i3c_hci *hci)
 257{
 258        void __iomem *curr_cap = hci->EXTCAPS_regs;
 259        void __iomem *end = curr_cap + 0x1000; /* some arbitrary limit */
 260        u32 cap_header, cap_id, cap_length;
 261        const struct hci_ext_caps *cap_entry;
 262        int i, err = 0;
 263
 264        if (!curr_cap)
 265                return 0;
 266
 267        for (; !err && curr_cap < end; curr_cap += cap_length * 4) {
 268                cap_header = readl(curr_cap);
 269                cap_id = FIELD_GET(CAP_HEADER_ID, cap_header);
 270                cap_length = FIELD_GET(CAP_HEADER_LENGTH, cap_header);
 271                DBG("id=0x%02x length=%d", cap_id, cap_length);
 272                if (!cap_length)
 273                        break;
 274                if (curr_cap + cap_length * 4 >= end) {
 275                        dev_err(&hci->master.dev,
 276                                "ext_cap 0x%02x has size %d (too big)\n",
 277                                cap_id, cap_length);
 278                        err = -EINVAL;
 279                        break;
 280                }
 281
 282                if (cap_id >= 0xc0 && cap_id <= 0xcf) {
 283                        err = hci_extcap_vendor_specific(hci, curr_cap,
 284                                                         cap_id, cap_length);
 285                        continue;
 286                }
 287
 288                cap_entry = NULL;
 289                for (i = 0; i < ARRAY_SIZE(ext_capabilities); i++) {
 290                        if (ext_capabilities[i].id == cap_id) {
 291                                cap_entry = &ext_capabilities[i];
 292                                break;
 293                        }
 294                }
 295                if (!cap_entry) {
 296                        dev_notice(&hci->master.dev,
 297                                   "unknown ext_cap 0x%02x\n", cap_id);
 298                } else if (cap_length < cap_entry->min_length) {
 299                        dev_err(&hci->master.dev,
 300                                "ext_cap 0x%02x has size %d (expecting >= %d)\n",
 301                                cap_id, cap_length, cap_entry->min_length);
 302                        err = -EINVAL;
 303                } else {
 304                        err = cap_entry->parser(hci, curr_cap);
 305                }
 306        }
 307        return err;
 308}
 309