Merge tag 'i3c/for-6.7' of git://git.kernel.org/pub/scm/linux/kernel/git/i3c/linux
[sfrench/cifs-2.6.git] / drivers / i3c / master / mipi-i3c-hci / dat_v1.c
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
43 static 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
52 static 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
83 static void hci_dat_v1_cleanup(struct i3c_hci *hci)
84 {
85         bitmap_free(hci->DAT_data);
86         hci->DAT_data = NULL;
87 }
88
89 static 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
110 static 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
118 static 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
130 static 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
141 static 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
154 static 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
167 static 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
181 const 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 };