xref: /openbmc/linux/drivers/i3c/master/mipi-i3c-hci/dat_v1.c (revision b97d6790d03b763eca08847a9a5869a4291b9f9a)
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 
89ad9a52cSNicolas Pitre #include <linux/bitfield.h>
99ad9a52cSNicolas Pitre #include <linux/bitmap.h>
109ad9a52cSNicolas Pitre #include <linux/device.h>
119ad9a52cSNicolas Pitre #include <linux/errno.h>
129ad9a52cSNicolas Pitre #include <linux/i3c/master.h>
139ad9a52cSNicolas Pitre #include <linux/io.h>
149ad9a52cSNicolas Pitre 
159ad9a52cSNicolas Pitre #include "hci.h"
169ad9a52cSNicolas Pitre #include "dat.h"
179ad9a52cSNicolas Pitre 
189ad9a52cSNicolas Pitre 
199ad9a52cSNicolas Pitre /*
209ad9a52cSNicolas Pitre  * Device Address Table Structure
219ad9a52cSNicolas Pitre  */
229ad9a52cSNicolas Pitre 
239ad9a52cSNicolas Pitre #define DAT_1_AUTOCMD_HDR_CODE		W1_MASK(58, 51)
249ad9a52cSNicolas Pitre #define DAT_1_AUTOCMD_MODE		W1_MASK(50, 48)
259ad9a52cSNicolas Pitre #define DAT_1_AUTOCMD_VALUE		W1_MASK(47, 40)
269ad9a52cSNicolas Pitre #define DAT_1_AUTOCMD_MASK		W1_MASK(39, 32)
279ad9a52cSNicolas Pitre /*	DAT_0_I2C_DEVICE		W0_BIT_(31) */
289ad9a52cSNicolas Pitre #define DAT_0_DEV_NACK_RETRY_CNT	W0_MASK(30, 29)
299ad9a52cSNicolas Pitre #define DAT_0_RING_ID			W0_MASK(28, 26)
309ad9a52cSNicolas Pitre #define DAT_0_DYNADDR_PARITY		W0_BIT_(23)
319ad9a52cSNicolas Pitre #define DAT_0_DYNAMIC_ADDRESS		W0_MASK(22, 16)
329ad9a52cSNicolas Pitre #define DAT_0_TS			W0_BIT_(15)
339ad9a52cSNicolas Pitre #define DAT_0_MR_REJECT			W0_BIT_(14)
349ad9a52cSNicolas Pitre /*	DAT_0_SIR_REJECT		W0_BIT_(13) */
359ad9a52cSNicolas Pitre /*	DAT_0_IBI_PAYLOAD		W0_BIT_(12) */
369ad9a52cSNicolas Pitre #define DAT_0_STATIC_ADDRESS		W0_MASK(6, 0)
379ad9a52cSNicolas Pitre 
389ad9a52cSNicolas Pitre #define dat_w0_read(i)		readl(hci->DAT_regs + (i) * 8)
399ad9a52cSNicolas Pitre #define dat_w1_read(i)		readl(hci->DAT_regs + (i) * 8 + 4)
409ad9a52cSNicolas Pitre #define dat_w0_write(i, v)	writel(v, hci->DAT_regs + (i) * 8)
419ad9a52cSNicolas Pitre #define dat_w1_write(i, v)	writel(v, hci->DAT_regs + (i) * 8 + 4)
429ad9a52cSNicolas Pitre 
dynaddr_parity(unsigned int addr)439ad9a52cSNicolas Pitre static inline bool dynaddr_parity(unsigned int addr)
449ad9a52cSNicolas Pitre {
459ad9a52cSNicolas Pitre 	addr |= 1 << 7;
469ad9a52cSNicolas Pitre 	addr += addr >> 4;
479ad9a52cSNicolas Pitre 	addr += addr >> 2;
489ad9a52cSNicolas Pitre 	addr += addr >> 1;
499ad9a52cSNicolas Pitre 	return (addr & 1);
509ad9a52cSNicolas Pitre }
519ad9a52cSNicolas Pitre 
hci_dat_v1_init(struct i3c_hci * hci)529ad9a52cSNicolas Pitre static int hci_dat_v1_init(struct i3c_hci *hci)
539ad9a52cSNicolas Pitre {
549ad9a52cSNicolas Pitre 	unsigned int dat_idx;
559ad9a52cSNicolas Pitre 
569ad9a52cSNicolas Pitre 	if (!hci->DAT_regs) {
579ad9a52cSNicolas Pitre 		dev_err(&hci->master.dev,
589ad9a52cSNicolas Pitre 			"only DAT in register space is supported at the moment\n");
599ad9a52cSNicolas Pitre 		return -EOPNOTSUPP;
609ad9a52cSNicolas Pitre 	}
619ad9a52cSNicolas Pitre 	if (hci->DAT_entry_size != 8) {
629ad9a52cSNicolas Pitre 		dev_err(&hci->master.dev,
639ad9a52cSNicolas Pitre 			"only 8-bytes DAT entries are supported at the moment\n");
649ad9a52cSNicolas Pitre 		return -EOPNOTSUPP;
659ad9a52cSNicolas Pitre 	}
669ad9a52cSNicolas Pitre 
67*eed74230SBilly Tsai 	if (!hci->DAT_data) {
689ad9a52cSNicolas Pitre 		/* use a bitmap for faster free slot search */
699ad9a52cSNicolas Pitre 		hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
709ad9a52cSNicolas Pitre 		if (!hci->DAT_data)
719ad9a52cSNicolas Pitre 			return -ENOMEM;
729ad9a52cSNicolas Pitre 
739ad9a52cSNicolas Pitre 		/* clear them */
749ad9a52cSNicolas Pitre 		for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
759ad9a52cSNicolas Pitre 			dat_w0_write(dat_idx, 0);
769ad9a52cSNicolas Pitre 			dat_w1_write(dat_idx, 0);
779ad9a52cSNicolas Pitre 		}
78*eed74230SBilly Tsai 	}
799ad9a52cSNicolas Pitre 
809ad9a52cSNicolas Pitre 	return 0;
819ad9a52cSNicolas Pitre }
829ad9a52cSNicolas Pitre 
hci_dat_v1_cleanup(struct i3c_hci * hci)839ad9a52cSNicolas Pitre static void hci_dat_v1_cleanup(struct i3c_hci *hci)
849ad9a52cSNicolas Pitre {
859ad9a52cSNicolas Pitre 	bitmap_free(hci->DAT_data);
869ad9a52cSNicolas Pitre 	hci->DAT_data = NULL;
879ad9a52cSNicolas Pitre }
889ad9a52cSNicolas Pitre 
hci_dat_v1_alloc_entry(struct i3c_hci * hci)899ad9a52cSNicolas Pitre static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
909ad9a52cSNicolas Pitre {
919ad9a52cSNicolas Pitre 	unsigned int dat_idx;
92*eed74230SBilly Tsai 	int ret;
939ad9a52cSNicolas Pitre 
94*eed74230SBilly Tsai 	if (!hci->DAT_data) {
95*eed74230SBilly Tsai 		ret = hci_dat_v1_init(hci);
96*eed74230SBilly Tsai 		if (ret)
97*eed74230SBilly Tsai 			return ret;
98*eed74230SBilly Tsai 	}
999ad9a52cSNicolas Pitre 	dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
1009ad9a52cSNicolas Pitre 	if (dat_idx >= hci->DAT_entries)
1019ad9a52cSNicolas Pitre 		return -ENOENT;
1029ad9a52cSNicolas Pitre 	__set_bit(dat_idx, hci->DAT_data);
1039ad9a52cSNicolas Pitre 
1049ad9a52cSNicolas Pitre 	/* default flags */
1059ad9a52cSNicolas Pitre 	dat_w0_write(dat_idx, DAT_0_SIR_REJECT | DAT_0_MR_REJECT);
1069ad9a52cSNicolas Pitre 
1079ad9a52cSNicolas Pitre 	return dat_idx;
1089ad9a52cSNicolas Pitre }
1099ad9a52cSNicolas Pitre 
hci_dat_v1_free_entry(struct i3c_hci * hci,unsigned int dat_idx)1109ad9a52cSNicolas Pitre static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)
1119ad9a52cSNicolas Pitre {
1129ad9a52cSNicolas Pitre 	dat_w0_write(dat_idx, 0);
1139ad9a52cSNicolas Pitre 	dat_w1_write(dat_idx, 0);
114*eed74230SBilly Tsai 	if (hci->DAT_data)
1159ad9a52cSNicolas Pitre 		__clear_bit(dat_idx, hci->DAT_data);
1169ad9a52cSNicolas Pitre }
1179ad9a52cSNicolas Pitre 
hci_dat_v1_set_dynamic_addr(struct i3c_hci * hci,unsigned int dat_idx,u8 address)1189ad9a52cSNicolas Pitre static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
1199ad9a52cSNicolas Pitre 					unsigned int dat_idx, u8 address)
1209ad9a52cSNicolas Pitre {
1219ad9a52cSNicolas Pitre 	u32 dat_w0;
1229ad9a52cSNicolas Pitre 
1239ad9a52cSNicolas Pitre 	dat_w0 = dat_w0_read(dat_idx);
1249ad9a52cSNicolas Pitre 	dat_w0 &= ~(DAT_0_DYNAMIC_ADDRESS | DAT_0_DYNADDR_PARITY);
1259ad9a52cSNicolas Pitre 	dat_w0 |= FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) |
1269ad9a52cSNicolas Pitre 		  (dynaddr_parity(address) ? DAT_0_DYNADDR_PARITY : 0);
1279ad9a52cSNicolas Pitre 	dat_w0_write(dat_idx, dat_w0);
1289ad9a52cSNicolas Pitre }
1299ad9a52cSNicolas Pitre 
hci_dat_v1_set_static_addr(struct i3c_hci * hci,unsigned int dat_idx,u8 address)1309ad9a52cSNicolas Pitre static void hci_dat_v1_set_static_addr(struct i3c_hci *hci,
1319ad9a52cSNicolas Pitre 				       unsigned int dat_idx, u8 address)
1329ad9a52cSNicolas Pitre {
1339ad9a52cSNicolas Pitre 	u32 dat_w0;
1349ad9a52cSNicolas Pitre 
1359ad9a52cSNicolas Pitre 	dat_w0 = dat_w0_read(dat_idx);
1369ad9a52cSNicolas Pitre 	dat_w0 &= ~DAT_0_STATIC_ADDRESS;
1379ad9a52cSNicolas Pitre 	dat_w0 |= FIELD_PREP(DAT_0_STATIC_ADDRESS, address);
1389ad9a52cSNicolas Pitre 	dat_w0_write(dat_idx, dat_w0);
1399ad9a52cSNicolas Pitre }
1409ad9a52cSNicolas Pitre 
hci_dat_v1_set_flags(struct i3c_hci * hci,unsigned int dat_idx,u32 w0_flags,u32 w1_flags)1419ad9a52cSNicolas Pitre static void hci_dat_v1_set_flags(struct i3c_hci *hci, unsigned int dat_idx,
1429ad9a52cSNicolas Pitre 				 u32 w0_flags, u32 w1_flags)
1439ad9a52cSNicolas Pitre {
1449ad9a52cSNicolas Pitre 	u32 dat_w0, dat_w1;
1459ad9a52cSNicolas Pitre 
1469ad9a52cSNicolas Pitre 	dat_w0 = dat_w0_read(dat_idx);
1479ad9a52cSNicolas Pitre 	dat_w1 = dat_w1_read(dat_idx);
1489ad9a52cSNicolas Pitre 	dat_w0 |= w0_flags;
1499ad9a52cSNicolas Pitre 	dat_w1 |= w1_flags;
1509ad9a52cSNicolas Pitre 	dat_w0_write(dat_idx, dat_w0);
1519ad9a52cSNicolas Pitre 	dat_w1_write(dat_idx, dat_w1);
1529ad9a52cSNicolas Pitre }
1539ad9a52cSNicolas Pitre 
hci_dat_v1_clear_flags(struct i3c_hci * hci,unsigned int dat_idx,u32 w0_flags,u32 w1_flags)1549ad9a52cSNicolas Pitre static void hci_dat_v1_clear_flags(struct i3c_hci *hci, unsigned int dat_idx,
1559ad9a52cSNicolas Pitre 				   u32 w0_flags, u32 w1_flags)
1569ad9a52cSNicolas Pitre {
1579ad9a52cSNicolas Pitre 	u32 dat_w0, dat_w1;
1589ad9a52cSNicolas Pitre 
1599ad9a52cSNicolas Pitre 	dat_w0 = dat_w0_read(dat_idx);
1609ad9a52cSNicolas Pitre 	dat_w1 = dat_w1_read(dat_idx);
1619ad9a52cSNicolas Pitre 	dat_w0 &= ~w0_flags;
1629ad9a52cSNicolas Pitre 	dat_w1 &= ~w1_flags;
1639ad9a52cSNicolas Pitre 	dat_w0_write(dat_idx, dat_w0);
1649ad9a52cSNicolas Pitre 	dat_w1_write(dat_idx, dat_w1);
1659ad9a52cSNicolas Pitre }
1669ad9a52cSNicolas Pitre 
hci_dat_v1_get_index(struct i3c_hci * hci,u8 dev_addr)1679ad9a52cSNicolas Pitre static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr)
1689ad9a52cSNicolas Pitre {
1699ad9a52cSNicolas Pitre 	unsigned int dat_idx;
1709ad9a52cSNicolas Pitre 	u32 dat_w0;
1719ad9a52cSNicolas Pitre 
1723f43926fSChristophe JAILLET 	for_each_set_bit(dat_idx, hci->DAT_data, hci->DAT_entries) {
1739ad9a52cSNicolas Pitre 		dat_w0 = dat_w0_read(dat_idx);
1749ad9a52cSNicolas Pitre 		if (FIELD_GET(DAT_0_DYNAMIC_ADDRESS, dat_w0) == dev_addr)
1759ad9a52cSNicolas Pitre 			return dat_idx;
1769ad9a52cSNicolas Pitre 	}
1779ad9a52cSNicolas Pitre 
1789ad9a52cSNicolas Pitre 	return -ENODEV;
1799ad9a52cSNicolas Pitre }
1809ad9a52cSNicolas Pitre 
1819ad9a52cSNicolas Pitre const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
1829ad9a52cSNicolas Pitre 	.init			= hci_dat_v1_init,
1839ad9a52cSNicolas Pitre 	.cleanup		= hci_dat_v1_cleanup,
1849ad9a52cSNicolas Pitre 	.alloc_entry		= hci_dat_v1_alloc_entry,
1859ad9a52cSNicolas Pitre 	.free_entry		= hci_dat_v1_free_entry,
1869ad9a52cSNicolas Pitre 	.set_dynamic_addr	= hci_dat_v1_set_dynamic_addr,
1879ad9a52cSNicolas Pitre 	.set_static_addr	= hci_dat_v1_set_static_addr,
1889ad9a52cSNicolas Pitre 	.set_flags		= hci_dat_v1_set_flags,
1899ad9a52cSNicolas Pitre 	.clear_flags		= hci_dat_v1_clear_flags,
1909ad9a52cSNicolas Pitre 	.get_index		= hci_dat_v1_get_index,
1919ad9a52cSNicolas Pitre };
192