Skip to content
Snippets Groups Projects
Commit 57a9ea66 authored by Ma, Hua's avatar Ma, Hua Committed by Kenneth Johansson
Browse files

Support UMT driver

parent 1b038b1e
No related branches found
No related tags found
No related merge requests found
......@@ -31,6 +31,9 @@
#define read_c0_vpeconf1() __read_32bit_c0_register($1, 3)
#define write_c0_vpeconf1(val) __write_32bit_c0_register($1, 3, val)
#define read_c0_yqmask() __read_32bit_c0_register($1, 4)
#define write_c0_yqmask(val) __write_32bit_c0_register($1, 4, val)
#define read_c0_tcstatus() __read_32bit_c0_register($2, 1)
#define write_c0_tcstatus(val) __write_32bit_c0_register($2, 1, val)
......@@ -368,6 +371,19 @@ do { \
ehb(); \
} while (0)
#define mips_mt_yield(yq) \
({ \
unsigned int __yq = (yq); \
unsigned int __res; \
__asm__ __volatile__( \
".set mips32r2\n" \
".set mt\n" \
"yield %0,%z1\n" \
: "=d" (__res) \
: "dJ" (__yq)); \
\
__res; \
})
/* you *must* set the target tc (settc) before trying to use these */
#define read_vpe_c0_vpecontrol() mftc0(1, 1)
......
......@@ -603,4 +603,22 @@ config DMATEST
config DMA_ENGINE_RAID
bool
config LTQ_HWMCPY
bool "Lantiq Hardware Memcopy Engine"
default n
depends on SOC_GRX500
help
Lantiq Hardware Memory Copy Engine.
config LTQ_UMT_EXPAND_MODE
bool "Lantiq UMT in A21 expand mode"
depends on LTQ_HWMCPY && SOC_GRX500
help
In expand mode, UMT HW is able to support max
four UMT port. CBM port and DMA channels that
associated with the UMT port can be configurable.
Expand mode use dedicated OCP master to send out
the UMT message instead of using DMA as in the
legacy mode.
endif
......@@ -67,6 +67,8 @@ obj-$(CONFIG_TI_DMA_CROSSBAR) += ti-dma-crossbar.o
obj-$(CONFIG_TI_EDMA) += edma.o
obj-$(CONFIG_XGENE_DMA) += xgene-dma.o
obj-$(CONFIG_ZX_DMA) += zx296702_dma.o
obj-$(CONFIG_LTQ_UMT_EXPAND_MODE) += ltq_umt_expand.o
obj-$(CONFIG_LTQ_HWMCPY) += ltq_hwmcpy.o
obj-y += qcom/
obj-y += xilinx/
......
This diff is collapsed.
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Copyright (C) 2015 Zhu YiXin<yixin.zhu@lantiq.com>
* Copyright (C) 2016 Intel Corporation.
*/
#ifndef __HWMCPY_H__
#define __HWMCPY_H__
#define MCPY_PORTS_NUM 8
#define UMT_PORTS_NUM 4
#define MCPY_CMD_DEPTH 16
#define MCPY_OWN_BIT BIT(31)
#define MIN_UMT_PRD 20
enum mcpy_prio {
MCPY_PRIO_LOW = 0, /* Can Sleep */
MCPY_PRIO_HIGH, /* Cannot Sleep */
};
enum {
MCPY_CMD_GATHER = 0x1,
MCPY_CMD_LAST = 0x2,
MCPY_CMD_IPC = 0x4,
MCPY_CMD_SRC_FLUSH = 0x8,
MCPY_CMD_SRC_INV = 0x10,
MCPY_CMD_DST_FLUSH = 0x20,
MCPY_CMD_DST_INV = 0x40,
};
enum mcpy_trunk_size {
MCPY_TKSZ_512B = 0,
MCPY_TKSZ_1KB,
MCPY_TKSZ_2KB,
MCPY_TKSZ_4KB,
MCPY_TKSZ_8KB,
MCPY_TKSZ_16KB,
MCPY_TKSZ_32KB,
MCPY_TKSZ_64KB,
MCPY_TKSZ_MAX
};
enum mcpy_irq_md {
MCPY_IRQ_MODE = 0,
MCPY_YLD_MODE = 1,
};
enum mcpy_cmd_stat {
MCPY_CMD_PROCESSING = 1,
MCPY_CMD_RELEASED = 0,
};
enum dma_chan_on_off {
DMA_CH_OFF = 0, /*!< DMA channel is OFF */
DMA_CH_ON = 1, /*!< DMA channel is ON */
};
#define MCPY_DEF_TRKSZ MCPY_TKSZ_512B
#define MCPY_DEF_IRQ_INTVAL 50
#define UMT_DEF_DMACID 7
struct mcpy_port;
struct mcpy_ctrl;
struct mcpy_cmd {
struct mcpy_port *port; /* back pointer */
u32 context;
u32 trunk_size;
enum mcpy_cmd_stat status;
enum mcpy_type type;
u32 flags;
wait_queue_head_t cmdq;
/*struct semaphore sem; */
struct mutex mtx_lock;
struct semaphore sem_lock;
};
struct dma_ch {
u32 rch;
u32 tch;
u32 rch_dbase;
u32 tch_dbase;
u32 rch_dnum;
u32 tch_dnum;
enum dma_chan_on_off onoff;
char rch_name[32];
char tch_name[32];
};
enum mcpy_port_status {
MCPY_PORT_DISABLED = 1,
MCPY_PORT_ENABLED = 2,
MCPY_PORT_RESERVED = 4,
};
struct umt_port {
struct mcpy_umt *pctrl;
u32 umt_pid;
u32 ep_id;
enum umt_mode umt_mode;
enum umt_msg_mode msg_mode;
enum umt_status status;
u32 umt_period;
u32 umt_dst;
u32 cbm_pid; /* CBM WLAN ID (0 - 3) */
u32 dma_cid; /* DMA Chan ID */
enum umt_status suspend;
spinlock_t umt_port_lock;
};
struct mcpy_umt {
struct mcpy_ctrl *ctrl; /* back pointer */
struct umt_port ports[UMT_PORTS_NUM];
u32 dma_ctrlid;
enum umt_status status;
struct proc_dir_entry *proc;
spinlock_t umt_lock;
struct clk *clk;
};
struct mcpy_port {
struct mcpy_ctrl *ctrl; /* back pointer */
struct mcpy_port_ctrl *pctrl; /* back pointer */
const char *name;
u32 prio;
int vpe_id;
u32 pid;
int irq_no;
int yld_no;
u32 yld_pin;
struct mcpy_cmd cmd;
struct dma_ch chan;
enum mcpy_port_status status;
spinlock_t port_lock;
enum mcpy_trunk_size trunk_size;
enum mcpy_irq_md irq_mode;
u32 irq_intvl;
/* atomic_t users; remove for performace inprovement*/
u64 mib_bytes;
u64 mib_use_times;
};
struct mcpy_port_ctrl {
struct mcpy_ctrl *ctrl; /*back pointer */
u32 prio_lo_map; /* available port bitmap for low priority */
int lo_idx;
u32 *hi_per_vpe; /*Dedicated mcpy per VPE */
u32 *lo_per_vpe; /*Dedicated mcpy per VPE */
struct mcpy_port ports[MCPY_PORTS_NUM];
spinlock_t mcpy_lock;
};
struct mcpy_ctrl {
void __iomem *membase;
u32 phybase;
u32 dma_ctrl_id;
u32 dma_port_id;
struct mcpy_port_ctrl port_ctrl;
struct mcpy_umt umt;
struct device *dev;
struct proc_dir_entry *proc;
};
struct mcpy_cfg {
u32 prio;
int vpe_id;
u32 en;
};
/* Debug Level */
#undef MCPY_DBG_DEF
#define MCPY_DBG_DEF(name, value) MCPY_##name = BIT(value),
#define MCPY_DBG_LIST \
MCPY_DBG_DEF(ERR, 0) \
MCPY_DBG_DEF(EVENT, 1) \
MCPY_DBG_DEF(INIT, 2) \
MCPY_DBG_DEF(INFO, 3) \
MCPY_DBG_DEF(DBG, 4) \
MCPY_DBG_DEF(MAX, 10) \
enum {
MCPY_DBG_LIST
};
#undef MCPY_DBG_DEF
#define MCPY_DBG_DEF(name, value) DBG_##name,
enum {
MCPY_DBG_LIST
};
#undef MCPY_DBG_DEF
extern u32 g_mcpy_dbg;
#define mcpy_dbg(dbg_level, fmt, arg...) \
do { \
if (unlikely(g_mcpy_dbg & dbg_level)) { \
if (dbg_level & MCPY_ERR) { \
pr_err_ratelimited(fmt, ##arg); \
} else if ((dbg_level & MCPY_INFO) || \
(dbg_level & MCPY_INIT)) { \
pr_info_ratelimited(fmt, ##arg); \
} else { \
pr_debug_ratelimited(fmt, ##arg); \
} \
} \
} \
while (0)
int umt_init(struct mcpy_ctrl *);
struct mcpy_umt *mcpy_get_umt(void);
void setup_percpu_yqmask(u32 mask, int cpu);
#endif /* __HWMCPY_H__ */
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Copyright (C) 2015 Zhu YiXin<yixin.zhu@lantiq.com>
* Copyright (C) 2016 Intel Corporation.
*/
#ifndef __HWMCPY_ADDR_H__
#define __HWMCPY_ADDR_H__
#define MCPY_GCTRL 0x200
#define MCPY_INTERNAL_INT_EN 0x8C
#define MCPY_INTERNAL_INT_MASK 0x84
#define MCPY_INT_EN 0x88
#define MCPY_INT_MASK 0x80
#define MCPY_INT_STAT 0x90
#define MCPY_PORT_TO_CNT_0 0x300
#define MCPY_MRES_REG0_0 0x100
#define MCPY_MRES_REG1_0 0x104
#define MCPY_MIPS_CFG_0 0x240
#define MCPY_UMT_SW_MODE 0x94
#define MCPY_UMT_PERD 0xDC
#define MCPY_UMT_MSG(x) (0x220 + (x) * 4)
#define MCPY_UMT_DEST 0x230
#define MCPY_UMT_XBASE 0x400
#define MCPY_UMT_XOFFSET 0x100
#define MCPY_UMT_XMSG(x) (0x0 + (x) * 4)
#define MCPY_UMT_XPERIOD 0x20
#define MCPY_UMT_XDEST 0x30
#define MCPY_UMT_XSW_MODE 0x34
#define MCPY_UMT_TRG_MUX 0xE0
#define MCPY_UMT_CNT_CTRL 0xE4
#define MCPY_UMT_X_ADDR(x, off) (MCPY_UMT_XBASE + \
((x) - 1) * MCPY_UMT_XOFFSET + (off))
#define MRES_ERROR BIT(30)
#define MRES_DONE BIT(31)
#define MCPY_CMD_ERR BIT(16)
#define MCPY_LEN_ERR BIT(17)
extern void __iomem *g_mcpy_addr_base;
#define ltq_mcpy_r32(x) ltq_r32(g_mcpy_addr_base + (x))
#define ltq_mcpy_w32(x, y) ltq_w32((x), g_mcpy_addr_base + (y))
#define ltq_mcpy_w32_mask(x, y, z) \
ltq_w32_mask((x), (y), g_mcpy_addr_base + (z))
#define PORT_TO_CNT(pid) (MCPY_PORT_TO_CNT_0 + (pid) * 0x10)
#define PORT_MRES(pid) (MCPY_MRES_REG1_0 + (pid) * 0x10)
#define MCPY_SET_RESPONSE(pid) ltq_mcpy_w32(0, PORT_MRES(pid))
#define MCPY_GET_RESPONSE(pid) ltq_mcpy_r32(PORT_MRES(pid))
#define MCPY_MIPS_CFG(x) (MCPY_MIPS_CFG_0 + (x) * 0x10)
#define MCPY_CMD(cid, pid) ((cid) * 0x4 + (pid) * 0x10)
#define MCPY_DMA_RX_CID 12
#define MCPY_DMA_TX_CID (MCPY_DMA_RX_CID + 1)
#define UMT_DMA_RX_CID 28
#define UMT_DMA_TX_CID (UMT_DMA_RX_CID + 1)
#define MCPY_DBASE 0x10600
#define MCPY_DBASE_OFFSET 0x100
#define UMT_DBASE 0x11800
#define DMA_DESC_SIZE 16 /* 4 DWs */
#define DMA3_MASTERID 29
#endif /*__HWMCPY_ADDR_H__ */
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Copyright (C) 2015 Zhu YiXin<yixin.zhu@lantiq.com>
* Copyright (C) 2016 Intel Corporation.
*/
#include <linux/init.h>
#include <linux/platform_device.h>
#include <linux/module.h>
#include <linux/clk.h>
#include <linux/slab.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/proc_fs.h>
#include <linux/interrupt.h>
#include <linux/delay.h>
#include <linux/errno.h>
#include <linux/dma-mapping.h>
#include <linux/sched.h>
#include <linux/wait.h>
#include <linux/of.h>
#include <linux/of_irq.h>
#include <linux/seq_file.h>
#include <linux/dma/lantiq_dmax.h>
#include <linux/irqchip/mips-gic.h>
#include <lantiq.h>
#include <lantiq_soc.h>
#include <net/datapath_proc_api.h>
#include "ltq_hwmcpy_addr.h"
#include <linux/ltq_hwmcpy.h>
#include "ltq_hwmcpy.h"
static u32 g_dma_ctrl = DMA1TX;
/* 0x17AC == 20us according to register document*/
#define UMT_US_TO_CNT_DEFAULT_VALUE (0x17AC / 20)
static inline void umt_set_mode(u32 umt_id, enum umt_mode umt_mode)
{
u32 val, off;
if (!umt_id) {
ltq_mcpy_w32_mask(0x2, ((u32)umt_mode) << 1, MCPY_GCTRL);
} else {
off = 16 + (umt_id - 1) * 3;
val = ltq_mcpy_r32(MCPY_GCTRL) & ~(BIT(off));
ltq_mcpy_w32(val | (((u32)umt_mode) << off), MCPY_GCTRL);
}
}
static inline void umt_set_msgmode(u32 umt_id, enum umt_msg_mode msg_mode)
{
if (!umt_id)
ltq_mcpy_w32((u32)msg_mode, MCPY_UMT_SW_MODE);
else
ltq_mcpy_w32((u32)msg_mode,
MCPY_UMT_X_ADDR(umt_id, MCPY_UMT_XSW_MODE));
}
/* input in term of microseconds */
static inline u32 umt_us_to_cnt(int usec)
{
unsigned long cpuclk;
struct clk *clk;
struct mcpy_umt *pumt = mcpy_get_umt();
clk = pumt->clk;
if (!clk)
return usec * UMT_US_TO_CNT_DEFAULT_VALUE;
cpuclk = clk_get_rate(clk);
return usec * (cpuclk / 1000000);
}
static inline void umt_set_period(u32 umt_id, u32 umt_period)
{
umt_period = umt_us_to_cnt(umt_period);
if (!umt_id)
ltq_mcpy_w32(umt_period, MCPY_UMT_PERD);
else
ltq_mcpy_w32(umt_period,
MCPY_UMT_X_ADDR(umt_id, MCPY_UMT_XPERIOD));
}
static inline void umt_set_dst(u32 umt_id, u32 umt_dst)
{
if (!umt_id)
ltq_mcpy_w32(umt_dst, MCPY_UMT_DEST);
else
ltq_mcpy_w32(umt_dst,
MCPY_UMT_X_ADDR(umt_id, MCPY_UMT_XDEST));
}
static inline void umt_set_mux(u32 umt_id, u32 cbm_pid, u32 dma_cid)
{
u32 mux_sel;
cbm_pid = cbm_pid & 0xF;
dma_cid = dma_cid & 0xF;
mux_sel = ltq_mcpy_r32(MCPY_UMT_TRG_MUX) &
(~((0xF000F) << (umt_id * 4)));
mux_sel |= (dma_cid << (umt_id * 4)) |
(cbm_pid << (16 + (umt_id * 4)));
ltq_mcpy_w32(mux_sel, MCPY_UMT_TRG_MUX);
}
static inline void umt_set_endian(int dw_swp, int byte_swp)
{
u32 val;
val = ltq_mcpy_r32(MCPY_GCTRL);
if (byte_swp)
val |= BIT(28);
else
val &= ~(BIT(28));
if (dw_swp)
val |= BIT(29);
else
val &= ~(BIT(29));
ltq_mcpy_w32(val, MCPY_GCTRL);
}
static inline void umt_en_expand_mode(void)
{
u32 val;
val = ltq_mcpy_r32(MCPY_GCTRL) | BIT(31);
ltq_mcpy_w32(val, MCPY_GCTRL);
if (IS_ENABLED(CONFIG_CPU_BIG_ENDIAN))
umt_set_endian(1, 0);
else
umt_set_endian(1, 1);
}
static inline void umt_enable(u32 umt_id, enum umt_status status)
{
u32 val, off;
if (!umt_id) {
ltq_mcpy_w32_mask(0x4, ((u32)status) << 2, MCPY_GCTRL);
} else {
off = 17 + (umt_id - 1) * 3;
val = (ltq_mcpy_r32(MCPY_GCTRL) & ~BIT(off))
| (((u32)status) << off);
ltq_mcpy_w32(val, MCPY_GCTRL);
}
}
static inline void umt_suspend(u32 umt_id, enum umt_status status)
{
u32 val;
if (status)
val = ltq_mcpy_r32(MCPY_UMT_CNT_CTRL) | BIT(umt_id);
else
val = ltq_mcpy_r32(MCPY_UMT_CNT_CTRL) & (~(BIT(umt_id)));
ltq_mcpy_w32(val, MCPY_UMT_CNT_CTRL);
}
/*This function will disable umt */
static inline void umt_reset_umt(u32 umt_id)
{
u32 mode;
umt_enable(umt_id, UMT_DISABLE);
mode = ltq_mcpy_r32(MCPY_UMT_X_ADDR(umt_id, MCPY_UMT_XSW_MODE));
if (mode == UMT_SELFCNT_MODE) {
umt_set_mode(umt_id, UMT_USER_MODE);
umt_set_mode(umt_id, UMT_SELFCNT_MODE);
} else {
umt_set_mode(umt_id, UMT_SELFCNT_MODE);
umt_set_mode(umt_id, UMT_USER_MODE);
}
}
/**
* intput:
* @umt_id: UMT port id, (0 - 3)
* @ep_id: Aligned with datapath lib ep_id
* @period: measured in microseconds.
* ret: Fail < 0 / Success: 0
*/
int ltq_umt_set_period(u32 umt_id, u32 ep_id, u32 period)
{
struct mcpy_umt *pumt = mcpy_get_umt();
struct umt_port *port;
if (period < MIN_UMT_PRD || umt_id >= UMT_PORTS_NUM)
goto period_err;
if (pumt->status != UMT_ENABLE) {
mcpy_dbg(MCPY_ERR, "UMT is not initialized!\n");
return -ENODEV;
}
port = &pumt->ports[umt_id];
spin_lock_bh(&port->umt_port_lock);
if (port->ep_id != ep_id) {
spin_unlock_bh(&port->umt_port_lock);
goto period_err;
}
if (port->umt_period != period) {
port->umt_period = period;
umt_set_period(umt_id, port->umt_period);
}
spin_unlock_bh(&port->umt_port_lock);
return 0;
period_err:
mcpy_dbg(MCPY_ERR, "umt_id: %d, ep_id: %d, period: %d\n",
umt_id, ep_id, period);
return -EINVAL;
}
EXPORT_SYMBOL(ltq_umt_set_period);
/**
* API to configure the UMT port.
* input:
* @umt_id: (0 - 3)
* @ep_id: aligned with datapath lib EP
* @umt_mode: 0-self-counting mode, 1-user mode.
* @msg_mode: 0-No MSG, 1-MSG0 Only, 2-MSG1 Only, 3-MSG0 & MSG1.
* @dst: Destination PHY address.
* @period(ms): only applicable when set to self-counting mode.
* self-counting interval time. if 0, use the original setting.
* @enable: 1-Enable/0-Disable
* @ret: Fail < 0 , SUCCESS:0
*/
int ltq_umt_set_mode(u32 umt_id, u32 ep_id, u32 umt_mode, u32 msg_mode,
u32 phy_dst, u32 period, u32 enable)
{
struct mcpy_umt *pumt = mcpy_get_umt();
struct umt_port *port;
if (pumt->status != UMT_ENABLE) {
mcpy_dbg(MCPY_ERR, "UMT is not initialized!!\n");
return -ENODEV;
}
if ((umt_mode >= (u32)UMT_MODE_MAX) ||
(msg_mode >= (u32)UMT_MSG_MAX) ||
(enable >= (u32)UMT_STATUS_MAX) ||
(phy_dst == 0) || (period == 0) || (umt_id >= UMT_PORTS_NUM)) {
mcpy_dbg(MCPY_ERR, "umt_id: %d, umt_mode: %d, msg_mode: %d, enable: %d, phy_dst: %d\n",
umt_id, umt_mode, msg_mode, enable, phy_dst);
return -EINVAL;
}
port = &pumt->ports[umt_id];
spin_lock_bh(&port->umt_port_lock);
if (port->ep_id != ep_id) {
mcpy_dbg(MCPY_ERR, "input ep_id: %d, port ep_id: %d\n",
ep_id, port->ep_id);
spin_unlock_bh(&port->umt_port_lock);
return -EINVAL;
}
umt_reset_umt(umt_id);
port->umt_mode = (enum umt_mode)umt_mode;
port->msg_mode = (enum umt_msg_mode)msg_mode;
port->umt_dst = phy_dst;
port->umt_period = period;
port->status = (enum umt_status)enable;
umt_set_mode(umt_id, port->umt_mode);
umt_set_msgmode(umt_id, port->msg_mode);
umt_set_dst(umt_id, port->umt_dst);
umt_set_period(umt_id, port->umt_period);
umt_enable(umt_id, port->status);
/* setup the CBM/DMA mapping */
spin_unlock_bh(&port->umt_port_lock);
return 0;
}
EXPORT_SYMBOL(ltq_umt_set_mode);
/**
* API to enable/disable umt port
* input:
* @umt_id (0 - 3)
* @ep_id: aligned with datapath lib EP
* @enable: Enable: 1 / Disable: 0
* ret: Fail < 0, Success: 0
*/
int ltq_umt_enable(u32 umt_id, u32 ep_id, u32 enable)
{
struct mcpy_umt *pumt = mcpy_get_umt();
struct umt_port *port;
if (umt_id >= UMT_PORTS_NUM)
return -EINVAL;
if (enable >= (u32)UMT_STATUS_MAX || pumt->status != UMT_ENABLE)
return -ENODEV;
port = &pumt->ports[umt_id];
spin_lock_bh(&port->umt_port_lock);
if (port->ep_id != ep_id || port->umt_dst == 0 || port->ep_id == 0) {
mcpy_dbg(MCPY_ERR, "input ep_id: %d, umt port ep_id: %d, umt_dst: 0x%x\n",
ep_id, port->ep_id, port->umt_dst);
goto en_err;
}
if (port->status != enable) {
port->status = (enum umt_status)enable;
umt_enable(umt_id, port->status);
}
spin_unlock_bh(&port->umt_port_lock);
return 0;
en_err:
spin_unlock_bh(&port->umt_port_lock);
return -EINVAL;
}
EXPORT_SYMBOL(ltq_umt_enable);
/**
* API to suspend/resume umt US/DS counter
* input:
* @umt_id (0 - 3)
* @ep_id: aligned with datapath lib EP
* @enable: suspend: 1 / resume: 0
* ret: Fail < 0, Success: 0
*/
int ltq_umt_suspend(u32 umt_id, u32 ep_id, u32 enable)
{
struct mcpy_umt *pumt = mcpy_get_umt();
struct umt_port *port;
if (umt_id >= UMT_PORTS_NUM)
return -EINVAL;
if (enable >= (u32)UMT_STATUS_MAX || pumt->status != UMT_ENABLE)
return -ENODEV;
port = &pumt->ports[umt_id];
spin_lock_bh(&port->umt_port_lock);
if (port->ep_id != ep_id || port->umt_dst == 0 || port->ep_id == 0) {
mcpy_dbg(MCPY_ERR, "input ep_id: %d, umt port ep_id: %d, umt_dst: 0x%x\n",
ep_id, port->ep_id, port->umt_dst);
goto en_err;
}
if (port->suspend != enable) {
port->suspend = (enum umt_status)enable;
umt_enable(umt_id, port->status);
umt_suspend(umt_id, port->suspend);
}
spin_unlock_bh(&port->umt_port_lock);
return 0;
en_err:
spin_unlock_bh(&port->umt_port_lock);
return -EINVAL;
}
EXPORT_SYMBOL(ltq_umt_suspend);
/**
* API to request and allocate UMT port
* input:
* @ep_id: aligned with datapath lib EP.
* @cbm_pid: CBM Port ID(0-3), 0 - CBM port 4, 1 - CBM port 24,
* 2 - CBM port 25, 3 - CBM port 26
* output:
* @dma_ctrlid: DMA controller ID. aligned with DMA driver DMA controller ID
* @dma_cid: DMA channel ID.
* @umt_id: (0 - 3)
* ret: Fail: < 0, Success: 0
*/
int ltq_umt_request(u32 ep_id, u32 cbm_pid,
u32 *dma_ctrlid, u32 *dma_cid, u32 *umt_id)
{
int i, pid;
struct mcpy_umt *pumt = mcpy_get_umt();
struct umt_port *port;
if (!dma_ctrlid || !dma_cid || !umt_id) {
mcpy_dbg(MCPY_ERR, "Output pointer is NULL!\n");
goto param_err;
}
if (pumt->status != UMT_ENABLE) {
mcpy_dbg(MCPY_ERR, "UMT not initialized!\n");
goto param_err;
}
if (!ep_id) {
mcpy_dbg(MCPY_ERR, "%s: ep_id cannot be zero!\n", __func__);
goto param_err;
}
if (cbm_pid >= UMT_PORTS_NUM) {
mcpy_dbg(MCPY_ERR, "%s: cbm pid must be in ranage(0 - %d)\n",
__func__, UMT_PORTS_NUM);
goto param_err;
}
pid = -1;
spin_lock_bh(&pumt->umt_lock);
for (i = 0; i < UMT_PORTS_NUM; i++) {
port = &pumt->ports[i];
spin_lock_bh(&port->umt_port_lock);
if (port->ep_id == ep_id && port->cbm_pid == cbm_pid) {
pid = i;
spin_unlock_bh(&port->umt_port_lock);
break;
} else if (port->ep_id == 0 && pid == -1) {
pid = i;
}
spin_unlock_bh(&port->umt_port_lock);
}
spin_unlock_bh(&pumt->umt_lock);
if (pid < 0) {
mcpy_dbg(MCPY_ERR, "No free UMT port!\n");
return -ENODEV;
}
port = &pumt->ports[pid];
spin_lock_bh(&port->umt_port_lock);
port->ep_id = ep_id;
port->cbm_pid = cbm_pid;
umt_set_mux(port->umt_pid, port->cbm_pid, port->dma_cid);
*dma_ctrlid = pumt->dma_ctrlid;
*dma_cid = port->dma_cid;
*umt_id = port->umt_pid;
spin_unlock_bh(&port->umt_port_lock);
return 0;
param_err:
return -EINVAL;
}
EXPORT_SYMBOL(ltq_umt_request);
/**
* API to release umt port
* input:
* @umt_id (0 - 3)
* @ep_id: aligned with datapath lib EP
*
* ret: Fail < 0, Success: 0
*/
int ltq_umt_release(u32 umt_id, u32 ep_id)
{
struct mcpy_umt *pumt;
struct umt_port *port;
if (umt_id >= UMT_PORTS_NUM)
return -ENODEV;
pumt = mcpy_get_umt();
if (pumt->status != UMT_ENABLE) {
mcpy_dbg(MCPY_ERR, "UMT is not initialized!\n");
return -ENODEV;
}
port = &pumt->ports[umt_id];
spin_lock_bh(&port->umt_port_lock);
if (port->ep_id != ep_id) {
mcpy_dbg(MCPY_ERR, "input ep_id: %d, UMT port ep_id: %d\n",
ep_id, port->ep_id);
spin_unlock_bh(&port->umt_port_lock);
return -ENODEV;
}
port->ep_id = 0;
port->cbm_pid = 0;
port->umt_dst = 0;
port->umt_period = 0;
port->status = UMT_DISABLE;
umt_enable(port->umt_pid, UMT_DISABLE);
spin_unlock_bh(&port->umt_port_lock);
return 0;
}
EXPORT_SYMBOL(ltq_umt_release);
static void umt_port_init(struct mcpy_umt *pumt,
struct device_node *node, int pid)
{
char res_cid[32];
int cid;
struct umt_port *port;
port = &pumt->ports[pid];
sprintf(res_cid, "lantiq,umt%d-dmacid", pid);
if (of_property_read_u32(node, res_cid, &cid) < 0)
cid = UMT_DEF_DMACID + pid;
port->pctrl = pumt;
port->umt_pid = pid;
port->dma_cid = cid;
port->ep_id = 0;
port->status = UMT_DISABLE;
spin_lock_init(&port->umt_port_lock);
}
static void *umt_port_seq_start(struct seq_file *s, loff_t *pos)
{
struct mcpy_umt *pumt = s->private;
struct umt_port *port;
if (*pos >= UMT_PORTS_NUM)
return NULL;
port = &pumt->ports[*pos];
return port;
}
static void *umt_port_seq_next(struct seq_file *s, void *v, loff_t *pos)
{
struct mcpy_umt *pumt = s->private;
struct umt_port *port;
if (++*pos >= UMT_PORTS_NUM)
return NULL;
port = &pumt->ports[*pos];
return port;
}
static void umt_port_seq_stop(struct seq_file *s, void *v)
{
}
static int umt_port_seq_show(struct seq_file *s, void *v)
{
struct umt_port *port = v;
int pid = port->umt_pid;
u32 val;
seq_printf(s, "\nUMT port %d configuration\n", pid);
seq_puts(s, "-----------------------------------------\n");
seq_printf(s, "UMT port ep_id: %d\n", port->ep_id);
seq_printf(s, "UMT Mode: \t%s\n",
port->umt_mode == UMT_SELFCNT_MODE ?
"UMT SelfCounting Mode" : "UMT User Mode");
switch (port->msg_mode) {
case UMT_NO_MSG:
seq_puts(s, "UMT MSG Mode: \tUMT NO MSG\n");
break;
case UMT_MSG0_ONLY:
seq_puts(s, "UMT MSG Mode: \tUMT MSG0 Only\n");
break;
case UMT_MSG1_ONLY:
seq_puts(s, "UMT MSG Mode: \tUMT MSG1 Only\n");
break;
case UMT_MSG0_MSG1:
seq_puts(s, "UMT MSG Mode: \tUMT_MSG0_And_MSG1\n");
break;
default:
seq_printf(s, "UMT MSG Mode Error! Msg_mode: %d\n",
port->msg_mode);
}
seq_printf(s, "UMT DST: \t0x%x\n", port->umt_dst);
if (port->umt_mode == UMT_SELFCNT_MODE)
seq_printf(s, "UMT Period: \t%d(us)\n", port->umt_period);
seq_printf(s, "UMT Status: \t%s\n",
port->status == UMT_ENABLE ? "Enable" :
port->status == UMT_DISABLE ? "Disable" : "Init Fail");
seq_printf(s, "UMT DMA CID: \t%d\n", port->dma_cid);
seq_printf(s, "UMT CBM PID: \t%d\n", port->cbm_pid);
seq_printf(s, "++++Register dump of umt port: %d++++\n", pid);
if (pid == 0) {
seq_printf(s, "UMT Status: \t%s\n",
(ltq_mcpy_r32(MCPY_GCTRL) & BIT(2)) != 0 ?
"Enable" : "Disable");
seq_printf(s, "UMT Mode: \t%s\n",
(ltq_mcpy_r32(MCPY_GCTRL) & BIT(1)) != 0 ?
"UMT User MSG mode" : "UMT SelfCounting mode");
seq_printf(s, "UMT MSG Mode: \t%d\n",
ltq_mcpy_r32(MCPY_UMT_SW_MODE));
seq_printf(s, "UMT Dst: \t0x%x\n",
ltq_mcpy_r32(MCPY_UMT_DEST));
seq_printf(s, "UMT Period: \t0x%x\n",
ltq_mcpy_r32(MCPY_UMT_PERD));
seq_printf(s, "UMT MSG0: \t0x%x\n",
ltq_mcpy_r32(MCPY_UMT_MSG(0)));
seq_printf(s, "UMT MSG1: \t0x%x\n",
ltq_mcpy_r32(MCPY_UMT_MSG(1)));
} else {
seq_printf(s, "UMT Status: \t%s\n",
(ltq_mcpy_r32(MCPY_GCTRL) &
BIT(17 + 3 * (pid - 1))) != 0 ?
"Enable" : "Disable");
seq_printf(s, "UMT Mode: \t%s\n",
(ltq_mcpy_r32(MCPY_GCTRL) &
BIT(16 + 3 * (pid - 1))) != 0 ?
"UMT User MSG mode" : "UMT SelfCounting mode");
seq_printf(s, "UMT MSG Mode: \t%d\n",
ltq_mcpy_r32(MCPY_UMT_X_ADDR(pid, MCPY_UMT_XSW_MODE)));
seq_printf(s, "UMT Dst: \t0x%x\n",
ltq_mcpy_r32(MCPY_UMT_X_ADDR(pid, MCPY_UMT_XDEST)));
seq_printf(s, "UMT Period: \t0x%x\n",
ltq_mcpy_r32(MCPY_UMT_X_ADDR(pid, MCPY_UMT_XPERIOD)));
seq_printf(s, "UMT MSG0: \t0x%x\n",
ltq_mcpy_r32(MCPY_UMT_X_ADDR(pid, MCPY_UMT_XMSG(0))));
seq_printf(s, "UMT MSG1: \t0x%x\n",
ltq_mcpy_r32(MCPY_UMT_X_ADDR(pid, MCPY_UMT_XMSG(1))));
}
val = ltq_mcpy_r32(MCPY_UMT_TRG_MUX);
seq_printf(s, "DMA CID: \t%d\n",
(val & ((0xF) << (pid * 4))) >> (pid * 4));
seq_printf(s, "CBM PID: \t%d\n",
(val & ((0xF) << (16 + pid * 4))) >> (16 + pid * 4));
return 0;
}
static const struct seq_operations umt_port_seq_ops = {
.start = umt_port_seq_start,
.next = umt_port_seq_next,
.stop = umt_port_seq_stop,
.show = umt_port_seq_show,
};
static int umt_cfg_read_proc_open(struct inode *inode, struct file *file)
{
int ret = seq_open(file, &umt_port_seq_ops);
if (ret == 0) {
struct seq_file *m = file->private_data;
m->private = PDE_DATA(inode);
}
return ret;
}
static const struct file_operations mcpy_umt_proc_fops = {
.open = umt_cfg_read_proc_open,
.read = seq_read,
.llseek = seq_lseek,
.release = seq_release,
};
static int umt_proc_init(struct mcpy_umt *pumt)
{
struct proc_dir_entry *entry;
pumt->proc = proc_mkdir("umt", pumt->ctrl->proc);
if (!pumt->proc)
return -ENOMEM;
entry = proc_create_data("umt_info", 0x0, pumt->proc,
&mcpy_umt_proc_fops, pumt);
if (!entry)
goto err1;
return 0;
err1:
remove_proc_entry("umt", pumt->ctrl->proc);
mcpy_dbg(MCPY_ERR, "UMT proc create fail!\n");
return -1;
}
/* TODO: Register UMT error interrupt Handler */
int umt_init(struct mcpy_ctrl *pctrl)
{
struct device_node *node = pctrl->dev->of_node;
struct mcpy_umt *pumt;
int i;
pumt = &pctrl->umt;
pumt->ctrl = pctrl;
pumt->dma_ctrlid = g_dma_ctrl;
pumt->clk = of_clk_get_by_name(node, "freq");
if (IS_ERR_VALUE(pumt->clk)) {
pr_err("the clock for umt is missing, error no %d.\n",
(int)pumt->clk);
pumt->clk = NULL;
}
spin_lock_init(&pumt->umt_lock);
umt_en_expand_mode();
for (i = 0; i < UMT_PORTS_NUM; i++)
umt_port_init(pumt, node, i);
umt_proc_init(pumt);
pumt->status = UMT_ENABLE;
mcpy_dbg(MCPY_INFO, "UMT initialize success on processor: %d !\n",
smp_processor_id());
return 0;
}
......@@ -275,6 +275,8 @@ extern int gic_get_c0_fdc_int(void);
extern int gic_get_usm_range(struct resource *gic_usm_res);
extern unsigned long gic_read_reg(unsigned int reg);
extern void gic_write_reg(unsigned int reg, unsigned long val);
extern int gic_yield_setup(unsigned int cpu,
unsigned int pin, unsigned int irq);
#else /* CONFIG_MIPS_GIC */
......
/*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License version 2 as published
* by the Free Software Foundation.
*
* Copyright (C) 2015 Zhu YiXin<yixin.zhu@lantiq.com>
* Copyright (C) 2016 Intel Corporation.
*/
#ifndef __LTQ_HWMCPY_H__
#define __LTQ_HWMCPY_H__
enum {
HWMCPY_F_PRIO_LOW = 0x0,
HWMCPY_F_PRIO_HIGH = 0x1,
HWMCPY_F_IPC = 0x2,
HWMCPY_F_CHKSZ_1 = 0x4,
HWMCPY_F_CHKSZ_2 = 0x8,
HWMCPY_F_CHKSZ_3 = 0xC,
HWMCPY_F_CHKSZ_4 = 0x10,
/*
* Trunk size support from 0 to 0x7(512B,1KB to 64KB),
* start from bit 2, len 3 bits, max 0x1C
*/
HWMCPY_F_RESERVED = 0x20,
HWMCPY_F_CHKSZ_SET = 0x40,
HWMCPY_F_LAST = 0x80,
};
enum mcpy_type {
MCPY_PHY_TO_PHY = 0,
MCPY_PHY_TO_IOCU,
MCPY_IOCU_TO_PHY,
MCPY_IOCU_TO_IOCU,
MCPY_SW_CPY,
};
struct mcpy_frag {
void *ptr;
u16 offset;
u16 size;
};
enum umt_mode {
UMT_SELFCNT_MODE = 0,
UMT_USER_MODE = 1,
UMT_MODE_MAX,
};
enum umt_msg_mode {
UMT_NO_MSG = 0,
UMT_MSG0_ONLY = 1,
UMT_MSG1_ONLY = 2,
UMT_MSG0_MSG1 = 3,
UMT_MSG_MAX,
};
enum umt_status {
UMT_DISABLE = 0,
UMT_ENABLE = 1,
UMT_STATUS_MAX,
UMT_BROKEN,
};
extern void *ltq_hwmemcpy(void *dst, const void *src, u32 len,
u32 portid, enum mcpy_type mode, u32 flags);
extern int ltq_hwmcpy_sg(void *dst, const struct mcpy_frag *src, u32 frag_num,
u32 portid, enum mcpy_type mode, u32 flags);
extern int ltq_mcpy_reserve(void);
extern void ltq_mcpy_release(u32 pid);
int ltq_umt_set_period(u32 umt_id, u32 ep_id, u32 period);
int ltq_umt_set_mode(u32 umt_id, u32 ep_id, u32 umt_mode, u32 msg_mode,
u32 phy_dst, u32 period, u32 enable);
int ltq_umt_enable(u32 umt_id, u32 ep_id, u32 enable);
int ltq_umt_request(u32 ep_id, u32 cbm_pid,
u32 *dma_ctrlid, u32 *dma_cid, u32 *umt_id);
int ltq_umt_release(u32 umt_id, u32 ep_id);
int ltq_umt_suspend(u32 umt_id, u32 ep_id, u32 enable);
#endif /* __LTQ_HWMCPY_H__ */
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment