cachepc-linux

Fork of AMDESE/linux with modifications for CachePC side-channel attack
git clone https://git.sinitax.com/sinitax/cachepc-linux
Log | Files | Refs | README | LICENSE | sfeed.txt

ext_caps.c (8881B)


      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}