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