1// SPDX-License-Identifier: BSD-3-Clause
2/*
3 * Copyright (c) 2020, MIPI Alliance, Inc.
4 *
5 * Author: Nicolas Pitre <npitre@baylibre.com>
6 */
7
8#include <linux/bitfield.h>
9#include <linux/bitmap.h>
10#include <linux/device.h>
11#include <linux/errno.h>
12#include <linux/i3c/master.h>
13#include <linux/io.h>
14
15#include "hci.h"
16#include "dat.h"
17
18
19/*
20 * Device Address Table Structure
21 */
22
23#define DAT_1_AUTOCMD_HDR_CODE		W1_MASK(58, 51)
24#define DAT_1_AUTOCMD_MODE		W1_MASK(50, 48)
25#define DAT_1_AUTOCMD_VALUE		W1_MASK(47, 40)
26#define DAT_1_AUTOCMD_MASK		W1_MASK(39, 32)
27/*	DAT_0_I2C_DEVICE		W0_BIT_(31) */
28#define DAT_0_DEV_NACK_RETRY_CNT	W0_MASK(30, 29)
29#define DAT_0_RING_ID			W0_MASK(28, 26)
30#define DAT_0_DYNADDR_PARITY		W0_BIT_(23)
31#define DAT_0_DYNAMIC_ADDRESS		W0_MASK(22, 16)
32#define DAT_0_TS			W0_BIT_(15)
33#define DAT_0_MR_REJECT			W0_BIT_(14)
34/*	DAT_0_SIR_REJECT		W0_BIT_(13) */
35/*	DAT_0_IBI_PAYLOAD		W0_BIT_(12) */
36#define DAT_0_STATIC_ADDRESS		W0_MASK(6, 0)
37
38#define dat_w0_read(i)		readl(hci->DAT_regs + (i) * 8)
39#define dat_w1_read(i)		readl(hci->DAT_regs + (i) * 8 + 4)
40#define dat_w0_write(i, v)	writel(v, hci->DAT_regs + (i) * 8)
41#define dat_w1_write(i, v)	writel(v, hci->DAT_regs + (i) * 8 + 4)
42
43static inline bool dynaddr_parity(unsigned int addr)
44{
45	addr |= 1 << 7;
46	addr += addr >> 4;
47	addr += addr >> 2;
48	addr += addr >> 1;
49	return (addr & 1);
50}
51
52static int hci_dat_v1_init(struct i3c_hci *hci)
53{
54	unsigned int dat_idx;
55
56	if (!hci->DAT_regs) {
57		dev_err(&hci->master.dev,
58			"only DAT in register space is supported at the moment\n");
59		return -EOPNOTSUPP;
60	}
61	if (hci->DAT_entry_size != 8) {
62		dev_err(&hci->master.dev,
63			"only 8-bytes DAT entries are supported at the moment\n");
64		return -EOPNOTSUPP;
65	}
66
67	if (!hci->DAT_data) {
68		/* use a bitmap for faster free slot search */
69		hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
70		if (!hci->DAT_data)
71			return -ENOMEM;
72
73		/* clear them */
74		for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
75			dat_w0_write(dat_idx, 0);
76			dat_w1_write(dat_idx, 0);
77		}
78	}
79
80	return 0;
81}
82
83static void hci_dat_v1_cleanup(struct i3c_hci *hci)
84{
85	bitmap_free(hci->DAT_data);
86	hci->DAT_data = NULL;
87}
88
89static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
90{
91	unsigned int dat_idx;
92	int ret;
93
94	if (!hci->DAT_data) {
95		ret = hci_dat_v1_init(hci);
96		if (ret)
97			return ret;
98	}
99	dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
100	if (dat_idx >= hci->DAT_entries)
101		return -ENOENT;
102	__set_bit(dat_idx, hci->DAT_data);
103
104	/* default flags */
105	dat_w0_write(dat_idx, DAT_0_SIR_REJECT | DAT_0_MR_REJECT);
106
107	return dat_idx;
108}
109
110static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)
111{
112	dat_w0_write(dat_idx, 0);
113	dat_w1_write(dat_idx, 0);
114	if (hci->DAT_data)
115		__clear_bit(dat_idx, hci->DAT_data);
116}
117
118static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
119					unsigned int dat_idx, u8 address)
120{
121	u32 dat_w0;
122
123	dat_w0 = dat_w0_read(dat_idx);
124	dat_w0 &= ~(DAT_0_DYNAMIC_ADDRESS | DAT_0_DYNADDR_PARITY);
125	dat_w0 |= FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) |
126		  (dynaddr_parity(address) ? DAT_0_DYNADDR_PARITY : 0);
127	dat_w0_write(dat_idx, dat_w0);
128}
129
130static void hci_dat_v1_set_static_addr(struct i3c_hci *hci,
131				       unsigned int dat_idx, u8 address)
132{
133	u32 dat_w0;
134
135	dat_w0 = dat_w0_read(dat_idx);
136	dat_w0 &= ~DAT_0_STATIC_ADDRESS;
137	dat_w0 |= FIELD_PREP(DAT_0_STATIC_ADDRESS, address);
138	dat_w0_write(dat_idx, dat_w0);
139}
140
141static void hci_dat_v1_set_flags(struct i3c_hci *hci, unsigned int dat_idx,
142				 u32 w0_flags, u32 w1_flags)
143{
144	u32 dat_w0, dat_w1;
145
146	dat_w0 = dat_w0_read(dat_idx);
147	dat_w1 = dat_w1_read(dat_idx);
148	dat_w0 |= w0_flags;
149	dat_w1 |= w1_flags;
150	dat_w0_write(dat_idx, dat_w0);
151	dat_w1_write(dat_idx, dat_w1);
152}
153
154static void hci_dat_v1_clear_flags(struct i3c_hci *hci, unsigned int dat_idx,
155				   u32 w0_flags, u32 w1_flags)
156{
157	u32 dat_w0, dat_w1;
158
159	dat_w0 = dat_w0_read(dat_idx);
160	dat_w1 = dat_w1_read(dat_idx);
161	dat_w0 &= ~w0_flags;
162	dat_w1 &= ~w1_flags;
163	dat_w0_write(dat_idx, dat_w0);
164	dat_w1_write(dat_idx, dat_w1);
165}
166
167static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr)
168{
169	unsigned int dat_idx;
170	u32 dat_w0;
171
172	for_each_set_bit(dat_idx, hci->DAT_data, hci->DAT_entries) {
173		dat_w0 = dat_w0_read(dat_idx);
174		if (FIELD_GET(DAT_0_DYNAMIC_ADDRESS, dat_w0) == dev_addr)
175			return dat_idx;
176	}
177
178	return -ENODEV;
179}
180
181const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
182	.init			= hci_dat_v1_init,
183	.cleanup		= hci_dat_v1_cleanup,
184	.alloc_entry		= hci_dat_v1_alloc_entry,
185	.free_entry		= hci_dat_v1_free_entry,
186	.set_dynamic_addr	= hci_dat_v1_set_dynamic_addr,
187	.set_static_addr	= hci_dat_v1_set_static_addr,
188	.set_flags		= hci_dat_v1_set_flags,
189	.clear_flags		= hci_dat_v1_clear_flags,
190	.get_index		= hci_dat_v1_get_index,
191};
192