blob: b064db11a6b9e21b3f9374232273326975f346fc [file] [log] [blame]
developer1966afb2023-08-08 16:02:18 +08001// SPDX-License-Identifier: GPL-2.0-or-later
2/*
3 * Copyright (c) 2023 Mediatek Inc. All Rights Reserved.
4 *
5 * Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
6 */
7
8#include <linux/bitmap.h>
9#include <linux/bitops.h>
10#include <linux/err.h>
11#include <linux/refcount.h>
12#include <linux/spinlock.h>
13
14#include "pce/dipfilter.h"
15#include "pce/internal.h"
16#include "pce/netsys.h"
17
18struct dipfilter_entry {
19 struct dip_desc ddesc;
20 refcount_t refcnt;
21 u32 index;
22};
23
24struct dipfilter_hw {
25 struct dipfilter_entry dip_entries[FE_MEM_DIPFILTER_MAX_IDX];
26 DECLARE_BITMAP(dip_tbl, FE_MEM_DIPFILTER_MAX_IDX);
27 spinlock_t lock;
28};
29
30struct dipfilter_hw dip_hw;
31
32int mtk_pce_dipfilter_enable(void)
33{
34 mtk_pce_netsys_setbits(GLO_MEM_CFG, GDM_DIPFILTER_EN);
35
36 return 0;
37}
38
39void mtk_pce_dipfilter_disable(void)
40{
41 mtk_pce_netsys_clrbits(GLO_MEM_CFG, GDM_DIPFILTER_EN);
42}
43
44/*
45 * find registered dipfilter info
46 * return index on matched dipfilter info
47 * return NULL on not found
48 */
49static struct dipfilter_entry *__mtk_pce_dip_info_find(struct dip_desc *target)
50{
51 struct dip_desc *ddesc;
52 u32 idx = 0;
53
54 lockdep_assert_held(&dip_hw.lock);
55
56 /* can check this before acquiring lock */
57 if (!target
58 || target->tag == DIPFILTER_DISABLED
59 || target->tag >= __DIPFILTER_TAG_MAX) {
60 PCE_NOTICE("dip_desc is not enabled or invalid\n");
61 return NULL;
62 }
63
64 for_each_set_bit(idx, dip_hw.dip_tbl, FE_MEM_DIPFILTER_MAX_IDX) {
65 ddesc = &dip_hw.dip_entries[idx].ddesc;
66
67 if (target->tag != ddesc->tag)
68 continue;
69
70 if (ddesc->tag == DIPFILTER_IPV4
71 && ddesc->ipv4 == target->ipv4) {
72 return &dip_hw.dip_entries[idx];
73 } else if (ddesc->tag == DIPFILTER_IPV6
74 && !memcmp(ddesc->ipv6, target->ipv6, sizeof(u32) * 4)) {
75 return &dip_hw.dip_entries[idx];
76 }
77 }
78
79 return NULL;
80}
81
82int mtk_pce_dipfilter_desc_read(struct dip_desc *ddesc, u32 idx)
83{
84 struct fe_mem_msg msg;
85 unsigned long flag;
86 int ret;
87
88 if (!ddesc || idx > FE_MEM_DIPFILTER_MAX_IDX)
89 return -EINVAL;
90
91 mtk_pce_fe_mem_msg_config(&msg, FE_MEM_CMD_READ, FE_MEM_TYPE_DIPFILTER, idx);
92 memset(&msg.raw, 0, sizeof(msg.raw));
93
94 spin_lock_irqsave(&dip_hw.lock, flag);
95
96 ret = mtk_pce_fe_mem_msg_send(&msg);
97 if (ret)
98 goto unlock;
99
100 memcpy(ddesc, &msg.ddesc, sizeof(struct dip_desc));
101
102unlock:
103 spin_unlock_irqrestore(&dip_hw.lock, flag);
104
105 return ret;
106}
107
108static int __mtk_pce_dipfilter_entry_add(struct dip_desc *ddesc)
109{
110 struct fe_mem_msg fmsg;
111 struct dipfilter_entry *dip_entry;
112 int ret;
113 u32 idx;
114
115 lockdep_assert_held(&dip_hw.lock);
116
117 /* find available entry */
118 idx = find_first_zero_bit(dip_hw.dip_tbl, FE_MEM_DIPFILTER_MAX_IDX);
119 if (idx == FE_MEM_DIPFILTER_MAX_IDX) {
120 PCE_NOTICE("dipfilter full\n");
121 return -EBUSY;
122 }
123
124 /* prepare fe_mem message */
125 mtk_pce_fe_mem_msg_config(&fmsg, FE_MEM_CMD_WRITE, FE_MEM_TYPE_DIPFILTER, idx);
126
127 memset(&fmsg.raw, 0, sizeof(fmsg.raw));
128 memcpy(&fmsg.raw, ddesc, sizeof(struct dip_desc));
129
130 /* send fe_mem message */
131 ret = mtk_pce_fe_mem_msg_send(&fmsg);
132 if (ret) {
133 PCE_NOTICE("fe_mem send dipfilter desc failed\n");
134 return ret;
135 }
136
137 /* record installed dipfilter data */
138 dip_entry = &dip_hw.dip_entries[idx];
139 memcpy(&dip_entry->ddesc, ddesc, sizeof(struct dip_desc));
140
141 refcount_set(&dip_entry->refcnt, 1);
142
143 set_bit(idx, dip_hw.dip_tbl);
144
145 return 0;
146}
147
148int mtk_pce_dipfilter_entry_add(struct dip_desc *ddesc)
149{
150 struct dipfilter_entry *dip_entry;
151 unsigned long flag;
152 int ret = 0;
153
154 if (unlikely(!ddesc))
155 return 0;
156
157 spin_lock_irqsave(&dip_hw.lock, flag);
158
159 dip_entry = __mtk_pce_dip_info_find(ddesc);
160 if (dip_entry) {
161 refcount_inc(&dip_entry->refcnt);
162 goto unlock;
163 }
164
165 ret = __mtk_pce_dipfilter_entry_add(ddesc);
166
167unlock:
168 spin_unlock_irqrestore(&dip_hw.lock, flag);
169
170 return ret;
171}
172EXPORT_SYMBOL(mtk_pce_dipfilter_entry_add);
173
174int mtk_pce_dipfilter_entry_del(struct dip_desc *ddesc)
175{
176 struct fe_mem_msg fmsg;
177 struct dipfilter_entry *dip_entry;
178 unsigned long flag;
179 int ret;
180 u32 idx;
181
182 if (!ddesc)
183 return 0;
184
185 spin_lock_irqsave(&dip_hw.lock, flag);
186
187 dip_entry = __mtk_pce_dip_info_find(ddesc);
188 if (!dip_entry)
189 goto unlock;
190
191 dip_entry = &dip_hw.dip_entries[idx];
192 if (!refcount_dec_and_test(&dip_entry->refcnt))
193 /* dipfilter descriptor is still in use */
194 return 0;
195
196 mtk_pce_fe_mem_msg_config(&fmsg, FE_MEM_CMD_WRITE, FE_MEM_TYPE_DIPFILTER, idx);
197 memset(&fmsg.raw, 0, sizeof(fmsg.raw));
198
199 ret = mtk_pce_fe_mem_msg_send(&fmsg);
200 if (ret) {
201 PCE_NOTICE("fe_mem send dipfilter desc failed\n");
202 return ret;
203 }
204
205 memset(&dip_entry->ddesc, 0, sizeof(struct dip_desc));
206 clear_bit(idx, dip_hw.dip_tbl);
207
208unlock:
209 spin_unlock_irqrestore(&dip_hw.lock, flag);
210
211 return 0;
212}
213EXPORT_SYMBOL(mtk_pce_dipfilter_entry_del);
214
215static void mtk_pce_dipfilter_clean_up(void)
216{
217 struct fe_mem_msg fmsg = {
218 .cmd = FE_MEM_CMD_WRITE,
219 .type = FE_MEM_TYPE_DIPFILTER,
220 };
221 unsigned long flag;
222 int ret = 0;
223 u32 i = 0;
224
225 memset(&fmsg.raw, 0, sizeof(fmsg.raw));
226
227 spin_lock_irqsave(&dip_hw.lock, flag);
228
229 /* clear all dipfilter desc on init */
230 for (i = 0; i < FE_MEM_DIPFILTER_MAX_IDX; i++) {
231 fmsg.index = i;
232 ret = mtk_pce_fe_mem_msg_send(&fmsg);
233 if (ret)
234 goto unlock;
235
236 dip_hw.dip_entries[i].index = i;
237 }
238
239unlock:
240 spin_unlock_irqrestore(&dip_hw.lock, flag);
241}
242
243int mtk_pce_dipfilter_init(struct platform_device *pdev)
244{
245 spin_lock_init(&dip_hw.lock);
246
247 mtk_pce_dipfilter_clean_up();
248
249 return 0;
250}
251
252void mtk_pce_dipfilter_deinit(struct platform_device *pdev)
253{
254 mtk_pce_dipfilter_clean_up();
255}