xref: /linux/drivers/i3c/master/mipi-i3c-hci/core.c (revision 0074281bb6316108e0cff094bd4db78ab3eee236)
19ad9a52cSNicolas Pitre // SPDX-License-Identifier: BSD-3-Clause
29ad9a52cSNicolas Pitre /*
39ad9a52cSNicolas Pitre  * Copyright (c) 2020, MIPI Alliance, Inc.
49ad9a52cSNicolas Pitre  *
59ad9a52cSNicolas Pitre  * Author: Nicolas Pitre <npitre@baylibre.com>
69ad9a52cSNicolas Pitre  *
79ad9a52cSNicolas Pitre  * Core driver code with main interface to the I3C subsystem.
89ad9a52cSNicolas Pitre  */
99ad9a52cSNicolas Pitre 
109ad9a52cSNicolas Pitre #include <linux/bitfield.h>
119ad9a52cSNicolas Pitre #include <linux/device.h>
129ad9a52cSNicolas Pitre #include <linux/errno.h>
139ad9a52cSNicolas Pitre #include <linux/i3c/master.h>
149ad9a52cSNicolas Pitre #include <linux/interrupt.h>
159ad9a52cSNicolas Pitre #include <linux/iopoll.h>
169ad9a52cSNicolas Pitre #include <linux/module.h>
179ad9a52cSNicolas Pitre #include <linux/platform_device.h>
189ad9a52cSNicolas Pitre 
199ad9a52cSNicolas Pitre #include "hci.h"
209ad9a52cSNicolas Pitre #include "ext_caps.h"
219ad9a52cSNicolas Pitre #include "cmd.h"
229ad9a52cSNicolas Pitre #include "dat.h"
239ad9a52cSNicolas Pitre 
249ad9a52cSNicolas Pitre 
259ad9a52cSNicolas Pitre /*
269ad9a52cSNicolas Pitre  * Host Controller Capabilities and Operation Registers
279ad9a52cSNicolas Pitre  */
289ad9a52cSNicolas Pitre 
299ad9a52cSNicolas Pitre #define HCI_VERSION			0x00	/* HCI Version (in BCD) */
309ad9a52cSNicolas Pitre 
319ad9a52cSNicolas Pitre #define HC_CONTROL			0x04
329ad9a52cSNicolas Pitre #define HC_CONTROL_BUS_ENABLE		BIT(31)
339ad9a52cSNicolas Pitre #define HC_CONTROL_RESUME		BIT(30)
349ad9a52cSNicolas Pitre #define HC_CONTROL_ABORT		BIT(29)
359ad9a52cSNicolas Pitre #define HC_CONTROL_HALT_ON_CMD_TIMEOUT	BIT(12)
369ad9a52cSNicolas Pitre #define HC_CONTROL_HOT_JOIN_CTRL	BIT(8)	/* Hot-Join ACK/NACK Control */
379ad9a52cSNicolas Pitre #define HC_CONTROL_I2C_TARGET_PRESENT	BIT(7)
389ad9a52cSNicolas Pitre #define HC_CONTROL_PIO_MODE		BIT(6)	/* DMA/PIO Mode Selector */
399ad9a52cSNicolas Pitre #define HC_CONTROL_DATA_BIG_ENDIAN	BIT(4)
409ad9a52cSNicolas Pitre #define HC_CONTROL_IBA_INCLUDE		BIT(0)	/* Include I3C Broadcast Address */
419ad9a52cSNicolas Pitre 
429ad9a52cSNicolas Pitre #define MASTER_DEVICE_ADDR		0x08	/* Master Device Address */
439ad9a52cSNicolas Pitre #define MASTER_DYNAMIC_ADDR_VALID	BIT(31)	/* Dynamic Address is Valid */
449ad9a52cSNicolas Pitre #define MASTER_DYNAMIC_ADDR(v)		FIELD_PREP(GENMASK(22, 16), v)
459ad9a52cSNicolas Pitre 
469ad9a52cSNicolas Pitre #define HC_CAPABILITIES			0x0c
479ad9a52cSNicolas Pitre #define HC_CAP_SG_DC_EN			BIT(30)
489ad9a52cSNicolas Pitre #define HC_CAP_SG_IBI_EN		BIT(29)
499ad9a52cSNicolas Pitre #define HC_CAP_SG_CR_EN			BIT(28)
509ad9a52cSNicolas Pitre #define HC_CAP_MAX_DATA_LENGTH		GENMASK(24, 22)
519ad9a52cSNicolas Pitre #define HC_CAP_CMD_SIZE			GENMASK(21, 20)
529ad9a52cSNicolas Pitre #define HC_CAP_DIRECT_COMMANDS_EN	BIT(18)
539ad9a52cSNicolas Pitre #define HC_CAP_MULTI_LANE_EN		BIT(15)
549ad9a52cSNicolas Pitre #define HC_CAP_CMD_CCC_DEFBYTE		BIT(10)
559ad9a52cSNicolas Pitre #define HC_CAP_HDR_BT_EN		BIT(8)
569ad9a52cSNicolas Pitre #define HC_CAP_HDR_TS_EN		BIT(7)
579ad9a52cSNicolas Pitre #define HC_CAP_HDR_DDR_EN		BIT(6)
589ad9a52cSNicolas Pitre #define HC_CAP_NON_CURRENT_MASTER_CAP	BIT(5)	/* master handoff capable */
599ad9a52cSNicolas Pitre #define HC_CAP_DATA_BYTE_CFG_EN		BIT(4)	/* endian selection possible */
609ad9a52cSNicolas Pitre #define HC_CAP_AUTO_COMMAND		BIT(3)
619ad9a52cSNicolas Pitre #define HC_CAP_COMBO_COMMAND		BIT(2)
629ad9a52cSNicolas Pitre 
639ad9a52cSNicolas Pitre #define RESET_CONTROL			0x10
649ad9a52cSNicolas Pitre #define BUS_RESET			BIT(31)
659ad9a52cSNicolas Pitre #define BUS_RESET_TYPE			GENMASK(30, 29)
669ad9a52cSNicolas Pitre #define IBI_QUEUE_RST			BIT(5)
679ad9a52cSNicolas Pitre #define RX_FIFO_RST			BIT(4)
689ad9a52cSNicolas Pitre #define TX_FIFO_RST			BIT(3)
699ad9a52cSNicolas Pitre #define RESP_QUEUE_RST			BIT(2)
709ad9a52cSNicolas Pitre #define CMD_QUEUE_RST			BIT(1)
719ad9a52cSNicolas Pitre #define SOFT_RST			BIT(0)	/* Core Reset */
729ad9a52cSNicolas Pitre 
739ad9a52cSNicolas Pitre #define PRESENT_STATE			0x14
749ad9a52cSNicolas Pitre #define STATE_CURRENT_MASTER		BIT(2)
759ad9a52cSNicolas Pitre 
769ad9a52cSNicolas Pitre #define INTR_STATUS			0x20
779ad9a52cSNicolas Pitre #define INTR_STATUS_ENABLE		0x24
789ad9a52cSNicolas Pitre #define INTR_SIGNAL_ENABLE		0x28
799ad9a52cSNicolas Pitre #define INTR_FORCE			0x2c
809ad9a52cSNicolas Pitre #define INTR_HC_CMD_SEQ_UFLOW_STAT	BIT(12)	/* Cmd Sequence Underflow */
817479d267SJarkko Nikula #define INTR_HC_SEQ_CANCEL		BIT(11)	/* HC Cancelled Transaction Sequence */
829ad9a52cSNicolas Pitre #define INTR_HC_INTERNAL_ERR		BIT(10)	/* HC Internal Error */
839ad9a52cSNicolas Pitre 
849ad9a52cSNicolas Pitre #define DAT_SECTION			0x30	/* Device Address Table */
859ad9a52cSNicolas Pitre #define DAT_ENTRY_SIZE			GENMASK(31, 28)
869ad9a52cSNicolas Pitre #define DAT_TABLE_SIZE			GENMASK(18, 12)
879ad9a52cSNicolas Pitre #define DAT_TABLE_OFFSET		GENMASK(11, 0)
889ad9a52cSNicolas Pitre 
899ad9a52cSNicolas Pitre #define DCT_SECTION			0x34	/* Device Characteristics Table */
909ad9a52cSNicolas Pitre #define DCT_ENTRY_SIZE			GENMASK(31, 28)
919ad9a52cSNicolas Pitre #define DCT_TABLE_INDEX			GENMASK(23, 19)
929ad9a52cSNicolas Pitre #define DCT_TABLE_SIZE			GENMASK(18, 12)
939ad9a52cSNicolas Pitre #define DCT_TABLE_OFFSET		GENMASK(11, 0)
949ad9a52cSNicolas Pitre 
959ad9a52cSNicolas Pitre #define RING_HEADERS_SECTION		0x38
969ad9a52cSNicolas Pitre #define RING_HEADERS_OFFSET		GENMASK(15, 0)
979ad9a52cSNicolas Pitre 
989ad9a52cSNicolas Pitre #define PIO_SECTION			0x3c
999ad9a52cSNicolas Pitre #define PIO_REGS_OFFSET			GENMASK(15, 0)	/* PIO Offset */
1009ad9a52cSNicolas Pitre 
1019ad9a52cSNicolas Pitre #define EXT_CAPS_SECTION		0x40
1029ad9a52cSNicolas Pitre #define EXT_CAPS_OFFSET			GENMASK(15, 0)
1039ad9a52cSNicolas Pitre 
1049ad9a52cSNicolas Pitre #define IBI_NOTIFY_CTRL			0x58	/* IBI Notify Control */
1059ad9a52cSNicolas Pitre #define IBI_NOTIFY_SIR_REJECTED		BIT(3)	/* Rejected Target Interrupt Request */
1069ad9a52cSNicolas Pitre #define IBI_NOTIFY_MR_REJECTED		BIT(1)	/* Rejected Master Request Control */
1079ad9a52cSNicolas Pitre #define IBI_NOTIFY_HJ_REJECTED		BIT(0)	/* Rejected Hot-Join Control */
1089ad9a52cSNicolas Pitre 
1099ad9a52cSNicolas Pitre #define DEV_CTX_BASE_LO			0x60
1109ad9a52cSNicolas Pitre #define DEV_CTX_BASE_HI			0x64
1119ad9a52cSNicolas Pitre 
1129ad9a52cSNicolas Pitre 
to_i3c_hci(struct i3c_master_controller * m)1139ad9a52cSNicolas Pitre static inline struct i3c_hci *to_i3c_hci(struct i3c_master_controller *m)
1149ad9a52cSNicolas Pitre {
1159ad9a52cSNicolas Pitre 	return container_of(m, struct i3c_hci, master);
1169ad9a52cSNicolas Pitre }
1179ad9a52cSNicolas Pitre 
i3c_hci_bus_init(struct i3c_master_controller * m)1189ad9a52cSNicolas Pitre static int i3c_hci_bus_init(struct i3c_master_controller *m)
1199ad9a52cSNicolas Pitre {
1209ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
1219ad9a52cSNicolas Pitre 	struct i3c_device_info info;
1229ad9a52cSNicolas Pitre 	int ret;
1239ad9a52cSNicolas Pitre 
1249ad9a52cSNicolas Pitre 	DBG("");
1259ad9a52cSNicolas Pitre 
1269ad9a52cSNicolas Pitre 	if (hci->cmd == &mipi_i3c_hci_cmd_v1) {
1279ad9a52cSNicolas Pitre 		ret = mipi_i3c_hci_dat_v1.init(hci);
1289ad9a52cSNicolas Pitre 		if (ret)
1299ad9a52cSNicolas Pitre 			return ret;
1309ad9a52cSNicolas Pitre 	}
1319ad9a52cSNicolas Pitre 
1329ad9a52cSNicolas Pitre 	ret = i3c_master_get_free_addr(m, 0);
1339ad9a52cSNicolas Pitre 	if (ret < 0)
1349ad9a52cSNicolas Pitre 		return ret;
1359ad9a52cSNicolas Pitre 	reg_write(MASTER_DEVICE_ADDR,
1369ad9a52cSNicolas Pitre 		  MASTER_DYNAMIC_ADDR(ret) | MASTER_DYNAMIC_ADDR_VALID);
1379ad9a52cSNicolas Pitre 	memset(&info, 0, sizeof(info));
1389ad9a52cSNicolas Pitre 	info.dyn_addr = ret;
1399ad9a52cSNicolas Pitre 	ret = i3c_master_set_info(m, &info);
1409ad9a52cSNicolas Pitre 	if (ret)
1419ad9a52cSNicolas Pitre 		return ret;
1429ad9a52cSNicolas Pitre 
1439ad9a52cSNicolas Pitre 	ret = hci->io->init(hci);
1449ad9a52cSNicolas Pitre 	if (ret)
1459ad9a52cSNicolas Pitre 		return ret;
1469ad9a52cSNicolas Pitre 
147ced86959SShyam Sundar S K 	/* Set RESP_BUF_THLD to 0(n) to get 1(n+1) response */
148ced86959SShyam Sundar S K 	if (hci->quirks & HCI_QUIRK_RESP_BUF_THLD)
149ced86959SShyam Sundar S K 		amd_set_resp_buf_thld(hci);
150ced86959SShyam Sundar S K 
1519ad9a52cSNicolas Pitre 	reg_set(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
1529ad9a52cSNicolas Pitre 	DBG("HC_CONTROL = %#x", reg_read(HC_CONTROL));
1539ad9a52cSNicolas Pitre 
1549ad9a52cSNicolas Pitre 	return 0;
1559ad9a52cSNicolas Pitre }
1569ad9a52cSNicolas Pitre 
i3c_hci_bus_cleanup(struct i3c_master_controller * m)1579ad9a52cSNicolas Pitre static void i3c_hci_bus_cleanup(struct i3c_master_controller *m)
1589ad9a52cSNicolas Pitre {
1599ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
1604e40642cSJarkko Nikula 	struct platform_device *pdev = to_platform_device(m->dev.parent);
1619ad9a52cSNicolas Pitre 
1629ad9a52cSNicolas Pitre 	DBG("");
1639ad9a52cSNicolas Pitre 
1649ad9a52cSNicolas Pitre 	reg_clear(HC_CONTROL, HC_CONTROL_BUS_ENABLE);
1654e40642cSJarkko Nikula 	synchronize_irq(platform_get_irq(pdev, 0));
1669ad9a52cSNicolas Pitre 	hci->io->cleanup(hci);
1679ad9a52cSNicolas Pitre 	if (hci->cmd == &mipi_i3c_hci_cmd_v1)
1689ad9a52cSNicolas Pitre 		mipi_i3c_hci_dat_v1.cleanup(hci);
1699ad9a52cSNicolas Pitre }
1709ad9a52cSNicolas Pitre 
mipi_i3c_hci_resume(struct i3c_hci * hci)1719ad9a52cSNicolas Pitre void mipi_i3c_hci_resume(struct i3c_hci *hci)
1729ad9a52cSNicolas Pitre {
1733521fa63SJarkko Nikula 	reg_set(HC_CONTROL, HC_CONTROL_RESUME);
1749ad9a52cSNicolas Pitre }
1759ad9a52cSNicolas Pitre 
1769ad9a52cSNicolas Pitre /* located here rather than pio.c because needed bits are in core reg space */
mipi_i3c_hci_pio_reset(struct i3c_hci * hci)1779ad9a52cSNicolas Pitre void mipi_i3c_hci_pio_reset(struct i3c_hci *hci)
1789ad9a52cSNicolas Pitre {
1799ad9a52cSNicolas Pitre 	reg_write(RESET_CONTROL, RX_FIFO_RST | TX_FIFO_RST | RESP_QUEUE_RST);
1809ad9a52cSNicolas Pitre }
1819ad9a52cSNicolas Pitre 
1829ad9a52cSNicolas Pitre /* located here rather than dct.c because needed bits are in core reg space */
mipi_i3c_hci_dct_index_reset(struct i3c_hci * hci)1839ad9a52cSNicolas Pitre void mipi_i3c_hci_dct_index_reset(struct i3c_hci *hci)
1849ad9a52cSNicolas Pitre {
1859ad9a52cSNicolas Pitre 	reg_write(DCT_SECTION, FIELD_PREP(DCT_TABLE_INDEX, 0));
1869ad9a52cSNicolas Pitre }
1879ad9a52cSNicolas Pitre 
i3c_hci_send_ccc_cmd(struct i3c_master_controller * m,struct i3c_ccc_cmd * ccc)1889ad9a52cSNicolas Pitre static int i3c_hci_send_ccc_cmd(struct i3c_master_controller *m,
1899ad9a52cSNicolas Pitre 				struct i3c_ccc_cmd *ccc)
1909ad9a52cSNicolas Pitre {
1919ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
1929ad9a52cSNicolas Pitre 	struct hci_xfer *xfer;
1939ad9a52cSNicolas Pitre 	bool raw = !!(hci->quirks & HCI_QUIRK_RAW_CCC);
1949ad9a52cSNicolas Pitre 	bool prefixed = raw && !!(ccc->id & I3C_CCC_DIRECT);
1959ad9a52cSNicolas Pitre 	unsigned int nxfers = ccc->ndests + prefixed;
1969ad9a52cSNicolas Pitre 	DECLARE_COMPLETION_ONSTACK(done);
1979ad9a52cSNicolas Pitre 	int i, last, ret = 0;
1989ad9a52cSNicolas Pitre 
1999ad9a52cSNicolas Pitre 	DBG("cmd=%#x rnw=%d ndests=%d data[0].len=%d",
2009ad9a52cSNicolas Pitre 	    ccc->id, ccc->rnw, ccc->ndests, ccc->dests[0].payload.len);
2019ad9a52cSNicolas Pitre 
2029ad9a52cSNicolas Pitre 	xfer = hci_alloc_xfer(nxfers);
2039ad9a52cSNicolas Pitre 	if (!xfer)
2049ad9a52cSNicolas Pitre 		return -ENOMEM;
2059ad9a52cSNicolas Pitre 
2069ad9a52cSNicolas Pitre 	if (prefixed) {
2079ad9a52cSNicolas Pitre 		xfer->data = NULL;
2089ad9a52cSNicolas Pitre 		xfer->data_len = 0;
2099ad9a52cSNicolas Pitre 		xfer->rnw = false;
2109ad9a52cSNicolas Pitre 		hci->cmd->prep_ccc(hci, xfer, I3C_BROADCAST_ADDR,
2119ad9a52cSNicolas Pitre 				   ccc->id, true);
2129ad9a52cSNicolas Pitre 		xfer++;
2139ad9a52cSNicolas Pitre 	}
2149ad9a52cSNicolas Pitre 
2159ad9a52cSNicolas Pitre 	for (i = 0; i < nxfers - prefixed; i++) {
2169ad9a52cSNicolas Pitre 		xfer[i].data = ccc->dests[i].payload.data;
2179ad9a52cSNicolas Pitre 		xfer[i].data_len = ccc->dests[i].payload.len;
2189ad9a52cSNicolas Pitre 		xfer[i].rnw = ccc->rnw;
2199ad9a52cSNicolas Pitre 		ret = hci->cmd->prep_ccc(hci, &xfer[i], ccc->dests[i].addr,
2209ad9a52cSNicolas Pitre 					 ccc->id, raw);
2219ad9a52cSNicolas Pitre 		if (ret)
2229ad9a52cSNicolas Pitre 			goto out;
2239ad9a52cSNicolas Pitre 		xfer[i].cmd_desc[0] |= CMD_0_ROC;
2249ad9a52cSNicolas Pitre 	}
2259ad9a52cSNicolas Pitre 	last = i - 1;
2269ad9a52cSNicolas Pitre 	xfer[last].cmd_desc[0] |= CMD_0_TOC;
2279ad9a52cSNicolas Pitre 	xfer[last].completion = &done;
2289ad9a52cSNicolas Pitre 
2299ad9a52cSNicolas Pitre 	if (prefixed)
2309ad9a52cSNicolas Pitre 		xfer--;
2319ad9a52cSNicolas Pitre 
2329ad9a52cSNicolas Pitre 	ret = hci->io->queue_xfer(hci, xfer, nxfers);
2339ad9a52cSNicolas Pitre 	if (ret)
2349ad9a52cSNicolas Pitre 		goto out;
2359ad9a52cSNicolas Pitre 	if (!wait_for_completion_timeout(&done, HZ) &&
2369ad9a52cSNicolas Pitre 	    hci->io->dequeue_xfer(hci, xfer, nxfers)) {
2379ad9a52cSNicolas Pitre 		ret = -ETIME;
2389ad9a52cSNicolas Pitre 		goto out;
2399ad9a52cSNicolas Pitre 	}
2409ad9a52cSNicolas Pitre 	for (i = prefixed; i < nxfers; i++) {
2419ad9a52cSNicolas Pitre 		if (ccc->rnw)
2429ad9a52cSNicolas Pitre 			ccc->dests[i - prefixed].payload.len =
2439ad9a52cSNicolas Pitre 				RESP_DATA_LENGTH(xfer[i].response);
2449e0e9e85SJarkko Nikula 		switch (RESP_STATUS(xfer[i].response)) {
2459e0e9e85SJarkko Nikula 		case RESP_SUCCESS:
2469e0e9e85SJarkko Nikula 			continue;
2479e0e9e85SJarkko Nikula 		case RESP_ERR_ADDR_HEADER:
2489e0e9e85SJarkko Nikula 		case RESP_ERR_NACK:
2499e0e9e85SJarkko Nikula 			ccc->err = I3C_ERROR_M2;
2509e0e9e85SJarkko Nikula 			fallthrough;
2519e0e9e85SJarkko Nikula 		default:
2529ad9a52cSNicolas Pitre 			ret = -EIO;
2539ad9a52cSNicolas Pitre 			goto out;
2549ad9a52cSNicolas Pitre 		}
2559ad9a52cSNicolas Pitre 	}
2569ad9a52cSNicolas Pitre 
2579ad9a52cSNicolas Pitre 	if (ccc->rnw)
2589ad9a52cSNicolas Pitre 		DBG("got: %*ph",
2599ad9a52cSNicolas Pitre 		    ccc->dests[0].payload.len, ccc->dests[0].payload.data);
2609ad9a52cSNicolas Pitre 
2619ad9a52cSNicolas Pitre out:
2629ad9a52cSNicolas Pitre 	hci_free_xfer(xfer, nxfers);
2639ad9a52cSNicolas Pitre 	return ret;
2649ad9a52cSNicolas Pitre }
2659ad9a52cSNicolas Pitre 
i3c_hci_daa(struct i3c_master_controller * m)2669ad9a52cSNicolas Pitre static int i3c_hci_daa(struct i3c_master_controller *m)
2679ad9a52cSNicolas Pitre {
2689ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
2699ad9a52cSNicolas Pitre 
2709ad9a52cSNicolas Pitre 	DBG("");
2719ad9a52cSNicolas Pitre 
2729ad9a52cSNicolas Pitre 	return hci->cmd->perform_daa(hci);
2739ad9a52cSNicolas Pitre }
2749ad9a52cSNicolas Pitre 
i3c_hci_alloc_safe_xfer_buf(struct i3c_hci * hci,struct hci_xfer * xfer)2754afd7287SJarkko Nikula static int i3c_hci_alloc_safe_xfer_buf(struct i3c_hci *hci,
2764afd7287SJarkko Nikula 				       struct hci_xfer *xfer)
2774afd7287SJarkko Nikula {
2784afd7287SJarkko Nikula 	if (hci->io != &mipi_i3c_hci_dma ||
2794afd7287SJarkko Nikula 	    xfer->data == NULL || !is_vmalloc_addr(xfer->data))
2804afd7287SJarkko Nikula 		return 0;
2814afd7287SJarkko Nikula 
2824afd7287SJarkko Nikula 	if (xfer->rnw)
2834afd7287SJarkko Nikula 		xfer->bounce_buf = kzalloc(xfer->data_len, GFP_KERNEL);
2844afd7287SJarkko Nikula 	else
2854afd7287SJarkko Nikula 		xfer->bounce_buf = kmemdup(xfer->data,
2864afd7287SJarkko Nikula 					   xfer->data_len, GFP_KERNEL);
2874afd7287SJarkko Nikula 
2884afd7287SJarkko Nikula 	return xfer->bounce_buf == NULL ? -ENOMEM : 0;
2894afd7287SJarkko Nikula }
2904afd7287SJarkko Nikula 
i3c_hci_free_safe_xfer_buf(struct i3c_hci * hci,struct hci_xfer * xfer)2914afd7287SJarkko Nikula static void i3c_hci_free_safe_xfer_buf(struct i3c_hci *hci,
2924afd7287SJarkko Nikula 				       struct hci_xfer *xfer)
2934afd7287SJarkko Nikula {
2944afd7287SJarkko Nikula 	if (hci->io != &mipi_i3c_hci_dma || xfer->bounce_buf == NULL)
2954afd7287SJarkko Nikula 		return;
2964afd7287SJarkko Nikula 
2974afd7287SJarkko Nikula 	if (xfer->rnw)
2984afd7287SJarkko Nikula 		memcpy(xfer->data, xfer->bounce_buf, xfer->data_len);
2994afd7287SJarkko Nikula 
3004afd7287SJarkko Nikula 	kfree(xfer->bounce_buf);
3014afd7287SJarkko Nikula }
3024afd7287SJarkko Nikula 
i3c_hci_priv_xfers(struct i3c_dev_desc * dev,struct i3c_priv_xfer * i3c_xfers,int nxfers)3039ad9a52cSNicolas Pitre static int i3c_hci_priv_xfers(struct i3c_dev_desc *dev,
3049ad9a52cSNicolas Pitre 			      struct i3c_priv_xfer *i3c_xfers,
3059ad9a52cSNicolas Pitre 			      int nxfers)
3069ad9a52cSNicolas Pitre {
3079ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
3089ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
3099ad9a52cSNicolas Pitre 	struct hci_xfer *xfer;
3109ad9a52cSNicolas Pitre 	DECLARE_COMPLETION_ONSTACK(done);
3119ad9a52cSNicolas Pitre 	unsigned int size_limit;
3129ad9a52cSNicolas Pitre 	int i, last, ret = 0;
3139ad9a52cSNicolas Pitre 
3149ad9a52cSNicolas Pitre 	DBG("nxfers = %d", nxfers);
3159ad9a52cSNicolas Pitre 
3169ad9a52cSNicolas Pitre 	xfer = hci_alloc_xfer(nxfers);
3179ad9a52cSNicolas Pitre 	if (!xfer)
3189ad9a52cSNicolas Pitre 		return -ENOMEM;
3199ad9a52cSNicolas Pitre 
3209ad9a52cSNicolas Pitre 	size_limit = 1U << (16 + FIELD_GET(HC_CAP_MAX_DATA_LENGTH, hci->caps));
3219ad9a52cSNicolas Pitre 
3229ad9a52cSNicolas Pitre 	for (i = 0; i < nxfers; i++) {
3239ad9a52cSNicolas Pitre 		xfer[i].data_len = i3c_xfers[i].len;
3249ad9a52cSNicolas Pitre 		ret = -EFBIG;
3259ad9a52cSNicolas Pitre 		if (xfer[i].data_len >= size_limit)
3269ad9a52cSNicolas Pitre 			goto out;
3279ad9a52cSNicolas Pitre 		xfer[i].rnw = i3c_xfers[i].rnw;
3289ad9a52cSNicolas Pitre 		if (i3c_xfers[i].rnw) {
3299ad9a52cSNicolas Pitre 			xfer[i].data = i3c_xfers[i].data.in;
3309ad9a52cSNicolas Pitre 		} else {
3319ad9a52cSNicolas Pitre 			/* silence the const qualifier warning with a cast */
3329ad9a52cSNicolas Pitre 			xfer[i].data = (void *) i3c_xfers[i].data.out;
3339ad9a52cSNicolas Pitre 		}
3349ad9a52cSNicolas Pitre 		hci->cmd->prep_i3c_xfer(hci, dev, &xfer[i]);
3359ad9a52cSNicolas Pitre 		xfer[i].cmd_desc[0] |= CMD_0_ROC;
3364afd7287SJarkko Nikula 		ret = i3c_hci_alloc_safe_xfer_buf(hci, &xfer[i]);
3374afd7287SJarkko Nikula 		if (ret)
3384afd7287SJarkko Nikula 			goto out;
3399ad9a52cSNicolas Pitre 	}
3409ad9a52cSNicolas Pitre 	last = i - 1;
3419ad9a52cSNicolas Pitre 	xfer[last].cmd_desc[0] |= CMD_0_TOC;
3429ad9a52cSNicolas Pitre 	xfer[last].completion = &done;
3439ad9a52cSNicolas Pitre 
3449ad9a52cSNicolas Pitre 	ret = hci->io->queue_xfer(hci, xfer, nxfers);
3459ad9a52cSNicolas Pitre 	if (ret)
3469ad9a52cSNicolas Pitre 		goto out;
3479ad9a52cSNicolas Pitre 	if (!wait_for_completion_timeout(&done, HZ) &&
3489ad9a52cSNicolas Pitre 	    hci->io->dequeue_xfer(hci, xfer, nxfers)) {
3499ad9a52cSNicolas Pitre 		ret = -ETIME;
3509ad9a52cSNicolas Pitre 		goto out;
3519ad9a52cSNicolas Pitre 	}
3529ad9a52cSNicolas Pitre 	for (i = 0; i < nxfers; i++) {
3539ad9a52cSNicolas Pitre 		if (i3c_xfers[i].rnw)
3549ad9a52cSNicolas Pitre 			i3c_xfers[i].len = RESP_DATA_LENGTH(xfer[i].response);
3559ad9a52cSNicolas Pitre 		if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) {
3569ad9a52cSNicolas Pitre 			ret = -EIO;
3579ad9a52cSNicolas Pitre 			goto out;
3589ad9a52cSNicolas Pitre 		}
3599ad9a52cSNicolas Pitre 	}
3609ad9a52cSNicolas Pitre 
3619ad9a52cSNicolas Pitre out:
3624afd7287SJarkko Nikula 	for (i = 0; i < nxfers; i++)
3634afd7287SJarkko Nikula 		i3c_hci_free_safe_xfer_buf(hci, &xfer[i]);
3644afd7287SJarkko Nikula 
3659ad9a52cSNicolas Pitre 	hci_free_xfer(xfer, nxfers);
3669ad9a52cSNicolas Pitre 	return ret;
3679ad9a52cSNicolas Pitre }
3689ad9a52cSNicolas Pitre 
i3c_hci_i2c_xfers(struct i2c_dev_desc * dev,struct i2c_msg * i2c_xfers,int nxfers)3699ad9a52cSNicolas Pitre static int i3c_hci_i2c_xfers(struct i2c_dev_desc *dev,
3706866c91fSBilly Tsai 			     struct i2c_msg *i2c_xfers, int nxfers)
3719ad9a52cSNicolas Pitre {
3729ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i2c_dev_get_master(dev);
3739ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
3749ad9a52cSNicolas Pitre 	struct hci_xfer *xfer;
3759ad9a52cSNicolas Pitre 	DECLARE_COMPLETION_ONSTACK(done);
3769ad9a52cSNicolas Pitre 	int i, last, ret = 0;
3779ad9a52cSNicolas Pitre 
3789ad9a52cSNicolas Pitre 	DBG("nxfers = %d", nxfers);
3799ad9a52cSNicolas Pitre 
3809ad9a52cSNicolas Pitre 	xfer = hci_alloc_xfer(nxfers);
3819ad9a52cSNicolas Pitre 	if (!xfer)
3829ad9a52cSNicolas Pitre 		return -ENOMEM;
3839ad9a52cSNicolas Pitre 
3849ad9a52cSNicolas Pitre 	for (i = 0; i < nxfers; i++) {
385effed5daSBilly Tsai 		xfer[i].data = i2c_get_dma_safe_msg_buf(&i2c_xfers[i], 1);
3869ad9a52cSNicolas Pitre 		xfer[i].data_len = i2c_xfers[i].len;
3879ad9a52cSNicolas Pitre 		xfer[i].rnw = i2c_xfers[i].flags & I2C_M_RD;
3889ad9a52cSNicolas Pitre 		hci->cmd->prep_i2c_xfer(hci, dev, &xfer[i]);
3899ad9a52cSNicolas Pitre 		xfer[i].cmd_desc[0] |= CMD_0_ROC;
3909ad9a52cSNicolas Pitre 	}
3919ad9a52cSNicolas Pitre 	last = i - 1;
3929ad9a52cSNicolas Pitre 	xfer[last].cmd_desc[0] |= CMD_0_TOC;
3939ad9a52cSNicolas Pitre 	xfer[last].completion = &done;
3949ad9a52cSNicolas Pitre 
3959ad9a52cSNicolas Pitre 	ret = hci->io->queue_xfer(hci, xfer, nxfers);
3969ad9a52cSNicolas Pitre 	if (ret)
3979ad9a52cSNicolas Pitre 		goto out;
398*c0a90eb5SWolfram Sang 	if (!wait_for_completion_timeout(&done, m->i2c.timeout) &&
3999ad9a52cSNicolas Pitre 	    hci->io->dequeue_xfer(hci, xfer, nxfers)) {
4009ad9a52cSNicolas Pitre 		ret = -ETIME;
4019ad9a52cSNicolas Pitre 		goto out;
4029ad9a52cSNicolas Pitre 	}
4039ad9a52cSNicolas Pitre 	for (i = 0; i < nxfers; i++) {
4049ad9a52cSNicolas Pitre 		if (RESP_STATUS(xfer[i].response) != RESP_SUCCESS) {
4059ad9a52cSNicolas Pitre 			ret = -EIO;
4069ad9a52cSNicolas Pitre 			goto out;
4079ad9a52cSNicolas Pitre 		}
4089ad9a52cSNicolas Pitre 	}
4099ad9a52cSNicolas Pitre 
4109ad9a52cSNicolas Pitre out:
4114afd7287SJarkko Nikula 	for (i = 0; i < nxfers; i++)
412effed5daSBilly Tsai 		i2c_put_dma_safe_msg_buf(xfer[i].data, &i2c_xfers[i],
413effed5daSBilly Tsai 					 ret ? false : true);
4144afd7287SJarkko Nikula 
4159ad9a52cSNicolas Pitre 	hci_free_xfer(xfer, nxfers);
4169ad9a52cSNicolas Pitre 	return ret;
4179ad9a52cSNicolas Pitre }
4189ad9a52cSNicolas Pitre 
i3c_hci_attach_i3c_dev(struct i3c_dev_desc * dev)4199ad9a52cSNicolas Pitre static int i3c_hci_attach_i3c_dev(struct i3c_dev_desc *dev)
4209ad9a52cSNicolas Pitre {
4219ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
4229ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
4239ad9a52cSNicolas Pitre 	struct i3c_hci_dev_data *dev_data;
4249ad9a52cSNicolas Pitre 	int ret;
4259ad9a52cSNicolas Pitre 
4269ad9a52cSNicolas Pitre 	DBG("");
4279ad9a52cSNicolas Pitre 
4289ad9a52cSNicolas Pitre 	dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
4299ad9a52cSNicolas Pitre 	if (!dev_data)
4309ad9a52cSNicolas Pitre 		return -ENOMEM;
4319ad9a52cSNicolas Pitre 	if (hci->cmd == &mipi_i3c_hci_cmd_v1) {
4329ad9a52cSNicolas Pitre 		ret = mipi_i3c_hci_dat_v1.alloc_entry(hci);
4339ad9a52cSNicolas Pitre 		if (ret < 0) {
4349ad9a52cSNicolas Pitre 			kfree(dev_data);
4359ad9a52cSNicolas Pitre 			return ret;
4369ad9a52cSNicolas Pitre 		}
4372b50719dSBilly Tsai 		mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, ret,
4382b50719dSBilly Tsai 						     dev->info.dyn_addr ?: dev->info.static_addr);
4399ad9a52cSNicolas Pitre 		dev_data->dat_idx = ret;
4409ad9a52cSNicolas Pitre 	}
4419ad9a52cSNicolas Pitre 	i3c_dev_set_master_data(dev, dev_data);
4429ad9a52cSNicolas Pitre 	return 0;
4439ad9a52cSNicolas Pitre }
4449ad9a52cSNicolas Pitre 
i3c_hci_reattach_i3c_dev(struct i3c_dev_desc * dev,u8 old_dyn_addr)4459ad9a52cSNicolas Pitre static int i3c_hci_reattach_i3c_dev(struct i3c_dev_desc *dev, u8 old_dyn_addr)
4469ad9a52cSNicolas Pitre {
4479ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
4489ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
4499ad9a52cSNicolas Pitre 	struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
4509ad9a52cSNicolas Pitre 
4519ad9a52cSNicolas Pitre 	DBG("");
4529ad9a52cSNicolas Pitre 
4539ad9a52cSNicolas Pitre 	if (hci->cmd == &mipi_i3c_hci_cmd_v1)
4549ad9a52cSNicolas Pitre 		mipi_i3c_hci_dat_v1.set_dynamic_addr(hci, dev_data->dat_idx,
4559ad9a52cSNicolas Pitre 					     dev->info.dyn_addr);
4569ad9a52cSNicolas Pitre 	return 0;
4579ad9a52cSNicolas Pitre }
4589ad9a52cSNicolas Pitre 
i3c_hci_detach_i3c_dev(struct i3c_dev_desc * dev)4599ad9a52cSNicolas Pitre static void i3c_hci_detach_i3c_dev(struct i3c_dev_desc *dev)
4609ad9a52cSNicolas Pitre {
4619ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
4629ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
4639ad9a52cSNicolas Pitre 	struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
4649ad9a52cSNicolas Pitre 
4659ad9a52cSNicolas Pitre 	DBG("");
4669ad9a52cSNicolas Pitre 
4679ad9a52cSNicolas Pitre 	i3c_dev_set_master_data(dev, NULL);
4689ad9a52cSNicolas Pitre 	if (hci->cmd == &mipi_i3c_hci_cmd_v1)
4699ad9a52cSNicolas Pitre 		mipi_i3c_hci_dat_v1.free_entry(hci, dev_data->dat_idx);
4709ad9a52cSNicolas Pitre 	kfree(dev_data);
4719ad9a52cSNicolas Pitre }
4729ad9a52cSNicolas Pitre 
i3c_hci_attach_i2c_dev(struct i2c_dev_desc * dev)4739ad9a52cSNicolas Pitre static int i3c_hci_attach_i2c_dev(struct i2c_dev_desc *dev)
4749ad9a52cSNicolas Pitre {
4759ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i2c_dev_get_master(dev);
4769ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
4779ad9a52cSNicolas Pitre 	struct i3c_hci_dev_data *dev_data;
4789ad9a52cSNicolas Pitre 	int ret;
4799ad9a52cSNicolas Pitre 
4809ad9a52cSNicolas Pitre 	DBG("");
4819ad9a52cSNicolas Pitre 
4829ad9a52cSNicolas Pitre 	if (hci->cmd != &mipi_i3c_hci_cmd_v1)
4839ad9a52cSNicolas Pitre 		return 0;
4849ad9a52cSNicolas Pitre 	dev_data = kzalloc(sizeof(*dev_data), GFP_KERNEL);
4859ad9a52cSNicolas Pitre 	if (!dev_data)
4869ad9a52cSNicolas Pitre 		return -ENOMEM;
4879ad9a52cSNicolas Pitre 	ret = mipi_i3c_hci_dat_v1.alloc_entry(hci);
4889ad9a52cSNicolas Pitre 	if (ret < 0) {
4899ad9a52cSNicolas Pitre 		kfree(dev_data);
4909ad9a52cSNicolas Pitre 		return ret;
4919ad9a52cSNicolas Pitre 	}
4929ad9a52cSNicolas Pitre 	mipi_i3c_hci_dat_v1.set_static_addr(hci, ret, dev->addr);
4939ad9a52cSNicolas Pitre 	mipi_i3c_hci_dat_v1.set_flags(hci, ret, DAT_0_I2C_DEVICE, 0);
4949ad9a52cSNicolas Pitre 	dev_data->dat_idx = ret;
4959ad9a52cSNicolas Pitre 	i2c_dev_set_master_data(dev, dev_data);
4969ad9a52cSNicolas Pitre 	return 0;
4979ad9a52cSNicolas Pitre }
4989ad9a52cSNicolas Pitre 
i3c_hci_detach_i2c_dev(struct i2c_dev_desc * dev)4999ad9a52cSNicolas Pitre static void i3c_hci_detach_i2c_dev(struct i2c_dev_desc *dev)
5009ad9a52cSNicolas Pitre {
5019ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i2c_dev_get_master(dev);
5029ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
5039ad9a52cSNicolas Pitre 	struct i3c_hci_dev_data *dev_data = i2c_dev_get_master_data(dev);
5049ad9a52cSNicolas Pitre 
5059ad9a52cSNicolas Pitre 	DBG("");
5069ad9a52cSNicolas Pitre 
5079ad9a52cSNicolas Pitre 	if (dev_data) {
5089ad9a52cSNicolas Pitre 		i2c_dev_set_master_data(dev, NULL);
5099ad9a52cSNicolas Pitre 		if (hci->cmd == &mipi_i3c_hci_cmd_v1)
5109ad9a52cSNicolas Pitre 			mipi_i3c_hci_dat_v1.free_entry(hci, dev_data->dat_idx);
5119ad9a52cSNicolas Pitre 		kfree(dev_data);
5129ad9a52cSNicolas Pitre 	}
5139ad9a52cSNicolas Pitre }
5149ad9a52cSNicolas Pitre 
i3c_hci_request_ibi(struct i3c_dev_desc * dev,const struct i3c_ibi_setup * req)5159ad9a52cSNicolas Pitre static int i3c_hci_request_ibi(struct i3c_dev_desc *dev,
5169ad9a52cSNicolas Pitre 			       const struct i3c_ibi_setup *req)
5179ad9a52cSNicolas Pitre {
5189ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
5199ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
5209ad9a52cSNicolas Pitre 	struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
5219ad9a52cSNicolas Pitre 	unsigned int dat_idx = dev_data->dat_idx;
5229ad9a52cSNicolas Pitre 
5239ad9a52cSNicolas Pitre 	if (req->max_payload_len != 0)
5249ad9a52cSNicolas Pitre 		mipi_i3c_hci_dat_v1.set_flags(hci, dat_idx, DAT_0_IBI_PAYLOAD, 0);
5259ad9a52cSNicolas Pitre 	else
5269ad9a52cSNicolas Pitre 		mipi_i3c_hci_dat_v1.clear_flags(hci, dat_idx, DAT_0_IBI_PAYLOAD, 0);
5279ad9a52cSNicolas Pitre 	return hci->io->request_ibi(hci, dev, req);
5289ad9a52cSNicolas Pitre }
5299ad9a52cSNicolas Pitre 
i3c_hci_free_ibi(struct i3c_dev_desc * dev)5309ad9a52cSNicolas Pitre static void i3c_hci_free_ibi(struct i3c_dev_desc *dev)
5319ad9a52cSNicolas Pitre {
5329ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
5339ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
5349ad9a52cSNicolas Pitre 
5359ad9a52cSNicolas Pitre 	hci->io->free_ibi(hci, dev);
5369ad9a52cSNicolas Pitre }
5379ad9a52cSNicolas Pitre 
i3c_hci_enable_ibi(struct i3c_dev_desc * dev)5389ad9a52cSNicolas Pitre static int i3c_hci_enable_ibi(struct i3c_dev_desc *dev)
5399ad9a52cSNicolas Pitre {
5409ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
5419ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
5429ad9a52cSNicolas Pitre 	struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
5439ad9a52cSNicolas Pitre 
5449ad9a52cSNicolas Pitre 	mipi_i3c_hci_dat_v1.clear_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0);
5459ad9a52cSNicolas Pitre 	return i3c_master_enec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
5469ad9a52cSNicolas Pitre }
5479ad9a52cSNicolas Pitre 
i3c_hci_disable_ibi(struct i3c_dev_desc * dev)5489ad9a52cSNicolas Pitre static int i3c_hci_disable_ibi(struct i3c_dev_desc *dev)
5499ad9a52cSNicolas Pitre {
5509ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
5519ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
5529ad9a52cSNicolas Pitre 	struct i3c_hci_dev_data *dev_data = i3c_dev_get_master_data(dev);
5539ad9a52cSNicolas Pitre 
5549ad9a52cSNicolas Pitre 	mipi_i3c_hci_dat_v1.set_flags(hci, dev_data->dat_idx, DAT_0_SIR_REJECT, 0);
5559ad9a52cSNicolas Pitre 	return i3c_master_disec_locked(m, dev->info.dyn_addr, I3C_CCC_EVENT_SIR);
5569ad9a52cSNicolas Pitre }
5579ad9a52cSNicolas Pitre 
i3c_hci_recycle_ibi_slot(struct i3c_dev_desc * dev,struct i3c_ibi_slot * slot)5589ad9a52cSNicolas Pitre static void i3c_hci_recycle_ibi_slot(struct i3c_dev_desc *dev,
5599ad9a52cSNicolas Pitre 				     struct i3c_ibi_slot *slot)
5609ad9a52cSNicolas Pitre {
5619ad9a52cSNicolas Pitre 	struct i3c_master_controller *m = i3c_dev_get_master(dev);
5629ad9a52cSNicolas Pitre 	struct i3c_hci *hci = to_i3c_hci(m);
5639ad9a52cSNicolas Pitre 
5649ad9a52cSNicolas Pitre 	hci->io->recycle_ibi_slot(hci, dev, slot);
5659ad9a52cSNicolas Pitre }
5669ad9a52cSNicolas Pitre 
5679ad9a52cSNicolas Pitre static const struct i3c_master_controller_ops i3c_hci_ops = {
5689ad9a52cSNicolas Pitre 	.bus_init		= i3c_hci_bus_init,
5699ad9a52cSNicolas Pitre 	.bus_cleanup		= i3c_hci_bus_cleanup,
5709ad9a52cSNicolas Pitre 	.do_daa			= i3c_hci_daa,
5719ad9a52cSNicolas Pitre 	.send_ccc_cmd		= i3c_hci_send_ccc_cmd,
5729ad9a52cSNicolas Pitre 	.priv_xfers		= i3c_hci_priv_xfers,
5739ad9a52cSNicolas Pitre 	.i2c_xfers		= i3c_hci_i2c_xfers,
5749ad9a52cSNicolas Pitre 	.attach_i3c_dev		= i3c_hci_attach_i3c_dev,
5759ad9a52cSNicolas Pitre 	.reattach_i3c_dev	= i3c_hci_reattach_i3c_dev,
5769ad9a52cSNicolas Pitre 	.detach_i3c_dev		= i3c_hci_detach_i3c_dev,
5779ad9a52cSNicolas Pitre 	.attach_i2c_dev		= i3c_hci_attach_i2c_dev,
5789ad9a52cSNicolas Pitre 	.detach_i2c_dev		= i3c_hci_detach_i2c_dev,
5799ad9a52cSNicolas Pitre 	.request_ibi		= i3c_hci_request_ibi,
5809ad9a52cSNicolas Pitre 	.free_ibi		= i3c_hci_free_ibi,
5819ad9a52cSNicolas Pitre 	.enable_ibi		= i3c_hci_enable_ibi,
5829ad9a52cSNicolas Pitre 	.disable_ibi		= i3c_hci_disable_ibi,
5839ad9a52cSNicolas Pitre 	.recycle_ibi_slot	= i3c_hci_recycle_ibi_slot,
5849ad9a52cSNicolas Pitre };
5859ad9a52cSNicolas Pitre 
i3c_hci_irq_handler(int irq,void * dev_id)5869ad9a52cSNicolas Pitre static irqreturn_t i3c_hci_irq_handler(int irq, void *dev_id)
5879ad9a52cSNicolas Pitre {
5889ad9a52cSNicolas Pitre 	struct i3c_hci *hci = dev_id;
5899ad9a52cSNicolas Pitre 	irqreturn_t result = IRQ_NONE;
5909ad9a52cSNicolas Pitre 	u32 val;
5919ad9a52cSNicolas Pitre 
5929ad9a52cSNicolas Pitre 	val = reg_read(INTR_STATUS);
593a7035a8eSJarkko Nikula 	reg_write(INTR_STATUS, val);
5949ad9a52cSNicolas Pitre 	DBG("INTR_STATUS = %#x", val);
5959ad9a52cSNicolas Pitre 
596a7035a8eSJarkko Nikula 	if (val)
597279c2402SJarkko Nikula 		result = IRQ_HANDLED;
5989ad9a52cSNicolas Pitre 
5997479d267SJarkko Nikula 	if (val & INTR_HC_SEQ_CANCEL) {
6007479d267SJarkko Nikula 		dev_dbg(&hci->master.dev,
6017479d267SJarkko Nikula 			"Host Controller Cancelled Transaction Sequence\n");
6027479d267SJarkko Nikula 		val &= ~INTR_HC_SEQ_CANCEL;
6039ad9a52cSNicolas Pitre 	}
6049ad9a52cSNicolas Pitre 	if (val & INTR_HC_INTERNAL_ERR) {
6059ad9a52cSNicolas Pitre 		dev_err(&hci->master.dev, "Host Controller Internal Error\n");
6069ad9a52cSNicolas Pitre 		val &= ~INTR_HC_INTERNAL_ERR;
6079ad9a52cSNicolas Pitre 	}
60845357c9bSJarkko Nikula 
609bd916806SJarkko Nikula 	if (val)
610bd916806SJarkko Nikula 		dev_warn_once(&hci->master.dev,
611bd916806SJarkko Nikula 			      "unexpected INTR_STATUS %#x\n", val);
612bd916806SJarkko Nikula 
613279c2402SJarkko Nikula 	if (hci->io->irq_handler(hci))
614279c2402SJarkko Nikula 		result = IRQ_HANDLED;
61545357c9bSJarkko Nikula 
6169ad9a52cSNicolas Pitre 	return result;
6179ad9a52cSNicolas Pitre }
6189ad9a52cSNicolas Pitre 
i3c_hci_init(struct i3c_hci * hci)6199ad9a52cSNicolas Pitre static int i3c_hci_init(struct i3c_hci *hci)
6209ad9a52cSNicolas Pitre {
621039b2360SShyam Sundar S K 	bool size_in_dwords, mode_selector;
6229ad9a52cSNicolas Pitre 	u32 regval, offset;
6239ad9a52cSNicolas Pitre 	int ret;
6249ad9a52cSNicolas Pitre 
6259ad9a52cSNicolas Pitre 	/* Validate HCI hardware version */
6269ad9a52cSNicolas Pitre 	regval = reg_read(HCI_VERSION);
6279ad9a52cSNicolas Pitre 	hci->version_major = (regval >> 8) & 0xf;
6289ad9a52cSNicolas Pitre 	hci->version_minor = (regval >> 4) & 0xf;
6299ad9a52cSNicolas Pitre 	hci->revision = regval & 0xf;
6309ad9a52cSNicolas Pitre 	dev_notice(&hci->master.dev, "MIPI I3C HCI v%u.%u r%02u\n",
6319ad9a52cSNicolas Pitre 		   hci->version_major, hci->version_minor, hci->revision);
6329ad9a52cSNicolas Pitre 	/* known versions */
6339ad9a52cSNicolas Pitre 	switch (regval & ~0xf) {
6349ad9a52cSNicolas Pitre 	case 0x100:	/* version 1.0 */
6359ad9a52cSNicolas Pitre 	case 0x110:	/* version 1.1 */
6369ad9a52cSNicolas Pitre 	case 0x200:	/* version 2.0 */
6379ad9a52cSNicolas Pitre 		break;
6389ad9a52cSNicolas Pitre 	default:
6399ad9a52cSNicolas Pitre 		dev_err(&hci->master.dev, "unsupported HCI version\n");
6409ad9a52cSNicolas Pitre 		return -EPROTONOSUPPORT;
6419ad9a52cSNicolas Pitre 	}
6429ad9a52cSNicolas Pitre 
6439ad9a52cSNicolas Pitre 	hci->caps = reg_read(HC_CAPABILITIES);
6449ad9a52cSNicolas Pitre 	DBG("caps = %#x", hci->caps);
6459ad9a52cSNicolas Pitre 
646be90ae1bSJarkko Nikula 	size_in_dwords = hci->version_major < 1 ||
647be90ae1bSJarkko Nikula 			 (hci->version_major == 1 && hci->version_minor < 1);
648be90ae1bSJarkko Nikula 
6499ad9a52cSNicolas Pitre 	regval = reg_read(DAT_SECTION);
6509ad9a52cSNicolas Pitre 	offset = FIELD_GET(DAT_TABLE_OFFSET, regval);
6519ad9a52cSNicolas Pitre 	hci->DAT_regs = offset ? hci->base_regs + offset : NULL;
6529ad9a52cSNicolas Pitre 	hci->DAT_entries = FIELD_GET(DAT_TABLE_SIZE, regval);
6530676bfebSJarkko Nikula 	hci->DAT_entry_size = FIELD_GET(DAT_ENTRY_SIZE, regval) ? 0 : 8;
654be90ae1bSJarkko Nikula 	if (size_in_dwords)
655be90ae1bSJarkko Nikula 		hci->DAT_entries = 4 * hci->DAT_entries / hci->DAT_entry_size;
6569ad9a52cSNicolas Pitre 	dev_info(&hci->master.dev, "DAT: %u %u-bytes entries at offset %#x\n",
6570676bfebSJarkko Nikula 		 hci->DAT_entries, hci->DAT_entry_size, offset);
6589ad9a52cSNicolas Pitre 
6599ad9a52cSNicolas Pitre 	regval = reg_read(DCT_SECTION);
6609ad9a52cSNicolas Pitre 	offset = FIELD_GET(DCT_TABLE_OFFSET, regval);
6619ad9a52cSNicolas Pitre 	hci->DCT_regs = offset ? hci->base_regs + offset : NULL;
6629ad9a52cSNicolas Pitre 	hci->DCT_entries = FIELD_GET(DCT_TABLE_SIZE, regval);
6630676bfebSJarkko Nikula 	hci->DCT_entry_size = FIELD_GET(DCT_ENTRY_SIZE, regval) ? 0 : 16;
664be90ae1bSJarkko Nikula 	if (size_in_dwords)
665be90ae1bSJarkko Nikula 		hci->DCT_entries = 4 * hci->DCT_entries / hci->DCT_entry_size;
6669ad9a52cSNicolas Pitre 	dev_info(&hci->master.dev, "DCT: %u %u-bytes entries at offset %#x\n",
6670676bfebSJarkko Nikula 		 hci->DCT_entries, hci->DCT_entry_size, offset);
6689ad9a52cSNicolas Pitre 
6699ad9a52cSNicolas Pitre 	regval = reg_read(RING_HEADERS_SECTION);
6709ad9a52cSNicolas Pitre 	offset = FIELD_GET(RING_HEADERS_OFFSET, regval);
6719ad9a52cSNicolas Pitre 	hci->RHS_regs = offset ? hci->base_regs + offset : NULL;
6729ad9a52cSNicolas Pitre 	dev_info(&hci->master.dev, "Ring Headers at offset %#x\n", offset);
6739ad9a52cSNicolas Pitre 
6749ad9a52cSNicolas Pitre 	regval = reg_read(PIO_SECTION);
6759ad9a52cSNicolas Pitre 	offset = FIELD_GET(PIO_REGS_OFFSET, regval);
6769ad9a52cSNicolas Pitre 	hci->PIO_regs = offset ? hci->base_regs + offset : NULL;
6779ad9a52cSNicolas Pitre 	dev_info(&hci->master.dev, "PIO section at offset %#x\n", offset);
6789ad9a52cSNicolas Pitre 
6799ad9a52cSNicolas Pitre 	regval = reg_read(EXT_CAPS_SECTION);
6809ad9a52cSNicolas Pitre 	offset = FIELD_GET(EXT_CAPS_OFFSET, regval);
6819ad9a52cSNicolas Pitre 	hci->EXTCAPS_regs = offset ? hci->base_regs + offset : NULL;
6829ad9a52cSNicolas Pitre 	dev_info(&hci->master.dev, "Extended Caps at offset %#x\n", offset);
6839ad9a52cSNicolas Pitre 
6849ad9a52cSNicolas Pitre 	ret = i3c_hci_parse_ext_caps(hci);
6859ad9a52cSNicolas Pitre 	if (ret)
6869ad9a52cSNicolas Pitre 		return ret;
6879ad9a52cSNicolas Pitre 
6889ad9a52cSNicolas Pitre 	/*
6899ad9a52cSNicolas Pitre 	 * Now let's reset the hardware.
6909ad9a52cSNicolas Pitre 	 * SOFT_RST must be clear before we write to it.
6919ad9a52cSNicolas Pitre 	 * Then we must wait until it clears again.
6929ad9a52cSNicolas Pitre 	 */
6939ad9a52cSNicolas Pitre 	ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
6949ad9a52cSNicolas Pitre 				 !(regval & SOFT_RST), 1, 10000);
6959ad9a52cSNicolas Pitre 	if (ret)
6969ad9a52cSNicolas Pitre 		return -ENXIO;
6979ad9a52cSNicolas Pitre 	reg_write(RESET_CONTROL, SOFT_RST);
6989ad9a52cSNicolas Pitre 	ret = readx_poll_timeout(reg_read, RESET_CONTROL, regval,
6999ad9a52cSNicolas Pitre 				 !(regval & SOFT_RST), 1, 10000);
7009ad9a52cSNicolas Pitre 	if (ret)
7019ad9a52cSNicolas Pitre 		return -ENXIO;
7029ad9a52cSNicolas Pitre 
703eeeec6c5SJarkko Nikula 	/* Disable all interrupts */
7049ad9a52cSNicolas Pitre 	reg_write(INTR_SIGNAL_ENABLE, 0x0);
705eeeec6c5SJarkko Nikula 	/*
706eeeec6c5SJarkko Nikula 	 * Only allow bit 31:10 signal updates because
707eeeec6c5SJarkko Nikula 	 * Bit 0:9 are reserved in IP version >= 0.8
708eeeec6c5SJarkko Nikula 	 * Bit 0:5 are defined in IP version < 0.8 but not handled by PIO code
709eeeec6c5SJarkko Nikula 	 */
710eeeec6c5SJarkko Nikula 	reg_write(INTR_STATUS_ENABLE, GENMASK(31, 10));
7119ad9a52cSNicolas Pitre 
7129ad9a52cSNicolas Pitre 	/* Make sure our data ordering fits the host's */
7139ad9a52cSNicolas Pitre 	regval = reg_read(HC_CONTROL);
7147a2bccd1SLukas Bulwahn 	if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN)) {
7159ad9a52cSNicolas Pitre 		if (!(regval & HC_CONTROL_DATA_BIG_ENDIAN)) {
7169ad9a52cSNicolas Pitre 			regval |= HC_CONTROL_DATA_BIG_ENDIAN;
7179ad9a52cSNicolas Pitre 			reg_write(HC_CONTROL, regval);
7189ad9a52cSNicolas Pitre 			regval = reg_read(HC_CONTROL);
7199ad9a52cSNicolas Pitre 			if (!(regval & HC_CONTROL_DATA_BIG_ENDIAN)) {
7209ad9a52cSNicolas Pitre 				dev_err(&hci->master.dev, "cannot set BE mode\n");
7219ad9a52cSNicolas Pitre 				return -EOPNOTSUPP;
7229ad9a52cSNicolas Pitre 			}
7239ad9a52cSNicolas Pitre 		}
7249ad9a52cSNicolas Pitre 	} else {
7259ad9a52cSNicolas Pitre 		if (regval & HC_CONTROL_DATA_BIG_ENDIAN) {
7269ad9a52cSNicolas Pitre 			regval &= ~HC_CONTROL_DATA_BIG_ENDIAN;
7279ad9a52cSNicolas Pitre 			reg_write(HC_CONTROL, regval);
7289ad9a52cSNicolas Pitre 			regval = reg_read(HC_CONTROL);
7299ad9a52cSNicolas Pitre 			if (regval & HC_CONTROL_DATA_BIG_ENDIAN) {
7309ad9a52cSNicolas Pitre 				dev_err(&hci->master.dev, "cannot clear BE mode\n");
7319ad9a52cSNicolas Pitre 				return -EOPNOTSUPP;
7329ad9a52cSNicolas Pitre 			}
7339ad9a52cSNicolas Pitre 		}
7349ad9a52cSNicolas Pitre 	}
7359ad9a52cSNicolas Pitre 
7369ad9a52cSNicolas Pitre 	/* Select our command descriptor model */
7379ad9a52cSNicolas Pitre 	switch (FIELD_GET(HC_CAP_CMD_SIZE, hci->caps)) {
7389ad9a52cSNicolas Pitre 	case 0:
7399ad9a52cSNicolas Pitre 		hci->cmd = &mipi_i3c_hci_cmd_v1;
7409ad9a52cSNicolas Pitre 		break;
7419ad9a52cSNicolas Pitre 	case 1:
7429ad9a52cSNicolas Pitre 		hci->cmd = &mipi_i3c_hci_cmd_v2;
7439ad9a52cSNicolas Pitre 		break;
7449ad9a52cSNicolas Pitre 	default:
7459ad9a52cSNicolas Pitre 		dev_err(&hci->master.dev, "wrong CMD_SIZE capability value\n");
7469ad9a52cSNicolas Pitre 		return -EINVAL;
7479ad9a52cSNicolas Pitre 	}
7489ad9a52cSNicolas Pitre 
749039b2360SShyam Sundar S K 	mode_selector = hci->version_major > 1 ||
750039b2360SShyam Sundar S K 				(hci->version_major == 1 && hci->version_minor > 0);
751039b2360SShyam Sundar S K 
75201408932SShyam Sundar S K 	/* Quirk for HCI_QUIRK_PIO_MODE on AMD platforms */
75301408932SShyam Sundar S K 	if (hci->quirks & HCI_QUIRK_PIO_MODE)
75401408932SShyam Sundar S K 		hci->RHS_regs = NULL;
75501408932SShyam Sundar S K 
7569ad9a52cSNicolas Pitre 	/* Try activating DMA operations first */
7579ad9a52cSNicolas Pitre 	if (hci->RHS_regs) {
7589ad9a52cSNicolas Pitre 		reg_clear(HC_CONTROL, HC_CONTROL_PIO_MODE);
759039b2360SShyam Sundar S K 		if (mode_selector && (reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
7609ad9a52cSNicolas Pitre 			dev_err(&hci->master.dev, "PIO mode is stuck\n");
7619ad9a52cSNicolas Pitre 			ret = -EIO;
7629ad9a52cSNicolas Pitre 		} else {
7639ad9a52cSNicolas Pitre 			hci->io = &mipi_i3c_hci_dma;
7649ad9a52cSNicolas Pitre 			dev_info(&hci->master.dev, "Using DMA\n");
7659ad9a52cSNicolas Pitre 		}
7669ad9a52cSNicolas Pitre 	}
7679ad9a52cSNicolas Pitre 
7689ad9a52cSNicolas Pitre 	/* If no DMA, try PIO */
7699ad9a52cSNicolas Pitre 	if (!hci->io && hci->PIO_regs) {
7709ad9a52cSNicolas Pitre 		reg_set(HC_CONTROL, HC_CONTROL_PIO_MODE);
771039b2360SShyam Sundar S K 		if (mode_selector && !(reg_read(HC_CONTROL) & HC_CONTROL_PIO_MODE)) {
7729ad9a52cSNicolas Pitre 			dev_err(&hci->master.dev, "DMA mode is stuck\n");
7739ad9a52cSNicolas Pitre 			ret = -EIO;
7749ad9a52cSNicolas Pitre 		} else {
7759ad9a52cSNicolas Pitre 			hci->io = &mipi_i3c_hci_pio;
7769ad9a52cSNicolas Pitre 			dev_info(&hci->master.dev, "Using PIO\n");
7779ad9a52cSNicolas Pitre 		}
7789ad9a52cSNicolas Pitre 	}
7799ad9a52cSNicolas Pitre 
7809ad9a52cSNicolas Pitre 	if (!hci->io) {
7819ad9a52cSNicolas Pitre 		dev_err(&hci->master.dev, "neither DMA nor PIO can be used\n");
7829ad9a52cSNicolas Pitre 		if (!ret)
7839ad9a52cSNicolas Pitre 			ret = -EINVAL;
7849ad9a52cSNicolas Pitre 		return ret;
7859ad9a52cSNicolas Pitre 	}
7869ad9a52cSNicolas Pitre 
78746d4daa5SShyam Sundar S K 	/* Configure OD and PP timings for AMD platforms */
78846d4daa5SShyam Sundar S K 	if (hci->quirks & HCI_QUIRK_OD_PP_TIMING)
78946d4daa5SShyam Sundar S K 		amd_set_od_pp_timing(hci);
79046d4daa5SShyam Sundar S K 
7919ad9a52cSNicolas Pitre 	return 0;
7929ad9a52cSNicolas Pitre }
7939ad9a52cSNicolas Pitre 
i3c_hci_probe(struct platform_device * pdev)7949ad9a52cSNicolas Pitre static int i3c_hci_probe(struct platform_device *pdev)
7959ad9a52cSNicolas Pitre {
7969ad9a52cSNicolas Pitre 	struct i3c_hci *hci;
7979ad9a52cSNicolas Pitre 	int irq, ret;
7989ad9a52cSNicolas Pitre 
7999ad9a52cSNicolas Pitre 	hci = devm_kzalloc(&pdev->dev, sizeof(*hci), GFP_KERNEL);
8009ad9a52cSNicolas Pitre 	if (!hci)
8019ad9a52cSNicolas Pitre 		return -ENOMEM;
8029ad9a52cSNicolas Pitre 	hci->base_regs = devm_platform_ioremap_resource(pdev, 0);
8039ad9a52cSNicolas Pitre 	if (IS_ERR(hci->base_regs))
8049ad9a52cSNicolas Pitre 		return PTR_ERR(hci->base_regs);
8059ad9a52cSNicolas Pitre 
8069ad9a52cSNicolas Pitre 	platform_set_drvdata(pdev, hci);
8079ad9a52cSNicolas Pitre 	/* temporary for dev_printk's, to be replaced in i3c_master_register */
8089ad9a52cSNicolas Pitre 	hci->master.dev.init_name = dev_name(&pdev->dev);
8099ad9a52cSNicolas Pitre 
81001408932SShyam Sundar S K 	hci->quirks = (unsigned long)device_get_match_data(&pdev->dev);
81101408932SShyam Sundar S K 
8129ad9a52cSNicolas Pitre 	ret = i3c_hci_init(hci);
8139ad9a52cSNicolas Pitre 	if (ret)
8149ad9a52cSNicolas Pitre 		return ret;
8159ad9a52cSNicolas Pitre 
8169ad9a52cSNicolas Pitre 	irq = platform_get_irq(pdev, 0);
8179ad9a52cSNicolas Pitre 	ret = devm_request_irq(&pdev->dev, irq, i3c_hci_irq_handler,
8189ad9a52cSNicolas Pitre 			       0, NULL, hci);
8199ad9a52cSNicolas Pitre 	if (ret)
8209ad9a52cSNicolas Pitre 		return ret;
8219ad9a52cSNicolas Pitre 
8229ad9a52cSNicolas Pitre 	ret = i3c_master_register(&hci->master, &pdev->dev,
8239ad9a52cSNicolas Pitre 				  &i3c_hci_ops, false);
8249ad9a52cSNicolas Pitre 	if (ret)
8259ad9a52cSNicolas Pitre 		return ret;
8269ad9a52cSNicolas Pitre 
8279ad9a52cSNicolas Pitre 	return 0;
8289ad9a52cSNicolas Pitre }
8299ad9a52cSNicolas Pitre 
i3c_hci_remove(struct platform_device * pdev)830f959ec61SUwe Kleine-König static void i3c_hci_remove(struct platform_device *pdev)
8319ad9a52cSNicolas Pitre {
8329ad9a52cSNicolas Pitre 	struct i3c_hci *hci = platform_get_drvdata(pdev);
8339ad9a52cSNicolas Pitre 
8340f74f8b6SUwe Kleine-König 	i3c_master_unregister(&hci->master);
8359ad9a52cSNicolas Pitre }
8369ad9a52cSNicolas Pitre 
837291b5c98SNathan Chancellor static const __maybe_unused struct of_device_id i3c_hci_of_match[] = {
8389ad9a52cSNicolas Pitre 	{ .compatible = "mipi-i3c-hci", },
8399ad9a52cSNicolas Pitre 	{},
8409ad9a52cSNicolas Pitre };
8419ad9a52cSNicolas Pitre MODULE_DEVICE_TABLE(of, i3c_hci_of_match);
8429ad9a52cSNicolas Pitre 
8438d2e56efSShyam Sundar S K static const struct acpi_device_id i3c_hci_acpi_match[] = {
844ced86959SShyam Sundar S K 	{ "AMDI5017", HCI_QUIRK_PIO_MODE | HCI_QUIRK_OD_PP_TIMING | HCI_QUIRK_RESP_BUF_THLD },
8458d2e56efSShyam Sundar S K 	{}
8468d2e56efSShyam Sundar S K };
8478d2e56efSShyam Sundar S K MODULE_DEVICE_TABLE(acpi, i3c_hci_acpi_match);
8488d2e56efSShyam Sundar S K 
8499ad9a52cSNicolas Pitre static struct platform_driver i3c_hci_driver = {
8509ad9a52cSNicolas Pitre 	.probe = i3c_hci_probe,
851e70140baSLinus Torvalds 	.remove = i3c_hci_remove,
8529ad9a52cSNicolas Pitre 	.driver = {
8539ad9a52cSNicolas Pitre 		.name = "mipi-i3c-hci",
8549ad9a52cSNicolas Pitre 		.of_match_table = of_match_ptr(i3c_hci_of_match),
8558d2e56efSShyam Sundar S K 		.acpi_match_table = i3c_hci_acpi_match,
8569ad9a52cSNicolas Pitre 	},
8579ad9a52cSNicolas Pitre };
8589ad9a52cSNicolas Pitre module_platform_driver(i3c_hci_driver);
859f656f6bdSJarkko Nikula MODULE_ALIAS("platform:mipi-i3c-hci");
8609ad9a52cSNicolas Pitre 
8619ad9a52cSNicolas Pitre MODULE_AUTHOR("Nicolas Pitre <npitre@baylibre.com>");
8629ad9a52cSNicolas Pitre MODULE_DESCRIPTION("MIPI I3C HCI driver");
8639ad9a52cSNicolas Pitre MODULE_LICENSE("Dual BSD/GPL");
864