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