blob: 60371595cff1ad8358c6dcbbd238e23cc048c37e [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;
developer1966afb2023-08-08 16:02:18 +0800180
181 if (!ddesc)
182 return 0;
183
184 spin_lock_irqsave(&dip_hw.lock, flag);
185
186 dip_entry = __mtk_pce_dip_info_find(ddesc);
187 if (!dip_entry)
188 goto unlock;
189
developer1966afb2023-08-08 16:02:18 +0800190 if (!refcount_dec_and_test(&dip_entry->refcnt))
191 /* dipfilter descriptor is still in use */
192 return 0;
193
developere005aee2023-09-12 16:16:22 +0800194 mtk_pce_fe_mem_msg_config(&fmsg, FE_MEM_CMD_WRITE, FE_MEM_TYPE_DIPFILTER,
195 dip_entry->index);
developer1966afb2023-08-08 16:02:18 +0800196 memset(&fmsg.raw, 0, sizeof(fmsg.raw));
197
198 ret = mtk_pce_fe_mem_msg_send(&fmsg);
199 if (ret) {
200 PCE_NOTICE("fe_mem send dipfilter desc failed\n");
201 return ret;
202 }
203
204 memset(&dip_entry->ddesc, 0, sizeof(struct dip_desc));
developere005aee2023-09-12 16:16:22 +0800205 clear_bit(dip_entry->index, dip_hw.dip_tbl);
developer1966afb2023-08-08 16:02:18 +0800206
207unlock:
208 spin_unlock_irqrestore(&dip_hw.lock, flag);
209
210 return 0;
211}
212EXPORT_SYMBOL(mtk_pce_dipfilter_entry_del);
213
214static void mtk_pce_dipfilter_clean_up(void)
215{
216 struct fe_mem_msg fmsg = {
217 .cmd = FE_MEM_CMD_WRITE,
218 .type = FE_MEM_TYPE_DIPFILTER,
219 };
220 unsigned long flag;
221 int ret = 0;
222 u32 i = 0;
223
224 memset(&fmsg.raw, 0, sizeof(fmsg.raw));
225
226 spin_lock_irqsave(&dip_hw.lock, flag);
227
228 /* clear all dipfilter desc on init */
229 for (i = 0; i < FE_MEM_DIPFILTER_MAX_IDX; i++) {
230 fmsg.index = i;
231 ret = mtk_pce_fe_mem_msg_send(&fmsg);
232 if (ret)
233 goto unlock;
234
235 dip_hw.dip_entries[i].index = i;
236 }
237
238unlock:
239 spin_unlock_irqrestore(&dip_hw.lock, flag);
240}
241
242int mtk_pce_dipfilter_init(struct platform_device *pdev)
243{
244 spin_lock_init(&dip_hw.lock);
245
246 mtk_pce_dipfilter_clean_up();
247
248 return 0;
249}
250
251void mtk_pce_dipfilter_deinit(struct platform_device *pdev)
252{
253 mtk_pce_dipfilter_clean_up();
254}