soc/fsl: Introduce DPAA 1.x QMan device driver

This driver enables the Freescale DPAA 1.x Queue Manager block.
QMan is a hardware accelerator that manages frame queues.  It allows
CPUs and other accelerators connected to the SoC datapath to enqueue
and dequeue ethernet frames, thus providing the infrastructure for
data exchange among CPUs and datapath accelerators.

Signed-off-by: Roy Pledge <roy.pledge@nxp.com>
Signed-off-by: Claudiu Manoil <claudiu.manoil@nxp.com>
Signed-off-by: Scott Wood <oss@buserror.net>
This commit is contained in:
Claudiu Manoil 2016-09-22 18:04:09 +03:00 committed by Scott Wood
parent 1f9c0a7727
commit c535e923bb
6 changed files with 5492 additions and 2 deletions

View File

@ -1,2 +1,3 @@
obj-$(CONFIG_FSL_DPAA) += bman_ccsr.o bman_portal.o \
bman.o
obj-$(CONFIG_FSL_DPAA) += bman_ccsr.o qman_ccsr.o \
bman_portal.o qman_portal.o \
bman.o qman.o

2881
drivers/soc/fsl/qbman/qman.c Normal file

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,808 @@
/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Freescale Semiconductor nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation, either version 2 of that License or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "qman_priv.h"
u16 qman_ip_rev;
EXPORT_SYMBOL(qman_ip_rev);
u16 qm_channel_pool1 = QMAN_CHANNEL_POOL1;
EXPORT_SYMBOL(qm_channel_pool1);
/* Register offsets */
#define REG_QCSP_LIO_CFG(n) (0x0000 + ((n) * 0x10))
#define REG_QCSP_IO_CFG(n) (0x0004 + ((n) * 0x10))
#define REG_QCSP_DD_CFG(n) (0x000c + ((n) * 0x10))
#define REG_DD_CFG 0x0200
#define REG_DCP_CFG(n) (0x0300 + ((n) * 0x10))
#define REG_DCP_DD_CFG(n) (0x0304 + ((n) * 0x10))
#define REG_DCP_DLM_AVG(n) (0x030c + ((n) * 0x10))
#define REG_PFDR_FPC 0x0400
#define REG_PFDR_FP_HEAD 0x0404
#define REG_PFDR_FP_TAIL 0x0408
#define REG_PFDR_FP_LWIT 0x0410
#define REG_PFDR_CFG 0x0414
#define REG_SFDR_CFG 0x0500
#define REG_SFDR_IN_USE 0x0504
#define REG_WQ_CS_CFG(n) (0x0600 + ((n) * 0x04))
#define REG_WQ_DEF_ENC_WQID 0x0630
#define REG_WQ_SC_DD_CFG(n) (0x640 + ((n) * 0x04))
#define REG_WQ_PC_DD_CFG(n) (0x680 + ((n) * 0x04))
#define REG_WQ_DC0_DD_CFG(n) (0x6c0 + ((n) * 0x04))
#define REG_WQ_DC1_DD_CFG(n) (0x700 + ((n) * 0x04))
#define REG_WQ_DCn_DD_CFG(n) (0x6c0 + ((n) * 0x40)) /* n=2,3 */
#define REG_CM_CFG 0x0800
#define REG_ECSR 0x0a00
#define REG_ECIR 0x0a04
#define REG_EADR 0x0a08
#define REG_ECIR2 0x0a0c
#define REG_EDATA(n) (0x0a10 + ((n) * 0x04))
#define REG_SBEC(n) (0x0a80 + ((n) * 0x04))
#define REG_MCR 0x0b00
#define REG_MCP(n) (0x0b04 + ((n) * 0x04))
#define REG_MISC_CFG 0x0be0
#define REG_HID_CFG 0x0bf0
#define REG_IDLE_STAT 0x0bf4
#define REG_IP_REV_1 0x0bf8
#define REG_IP_REV_2 0x0bfc
#define REG_FQD_BARE 0x0c00
#define REG_PFDR_BARE 0x0c20
#define REG_offset_BAR 0x0004 /* relative to REG_[FQD|PFDR]_BARE */
#define REG_offset_AR 0x0010 /* relative to REG_[FQD|PFDR]_BARE */
#define REG_QCSP_BARE 0x0c80
#define REG_QCSP_BAR 0x0c84
#define REG_CI_SCHED_CFG 0x0d00
#define REG_SRCIDR 0x0d04
#define REG_LIODNR 0x0d08
#define REG_CI_RLM_AVG 0x0d14
#define REG_ERR_ISR 0x0e00
#define REG_ERR_IER 0x0e04
#define REG_REV3_QCSP_LIO_CFG(n) (0x1000 + ((n) * 0x10))
#define REG_REV3_QCSP_IO_CFG(n) (0x1004 + ((n) * 0x10))
#define REG_REV3_QCSP_DD_CFG(n) (0x100c + ((n) * 0x10))
/* Assists for QMAN_MCR */
#define MCR_INIT_PFDR 0x01000000
#define MCR_get_rslt(v) (u8)((v) >> 24)
#define MCR_rslt_idle(r) (!(r) || ((r) >= 0xf0))
#define MCR_rslt_ok(r) ((r) == 0xf0)
#define MCR_rslt_eaccess(r) ((r) == 0xf8)
#define MCR_rslt_inval(r) ((r) == 0xff)
/*
* Corenet initiator settings. Stash request queues are 4-deep to match cores
* ability to snarf. Stash priority is 3, other priorities are 2.
*/
#define QM_CI_SCHED_CFG_SRCCIV 4
#define QM_CI_SCHED_CFG_SRQ_W 3
#define QM_CI_SCHED_CFG_RW_W 2
#define QM_CI_SCHED_CFG_BMAN_W 2
/* write SRCCIV enable */
#define QM_CI_SCHED_CFG_SRCCIV_EN BIT(31)
/* Follows WQ_CS_CFG0-5 */
enum qm_wq_class {
qm_wq_portal = 0,
qm_wq_pool = 1,
qm_wq_fman0 = 2,
qm_wq_fman1 = 3,
qm_wq_caam = 4,
qm_wq_pme = 5,
qm_wq_first = qm_wq_portal,
qm_wq_last = qm_wq_pme
};
/* Follows FQD_[BARE|BAR|AR] and PFDR_[BARE|BAR|AR] */
enum qm_memory {
qm_memory_fqd,
qm_memory_pfdr
};
/* Used by all error interrupt registers except 'inhibit' */
#define QM_EIRQ_CIDE 0x20000000 /* Corenet Initiator Data Error */
#define QM_EIRQ_CTDE 0x10000000 /* Corenet Target Data Error */
#define QM_EIRQ_CITT 0x08000000 /* Corenet Invalid Target Transaction */
#define QM_EIRQ_PLWI 0x04000000 /* PFDR Low Watermark */
#define QM_EIRQ_MBEI 0x02000000 /* Multi-bit ECC Error */
#define QM_EIRQ_SBEI 0x01000000 /* Single-bit ECC Error */
#define QM_EIRQ_PEBI 0x00800000 /* PFDR Enqueues Blocked Interrupt */
#define QM_EIRQ_IFSI 0x00020000 /* Invalid FQ Flow Control State */
#define QM_EIRQ_ICVI 0x00010000 /* Invalid Command Verb */
#define QM_EIRQ_IDDI 0x00000800 /* Invalid Dequeue (Direct-connect) */
#define QM_EIRQ_IDFI 0x00000400 /* Invalid Dequeue FQ */
#define QM_EIRQ_IDSI 0x00000200 /* Invalid Dequeue Source */
#define QM_EIRQ_IDQI 0x00000100 /* Invalid Dequeue Queue */
#define QM_EIRQ_IECE 0x00000010 /* Invalid Enqueue Configuration */
#define QM_EIRQ_IEOI 0x00000008 /* Invalid Enqueue Overflow */
#define QM_EIRQ_IESI 0x00000004 /* Invalid Enqueue State */
#define QM_EIRQ_IECI 0x00000002 /* Invalid Enqueue Channel */
#define QM_EIRQ_IEQI 0x00000001 /* Invalid Enqueue Queue */
/* QMAN_ECIR valid error bit */
#define PORTAL_ECSR_ERR (QM_EIRQ_IEQI | QM_EIRQ_IESI | QM_EIRQ_IEOI | \
QM_EIRQ_IDQI | QM_EIRQ_IDSI | QM_EIRQ_IDFI | \
QM_EIRQ_IDDI | QM_EIRQ_ICVI | QM_EIRQ_IFSI)
#define FQID_ECSR_ERR (QM_EIRQ_IEQI | QM_EIRQ_IECI | QM_EIRQ_IESI | \
QM_EIRQ_IEOI | QM_EIRQ_IDQI | QM_EIRQ_IDFI | \
QM_EIRQ_IFSI)
struct qm_ecir {
u32 info; /* res[30-31], ptyp[29], pnum[24-28], fqid[0-23] */
};
static bool qm_ecir_is_dcp(const struct qm_ecir *p)
{
return p->info & BIT(29);
}
static int qm_ecir_get_pnum(const struct qm_ecir *p)
{
return (p->info >> 24) & 0x1f;
}
static int qm_ecir_get_fqid(const struct qm_ecir *p)
{
return p->info & (BIT(24) - 1);
}
struct qm_ecir2 {
u32 info; /* ptyp[31], res[10-30], pnum[0-9] */
};
static bool qm_ecir2_is_dcp(const struct qm_ecir2 *p)
{
return p->info & BIT(31);
}
static int qm_ecir2_get_pnum(const struct qm_ecir2 *p)
{
return p->info & (BIT(10) - 1);
}
struct qm_eadr {
u32 info; /* memid[24-27], eadr[0-11] */
/* v3: memid[24-28], eadr[0-15] */
};
static int qm_eadr_get_memid(const struct qm_eadr *p)
{
return (p->info >> 24) & 0xf;
}
static int qm_eadr_get_eadr(const struct qm_eadr *p)
{
return p->info & (BIT(12) - 1);
}
static int qm_eadr_v3_get_memid(const struct qm_eadr *p)
{
return (p->info >> 24) & 0x1f;
}
static int qm_eadr_v3_get_eadr(const struct qm_eadr *p)
{
return p->info & (BIT(16) - 1);
}
struct qman_hwerr_txt {
u32 mask;
const char *txt;
};
static const struct qman_hwerr_txt qman_hwerr_txts[] = {
{ QM_EIRQ_CIDE, "Corenet Initiator Data Error" },
{ QM_EIRQ_CTDE, "Corenet Target Data Error" },
{ QM_EIRQ_CITT, "Corenet Invalid Target Transaction" },
{ QM_EIRQ_PLWI, "PFDR Low Watermark" },
{ QM_EIRQ_MBEI, "Multi-bit ECC Error" },
{ QM_EIRQ_SBEI, "Single-bit ECC Error" },
{ QM_EIRQ_PEBI, "PFDR Enqueues Blocked Interrupt" },
{ QM_EIRQ_ICVI, "Invalid Command Verb" },
{ QM_EIRQ_IFSI, "Invalid Flow Control State" },
{ QM_EIRQ_IDDI, "Invalid Dequeue (Direct-connect)" },
{ QM_EIRQ_IDFI, "Invalid Dequeue FQ" },
{ QM_EIRQ_IDSI, "Invalid Dequeue Source" },
{ QM_EIRQ_IDQI, "Invalid Dequeue Queue" },
{ QM_EIRQ_IECE, "Invalid Enqueue Configuration" },
{ QM_EIRQ_IEOI, "Invalid Enqueue Overflow" },
{ QM_EIRQ_IESI, "Invalid Enqueue State" },
{ QM_EIRQ_IECI, "Invalid Enqueue Channel" },
{ QM_EIRQ_IEQI, "Invalid Enqueue Queue" },
};
struct qman_error_info_mdata {
u16 addr_mask;
u16 bits;
const char *txt;
};
static const struct qman_error_info_mdata error_mdata[] = {
{ 0x01FF, 24, "FQD cache tag memory 0" },
{ 0x01FF, 24, "FQD cache tag memory 1" },
{ 0x01FF, 24, "FQD cache tag memory 2" },
{ 0x01FF, 24, "FQD cache tag memory 3" },
{ 0x0FFF, 512, "FQD cache memory" },
{ 0x07FF, 128, "SFDR memory" },
{ 0x01FF, 72, "WQ context memory" },
{ 0x00FF, 240, "CGR memory" },
{ 0x00FF, 302, "Internal Order Restoration List memory" },
{ 0x01FF, 256, "SW portal ring memory" },
};
#define QMAN_ERRS_TO_DISABLE (QM_EIRQ_PLWI | QM_EIRQ_PEBI)
/*
* TODO: unimplemented registers
*
* Keeping a list here of QMan registers I have not yet covered;
* QCSP_DD_IHRSR, QCSP_DD_IHRFR, QCSP_DD_HASR,
* DCP_DD_IHRSR, DCP_DD_IHRFR, DCP_DD_HASR, CM_CFG,
* QMAN_EECC, QMAN_SBET, QMAN_EINJ, QMAN_SBEC0-12
*/
/* Pointer to the start of the QMan's CCSR space */
static u32 __iomem *qm_ccsr_start;
/* A SDQCR mask comprising all the available/visible pool channels */
static u32 qm_pools_sdqcr;
static inline u32 qm_ccsr_in(u32 offset)
{
return ioread32be(qm_ccsr_start + offset/4);
}
static inline void qm_ccsr_out(u32 offset, u32 val)
{
iowrite32be(val, qm_ccsr_start + offset/4);
}
u32 qm_get_pools_sdqcr(void)
{
return qm_pools_sdqcr;
}
enum qm_dc_portal {
qm_dc_portal_fman0 = 0,
qm_dc_portal_fman1 = 1
};
static void qm_set_dc(enum qm_dc_portal portal, int ed, u8 sernd)
{
DPAA_ASSERT(!ed || portal == qm_dc_portal_fman0 ||
portal == qm_dc_portal_fman1);
if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
qm_ccsr_out(REG_DCP_CFG(portal),
(ed ? 0x1000 : 0) | (sernd & 0x3ff));
else
qm_ccsr_out(REG_DCP_CFG(portal),
(ed ? 0x100 : 0) | (sernd & 0x1f));
}
static void qm_set_wq_scheduling(enum qm_wq_class wq_class,
u8 cs_elev, u8 csw2, u8 csw3, u8 csw4,
u8 csw5, u8 csw6, u8 csw7)
{
qm_ccsr_out(REG_WQ_CS_CFG(wq_class), ((cs_elev & 0xff) << 24) |
((csw2 & 0x7) << 20) | ((csw3 & 0x7) << 16) |
((csw4 & 0x7) << 12) | ((csw5 & 0x7) << 8) |
((csw6 & 0x7) << 4) | (csw7 & 0x7));
}
static void qm_set_hid(void)
{
qm_ccsr_out(REG_HID_CFG, 0);
}
static void qm_set_corenet_initiator(void)
{
qm_ccsr_out(REG_CI_SCHED_CFG, QM_CI_SCHED_CFG_SRCCIV_EN |
(QM_CI_SCHED_CFG_SRCCIV << 24) |
(QM_CI_SCHED_CFG_SRQ_W << 8) |
(QM_CI_SCHED_CFG_RW_W << 4) |
QM_CI_SCHED_CFG_BMAN_W);
}
static void qm_get_version(u16 *id, u8 *major, u8 *minor)
{
u32 v = qm_ccsr_in(REG_IP_REV_1);
*id = (v >> 16);
*major = (v >> 8) & 0xff;
*minor = v & 0xff;
}
#define PFDR_AR_EN BIT(31)
static void qm_set_memory(enum qm_memory memory, u64 ba, u32 size)
{
u32 offset = (memory == qm_memory_fqd) ? REG_FQD_BARE : REG_PFDR_BARE;
u32 exp = ilog2(size);
/* choke if size isn't within range */
DPAA_ASSERT((size >= 4096) && (size <= 1024*1024*1024) &&
is_power_of_2(size));
/* choke if 'ba' has lower-alignment than 'size' */
DPAA_ASSERT(!(ba & (size - 1)));
qm_ccsr_out(offset, upper_32_bits(ba));
qm_ccsr_out(offset + REG_offset_BAR, lower_32_bits(ba));
qm_ccsr_out(offset + REG_offset_AR, PFDR_AR_EN | (exp - 1));
}
static void qm_set_pfdr_threshold(u32 th, u8 k)
{
qm_ccsr_out(REG_PFDR_FP_LWIT, th & 0xffffff);
qm_ccsr_out(REG_PFDR_CFG, k);
}
static void qm_set_sfdr_threshold(u16 th)
{
qm_ccsr_out(REG_SFDR_CFG, th & 0x3ff);
}
static int qm_init_pfdr(struct device *dev, u32 pfdr_start, u32 num)
{
u8 rslt = MCR_get_rslt(qm_ccsr_in(REG_MCR));
DPAA_ASSERT(pfdr_start && !(pfdr_start & 7) && !(num & 7) && num);
/* Make sure the command interface is 'idle' */
if (!MCR_rslt_idle(rslt)) {
dev_crit(dev, "QMAN_MCR isn't idle");
WARN_ON(1);
}
/* Write the MCR command params then the verb */
qm_ccsr_out(REG_MCP(0), pfdr_start);
/*
* TODO: remove this - it's a workaround for a model bug that is
* corrected in more recent versions. We use the workaround until
* everyone has upgraded.
*/
qm_ccsr_out(REG_MCP(1), pfdr_start + num - 16);
dma_wmb();
qm_ccsr_out(REG_MCR, MCR_INIT_PFDR);
/* Poll for the result */
do {
rslt = MCR_get_rslt(qm_ccsr_in(REG_MCR));
} while (!MCR_rslt_idle(rslt));
if (MCR_rslt_ok(rslt))
return 0;
if (MCR_rslt_eaccess(rslt))
return -EACCES;
if (MCR_rslt_inval(rslt))
return -EINVAL;
dev_crit(dev, "Unexpected result from MCR_INIT_PFDR: %02x\n", rslt);
return -ENODEV;
}
/*
* Ideally we would use the DMA API to turn rmem->base into a DMA address
* (especially if iommu translations ever get involved). Unfortunately, the
* DMA API currently does not allow mapping anything that is not backed with
* a struct page.
*/
static dma_addr_t fqd_a, pfdr_a;
static size_t fqd_sz, pfdr_sz;
static int qman_fqd(struct reserved_mem *rmem)
{
fqd_a = rmem->base;
fqd_sz = rmem->size;
WARN_ON(!(fqd_a && fqd_sz));
return 0;
}
RESERVEDMEM_OF_DECLARE(qman_fqd, "fsl,qman-fqd", qman_fqd);
static int qman_pfdr(struct reserved_mem *rmem)
{
pfdr_a = rmem->base;
pfdr_sz = rmem->size;
WARN_ON(!(pfdr_a && pfdr_sz));
return 0;
}
RESERVEDMEM_OF_DECLARE(qman_pfdr, "fsl,qman-pfdr", qman_pfdr);
static unsigned int qm_get_fqid_maxcnt(void)
{
return fqd_sz / 64;
}
/*
* Flush this memory range from data cache so that QMAN originated
* transactions for this memory region could be marked non-coherent.
*/
static int zero_priv_mem(struct device *dev, struct device_node *node,
phys_addr_t addr, size_t sz)
{
/* map as cacheable, non-guarded */
void __iomem *tmpp = ioremap_prot(addr, sz, 0);
memset_io(tmpp, 0, sz);
flush_dcache_range((unsigned long)tmpp,
(unsigned long)tmpp + sz);
iounmap(tmpp);
return 0;
}
static void log_edata_bits(struct device *dev, u32 bit_count)
{
u32 i, j, mask = 0xffffffff;
dev_warn(dev, "ErrInt, EDATA:\n");
i = bit_count / 32;
if (bit_count % 32) {
i++;
mask = ~(mask << bit_count % 32);
}
j = 16 - i;
dev_warn(dev, " 0x%08x\n", qm_ccsr_in(REG_EDATA(j)) & mask);
j++;
for (; j < 16; j++)
dev_warn(dev, " 0x%08x\n", qm_ccsr_in(REG_EDATA(j)));
}
static void log_additional_error_info(struct device *dev, u32 isr_val,
u32 ecsr_val)
{
struct qm_ecir ecir_val;
struct qm_eadr eadr_val;
int memid;
ecir_val.info = qm_ccsr_in(REG_ECIR);
/* Is portal info valid */
if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) {
struct qm_ecir2 ecir2_val;
ecir2_val.info = qm_ccsr_in(REG_ECIR2);
if (ecsr_val & PORTAL_ECSR_ERR) {
dev_warn(dev, "ErrInt: %s id %d\n",
qm_ecir2_is_dcp(&ecir2_val) ? "DCP" : "SWP",
qm_ecir2_get_pnum(&ecir2_val));
}
if (ecsr_val & (FQID_ECSR_ERR | QM_EIRQ_IECE))
dev_warn(dev, "ErrInt: ecir.fqid 0x%x\n",
qm_ecir_get_fqid(&ecir_val));
if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) {
eadr_val.info = qm_ccsr_in(REG_EADR);
memid = qm_eadr_v3_get_memid(&eadr_val);
dev_warn(dev, "ErrInt: EADR Memory: %s, 0x%x\n",
error_mdata[memid].txt,
error_mdata[memid].addr_mask
& qm_eadr_v3_get_eadr(&eadr_val));
log_edata_bits(dev, error_mdata[memid].bits);
}
} else {
if (ecsr_val & PORTAL_ECSR_ERR) {
dev_warn(dev, "ErrInt: %s id %d\n",
qm_ecir_is_dcp(&ecir_val) ? "DCP" : "SWP",
qm_ecir_get_pnum(&ecir_val));
}
if (ecsr_val & FQID_ECSR_ERR)
dev_warn(dev, "ErrInt: ecir.fqid 0x%x\n",
qm_ecir_get_fqid(&ecir_val));
if (ecsr_val & (QM_EIRQ_SBEI|QM_EIRQ_MBEI)) {
eadr_val.info = qm_ccsr_in(REG_EADR);
memid = qm_eadr_get_memid(&eadr_val);
dev_warn(dev, "ErrInt: EADR Memory: %s, 0x%x\n",
error_mdata[memid].txt,
error_mdata[memid].addr_mask
& qm_eadr_get_eadr(&eadr_val));
log_edata_bits(dev, error_mdata[memid].bits);
}
}
}
static irqreturn_t qman_isr(int irq, void *ptr)
{
u32 isr_val, ier_val, ecsr_val, isr_mask, i;
struct device *dev = ptr;
ier_val = qm_ccsr_in(REG_ERR_IER);
isr_val = qm_ccsr_in(REG_ERR_ISR);
ecsr_val = qm_ccsr_in(REG_ECSR);
isr_mask = isr_val & ier_val;
if (!isr_mask)
return IRQ_NONE;
for (i = 0; i < ARRAY_SIZE(qman_hwerr_txts); i++) {
if (qman_hwerr_txts[i].mask & isr_mask) {
dev_err_ratelimited(dev, "ErrInt: %s\n",
qman_hwerr_txts[i].txt);
if (qman_hwerr_txts[i].mask & ecsr_val) {
log_additional_error_info(dev, isr_mask,
ecsr_val);
/* Re-arm error capture registers */
qm_ccsr_out(REG_ECSR, ecsr_val);
}
if (qman_hwerr_txts[i].mask & QMAN_ERRS_TO_DISABLE) {
dev_dbg(dev, "Disabling error 0x%x\n",
qman_hwerr_txts[i].mask);
ier_val &= ~qman_hwerr_txts[i].mask;
qm_ccsr_out(REG_ERR_IER, ier_val);
}
}
}
qm_ccsr_out(REG_ERR_ISR, isr_val);
return IRQ_HANDLED;
}
static int qman_init_ccsr(struct device *dev)
{
int i, err;
/* FQD memory */
qm_set_memory(qm_memory_fqd, fqd_a, fqd_sz);
/* PFDR memory */
qm_set_memory(qm_memory_pfdr, pfdr_a, pfdr_sz);
err = qm_init_pfdr(dev, 8, pfdr_sz / 64 - 8);
if (err)
return err;
/* thresholds */
qm_set_pfdr_threshold(512, 64);
qm_set_sfdr_threshold(128);
/* clear stale PEBI bit from interrupt status register */
qm_ccsr_out(REG_ERR_ISR, QM_EIRQ_PEBI);
/* corenet initiator settings */
qm_set_corenet_initiator();
/* HID settings */
qm_set_hid();
/* Set scheduling weights to defaults */
for (i = qm_wq_first; i <= qm_wq_last; i++)
qm_set_wq_scheduling(i, 0, 0, 0, 0, 0, 0, 0);
/* We are not prepared to accept ERNs for hardware enqueues */
qm_set_dc(qm_dc_portal_fman0, 1, 0);
qm_set_dc(qm_dc_portal_fman1, 1, 0);
return 0;
}
#define LIO_CFG_LIODN_MASK 0x0fff0000
void qman_liodn_fixup(u16 channel)
{
static int done;
static u32 liodn_offset;
u32 before, after;
int idx = channel - QM_CHANNEL_SWPORTAL0;
if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
before = qm_ccsr_in(REG_REV3_QCSP_LIO_CFG(idx));
else
before = qm_ccsr_in(REG_QCSP_LIO_CFG(idx));
if (!done) {
liodn_offset = before & LIO_CFG_LIODN_MASK;
done = 1;
return;
}
after = (before & (~LIO_CFG_LIODN_MASK)) | liodn_offset;
if ((qman_ip_rev & 0xFF00) >= QMAN_REV30)
qm_ccsr_out(REG_REV3_QCSP_LIO_CFG(idx), after);
else
qm_ccsr_out(REG_QCSP_LIO_CFG(idx), after);
}
#define IO_CFG_SDEST_MASK 0x00ff0000
void qman_set_sdest(u16 channel, unsigned int cpu_idx)
{
int idx = channel - QM_CHANNEL_SWPORTAL0;
u32 before, after;
if ((qman_ip_rev & 0xFF00) >= QMAN_REV30) {
before = qm_ccsr_in(REG_REV3_QCSP_IO_CFG(idx));
/* Each pair of vcpu share the same SRQ(SDEST) */
cpu_idx /= 2;
after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16);
qm_ccsr_out(REG_REV3_QCSP_IO_CFG(idx), after);
} else {
before = qm_ccsr_in(REG_QCSP_IO_CFG(idx));
after = (before & (~IO_CFG_SDEST_MASK)) | (cpu_idx << 16);
qm_ccsr_out(REG_QCSP_IO_CFG(idx), after);
}
}
static int qman_resource_init(struct device *dev)
{
int pool_chan_num, cgrid_num;
int ret, i;
switch (qman_ip_rev >> 8) {
case 1:
pool_chan_num = 15;
cgrid_num = 256;
break;
case 2:
pool_chan_num = 3;
cgrid_num = 64;
break;
case 3:
pool_chan_num = 15;
cgrid_num = 256;
break;
default:
return -ENODEV;
}
ret = gen_pool_add(qm_qpalloc, qm_channel_pool1 | DPAA_GENALLOC_OFF,
pool_chan_num, -1);
if (ret) {
dev_err(dev, "Failed to seed pool channels (%d)\n", ret);
return ret;
}
ret = gen_pool_add(qm_cgralloc, DPAA_GENALLOC_OFF, cgrid_num, -1);
if (ret) {
dev_err(dev, "Failed to seed CGRID range (%d)\n", ret);
return ret;
}
/* parse pool channels into the SDQCR mask */
for (i = 0; i < cgrid_num; i++)
qm_pools_sdqcr |= QM_SDQCR_CHANNELS_POOL_CONV(i);
ret = gen_pool_add(qm_fqalloc, QM_FQID_RANGE_START | DPAA_GENALLOC_OFF,
qm_get_fqid_maxcnt() - QM_FQID_RANGE_START, -1);
if (ret) {
dev_err(dev, "Failed to seed FQID range (%d)\n", ret);
return ret;
}
return 0;
}
static int fsl_qman_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct resource *res;
int ret, err_irq;
u16 id;
u8 major, minor;
res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
if (!res) {
dev_err(dev, "Can't get %s property 'IORESOURCE_MEM'\n",
node->full_name);
return -ENXIO;
}
qm_ccsr_start = devm_ioremap(dev, res->start, resource_size(res));
if (!qm_ccsr_start)
return -ENXIO;
qm_get_version(&id, &major, &minor);
if (major == 1 && minor == 0) {
dev_err(dev, "Rev1.0 on P4080 rev1 is not supported!\n");
return -ENODEV;
} else if (major == 1 && minor == 1)
qman_ip_rev = QMAN_REV11;
else if (major == 1 && minor == 2)
qman_ip_rev = QMAN_REV12;
else if (major == 2 && minor == 0)
qman_ip_rev = QMAN_REV20;
else if (major == 3 && minor == 0)
qman_ip_rev = QMAN_REV30;
else if (major == 3 && minor == 1)
qman_ip_rev = QMAN_REV31;
else {
dev_err(dev, "Unknown QMan version\n");
return -ENODEV;
}
if ((qman_ip_rev & 0xff00) >= QMAN_REV30)
qm_channel_pool1 = QMAN_CHANNEL_POOL1_REV3;
ret = zero_priv_mem(dev, node, fqd_a, fqd_sz);
WARN_ON(ret);
if (ret)
return -ENODEV;
ret = qman_init_ccsr(dev);
if (ret) {
dev_err(dev, "CCSR setup failed\n");
return ret;
}
err_irq = platform_get_irq(pdev, 0);
if (err_irq <= 0) {
dev_info(dev, "Can't get %s property 'interrupts'\n",
node->full_name);
return -ENODEV;
}
ret = devm_request_irq(dev, err_irq, qman_isr, IRQF_SHARED, "qman-err",
dev);
if (ret) {
dev_err(dev, "devm_request_irq() failed %d for '%s'\n",
ret, node->full_name);
return ret;
}
/*
* Write-to-clear any stale bits, (eg. starvation being asserted prior
* to resource allocation during driver init).
*/
qm_ccsr_out(REG_ERR_ISR, 0xffffffff);
/* Enable Error Interrupts */
qm_ccsr_out(REG_ERR_IER, 0xffffffff);
qm_fqalloc = devm_gen_pool_create(dev, 0, -1, "qman-fqalloc");
if (IS_ERR(qm_fqalloc)) {
ret = PTR_ERR(qm_fqalloc);
dev_err(dev, "qman-fqalloc pool init failed (%d)\n", ret);
return ret;
}
qm_qpalloc = devm_gen_pool_create(dev, 0, -1, "qman-qpalloc");
if (IS_ERR(qm_qpalloc)) {
ret = PTR_ERR(qm_qpalloc);
dev_err(dev, "qman-qpalloc pool init failed (%d)\n", ret);
return ret;
}
qm_cgralloc = devm_gen_pool_create(dev, 0, -1, "qman-cgralloc");
if (IS_ERR(qm_cgralloc)) {
ret = PTR_ERR(qm_cgralloc);
dev_err(dev, "qman-cgralloc pool init failed (%d)\n", ret);
return ret;
}
ret = qman_resource_init(dev);
if (ret)
return ret;
ret = qman_alloc_fq_table(qm_get_fqid_maxcnt());
if (ret)
return ret;
ret = qman_wq_alloc();
if (ret)
return ret;
return 0;
}
static const struct of_device_id fsl_qman_ids[] = {
{
.compatible = "fsl,qman",
},
{}
};
static struct platform_driver fsl_qman_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = fsl_qman_ids,
.suppress_bind_attrs = true,
},
.probe = fsl_qman_probe,
};
builtin_platform_driver(fsl_qman_driver);

View File

@ -0,0 +1,355 @@
/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Freescale Semiconductor nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation, either version 2 of that License or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#include "qman_priv.h"
/* Enable portal interupts (as opposed to polling mode) */
#define CONFIG_FSL_DPA_PIRQ_SLOW 1
#define CONFIG_FSL_DPA_PIRQ_FAST 1
static struct cpumask portal_cpus;
/* protect qman global registers and global data shared among portals */
static DEFINE_SPINLOCK(qman_lock);
static void portal_set_cpu(struct qm_portal_config *pcfg, int cpu)
{
#ifdef CONFIG_FSL_PAMU
struct device *dev = pcfg->dev;
int window_count = 1;
struct iommu_domain_geometry geom_attr;
struct pamu_stash_attribute stash_attr;
int ret;
pcfg->iommu_domain = iommu_domain_alloc(&platform_bus_type);
if (!pcfg->iommu_domain) {
dev_err(dev, "%s(): iommu_domain_alloc() failed", __func__);
goto no_iommu;
}
geom_attr.aperture_start = 0;
geom_attr.aperture_end =
((dma_addr_t)1 << min(8 * sizeof(dma_addr_t), (size_t)36)) - 1;
geom_attr.force_aperture = true;
ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_GEOMETRY,
&geom_attr);
if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
ret);
goto out_domain_free;
}
ret = iommu_domain_set_attr(pcfg->iommu_domain, DOMAIN_ATTR_WINDOWS,
&window_count);
if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
ret);
goto out_domain_free;
}
stash_attr.cpu = cpu;
stash_attr.cache = PAMU_ATTR_CACHE_L1;
ret = iommu_domain_set_attr(pcfg->iommu_domain,
DOMAIN_ATTR_FSL_PAMU_STASH,
&stash_attr);
if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_set_attr() = %d",
__func__, ret);
goto out_domain_free;
}
ret = iommu_domain_window_enable(pcfg->iommu_domain, 0, 0, 1ULL << 36,
IOMMU_READ | IOMMU_WRITE);
if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_window_enable() = %d",
__func__, ret);
goto out_domain_free;
}
ret = iommu_attach_device(pcfg->iommu_domain, dev);
if (ret < 0) {
dev_err(dev, "%s(): iommu_device_attach() = %d", __func__,
ret);
goto out_domain_free;
}
ret = iommu_domain_set_attr(pcfg->iommu_domain,
DOMAIN_ATTR_FSL_PAMU_ENABLE,
&window_count);
if (ret < 0) {
dev_err(dev, "%s(): iommu_domain_set_attr() = %d", __func__,
ret);
goto out_detach_device;
}
no_iommu:
#endif
qman_set_sdest(pcfg->channel, cpu);
return;
#ifdef CONFIG_FSL_PAMU
out_detach_device:
iommu_detach_device(pcfg->iommu_domain, NULL);
out_domain_free:
iommu_domain_free(pcfg->iommu_domain);
pcfg->iommu_domain = NULL;
#endif
}
static struct qman_portal *init_pcfg(struct qm_portal_config *pcfg)
{
struct qman_portal *p;
u32 irq_sources = 0;
/* We need the same LIODN offset for all portals */
qman_liodn_fixup(pcfg->channel);
pcfg->iommu_domain = NULL;
portal_set_cpu(pcfg, pcfg->cpu);
p = qman_create_affine_portal(pcfg, NULL);
if (!p) {
dev_crit(pcfg->dev, "%s: Portal failure on cpu %d\n",
__func__, pcfg->cpu);
return NULL;
}
/* Determine what should be interrupt-vs-poll driven */
#ifdef CONFIG_FSL_DPA_PIRQ_SLOW
irq_sources |= QM_PIRQ_EQCI | QM_PIRQ_EQRI | QM_PIRQ_MRI |
QM_PIRQ_CSCI;
#endif
#ifdef CONFIG_FSL_DPA_PIRQ_FAST
irq_sources |= QM_PIRQ_DQRI;
#endif
qman_p_irqsource_add(p, irq_sources);
spin_lock(&qman_lock);
if (cpumask_equal(&portal_cpus, cpu_possible_mask)) {
/* all assigned portals are initialized now */
qman_init_cgr_all();
}
spin_unlock(&qman_lock);
dev_info(pcfg->dev, "Portal initialised, cpu %d\n", pcfg->cpu);
return p;
}
static void qman_portal_update_sdest(const struct qm_portal_config *pcfg,
unsigned int cpu)
{
#ifdef CONFIG_FSL_PAMU /* TODO */
struct pamu_stash_attribute stash_attr;
int ret;
if (pcfg->iommu_domain) {
stash_attr.cpu = cpu;
stash_attr.cache = PAMU_ATTR_CACHE_L1;
ret = iommu_domain_set_attr(pcfg->iommu_domain,
DOMAIN_ATTR_FSL_PAMU_STASH, &stash_attr);
if (ret < 0) {
dev_err(pcfg->dev,
"Failed to update pamu stash setting\n");
return;
}
}
#endif
qman_set_sdest(pcfg->channel, cpu);
}
static void qman_offline_cpu(unsigned int cpu)
{
struct qman_portal *p;
const struct qm_portal_config *pcfg;
p = affine_portals[cpu];
if (p) {
pcfg = qman_get_qm_portal_config(p);
if (pcfg) {
irq_set_affinity(pcfg->irq, cpumask_of(0));
qman_portal_update_sdest(pcfg, 0);
}
}
}
static void qman_online_cpu(unsigned int cpu)
{
struct qman_portal *p;
const struct qm_portal_config *pcfg;
p = affine_portals[cpu];
if (p) {
pcfg = qman_get_qm_portal_config(p);
if (pcfg) {
irq_set_affinity(pcfg->irq, cpumask_of(cpu));
qman_portal_update_sdest(pcfg, cpu);
}
}
}
static int qman_hotplug_cpu_callback(struct notifier_block *nfb,
unsigned long action, void *hcpu)
{
unsigned int cpu = (unsigned long)hcpu;
switch (action) {
case CPU_ONLINE:
case CPU_ONLINE_FROZEN:
qman_online_cpu(cpu);
break;
case CPU_DOWN_PREPARE:
case CPU_DOWN_PREPARE_FROZEN:
qman_offline_cpu(cpu);
default:
break;
}
return NOTIFY_OK;
}
static struct notifier_block qman_hotplug_cpu_notifier = {
.notifier_call = qman_hotplug_cpu_callback,
};
static int qman_portal_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct device_node *node = dev->of_node;
struct qm_portal_config *pcfg;
struct resource *addr_phys[2];
const u32 *channel;
void __iomem *va;
int irq, len, cpu;
pcfg = devm_kmalloc(dev, sizeof(*pcfg), GFP_KERNEL);
if (!pcfg)
return -ENOMEM;
pcfg->dev = dev;
addr_phys[0] = platform_get_resource(pdev, IORESOURCE_MEM,
DPAA_PORTAL_CE);
if (!addr_phys[0]) {
dev_err(dev, "Can't get %s property 'reg::CE'\n",
node->full_name);
return -ENXIO;
}
addr_phys[1] = platform_get_resource(pdev, IORESOURCE_MEM,
DPAA_PORTAL_CI);
if (!addr_phys[1]) {
dev_err(dev, "Can't get %s property 'reg::CI'\n",
node->full_name);
return -ENXIO;
}
channel = of_get_property(node, "cell-index", &len);
if (!channel || (len != 4)) {
dev_err(dev, "Can't get %s property 'cell-index'\n",
node->full_name);
return -ENXIO;
}
pcfg->channel = *channel;
pcfg->cpu = -1;
irq = platform_get_irq(pdev, 0);
if (irq <= 0) {
dev_err(dev, "Can't get %s IRQ\n", node->full_name);
return -ENXIO;
}
pcfg->irq = irq;
va = ioremap_prot(addr_phys[0]->start, resource_size(addr_phys[0]), 0);
if (!va)
goto err_ioremap1;
pcfg->addr_virt[DPAA_PORTAL_CE] = va;
va = ioremap_prot(addr_phys[1]->start, resource_size(addr_phys[1]),
_PAGE_GUARDED | _PAGE_NO_CACHE);
if (!va)
goto err_ioremap2;
pcfg->addr_virt[DPAA_PORTAL_CI] = va;
pcfg->pools = qm_get_pools_sdqcr();
spin_lock(&qman_lock);
cpu = cpumask_next_zero(-1, &portal_cpus);
if (cpu >= nr_cpu_ids) {
/* unassigned portal, skip init */
spin_unlock(&qman_lock);
return 0;
}
cpumask_set_cpu(cpu, &portal_cpus);
spin_unlock(&qman_lock);
pcfg->cpu = cpu;
if (!init_pcfg(pcfg))
goto err_ioremap2;
/* clear irq affinity if assigned cpu is offline */
if (!cpu_online(cpu))
qman_offline_cpu(cpu);
return 0;
err_ioremap2:
iounmap(pcfg->addr_virt[DPAA_PORTAL_CE]);
err_ioremap1:
dev_err(dev, "ioremap failed\n");
return -ENXIO;
}
static const struct of_device_id qman_portal_ids[] = {
{
.compatible = "fsl,qman-portal",
},
{}
};
MODULE_DEVICE_TABLE(of, qman_portal_ids);
static struct platform_driver qman_portal_driver = {
.driver = {
.name = KBUILD_MODNAME,
.of_match_table = qman_portal_ids,
},
.probe = qman_portal_probe,
};
static int __init qman_portal_driver_register(struct platform_driver *drv)
{
int ret;
ret = platform_driver_register(drv);
if (ret < 0)
return ret;
register_hotcpu_notifier(&qman_hotplug_cpu_notifier);
return 0;
}
module_driver(qman_portal_driver,
qman_portal_driver_register, platform_driver_unregister);

View File

@ -0,0 +1,371 @@
/* Copyright 2008 - 2016 Freescale Semiconductor, Inc.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions are met:
* * Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* * Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
* * Neither the name of Freescale Semiconductor nor the
* names of its contributors may be used to endorse or promote products
* derived from this software without specific prior written permission.
*
* ALTERNATIVELY, this software may be distributed under the terms of the
* GNU General Public License ("GPL") as published by the Free Software
* Foundation, either version 2 of that License or (at your option) any
* later version.
*
* THIS SOFTWARE IS PROVIDED BY Freescale Semiconductor ``AS IS'' AND ANY
* EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
* WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
* DISCLAIMED. IN NO EVENT SHALL Freescale Semiconductor BE LIABLE FOR ANY
* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
* (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
* LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND
* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
* SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
#include "dpaa_sys.h"
#include <soc/fsl/qman.h>
#include <linux/iommu.h>
#if defined(CONFIG_FSL_PAMU)
#include <asm/fsl_pamu_stash.h>
#endif
struct qm_mcr_querywq {
u8 verb;
u8 result;
u16 channel_wq; /* ignores wq (3 lsbits): _res[0-2] */
u8 __reserved[28];
u32 wq_len[8];
} __packed;
static inline u16 qm_mcr_querywq_get_chan(const struct qm_mcr_querywq *wq)
{
return wq->channel_wq >> 3;
}
struct __qm_mcr_querycongestion {
u32 state[8];
};
/* "Query Congestion Group State" */
struct qm_mcr_querycongestion {
u8 verb;
u8 result;
u8 __reserved[30];
/* Access this struct using qman_cgrs_get() */
struct __qm_mcr_querycongestion state;
} __packed;
/* "Query CGR" */
struct qm_mcr_querycgr {
u8 verb;
u8 result;
u16 __reserved1;
struct __qm_mc_cgr cgr; /* CGR fields */
u8 __reserved2[6];
u8 i_bcnt_hi; /* high 8-bits of 40-bit "Instant" */
u32 i_bcnt_lo; /* low 32-bits of 40-bit */
u8 __reserved3[3];
u8 a_bcnt_hi; /* high 8-bits of 40-bit "Average" */
u32 a_bcnt_lo; /* low 32-bits of 40-bit */
u32 cscn_targ_swp[4];
} __packed;
static inline u64 qm_mcr_querycgr_i_get64(const struct qm_mcr_querycgr *q)
{
return ((u64)q->i_bcnt_hi << 32) | (u64)q->i_bcnt_lo;
}
static inline u64 qm_mcr_querycgr_a_get64(const struct qm_mcr_querycgr *q)
{
return ((u64)q->a_bcnt_hi << 32) | (u64)q->a_bcnt_lo;
}
/* "Query FQ Non-Programmable Fields" */
struct qm_mcc_queryfq_np {
u8 _ncw_verb;
u8 __reserved1[3];
u32 fqid; /* 24-bit */
u8 __reserved2[56];
} __packed;
struct qm_mcr_queryfq_np {
u8 verb;
u8 result;
u8 __reserved1;
u8 state; /* QM_MCR_NP_STATE_*** */
u32 fqd_link; /* 24-bit, _res2[24-31] */
u16 odp_seq; /* 14-bit, _res3[14-15] */
u16 orp_nesn; /* 14-bit, _res4[14-15] */
u16 orp_ea_hseq; /* 15-bit, _res5[15] */
u16 orp_ea_tseq; /* 15-bit, _res6[15] */
u32 orp_ea_hptr; /* 24-bit, _res7[24-31] */
u32 orp_ea_tptr; /* 24-bit, _res8[24-31] */
u32 pfdr_hptr; /* 24-bit, _res9[24-31] */
u32 pfdr_tptr; /* 24-bit, _res10[24-31] */
u8 __reserved2[5];
u8 is; /* 1-bit, _res12[1-7] */
u16 ics_surp;
u32 byte_cnt;
u32 frm_cnt; /* 24-bit, _res13[24-31] */
u32 __reserved3;
u16 ra1_sfdr; /* QM_MCR_NP_RA1_*** */
u16 ra2_sfdr; /* QM_MCR_NP_RA2_*** */
u16 __reserved4;
u16 od1_sfdr; /* QM_MCR_NP_OD1_*** */
u16 od2_sfdr; /* QM_MCR_NP_OD2_*** */
u16 od3_sfdr; /* QM_MCR_NP_OD3_*** */
} __packed;
#define QM_MCR_NP_STATE_FE 0x10
#define QM_MCR_NP_STATE_R 0x08
#define QM_MCR_NP_STATE_MASK 0x07 /* Reads FQD::STATE; */
#define QM_MCR_NP_STATE_OOS 0x00
#define QM_MCR_NP_STATE_RETIRED 0x01
#define QM_MCR_NP_STATE_TEN_SCHED 0x02
#define QM_MCR_NP_STATE_TRU_SCHED 0x03
#define QM_MCR_NP_STATE_PARKED 0x04
#define QM_MCR_NP_STATE_ACTIVE 0x05
#define QM_MCR_NP_PTR_MASK 0x07ff /* for RA[12] & OD[123] */
#define QM_MCR_NP_RA1_NRA(v) (((v) >> 14) & 0x3) /* FQD::NRA */
#define QM_MCR_NP_RA2_IT(v) (((v) >> 14) & 0x1) /* FQD::IT */
#define QM_MCR_NP_OD1_NOD(v) (((v) >> 14) & 0x3) /* FQD::NOD */
#define QM_MCR_NP_OD3_NPC(v) (((v) >> 14) & 0x3) /* FQD::NPC */
enum qm_mcr_queryfq_np_masks {
qm_mcr_fqd_link_mask = BIT(24)-1,
qm_mcr_odp_seq_mask = BIT(14)-1,
qm_mcr_orp_nesn_mask = BIT(14)-1,
qm_mcr_orp_ea_hseq_mask = BIT(15)-1,
qm_mcr_orp_ea_tseq_mask = BIT(15)-1,
qm_mcr_orp_ea_hptr_mask = BIT(24)-1,
qm_mcr_orp_ea_tptr_mask = BIT(24)-1,
qm_mcr_pfdr_hptr_mask = BIT(24)-1,
qm_mcr_pfdr_tptr_mask = BIT(24)-1,
qm_mcr_is_mask = BIT(1)-1,
qm_mcr_frm_cnt_mask = BIT(24)-1,
};
#define qm_mcr_np_get(np, field) \
((np)->field & (qm_mcr_##field##_mask))
/* Congestion Groups */
/*
* This wrapper represents a bit-array for the state of the 256 QMan congestion
* groups. Is also used as a *mask* for congestion groups, eg. so we ignore
* those that don't concern us. We harness the structure and accessor details
* already used in the management command to query congestion groups.
*/
#define CGR_BITS_PER_WORD 5
#define CGR_WORD(x) ((x) >> CGR_BITS_PER_WORD)
#define CGR_BIT(x) (BIT(31) >> ((x) & 0x1f))
#define CGR_NUM (sizeof(struct __qm_mcr_querycongestion) << 3)
struct qman_cgrs {
struct __qm_mcr_querycongestion q;
};
static inline void qman_cgrs_init(struct qman_cgrs *c)
{
memset(c, 0, sizeof(*c));
}
static inline void qman_cgrs_fill(struct qman_cgrs *c)
{
memset(c, 0xff, sizeof(*c));
}
static inline int qman_cgrs_get(struct qman_cgrs *c, u8 cgr)
{
return c->q.state[CGR_WORD(cgr)] & CGR_BIT(cgr);
}
static inline void qman_cgrs_cp(struct qman_cgrs *dest,
const struct qman_cgrs *src)
{
*dest = *src;
}
static inline void qman_cgrs_and(struct qman_cgrs *dest,
const struct qman_cgrs *a, const struct qman_cgrs *b)
{
int ret;
u32 *_d = dest->q.state;
const u32 *_a = a->q.state;
const u32 *_b = b->q.state;
for (ret = 0; ret < 8; ret++)
*_d++ = *_a++ & *_b++;
}
static inline void qman_cgrs_xor(struct qman_cgrs *dest,
const struct qman_cgrs *a, const struct qman_cgrs *b)
{
int ret;
u32 *_d = dest->q.state;
const u32 *_a = a->q.state;
const u32 *_b = b->q.state;
for (ret = 0; ret < 8; ret++)
*_d++ = *_a++ ^ *_b++;
}
void qman_init_cgr_all(void);
struct qm_portal_config {
/*
* Corenet portal addresses;
* [0]==cache-enabled, [1]==cache-inhibited.
*/
void __iomem *addr_virt[2];
struct device *dev;
struct iommu_domain *iommu_domain;
/* Allow these to be joined in lists */
struct list_head list;
/* User-visible portal configuration settings */
/* portal is affined to this cpu */
int cpu;
/* portal interrupt line */
int irq;
/*
* the portal's dedicated channel id, used initialising
* frame queues to target this portal when scheduled
*/
u16 channel;
/*
* mask of pool channels this portal has dequeue access to
* (using QM_SDQCR_CHANNELS_POOL(n) for the bitmask)
*/
u32 pools;
};
/* Revision info (for errata and feature handling) */
#define QMAN_REV11 0x0101
#define QMAN_REV12 0x0102
#define QMAN_REV20 0x0200
#define QMAN_REV30 0x0300
#define QMAN_REV31 0x0301
extern u16 qman_ip_rev; /* 0 if uninitialised, otherwise QMAN_REVx */
#define QM_FQID_RANGE_START 1 /* FQID 0 reserved for internal use */
extern struct gen_pool *qm_fqalloc; /* FQID allocator */
extern struct gen_pool *qm_qpalloc; /* pool-channel allocator */
extern struct gen_pool *qm_cgralloc; /* CGR ID allocator */
u32 qm_get_pools_sdqcr(void);
int qman_wq_alloc(void);
void qman_liodn_fixup(u16 channel);
void qman_set_sdest(u16 channel, unsigned int cpu_idx);
struct qman_portal *qman_create_affine_portal(
const struct qm_portal_config *config,
const struct qman_cgrs *cgrs);
const struct qm_portal_config *qman_destroy_affine_portal(void);
/*
* qman_query_fq - Queries FQD fields (via h/w query command)
* @fq: the frame queue object to be queried
* @fqd: storage for the queried FQD fields
*/
int qman_query_fq(struct qman_fq *fq, struct qm_fqd *fqd);
/*
* For qman_volatile_dequeue(); Choose one PRECEDENCE. EXACT is optional. Use
* NUMFRAMES(n) (6-bit) or NUMFRAMES_TILLEMPTY to fill in the frame-count. Use
* FQID(n) to fill in the frame queue ID.
*/
#define QM_VDQCR_PRECEDENCE_VDQCR 0x0
#define QM_VDQCR_PRECEDENCE_SDQCR 0x80000000
#define QM_VDQCR_EXACT 0x40000000
#define QM_VDQCR_NUMFRAMES_MASK 0x3f000000
#define QM_VDQCR_NUMFRAMES_SET(n) (((n) & 0x3f) << 24)
#define QM_VDQCR_NUMFRAMES_GET(n) (((n) >> 24) & 0x3f)
#define QM_VDQCR_NUMFRAMES_TILLEMPTY QM_VDQCR_NUMFRAMES_SET(0)
#define QMAN_VOLATILE_FLAG_WAIT 0x00000001 /* wait if VDQCR is in use */
#define QMAN_VOLATILE_FLAG_WAIT_INT 0x00000002 /* if wait, interruptible? */
#define QMAN_VOLATILE_FLAG_FINISH 0x00000004 /* wait till VDQCR completes */
/*
* qman_volatile_dequeue - Issue a volatile dequeue command
* @fq: the frame queue object to dequeue from
* @flags: a bit-mask of QMAN_VOLATILE_FLAG_*** options
* @vdqcr: bit mask of QM_VDQCR_*** options, as per qm_dqrr_vdqcr_set()
*
* Attempts to lock access to the portal's VDQCR volatile dequeue functionality.
* The function will block and sleep if QMAN_VOLATILE_FLAG_WAIT is specified and
* the VDQCR is already in use, otherwise returns non-zero for failure. If
* QMAN_VOLATILE_FLAG_FINISH is specified, the function will only return once
* the VDQCR command has finished executing (ie. once the callback for the last
* DQRR entry resulting from the VDQCR command has been called). If not using
* the FINISH flag, completion can be determined either by detecting the
* presence of the QM_DQRR_STAT_UNSCHEDULED and QM_DQRR_STAT_DQCR_EXPIRED bits
* in the "stat" parameter passed to the FQ's dequeue callback, or by waiting
* for the QMAN_FQ_STATE_VDQCR bit to disappear.
*/
int qman_volatile_dequeue(struct qman_fq *fq, u32 flags, u32 vdqcr);
int qman_alloc_fq_table(u32 num_fqids);
/* QMan s/w corenet portal, low-level i/face */
/*
* For qm_dqrr_sdqcr_set(); Choose one SOURCE. Choose one COUNT. Choose one
* dequeue TYPE. Choose TOKEN (8-bit).
* If SOURCE == CHANNELS,
* Choose CHANNELS_DEDICATED and/or CHANNELS_POOL(n).
* You can choose DEDICATED_PRECEDENCE if the portal channel should have
* priority.
* If SOURCE == SPECIFICWQ,
* Either select the work-queue ID with SPECIFICWQ_WQ(), or select the
* channel (SPECIFICWQ_DEDICATED or SPECIFICWQ_POOL()) and specify the
* work-queue priority (0-7) with SPECIFICWQ_WQ() - either way, you get the
* same value.
*/
#define QM_SDQCR_SOURCE_CHANNELS 0x0
#define QM_SDQCR_SOURCE_SPECIFICWQ 0x40000000
#define QM_SDQCR_COUNT_EXACT1 0x0
#define QM_SDQCR_COUNT_UPTO3 0x20000000
#define QM_SDQCR_DEDICATED_PRECEDENCE 0x10000000
#define QM_SDQCR_TYPE_MASK 0x03000000
#define QM_SDQCR_TYPE_NULL 0x0
#define QM_SDQCR_TYPE_PRIO_QOS 0x01000000
#define QM_SDQCR_TYPE_ACTIVE_QOS 0x02000000
#define QM_SDQCR_TYPE_ACTIVE 0x03000000
#define QM_SDQCR_TOKEN_MASK 0x00ff0000
#define QM_SDQCR_TOKEN_SET(v) (((v) & 0xff) << 16)
#define QM_SDQCR_TOKEN_GET(v) (((v) >> 16) & 0xff)
#define QM_SDQCR_CHANNELS_DEDICATED 0x00008000
#define QM_SDQCR_SPECIFICWQ_MASK 0x000000f7
#define QM_SDQCR_SPECIFICWQ_DEDICATED 0x00000000
#define QM_SDQCR_SPECIFICWQ_POOL(n) ((n) << 4)
#define QM_SDQCR_SPECIFICWQ_WQ(n) (n)
/* For qm_dqrr_vdqcr_set(): use FQID(n) to fill in the frame queue ID */
#define QM_VDQCR_FQID_MASK 0x00ffffff
#define QM_VDQCR_FQID(n) ((n) & QM_VDQCR_FQID_MASK)
/*
* Used by all portal interrupt registers except 'inhibit'
* Channels with frame availability
*/
#define QM_PIRQ_DQAVAIL 0x0000ffff
/* The DQAVAIL interrupt fields break down into these bits; */
#define QM_DQAVAIL_PORTAL 0x8000 /* Portal channel */
#define QM_DQAVAIL_POOL(n) (0x8000 >> (n)) /* Pool channel, n==[1..15] */
#define QM_DQAVAIL_MASK 0xffff
/* This mask contains all the "irqsource" bits visible to API users */
#define QM_PIRQ_VISIBLE (QM_PIRQ_SLOW | QM_PIRQ_DQRI)
extern struct qman_portal *affine_portals[NR_CPUS];
const struct qm_portal_config *qman_get_qm_portal_config(
struct qman_portal *portal);

1074
include/soc/fsl/qman.h Normal file

File diff suppressed because it is too large Load Diff