162306a36Sopenharmony_ci// SPDX-License-Identifier: BSD-3-Clause
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * Copyright (c) 2020, MIPI Alliance, Inc.
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Author: Nicolas Pitre <npitre@baylibre.com>
662306a36Sopenharmony_ci */
762306a36Sopenharmony_ci
862306a36Sopenharmony_ci#include <linux/bitfield.h>
962306a36Sopenharmony_ci#include <linux/device.h>
1062306a36Sopenharmony_ci#include <linux/errno.h>
1162306a36Sopenharmony_ci#include <linux/i3c/master.h>
1262306a36Sopenharmony_ci#include <linux/kernel.h>
1362306a36Sopenharmony_ci#include <linux/io.h>
1462306a36Sopenharmony_ci
1562306a36Sopenharmony_ci#include "hci.h"
1662306a36Sopenharmony_ci#include "ext_caps.h"
1762306a36Sopenharmony_ci#include "xfer_mode_rate.h"
1862306a36Sopenharmony_ci
1962306a36Sopenharmony_ci
2062306a36Sopenharmony_ci/* Extended Capability Header */
2162306a36Sopenharmony_ci#define CAP_HEADER_LENGTH		GENMASK(23, 8)
2262306a36Sopenharmony_ci#define CAP_HEADER_ID			GENMASK(7, 0)
2362306a36Sopenharmony_ci
2462306a36Sopenharmony_cistatic int hci_extcap_hardware_id(struct i3c_hci *hci, void __iomem *base)
2562306a36Sopenharmony_ci{
2662306a36Sopenharmony_ci	hci->vendor_mipi_id	= readl(base + 0x04);
2762306a36Sopenharmony_ci	hci->vendor_version_id	= readl(base + 0x08);
2862306a36Sopenharmony_ci	hci->vendor_product_id	= readl(base + 0x0c);
2962306a36Sopenharmony_ci
3062306a36Sopenharmony_ci	dev_info(&hci->master.dev, "vendor MIPI ID: %#x\n", hci->vendor_mipi_id);
3162306a36Sopenharmony_ci	dev_info(&hci->master.dev, "vendor version ID: %#x\n", hci->vendor_version_id);
3262306a36Sopenharmony_ci	dev_info(&hci->master.dev, "vendor product ID: %#x\n", hci->vendor_product_id);
3362306a36Sopenharmony_ci
3462306a36Sopenharmony_ci	/* ought to go in a table if this grows too much */
3562306a36Sopenharmony_ci	switch (hci->vendor_mipi_id) {
3662306a36Sopenharmony_ci	case MIPI_VENDOR_NXP:
3762306a36Sopenharmony_ci		hci->quirks |= HCI_QUIRK_RAW_CCC;
3862306a36Sopenharmony_ci		DBG("raw CCC quirks set");
3962306a36Sopenharmony_ci		break;
4062306a36Sopenharmony_ci	}
4162306a36Sopenharmony_ci
4262306a36Sopenharmony_ci	return 0;
4362306a36Sopenharmony_ci}
4462306a36Sopenharmony_ci
4562306a36Sopenharmony_cistatic int hci_extcap_master_config(struct i3c_hci *hci, void __iomem *base)
4662306a36Sopenharmony_ci{
4762306a36Sopenharmony_ci	u32 master_config = readl(base + 0x04);
4862306a36Sopenharmony_ci	unsigned int operation_mode = FIELD_GET(GENMASK(5, 4), master_config);
4962306a36Sopenharmony_ci	static const char * const functionality[] = {
5062306a36Sopenharmony_ci		"(unknown)", "master only", "target only",
5162306a36Sopenharmony_ci		"primary/secondary master" };
5262306a36Sopenharmony_ci	dev_info(&hci->master.dev, "operation mode: %s\n", functionality[operation_mode]);
5362306a36Sopenharmony_ci	if (operation_mode & 0x1)
5462306a36Sopenharmony_ci		return 0;
5562306a36Sopenharmony_ci	dev_err(&hci->master.dev, "only master mode is currently supported\n");
5662306a36Sopenharmony_ci	return -EOPNOTSUPP;
5762306a36Sopenharmony_ci}
5862306a36Sopenharmony_ci
5962306a36Sopenharmony_cistatic int hci_extcap_multi_bus(struct i3c_hci *hci, void __iomem *base)
6062306a36Sopenharmony_ci{
6162306a36Sopenharmony_ci	u32 bus_instance = readl(base + 0x04);
6262306a36Sopenharmony_ci	unsigned int count = FIELD_GET(GENMASK(3, 0), bus_instance);
6362306a36Sopenharmony_ci
6462306a36Sopenharmony_ci	dev_info(&hci->master.dev, "%d bus instances\n", count);
6562306a36Sopenharmony_ci	return 0;
6662306a36Sopenharmony_ci}
6762306a36Sopenharmony_ci
6862306a36Sopenharmony_cistatic int hci_extcap_xfer_modes(struct i3c_hci *hci, void __iomem *base)
6962306a36Sopenharmony_ci{
7062306a36Sopenharmony_ci	u32 header = readl(base);
7162306a36Sopenharmony_ci	u32 entries = FIELD_GET(CAP_HEADER_LENGTH, header) - 1;
7262306a36Sopenharmony_ci	unsigned int index;
7362306a36Sopenharmony_ci
7462306a36Sopenharmony_ci	dev_info(&hci->master.dev, "transfer mode table has %d entries\n",
7562306a36Sopenharmony_ci		 entries);
7662306a36Sopenharmony_ci	base += 4;  /* skip header */
7762306a36Sopenharmony_ci	for (index = 0; index < entries; index++) {
7862306a36Sopenharmony_ci		u32 mode_entry = readl(base);
7962306a36Sopenharmony_ci
8062306a36Sopenharmony_ci		DBG("mode %d: 0x%08x", index, mode_entry);
8162306a36Sopenharmony_ci		/* TODO: will be needed when I3C core does more than SDR */
8262306a36Sopenharmony_ci		base += 4;
8362306a36Sopenharmony_ci	}
8462306a36Sopenharmony_ci
8562306a36Sopenharmony_ci	return 0;
8662306a36Sopenharmony_ci}
8762306a36Sopenharmony_ci
8862306a36Sopenharmony_cistatic int hci_extcap_xfer_rates(struct i3c_hci *hci, void __iomem *base)
8962306a36Sopenharmony_ci{
9062306a36Sopenharmony_ci	u32 header = readl(base);
9162306a36Sopenharmony_ci	u32 entries = FIELD_GET(CAP_HEADER_LENGTH, header) - 1;
9262306a36Sopenharmony_ci	u32 rate_entry;
9362306a36Sopenharmony_ci	unsigned int index, rate, rate_id, mode_id;
9462306a36Sopenharmony_ci
9562306a36Sopenharmony_ci	base += 4;  /* skip header */
9662306a36Sopenharmony_ci
9762306a36Sopenharmony_ci	dev_info(&hci->master.dev, "available data rates:\n");
9862306a36Sopenharmony_ci	for (index = 0; index < entries; index++) {
9962306a36Sopenharmony_ci		rate_entry = readl(base);
10062306a36Sopenharmony_ci		DBG("entry %d: 0x%08x", index, rate_entry);
10162306a36Sopenharmony_ci		rate = FIELD_GET(XFERRATE_ACTUAL_RATE_KHZ, rate_entry);
10262306a36Sopenharmony_ci		rate_id = FIELD_GET(XFERRATE_RATE_ID, rate_entry);
10362306a36Sopenharmony_ci		mode_id = FIELD_GET(XFERRATE_MODE_ID, rate_entry);
10462306a36Sopenharmony_ci		dev_info(&hci->master.dev, "rate %d for %s = %d kHz\n",
10562306a36Sopenharmony_ci			 rate_id,
10662306a36Sopenharmony_ci			 mode_id == XFERRATE_MODE_I3C ? "I3C" :
10762306a36Sopenharmony_ci			 mode_id == XFERRATE_MODE_I2C ? "I2C" :
10862306a36Sopenharmony_ci			 "unknown mode",
10962306a36Sopenharmony_ci			 rate);
11062306a36Sopenharmony_ci		base += 4;
11162306a36Sopenharmony_ci	}
11262306a36Sopenharmony_ci
11362306a36Sopenharmony_ci	return 0;
11462306a36Sopenharmony_ci}
11562306a36Sopenharmony_ci
11662306a36Sopenharmony_cistatic int hci_extcap_auto_command(struct i3c_hci *hci, void __iomem *base)
11762306a36Sopenharmony_ci{
11862306a36Sopenharmony_ci	u32 autocmd_ext_caps = readl(base + 0x04);
11962306a36Sopenharmony_ci	unsigned int max_count = FIELD_GET(GENMASK(3, 0), autocmd_ext_caps);
12062306a36Sopenharmony_ci	u32 autocmd_ext_config = readl(base + 0x08);
12162306a36Sopenharmony_ci	unsigned int count = FIELD_GET(GENMASK(3, 0), autocmd_ext_config);
12262306a36Sopenharmony_ci
12362306a36Sopenharmony_ci	dev_info(&hci->master.dev, "%d/%d active auto-command entries\n",
12462306a36Sopenharmony_ci		 count, max_count);
12562306a36Sopenharmony_ci	/* remember auto-command register location for later use */
12662306a36Sopenharmony_ci	hci->AUTOCMD_regs = base;
12762306a36Sopenharmony_ci	return 0;
12862306a36Sopenharmony_ci}
12962306a36Sopenharmony_ci
13062306a36Sopenharmony_cistatic int hci_extcap_debug(struct i3c_hci *hci, void __iomem *base)
13162306a36Sopenharmony_ci{
13262306a36Sopenharmony_ci	dev_info(&hci->master.dev, "debug registers present\n");
13362306a36Sopenharmony_ci	hci->DEBUG_regs = base;
13462306a36Sopenharmony_ci	return 0;
13562306a36Sopenharmony_ci}
13662306a36Sopenharmony_ci
13762306a36Sopenharmony_cistatic int hci_extcap_scheduled_cmd(struct i3c_hci *hci, void __iomem *base)
13862306a36Sopenharmony_ci{
13962306a36Sopenharmony_ci	dev_info(&hci->master.dev, "scheduled commands available\n");
14062306a36Sopenharmony_ci	/* hci->schedcmd_regs = base; */
14162306a36Sopenharmony_ci	return 0;
14262306a36Sopenharmony_ci}
14362306a36Sopenharmony_ci
14462306a36Sopenharmony_cistatic int hci_extcap_non_curr_master(struct i3c_hci *hci, void __iomem *base)
14562306a36Sopenharmony_ci{
14662306a36Sopenharmony_ci	dev_info(&hci->master.dev, "Non-Current Master support available\n");
14762306a36Sopenharmony_ci	/* hci->NCM_regs = base; */
14862306a36Sopenharmony_ci	return 0;
14962306a36Sopenharmony_ci}
15062306a36Sopenharmony_ci
15162306a36Sopenharmony_cistatic int hci_extcap_ccc_resp_conf(struct i3c_hci *hci, void __iomem *base)
15262306a36Sopenharmony_ci{
15362306a36Sopenharmony_ci	dev_info(&hci->master.dev, "CCC Response Configuration available\n");
15462306a36Sopenharmony_ci	return 0;
15562306a36Sopenharmony_ci}
15662306a36Sopenharmony_ci
15762306a36Sopenharmony_cistatic int hci_extcap_global_DAT(struct i3c_hci *hci, void __iomem *base)
15862306a36Sopenharmony_ci{
15962306a36Sopenharmony_ci	dev_info(&hci->master.dev, "Global DAT available\n");
16062306a36Sopenharmony_ci	return 0;
16162306a36Sopenharmony_ci}
16262306a36Sopenharmony_ci
16362306a36Sopenharmony_cistatic int hci_extcap_multilane(struct i3c_hci *hci, void __iomem *base)
16462306a36Sopenharmony_ci{
16562306a36Sopenharmony_ci	dev_info(&hci->master.dev, "Master Multi-Lane support available\n");
16662306a36Sopenharmony_ci	return 0;
16762306a36Sopenharmony_ci}
16862306a36Sopenharmony_ci
16962306a36Sopenharmony_cistatic int hci_extcap_ncm_multilane(struct i3c_hci *hci, void __iomem *base)
17062306a36Sopenharmony_ci{
17162306a36Sopenharmony_ci	dev_info(&hci->master.dev, "NCM Multi-Lane support available\n");
17262306a36Sopenharmony_ci	return 0;
17362306a36Sopenharmony_ci}
17462306a36Sopenharmony_ci
17562306a36Sopenharmony_cistruct hci_ext_caps {
17662306a36Sopenharmony_ci	u8  id;
17762306a36Sopenharmony_ci	u16 min_length;
17862306a36Sopenharmony_ci	int (*parser)(struct i3c_hci *hci, void __iomem *base);
17962306a36Sopenharmony_ci};
18062306a36Sopenharmony_ci
18162306a36Sopenharmony_ci#define EXT_CAP(_id, _highest_mandatory_reg_offset, _parser) \
18262306a36Sopenharmony_ci	{ .id = (_id), .parser = (_parser), \
18362306a36Sopenharmony_ci	  .min_length = (_highest_mandatory_reg_offset)/4 + 1 }
18462306a36Sopenharmony_ci
18562306a36Sopenharmony_cistatic const struct hci_ext_caps ext_capabilities[] = {
18662306a36Sopenharmony_ci	EXT_CAP(0x01, 0x0c, hci_extcap_hardware_id),
18762306a36Sopenharmony_ci	EXT_CAP(0x02, 0x04, hci_extcap_master_config),
18862306a36Sopenharmony_ci	EXT_CAP(0x03, 0x04, hci_extcap_multi_bus),
18962306a36Sopenharmony_ci	EXT_CAP(0x04, 0x24, hci_extcap_xfer_modes),
19062306a36Sopenharmony_ci	EXT_CAP(0x05, 0x08, hci_extcap_auto_command),
19162306a36Sopenharmony_ci	EXT_CAP(0x08, 0x40, hci_extcap_xfer_rates),
19262306a36Sopenharmony_ci	EXT_CAP(0x0c, 0x10, hci_extcap_debug),
19362306a36Sopenharmony_ci	EXT_CAP(0x0d, 0x0c, hci_extcap_scheduled_cmd),
19462306a36Sopenharmony_ci	EXT_CAP(0x0e, 0x80, hci_extcap_non_curr_master), /* TODO confirm size */
19562306a36Sopenharmony_ci	EXT_CAP(0x0f, 0x04, hci_extcap_ccc_resp_conf),
19662306a36Sopenharmony_ci	EXT_CAP(0x10, 0x08, hci_extcap_global_DAT),
19762306a36Sopenharmony_ci	EXT_CAP(0x9d, 0x04,	hci_extcap_multilane),
19862306a36Sopenharmony_ci	EXT_CAP(0x9e, 0x04, hci_extcap_ncm_multilane),
19962306a36Sopenharmony_ci};
20062306a36Sopenharmony_ci
20162306a36Sopenharmony_cistatic int hci_extcap_vendor_NXP(struct i3c_hci *hci, void __iomem *base)
20262306a36Sopenharmony_ci{
20362306a36Sopenharmony_ci	hci->vendor_data = (__force void *)base;
20462306a36Sopenharmony_ci	dev_info(&hci->master.dev, "Build Date Info = %#x\n", readl(base + 1*4));
20562306a36Sopenharmony_ci	/* reset the FPGA */
20662306a36Sopenharmony_ci	writel(0xdeadbeef, base + 1*4);
20762306a36Sopenharmony_ci	return 0;
20862306a36Sopenharmony_ci}
20962306a36Sopenharmony_ci
21062306a36Sopenharmony_cistruct hci_ext_cap_vendor_specific {
21162306a36Sopenharmony_ci	u32 vendor;
21262306a36Sopenharmony_ci	u8  cap;
21362306a36Sopenharmony_ci	u16 min_length;
21462306a36Sopenharmony_ci	int (*parser)(struct i3c_hci *hci, void __iomem *base);
21562306a36Sopenharmony_ci};
21662306a36Sopenharmony_ci
21762306a36Sopenharmony_ci#define EXT_CAP_VENDOR(_vendor, _cap, _highest_mandatory_reg_offset) \
21862306a36Sopenharmony_ci	{ .vendor = (MIPI_VENDOR_##_vendor), .cap = (_cap), \
21962306a36Sopenharmony_ci	  .parser = (hci_extcap_vendor_##_vendor), \
22062306a36Sopenharmony_ci	  .min_length = (_highest_mandatory_reg_offset)/4 + 1 }
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_cistatic const struct hci_ext_cap_vendor_specific vendor_ext_caps[] = {
22362306a36Sopenharmony_ci	EXT_CAP_VENDOR(NXP, 0xc0, 0x20),
22462306a36Sopenharmony_ci};
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_cistatic int hci_extcap_vendor_specific(struct i3c_hci *hci, void __iomem *base,
22762306a36Sopenharmony_ci				      u32 cap_id, u32 cap_length)
22862306a36Sopenharmony_ci{
22962306a36Sopenharmony_ci	const struct hci_ext_cap_vendor_specific *vendor_cap_entry;
23062306a36Sopenharmony_ci	int i;
23162306a36Sopenharmony_ci
23262306a36Sopenharmony_ci	vendor_cap_entry = NULL;
23362306a36Sopenharmony_ci	for (i = 0; i < ARRAY_SIZE(vendor_ext_caps); i++) {
23462306a36Sopenharmony_ci		if (vendor_ext_caps[i].vendor == hci->vendor_mipi_id &&
23562306a36Sopenharmony_ci		    vendor_ext_caps[i].cap == cap_id) {
23662306a36Sopenharmony_ci			vendor_cap_entry = &vendor_ext_caps[i];
23762306a36Sopenharmony_ci			break;
23862306a36Sopenharmony_ci		}
23962306a36Sopenharmony_ci	}
24062306a36Sopenharmony_ci
24162306a36Sopenharmony_ci	if (!vendor_cap_entry) {
24262306a36Sopenharmony_ci		dev_notice(&hci->master.dev,
24362306a36Sopenharmony_ci			   "unknown ext_cap 0x%02x for vendor 0x%02x\n",
24462306a36Sopenharmony_ci			   cap_id, hci->vendor_mipi_id);
24562306a36Sopenharmony_ci		return 0;
24662306a36Sopenharmony_ci	}
24762306a36Sopenharmony_ci	if (cap_length < vendor_cap_entry->min_length) {
24862306a36Sopenharmony_ci		dev_err(&hci->master.dev,
24962306a36Sopenharmony_ci			"ext_cap 0x%02x has size %d (expecting >= %d)\n",
25062306a36Sopenharmony_ci			cap_id, cap_length, vendor_cap_entry->min_length);
25162306a36Sopenharmony_ci		return -EINVAL;
25262306a36Sopenharmony_ci	}
25362306a36Sopenharmony_ci	return vendor_cap_entry->parser(hci, base);
25462306a36Sopenharmony_ci}
25562306a36Sopenharmony_ci
25662306a36Sopenharmony_ciint i3c_hci_parse_ext_caps(struct i3c_hci *hci)
25762306a36Sopenharmony_ci{
25862306a36Sopenharmony_ci	void __iomem *curr_cap = hci->EXTCAPS_regs;
25962306a36Sopenharmony_ci	void __iomem *end = curr_cap + 0x1000; /* some arbitrary limit */
26062306a36Sopenharmony_ci	u32 cap_header, cap_id, cap_length;
26162306a36Sopenharmony_ci	const struct hci_ext_caps *cap_entry;
26262306a36Sopenharmony_ci	int i, err = 0;
26362306a36Sopenharmony_ci
26462306a36Sopenharmony_ci	if (!curr_cap)
26562306a36Sopenharmony_ci		return 0;
26662306a36Sopenharmony_ci
26762306a36Sopenharmony_ci	for (; !err && curr_cap < end; curr_cap += cap_length * 4) {
26862306a36Sopenharmony_ci		cap_header = readl(curr_cap);
26962306a36Sopenharmony_ci		cap_id = FIELD_GET(CAP_HEADER_ID, cap_header);
27062306a36Sopenharmony_ci		cap_length = FIELD_GET(CAP_HEADER_LENGTH, cap_header);
27162306a36Sopenharmony_ci		DBG("id=0x%02x length=%d", cap_id, cap_length);
27262306a36Sopenharmony_ci		if (!cap_length)
27362306a36Sopenharmony_ci			break;
27462306a36Sopenharmony_ci		if (curr_cap + cap_length * 4 >= end) {
27562306a36Sopenharmony_ci			dev_err(&hci->master.dev,
27662306a36Sopenharmony_ci				"ext_cap 0x%02x has size %d (too big)\n",
27762306a36Sopenharmony_ci				cap_id, cap_length);
27862306a36Sopenharmony_ci			err = -EINVAL;
27962306a36Sopenharmony_ci			break;
28062306a36Sopenharmony_ci		}
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci		if (cap_id >= 0xc0 && cap_id <= 0xcf) {
28362306a36Sopenharmony_ci			err = hci_extcap_vendor_specific(hci, curr_cap,
28462306a36Sopenharmony_ci							 cap_id, cap_length);
28562306a36Sopenharmony_ci			continue;
28662306a36Sopenharmony_ci		}
28762306a36Sopenharmony_ci
28862306a36Sopenharmony_ci		cap_entry = NULL;
28962306a36Sopenharmony_ci		for (i = 0; i < ARRAY_SIZE(ext_capabilities); i++) {
29062306a36Sopenharmony_ci			if (ext_capabilities[i].id == cap_id) {
29162306a36Sopenharmony_ci				cap_entry = &ext_capabilities[i];
29262306a36Sopenharmony_ci				break;
29362306a36Sopenharmony_ci			}
29462306a36Sopenharmony_ci		}
29562306a36Sopenharmony_ci		if (!cap_entry) {
29662306a36Sopenharmony_ci			dev_notice(&hci->master.dev,
29762306a36Sopenharmony_ci				   "unknown ext_cap 0x%02x\n", cap_id);
29862306a36Sopenharmony_ci		} else if (cap_length < cap_entry->min_length) {
29962306a36Sopenharmony_ci			dev_err(&hci->master.dev,
30062306a36Sopenharmony_ci				"ext_cap 0x%02x has size %d (expecting >= %d)\n",
30162306a36Sopenharmony_ci				cap_id, cap_length, cap_entry->min_length);
30262306a36Sopenharmony_ci			err = -EINVAL;
30362306a36Sopenharmony_ci		} else {
30462306a36Sopenharmony_ci			err = cap_entry->parser(hci, curr_cap);
30562306a36Sopenharmony_ci		}
30662306a36Sopenharmony_ci	}
30762306a36Sopenharmony_ci	return err;
30862306a36Sopenharmony_ci}
309