blob: c7053e71dd1d9e379aff154efaabf532ca174ad1 [file] [log] [blame]
// SPDX-License-Identifier: GPL-2.0-or-later
/*
* Copyright (c) 2023 MediaTek Inc. All Rights Reserved.
*
* Author: Ren-Ting Wang <ren-ting.wang@mediatek.com>
*/
#include <linux/bitmap.h>
#include <linux/bitops.h>
#include <linux/delay.h>
#include <linux/device.h>
#include <linux/io.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/platform_device.h>
#include "internal.h"
#include "mbox.h"
#include "mcu.h"
#include "tdma.h"
#include "tops.h"
#include "trm.h"
/* TDMA dump length */
#define TDMA_BASE_LEN (0x400)
static int tdma_trm_hw_dump(void *dst, u32 start_addr, u32 len);
struct tdma_hw {
void __iomem *base;
u32 start_ring;
struct mailbox_dev mgmt_mdev;
struct mailbox_dev offload_mdev[CORE_OFFLOAD_NUM];
};
struct tdma_hw tdma = {
.mgmt_mdev = MBOX_SEND_MGMT_DEV(NET),
.offload_mdev = {
[CORE_OFFLOAD_0] = MBOX_SEND_OFFLOAD_DEV(0, NET),
[CORE_OFFLOAD_1] = MBOX_SEND_OFFLOAD_DEV(1, NET),
[CORE_OFFLOAD_2] = MBOX_SEND_OFFLOAD_DEV(2, NET),
[CORE_OFFLOAD_3] = MBOX_SEND_OFFLOAD_DEV(3, NET),
},
};
static inline void tdma_write(u32 reg, u32 val)
{
writel(val, tdma.base + reg);
}
static inline void tdma_set(u32 reg, u32 mask)
{
setbits(tdma.base + reg, mask);
}
static inline void tdma_clr(u32 reg, u32 mask)
{
clrbits(tdma.base + reg, mask);
}
static inline void tdma_rmw(u32 reg, u32 mask, u32 val)
{
clrsetbits(tdma.base + reg, mask, val);
}
static inline u32 tdma_read(u32 reg)
{
return readl(tdma.base + reg);
}
static struct trm_config tdma_trm_configs[] = {
{
TRM_CFG_EN("netsys-tdma",
TDMA_BASE, TDMA_BASE_LEN,
0x0, TDMA_BASE_LEN,
0)
},
};
static struct trm_hw_config tdma_trm_hw_cfg = {
.trm_cfgs = tdma_trm_configs,
.cfg_len = ARRAY_SIZE(tdma_trm_configs),
.trm_hw_dump = tdma_trm_hw_dump,
};
static int tdma_trm_hw_dump(void *dst, u32 start_addr, u32 len)
{
u32 ofs;
if (unlikely(!dst))
return -ENODEV;
for (ofs = 0; len > 0; len -= 0x4, ofs += 0x4)
writel(tdma_read(start_addr + ofs), dst + ofs);
return 0;
}
static inline void tdma_prefetch_enable(bool en)
{
if (en) {
tdma_set(TDMA_PREF_TX_CFG, PREF_EN);
tdma_set(TDMA_PREF_RX_CFG, PREF_EN);
} else {
/* wait for prefetch idle */
while ((tdma_read(TDMA_PREF_TX_CFG) & PREF_BUSY)
|| (tdma_read(TDMA_PREF_RX_CFG) & PREF_BUSY))
;
tdma_write(TDMA_PREF_TX_CFG,
tdma_read(TDMA_PREF_TX_CFG) & (~PREF_EN));
tdma_write(TDMA_PREF_RX_CFG,
tdma_read(TDMA_PREF_RX_CFG) & (~PREF_EN));
}
}
static inline void tdma_writeback_enable(bool en)
{
if (en) {
tdma_set(TDMA_WRBK_TX_CFG, WRBK_EN);
tdma_set(TDMA_WRBK_RX_CFG, WRBK_EN);
} else {
/* wait for write back idle */
while ((tdma_read(TDMA_WRBK_TX_CFG) & WRBK_BUSY)
|| (tdma_read(TDMA_WRBK_RX_CFG) & WRBK_BUSY))
;
tdma_write(TDMA_WRBK_TX_CFG,
tdma_read(TDMA_WRBK_TX_CFG) & (~WRBK_EN));
tdma_write(TDMA_WRBK_RX_CFG,
tdma_read(TDMA_WRBK_RX_CFG) & (~WRBK_EN));
}
}
static inline void tdma_assert_prefetch_reset(bool en)
{
if (en) {
tdma_set(TDMA_PREF_TX_FIFO_CFG0, PREF_TX_RING0_CLEAR);
tdma_set(TDMA_PREF_RX_FIFO_CFG0,
PREF_RX_RINGX_CLEAR(0) | PREF_RX_RINGX_CLEAR(1));
tdma_set(TDMA_PREF_RX_FIFO_CFG1,
PREF_RX_RINGX_CLEAR(2) | PREF_RX_RINGX_CLEAR(3));
} else {
tdma_clr(TDMA_PREF_TX_FIFO_CFG0, PREF_TX_RING0_CLEAR);
tdma_clr(TDMA_PREF_RX_FIFO_CFG0,
PREF_RX_RINGX_CLEAR(0) | PREF_RX_RINGX_CLEAR(1));
tdma_clr(TDMA_PREF_RX_FIFO_CFG1,
PREF_RX_RINGX_CLEAR(2) | PREF_RX_RINGX_CLEAR(3));
}
}
static inline void tdma_assert_fifo_reset(bool en)
{
if (en) {
tdma_set(TDMA_TX_XDMA_FIFO_CFG0,
(PAR_FIFO_CLEAR
| CMD_FIFO_CLEAR
| DMAD_FIFO_CLEAR
| ARR_FIFO_CLEAR));
tdma_set(TDMA_RX_XDMA_FIFO_CFG0,
(PAR_FIFO_CLEAR
| CMD_FIFO_CLEAR
| DMAD_FIFO_CLEAR
| ARR_FIFO_CLEAR
| LEN_FIFO_CLEAR
| WID_FIFO_CLEAR
| BID_FIFO_CLEAR));
} else {
tdma_clr(TDMA_TX_XDMA_FIFO_CFG0,
(PAR_FIFO_CLEAR
| CMD_FIFO_CLEAR
| DMAD_FIFO_CLEAR
| ARR_FIFO_CLEAR));
tdma_clr(TDMA_RX_XDMA_FIFO_CFG0,
(PAR_FIFO_CLEAR
| CMD_FIFO_CLEAR
| DMAD_FIFO_CLEAR
| ARR_FIFO_CLEAR
| LEN_FIFO_CLEAR
| WID_FIFO_CLEAR
| BID_FIFO_CLEAR));
}
}
static inline void tdma_assert_writeback_reset(bool en)
{
if (en) {
tdma_set(TDMA_WRBK_TX_FIFO_CFG0, WRBK_RING_CLEAR);
tdma_set(TDMA_WRBK_RX_FIFO_CFGX(0), WRBK_RING_CLEAR);
tdma_set(TDMA_WRBK_RX_FIFO_CFGX(1), WRBK_RING_CLEAR);
tdma_set(TDMA_WRBK_RX_FIFO_CFGX(2), WRBK_RING_CLEAR);
tdma_set(TDMA_WRBK_RX_FIFO_CFGX(3), WRBK_RING_CLEAR);
} else {
tdma_clr(TDMA_WRBK_TX_FIFO_CFG0, WRBK_RING_CLEAR);
tdma_clr(TDMA_WRBK_RX_FIFO_CFGX(0), WRBK_RING_CLEAR);
tdma_clr(TDMA_WRBK_RX_FIFO_CFGX(1), WRBK_RING_CLEAR);
tdma_clr(TDMA_WRBK_RX_FIFO_CFGX(2), WRBK_RING_CLEAR);
tdma_clr(TDMA_WRBK_RX_FIFO_CFGX(3), WRBK_RING_CLEAR);
}
}
static inline void tdma_assert_prefetch_ring_reset(bool en)
{
if (en) {
tdma_set(TDMA_PREF_SIDX_CFG,
(TX_RING0_SIDX_CLR
| RX_RINGX_SIDX_CLR(0)
| RX_RINGX_SIDX_CLR(1)
| RX_RINGX_SIDX_CLR(2)
| RX_RINGX_SIDX_CLR(3)));
} else {
tdma_clr(TDMA_PREF_SIDX_CFG,
(TX_RING0_SIDX_CLR
| RX_RINGX_SIDX_CLR(0)
| RX_RINGX_SIDX_CLR(1)
| RX_RINGX_SIDX_CLR(2)
| RX_RINGX_SIDX_CLR(3)));
}
}
static inline void tdma_assert_writeback_ring_reset(bool en)
{
if (en) {
tdma_set(TDMA_WRBK_SIDX_CFG,
(TX_RING0_SIDX_CLR
| RX_RINGX_SIDX_CLR(0)
| RX_RINGX_SIDX_CLR(1)
| RX_RINGX_SIDX_CLR(2)
| RX_RINGX_SIDX_CLR(3)));
} else {
tdma_clr(TDMA_WRBK_SIDX_CFG,
(TX_RING0_SIDX_CLR
| RX_RINGX_SIDX_CLR(0)
| RX_RINGX_SIDX_CLR(1)
| RX_RINGX_SIDX_CLR(2)
| RX_RINGX_SIDX_CLR(3)));
}
}
static void mtk_tops_tdma_retrieve_last_state(void)
{
tdma.start_ring = tdma_read(TDMA_TX_CTX_IDX_0);
}
void mtk_tops_tdma_record_last_state(void)
{
tdma_write(TDMA_TX_CTX_IDX_0, tdma.start_ring);
}
static void tdma_get_next_rx_ring(void)
{
u32 pkt_num_per_core = tdma_read(TDMA_RX_MAX_CNT_X(0));
u32 ring[TDMA_RING_NUM] = {0};
u32 start = 0;
u32 tmp_idx;
u32 i;
for (i = 0; i < TDMA_RING_NUM; i++) {
tmp_idx = (tdma.start_ring + i) % TDMA_RING_NUM;
ring[i] = tdma_read(TDMA_RX_DRX_IDX_X(tmp_idx));
}
for (i = 1; i < TDMA_RING_NUM; i++) {
if (ring[i] >= (pkt_num_per_core - 1) && !ring[i - 1])
ring[i - 1] += pkt_num_per_core;
if (!ring[i] && ring[i - 1] >= (pkt_num_per_core - 1))
ring[i] = pkt_num_per_core;
if (ring[i] < ring[i - 1])
start = i;
}
tdma.start_ring = (tdma.start_ring + start) & TDMA_RING_NUM_MOD;
}
void mtk_tops_tdma_reset(void)
{
if (!mtk_tops_mcu_netsys_fe_rst())
/* get next start Rx ring if TDMA reset without NETSYS FE reset */
tdma_get_next_rx_ring();
else
/*
* NETSYS FE reset will restart CDM ring index
* so we don't need to calculate next ring index
*/
tdma.start_ring = 0;
/* then start reset TDMA */
tdma_assert_prefetch_reset(true);
tdma_assert_prefetch_reset(false);
tdma_assert_fifo_reset(true);
tdma_assert_fifo_reset(false);
tdma_assert_writeback_reset(true);
tdma_assert_writeback_reset(false);
/* reset tdma ring */
tdma_set(TDMA_RST_IDX,
(RST_DTX_IDX_0
| RST_DRX_IDX_X(0)
| RST_DRX_IDX_X(1)
| RST_DRX_IDX_X(2)
| RST_DRX_IDX_X(3)));
tdma_assert_prefetch_ring_reset(true);
tdma_assert_prefetch_ring_reset(false);
tdma_assert_writeback_ring_reset(true);
tdma_assert_writeback_ring_reset(false);
/* TODO: should we reset Tx/Rx CPU ring index? */
}
int mtk_tops_tdma_enable(void)
{
struct mailbox_msg msg = {
.msg1 = TOPS_NET_CMD_START,
.msg2 = tdma.start_ring,
};
int ret;
u32 i;
tdma_prefetch_enable(true);
tdma_set(TDMA_GLO_CFG0, RX_DMA_EN | TX_DMA_EN);
tdma_writeback_enable(true);
/* notify TOPS start network processing */
ret = mbox_send_msg_no_wait(&tdma.mgmt_mdev, &msg);
if (unlikely(ret))
return ret;
for (i = CORE_OFFLOAD_0; i < CORE_OFFLOAD_NUM; i++) {
ret = mbox_send_msg_no_wait(&tdma.offload_mdev[i], &msg);
if (unlikely(ret))
return ret;
}
return ret;
}
void mtk_tops_tdma_disable(void)
{
struct mailbox_msg msg = {
.msg1 = TOPS_NET_CMD_STOP,
};
u32 i;
if (mtk_tops_mcu_bring_up_done()) {
/* notify TOPS stop network processing */
if (unlikely(mbox_send_msg_no_wait(&tdma.mgmt_mdev, &msg)))
return;
for (i = CORE_OFFLOAD_0; i < CORE_OFFLOAD_NUM; i++) {
if (unlikely(mbox_send_msg_no_wait(&tdma.offload_mdev[i],
&msg)))
return;
}
}
tdma_prefetch_enable(false);
/* There is no need to wait for Tx/Rx idle before we stop Tx/Rx */
if (!mtk_tops_mcu_netsys_fe_rst())
while (tdma_read(TDMA_GLO_CFG0) & RX_DMA_BUSY)
;
tdma_write(TDMA_GLO_CFG0, tdma_read(TDMA_GLO_CFG0) & (~RX_DMA_EN));
if (!mtk_tops_mcu_netsys_fe_rst())
while (tdma_read(TDMA_GLO_CFG0) & TX_DMA_BUSY)
;
tdma_write(TDMA_GLO_CFG0, tdma_read(TDMA_GLO_CFG0) & (~TX_DMA_EN));
tdma_writeback_enable(false);
}
static int mtk_tops_tdma_register_mbox(void)
{
int ret;
int i;
ret = register_mbox_dev(MBOX_SEND, &tdma.mgmt_mdev);
if (ret) {
TOPS_ERR("register tdma mgmt mbox send failed: %d\n", ret);
return ret;
}
for (i = 0; i < CORE_OFFLOAD_NUM; i++) {
ret = register_mbox_dev(MBOX_SEND, &tdma.offload_mdev[i]);
if (ret) {
TOPS_ERR("register tdma offload %d mbox send failed: %d\n",
i, ret);
goto err_unregister_offload_mbox;
}
}
return ret;
err_unregister_offload_mbox:
for (i -= 1; i >= 0; i--)
unregister_mbox_dev(MBOX_SEND, &tdma.offload_mdev[i]);
unregister_mbox_dev(MBOX_SEND, &tdma.mgmt_mdev);
return ret;
}
static void mtk_tops_tdma_unregister_mbox(void)
{
int i;
unregister_mbox_dev(MBOX_SEND, &tdma.mgmt_mdev);
for (i = 0; i < CORE_OFFLOAD_NUM; i++)
unregister_mbox_dev(MBOX_SEND, &tdma.offload_mdev[i]);
}
static int mtk_tops_tdma_dts_init(struct platform_device *pdev)
{
struct device_node *fe_mem = NULL;
struct resource res;
int ret = 0;
fe_mem = of_parse_phandle(pdev->dev.of_node, "fe_mem", 0);
if (!fe_mem) {
TOPS_ERR("can not find fe_mem node\n");
return -ENODEV;
}
if (of_address_to_resource(fe_mem, 0, &res))
return -ENXIO;
/* map FE address */
tdma.base = devm_ioremap(&pdev->dev, res.start, resource_size(&res));
if (!tdma.base)
return -ENOMEM;
/* shift FE address to TDMA base */
tdma.base += TDMA_BASE;
of_node_put(fe_mem);
return ret;
}
int mtk_tops_tdma_init(struct platform_device *pdev)
{
int ret = 0;
ret = mtk_tops_tdma_register_mbox();
if (ret)
return ret;
ret = mtk_tops_tdma_dts_init(pdev);
if (ret)
return ret;
ret = mtk_trm_hw_config_register(TRM_TDMA, &tdma_trm_hw_cfg);
if (ret)
return ret;
mtk_tops_tdma_retrieve_last_state();
return ret;
}
void mtk_tops_tdma_deinit(struct platform_device *pdev)
{
mtk_trm_hw_config_unregister(TRM_TDMA, &tdma_trm_hw_cfg);
mtk_tops_tdma_unregister_mbox();
}