blob: 97bb49ff5b53bd8887303471a8b6446470156a81 [file] [log] [blame]
Nicolas Pitre9ad9a522020-11-11 17:05:10 -05001// 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 /* use a bitmap for faster free slot search */
68 hci->DAT_data = bitmap_zalloc(hci->DAT_entries, GFP_KERNEL);
69 if (!hci->DAT_data)
70 return -ENOMEM;
71
72 /* clear them */
73 for (dat_idx = 0; dat_idx < hci->DAT_entries; dat_idx++) {
74 dat_w0_write(dat_idx, 0);
75 dat_w1_write(dat_idx, 0);
76 }
77
78 return 0;
79}
80
81static void hci_dat_v1_cleanup(struct i3c_hci *hci)
82{
83 bitmap_free(hci->DAT_data);
84 hci->DAT_data = NULL;
85}
86
87static int hci_dat_v1_alloc_entry(struct i3c_hci *hci)
88{
89 unsigned int dat_idx;
90
91 dat_idx = find_first_zero_bit(hci->DAT_data, hci->DAT_entries);
92 if (dat_idx >= hci->DAT_entries)
93 return -ENOENT;
94 __set_bit(dat_idx, hci->DAT_data);
95
96 /* default flags */
97 dat_w0_write(dat_idx, DAT_0_SIR_REJECT | DAT_0_MR_REJECT);
98
99 return dat_idx;
100}
101
102static void hci_dat_v1_free_entry(struct i3c_hci *hci, unsigned int dat_idx)
103{
104 dat_w0_write(dat_idx, 0);
105 dat_w1_write(dat_idx, 0);
106 __clear_bit(dat_idx, hci->DAT_data);
107}
108
109static void hci_dat_v1_set_dynamic_addr(struct i3c_hci *hci,
110 unsigned int dat_idx, u8 address)
111{
112 u32 dat_w0;
113
114 dat_w0 = dat_w0_read(dat_idx);
115 dat_w0 &= ~(DAT_0_DYNAMIC_ADDRESS | DAT_0_DYNADDR_PARITY);
116 dat_w0 |= FIELD_PREP(DAT_0_DYNAMIC_ADDRESS, address) |
117 (dynaddr_parity(address) ? DAT_0_DYNADDR_PARITY : 0);
118 dat_w0_write(dat_idx, dat_w0);
119}
120
121static void hci_dat_v1_set_static_addr(struct i3c_hci *hci,
122 unsigned int dat_idx, u8 address)
123{
124 u32 dat_w0;
125
126 dat_w0 = dat_w0_read(dat_idx);
127 dat_w0 &= ~DAT_0_STATIC_ADDRESS;
128 dat_w0 |= FIELD_PREP(DAT_0_STATIC_ADDRESS, address);
129 dat_w0_write(dat_idx, dat_w0);
130}
131
132static void hci_dat_v1_set_flags(struct i3c_hci *hci, unsigned int dat_idx,
133 u32 w0_flags, u32 w1_flags)
134{
135 u32 dat_w0, dat_w1;
136
137 dat_w0 = dat_w0_read(dat_idx);
138 dat_w1 = dat_w1_read(dat_idx);
139 dat_w0 |= w0_flags;
140 dat_w1 |= w1_flags;
141 dat_w0_write(dat_idx, dat_w0);
142 dat_w1_write(dat_idx, dat_w1);
143}
144
145static void hci_dat_v1_clear_flags(struct i3c_hci *hci, unsigned int dat_idx,
146 u32 w0_flags, u32 w1_flags)
147{
148 u32 dat_w0, dat_w1;
149
150 dat_w0 = dat_w0_read(dat_idx);
151 dat_w1 = dat_w1_read(dat_idx);
152 dat_w0 &= ~w0_flags;
153 dat_w1 &= ~w1_flags;
154 dat_w0_write(dat_idx, dat_w0);
155 dat_w1_write(dat_idx, dat_w1);
156}
157
158static int hci_dat_v1_get_index(struct i3c_hci *hci, u8 dev_addr)
159{
160 unsigned int dat_idx;
161 u32 dat_w0;
162
Christophe JAILLET3f439262021-11-17 23:05:23 +0100163 for_each_set_bit(dat_idx, hci->DAT_data, hci->DAT_entries) {
Nicolas Pitre9ad9a522020-11-11 17:05:10 -0500164 dat_w0 = dat_w0_read(dat_idx);
165 if (FIELD_GET(DAT_0_DYNAMIC_ADDRESS, dat_w0) == dev_addr)
166 return dat_idx;
167 }
168
169 return -ENODEV;
170}
171
172const struct hci_dat_ops mipi_i3c_hci_dat_v1 = {
173 .init = hci_dat_v1_init,
174 .cleanup = hci_dat_v1_cleanup,
175 .alloc_entry = hci_dat_v1_alloc_entry,
176 .free_entry = hci_dat_v1_free_entry,
177 .set_dynamic_addr = hci_dat_v1_set_dynamic_addr,
178 .set_static_addr = hci_dat_v1_set_static_addr,
179 .set_flags = hci_dat_v1_set_flags,
180 .clear_flags = hci_dat_v1_clear_flags,
181 .get_index = hci_dat_v1_get_index,
182};