Skip to content

Commit cc51410

Browse files
Sujuan Chendavem330
authored andcommitted
net: ethernet: mtk_wed: introduce wed mcu support
Introduce WED mcu support used to configure WED WO chip. This is a preliminary patch in order to add RX Wireless Ethernet Dispatch available on MT7986 SoC. Tested-by: Daniel Golle <[email protected]> Co-developed-by: Lorenzo Bianconi <[email protected]> Signed-off-by: Lorenzo Bianconi <[email protected]> Signed-off-by: Sujuan Chen <[email protected]> Signed-off-by: David S. Miller <[email protected]>
1 parent ceb82ac commit cc51410

File tree

5 files changed

+540
-1
lines changed

5 files changed

+540
-1
lines changed

drivers/net/ethernet/mediatek/Makefile

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,7 +5,7 @@
55

66
obj-$(CONFIG_NET_MEDIATEK_SOC) += mtk_eth.o
77
mtk_eth-y := mtk_eth_soc.o mtk_sgmii.o mtk_eth_path.o mtk_ppe.o mtk_ppe_debugfs.o mtk_ppe_offload.o
8-
mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o
8+
mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed.o mtk_wed_mcu.o
99
ifdef CONFIG_DEBUG_FS
1010
mtk_eth-$(CONFIG_NET_MEDIATEK_SOC_WED) += mtk_wed_debugfs.o
1111
endif
Lines changed: 359 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,359 @@
1+
// SPDX-License-Identifier: GPL-2.0-only
2+
/* Copyright (C) 2022 MediaTek Inc.
3+
*
4+
* Author: Lorenzo Bianconi <[email protected]>
5+
* Sujuan Chen <[email protected]>
6+
*/
7+
8+
#include <linux/firmware.h>
9+
#include <linux/of_address.h>
10+
#include <linux/of_reserved_mem.h>
11+
#include <linux/mfd/syscon.h>
12+
#include <linux/soc/mediatek/mtk_wed.h>
13+
14+
#include "mtk_wed_regs.h"
15+
#include "mtk_wed_wo.h"
16+
#include "mtk_wed.h"
17+
18+
static u32 wo_r32(struct mtk_wed_wo *wo, u32 reg)
19+
{
20+
return readl(wo->boot.addr + reg);
21+
}
22+
23+
static void wo_w32(struct mtk_wed_wo *wo, u32 reg, u32 val)
24+
{
25+
writel(val, wo->boot.addr + reg);
26+
}
27+
28+
static struct sk_buff *
29+
mtk_wed_mcu_msg_alloc(const void *data, int data_len)
30+
{
31+
int length = sizeof(struct mtk_wed_mcu_hdr) + data_len;
32+
struct sk_buff *skb;
33+
34+
skb = alloc_skb(length, GFP_KERNEL);
35+
if (!skb)
36+
return NULL;
37+
38+
memset(skb->head, 0, length);
39+
skb_reserve(skb, sizeof(struct mtk_wed_mcu_hdr));
40+
if (data && data_len)
41+
skb_put_data(skb, data, data_len);
42+
43+
return skb;
44+
}
45+
46+
static struct sk_buff *
47+
mtk_wed_mcu_get_response(struct mtk_wed_wo *wo, unsigned long expires)
48+
{
49+
if (!time_is_after_jiffies(expires))
50+
return NULL;
51+
52+
wait_event_timeout(wo->mcu.wait, !skb_queue_empty(&wo->mcu.res_q),
53+
expires - jiffies);
54+
return skb_dequeue(&wo->mcu.res_q);
55+
}
56+
57+
void mtk_wed_mcu_rx_event(struct mtk_wed_wo *wo, struct sk_buff *skb)
58+
{
59+
skb_queue_tail(&wo->mcu.res_q, skb);
60+
wake_up(&wo->mcu.wait);
61+
}
62+
63+
void mtk_wed_mcu_rx_unsolicited_event(struct mtk_wed_wo *wo,
64+
struct sk_buff *skb)
65+
{
66+
struct mtk_wed_mcu_hdr *hdr = (struct mtk_wed_mcu_hdr *)skb->data;
67+
68+
switch (hdr->cmd) {
69+
case MTK_WED_WO_EVT_LOG_DUMP: {
70+
const char *msg = (const char *)(skb->data + sizeof(*hdr));
71+
72+
dev_notice(wo->hw->dev, "%s\n", msg);
73+
break;
74+
}
75+
case MTK_WED_WO_EVT_PROFILING: {
76+
struct mtk_wed_wo_log_info *info;
77+
u32 count = (skb->len - sizeof(*hdr)) / sizeof(*info);
78+
int i;
79+
80+
info = (struct mtk_wed_wo_log_info *)(skb->data + sizeof(*hdr));
81+
for (i = 0 ; i < count ; i++)
82+
dev_notice(wo->hw->dev,
83+
"SN:%u latency: total=%u, rro:%u, mod:%u\n",
84+
le32_to_cpu(info[i].sn),
85+
le32_to_cpu(info[i].total),
86+
le32_to_cpu(info[i].rro),
87+
le32_to_cpu(info[i].mod));
88+
break;
89+
}
90+
case MTK_WED_WO_EVT_RXCNT_INFO:
91+
break;
92+
default:
93+
break;
94+
}
95+
96+
dev_kfree_skb(skb);
97+
}
98+
99+
static int
100+
mtk_wed_mcu_skb_send_msg(struct mtk_wed_wo *wo, struct sk_buff *skb,
101+
int id, int cmd, u16 *wait_seq, bool wait_resp)
102+
{
103+
struct mtk_wed_mcu_hdr *hdr;
104+
105+
/* TODO: make it dynamic based on cmd */
106+
wo->mcu.timeout = 20 * HZ;
107+
108+
hdr = (struct mtk_wed_mcu_hdr *)skb_push(skb, sizeof(*hdr));
109+
hdr->cmd = cmd;
110+
hdr->length = cpu_to_le16(skb->len);
111+
112+
if (wait_resp && wait_seq) {
113+
u16 seq = ++wo->mcu.seq;
114+
115+
if (!seq)
116+
seq = ++wo->mcu.seq;
117+
*wait_seq = seq;
118+
119+
hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_NEED_RSP);
120+
hdr->seq = cpu_to_le16(seq);
121+
}
122+
if (id == MTK_WED_MODULE_ID_WO)
123+
hdr->flag |= cpu_to_le16(MTK_WED_WARP_CMD_FLAG_FROM_TO_WO);
124+
125+
dev_kfree_skb(skb);
126+
return 0;
127+
}
128+
129+
static int
130+
mtk_wed_mcu_parse_response(struct mtk_wed_wo *wo, struct sk_buff *skb,
131+
int cmd, int seq)
132+
{
133+
struct mtk_wed_mcu_hdr *hdr;
134+
135+
if (!skb) {
136+
dev_err(wo->hw->dev, "Message %08x (seq %d) timeout\n",
137+
cmd, seq);
138+
return -ETIMEDOUT;
139+
}
140+
141+
hdr = (struct mtk_wed_mcu_hdr *)skb->data;
142+
if (le16_to_cpu(hdr->seq) != seq)
143+
return -EAGAIN;
144+
145+
skb_pull(skb, sizeof(*hdr));
146+
switch (cmd) {
147+
case MTK_WED_WO_CMD_RXCNT_INFO:
148+
default:
149+
break;
150+
}
151+
152+
return 0;
153+
}
154+
155+
int mtk_wed_mcu_send_msg(struct mtk_wed_wo *wo, int id, int cmd,
156+
const void *data, int len, bool wait_resp)
157+
{
158+
unsigned long expires;
159+
struct sk_buff *skb;
160+
u16 seq;
161+
int ret;
162+
163+
skb = mtk_wed_mcu_msg_alloc(data, len);
164+
if (!skb)
165+
return -ENOMEM;
166+
167+
mutex_lock(&wo->mcu.mutex);
168+
169+
ret = mtk_wed_mcu_skb_send_msg(wo, skb, id, cmd, &seq, wait_resp);
170+
if (ret || !wait_resp)
171+
goto unlock;
172+
173+
expires = jiffies + wo->mcu.timeout;
174+
do {
175+
skb = mtk_wed_mcu_get_response(wo, expires);
176+
ret = mtk_wed_mcu_parse_response(wo, skb, cmd, seq);
177+
dev_kfree_skb(skb);
178+
} while (ret == -EAGAIN);
179+
180+
unlock:
181+
mutex_unlock(&wo->mcu.mutex);
182+
183+
return ret;
184+
}
185+
186+
static int
187+
mtk_wed_get_memory_region(struct mtk_wed_wo *wo,
188+
struct mtk_wed_wo_memory_region *region)
189+
{
190+
struct reserved_mem *rmem;
191+
struct device_node *np;
192+
int index;
193+
194+
index = of_property_match_string(wo->hw->node, "memory-region-names",
195+
region->name);
196+
if (index < 0)
197+
return index;
198+
199+
np = of_parse_phandle(wo->hw->node, "memory-region", index);
200+
if (!np)
201+
return -ENODEV;
202+
203+
rmem = of_reserved_mem_lookup(np);
204+
of_node_put(np);
205+
206+
if (!rmem)
207+
return -ENODEV;
208+
209+
region->phy_addr = rmem->base;
210+
region->size = rmem->size;
211+
region->addr = devm_ioremap(wo->hw->dev, region->phy_addr, region->size);
212+
213+
return !region->addr ? -EINVAL : 0;
214+
}
215+
216+
static int
217+
mtk_wed_mcu_run_firmware(struct mtk_wed_wo *wo, const struct firmware *fw,
218+
struct mtk_wed_wo_memory_region *region)
219+
{
220+
const u8 *first_region_ptr, *region_ptr, *trailer_ptr, *ptr = fw->data;
221+
const struct mtk_wed_fw_trailer *trailer;
222+
const struct mtk_wed_fw_region *fw_region;
223+
224+
trailer_ptr = fw->data + fw->size - sizeof(*trailer);
225+
trailer = (const struct mtk_wed_fw_trailer *)trailer_ptr;
226+
region_ptr = trailer_ptr - trailer->num_region * sizeof(*fw_region);
227+
first_region_ptr = region_ptr;
228+
229+
while (region_ptr < trailer_ptr) {
230+
u32 length;
231+
232+
fw_region = (const struct mtk_wed_fw_region *)region_ptr;
233+
length = le32_to_cpu(fw_region->len);
234+
235+
if (region->phy_addr != le32_to_cpu(fw_region->addr))
236+
goto next;
237+
238+
if (region->size < length)
239+
goto next;
240+
241+
if (first_region_ptr < ptr + length)
242+
goto next;
243+
244+
if (region->shared && region->consumed)
245+
return 0;
246+
247+
if (!region->shared || !region->consumed) {
248+
memcpy_toio(region->addr, ptr, length);
249+
region->consumed = true;
250+
return 0;
251+
}
252+
next:
253+
region_ptr += sizeof(*fw_region);
254+
ptr += length;
255+
}
256+
257+
return -EINVAL;
258+
}
259+
260+
static int
261+
mtk_wed_mcu_load_firmware(struct mtk_wed_wo *wo)
262+
{
263+
static struct mtk_wed_wo_memory_region mem_region[] = {
264+
[MTK_WED_WO_REGION_EMI] = {
265+
.name = "wo-emi",
266+
},
267+
[MTK_WED_WO_REGION_ILM] = {
268+
.name = "wo-ilm",
269+
},
270+
[MTK_WED_WO_REGION_DATA] = {
271+
.name = "wo-data",
272+
.shared = true,
273+
},
274+
};
275+
const struct mtk_wed_fw_trailer *trailer;
276+
const struct firmware *fw;
277+
const char *fw_name;
278+
u32 val, boot_cr;
279+
int ret, i;
280+
281+
/* load firmware region metadata */
282+
for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
283+
ret = mtk_wed_get_memory_region(wo, &mem_region[i]);
284+
if (ret)
285+
return ret;
286+
}
287+
288+
wo->boot.name = "wo-boot";
289+
ret = mtk_wed_get_memory_region(wo, &wo->boot);
290+
if (ret)
291+
return ret;
292+
293+
/* set dummy cr */
294+
wed_w32(wo->hw->wed_dev, MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL,
295+
wo->hw->index + 1);
296+
297+
/* load firmware */
298+
fw_name = wo->hw->index ? MT7986_FIRMWARE_WO1 : MT7986_FIRMWARE_WO0;
299+
ret = request_firmware(&fw, fw_name, wo->hw->dev);
300+
if (ret)
301+
return ret;
302+
303+
trailer = (void *)(fw->data + fw->size -
304+
sizeof(struct mtk_wed_fw_trailer));
305+
dev_info(wo->hw->dev,
306+
"MTK WED WO Firmware Version: %.10s, Build Time: %.15s\n",
307+
trailer->fw_ver, trailer->build_date);
308+
dev_info(wo->hw->dev, "MTK WED WO Chip ID %02x Region %d\n",
309+
trailer->chip_id, trailer->num_region);
310+
311+
for (i = 0; i < ARRAY_SIZE(mem_region); i++) {
312+
ret = mtk_wed_mcu_run_firmware(wo, fw, &mem_region[i]);
313+
if (ret)
314+
goto out;
315+
}
316+
317+
/* set the start address */
318+
boot_cr = wo->hw->index ? MTK_WO_MCU_CFG_LS_WA_BOOT_ADDR_ADDR
319+
: MTK_WO_MCU_CFG_LS_WM_BOOT_ADDR_ADDR;
320+
wo_w32(wo, boot_cr, mem_region[MTK_WED_WO_REGION_EMI].phy_addr >> 16);
321+
/* wo firmware reset */
322+
wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCCR_CLR_ADDR, 0xc00);
323+
324+
val = wo_r32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR);
325+
val |= wo->hw->index ? MTK_WO_MCU_CFG_LS_WF_WM_WA_WA_CPU_RSTB_MASK
326+
: MTK_WO_MCU_CFG_LS_WF_WM_WA_WM_CPU_RSTB_MASK;
327+
wo_w32(wo, MTK_WO_MCU_CFG_LS_WF_MCU_CFG_WM_WA_ADDR, val);
328+
out:
329+
release_firmware(fw);
330+
331+
return ret;
332+
}
333+
334+
static u32
335+
mtk_wed_mcu_read_fw_dl(struct mtk_wed_wo *wo)
336+
{
337+
return wed_r32(wo->hw->wed_dev,
338+
MTK_WED_SCR0 + 4 * MTK_WED_DUMMY_CR_FWDL);
339+
}
340+
341+
int mtk_wed_mcu_init(struct mtk_wed_wo *wo)
342+
{
343+
u32 val;
344+
int ret;
345+
346+
skb_queue_head_init(&wo->mcu.res_q);
347+
init_waitqueue_head(&wo->mcu.wait);
348+
mutex_init(&wo->mcu.mutex);
349+
350+
ret = mtk_wed_mcu_load_firmware(wo);
351+
if (ret)
352+
return ret;
353+
354+
return readx_poll_timeout(mtk_wed_mcu_read_fw_dl, wo, val, !val,
355+
100, MTK_FW_DL_TIMEOUT);
356+
}
357+
358+
MODULE_FIRMWARE(MT7986_FIRMWARE_WO0);
359+
MODULE_FIRMWARE(MT7986_FIRMWARE_WO1);

drivers/net/ethernet/mediatek/mtk_wed_regs.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -152,6 +152,7 @@ struct mtk_wdma_desc {
152152

153153
#define MTK_WED_RING_RX(_n) (0x400 + (_n) * 0x10)
154154

155+
#define MTK_WED_SCR0 0x3c0
155156
#define MTK_WED_WPDMA_INT_TRIGGER 0x504
156157
#define MTK_WED_WPDMA_INT_TRIGGER_RX_DONE BIT(1)
157158
#define MTK_WED_WPDMA_INT_TRIGGER_TX_DONE GENMASK(5, 4)

0 commit comments

Comments
 (0)