/* SPDX-License-Identifier: BSD-3-Clause * * Copyright (C) 2014-2016 Freescale Semiconductor, Inc. * Copyright 2018 NXP * */ #include "qbman_sys.h" #include "qbman_portal.h" /* QBMan portal management command codes */ #define QBMAN_MC_ACQUIRE 0x30 #define QBMAN_WQCHAN_CONFIGURE 0x46 /* Reverse mapping of QBMAN_CENA_SWP_DQRR() */ #define QBMAN_IDX_FROM_DQRR(p) (((unsigned long)p & 0x1ff) >> 6) /* QBMan FQ management command codes */ #define QBMAN_FQ_SCHEDULE 0x48 #define QBMAN_FQ_FORCE 0x49 #define QBMAN_FQ_XON 0x4d #define QBMAN_FQ_XOFF 0x4e /*******************************/ /* Pre-defined attribute codes */ /*******************************/ #define QBMAN_RESPONSE_VERB_MASK 0x7f /*************************/ /* SDQCR attribute codes */ /*************************/ #define QB_SDQCR_FC_SHIFT 29 #define QB_SDQCR_FC_MASK 0x1 #define QB_SDQCR_DCT_SHIFT 24 #define QB_SDQCR_DCT_MASK 0x3 #define QB_SDQCR_TOK_SHIFT 16 #define QB_SDQCR_TOK_MASK 0xff #define QB_SDQCR_SRC_SHIFT 0 #define QB_SDQCR_SRC_MASK 0xffff /* opaque token for static dequeues */ #define QMAN_SDQCR_TOKEN 0xbb enum qbman_sdqcr_dct { qbman_sdqcr_dct_null = 0, qbman_sdqcr_dct_prio_ics, qbman_sdqcr_dct_active_ics, qbman_sdqcr_dct_active }; enum qbman_sdqcr_fc { qbman_sdqcr_fc_one = 0, qbman_sdqcr_fc_up_to_3 = 1 }; /* We need to keep track of which SWP triggered a pull command * so keep an array of portal IDs and use the token field to * be able to find the proper portal */ #define MAX_QBMAN_PORTALS 64 static struct qbman_swp *portal_idx_map[MAX_QBMAN_PORTALS]; /* Internal Function declaration */ static int qbman_swp_enqueue_array_mode_direct(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd); static int qbman_swp_enqueue_array_mode_mem_back(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd); static int qbman_swp_enqueue_ring_mode_direct(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd); static int qbman_swp_enqueue_ring_mode_mem_back(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd); static int qbman_swp_enqueue_multiple_direct(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, uint32_t *flags, int num_frames); static int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, uint32_t *flags, int num_frames); static int qbman_swp_enqueue_multiple_desc_direct(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, int num_frames); static int qbman_swp_enqueue_multiple_desc_mem_back(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, int num_frames); static int qbman_swp_pull_direct(struct qbman_swp *s, struct qbman_pull_desc *d); static int qbman_swp_pull_mem_back(struct qbman_swp *s, struct qbman_pull_desc *d); const struct qbman_result *qbman_swp_dqrr_next_direct(struct qbman_swp *s); const struct qbman_result *qbman_swp_dqrr_next_mem_back(struct qbman_swp *s); static int qbman_swp_release_direct(struct qbman_swp *s, const struct qbman_release_desc *d, const uint64_t *buffers, unsigned int num_buffers); static int qbman_swp_release_mem_back(struct qbman_swp *s, const struct qbman_release_desc *d, const uint64_t *buffers, unsigned int num_buffers); /* Function pointers */ static int (*qbman_swp_enqueue_array_mode_ptr)(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd) = qbman_swp_enqueue_array_mode_direct; static int (*qbman_swp_enqueue_ring_mode_ptr)(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd) = qbman_swp_enqueue_ring_mode_direct; static int (*qbman_swp_enqueue_multiple_ptr)(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, uint32_t *flags, int num_frames) = qbman_swp_enqueue_multiple_direct; static int (*qbman_swp_enqueue_multiple_desc_ptr)(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, int num_frames) = qbman_swp_enqueue_multiple_desc_direct; static int (*qbman_swp_pull_ptr)(struct qbman_swp *s, struct qbman_pull_desc *d) = qbman_swp_pull_direct; const struct qbman_result *(*qbman_swp_dqrr_next_ptr)(struct qbman_swp *s) = qbman_swp_dqrr_next_direct; static int (*qbman_swp_release_ptr)(struct qbman_swp *s, const struct qbman_release_desc *d, const uint64_t *buffers, unsigned int num_buffers) = qbman_swp_release_direct; /*********************************/ /* Portal constructor/destructor */ /*********************************/ /* Software portals should always be in the power-on state when we initialise, * due to the CCSR-based portal reset functionality that MC has. * * Erk! Turns out that QMan versions prior to 4.1 do not correctly reset DQRR * valid-bits, so we need to support a workaround where we don't trust * valid-bits when detecting new entries until any stale ring entries have been * overwritten at least once. The idea is that we read PI for the first few * entries, then switch to valid-bit after that. The trick is to clear the * bug-work-around boolean once the PI wraps around the ring for the first time. * * Note: this still carries a slight additional cost once the decrementer hits * zero. */ struct qbman_swp *qbman_swp_init(const struct qbman_swp_desc *d) { int ret; uint32_t eqcr_pi; uint32_t mask_size; struct qbman_swp *p = malloc(sizeof(*p)); if (!p) return NULL; memset(p, 0, sizeof(struct qbman_swp)); p->desc = *d; #ifdef QBMAN_CHECKING p->mc.check = swp_mc_can_start; #endif p->mc.valid_bit = QB_VALID_BIT; p->sdq |= qbman_sdqcr_dct_prio_ics << QB_SDQCR_DCT_SHIFT; p->sdq |= qbman_sdqcr_fc_up_to_3 << QB_SDQCR_FC_SHIFT; p->sdq |= QMAN_SDQCR_TOKEN << QB_SDQCR_TOK_SHIFT; if ((d->qman_version & QMAN_REV_MASK) >= QMAN_REV_5000) p->mr.valid_bit = QB_VALID_BIT; atomic_set(&p->vdq.busy, 1); p->vdq.valid_bit = QB_VALID_BIT; p->dqrr.valid_bit = QB_VALID_BIT; qman_version = p->desc.qman_version; if ((qman_version & 0xFFFF0000) < QMAN_REV_4100) { p->dqrr.dqrr_size = 4; p->dqrr.reset_bug = 1; } else { p->dqrr.dqrr_size = 8; p->dqrr.reset_bug = 0; } ret = qbman_swp_sys_init(&p->sys, d, p->dqrr.dqrr_size); if (ret) { free(p); pr_err("qbman_swp_sys_init() failed %d\n", ret); return NULL; } /* Verify that the DQRRPI is 0 - if it is not the portal isn't * in default state which is an error */ if (qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_DQPI) & 0xF) { pr_err("qbman DQRR PI is not zero, portal is not clean\n"); free(p); return NULL; } /* SDQCR needs to be initialized to 0 when no channels are * being dequeued from or else the QMan HW will indicate an * error. The values that were calculated above will be * applied when dequeues from a specific channel are enabled. */ qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_SDQCR, 0); p->eqcr.pi_ring_size = 8; if ((qman_version & 0xFFFF0000) >= QMAN_REV_5000) { p->eqcr.pi_ring_size = 32; qbman_swp_enqueue_array_mode_ptr = qbman_swp_enqueue_array_mode_mem_back; qbman_swp_enqueue_ring_mode_ptr = qbman_swp_enqueue_ring_mode_mem_back; qbman_swp_enqueue_multiple_ptr = qbman_swp_enqueue_multiple_mem_back; qbman_swp_enqueue_multiple_desc_ptr = qbman_swp_enqueue_multiple_desc_mem_back; qbman_swp_pull_ptr = qbman_swp_pull_mem_back; qbman_swp_dqrr_next_ptr = qbman_swp_dqrr_next_mem_back; qbman_swp_release_ptr = qbman_swp_release_mem_back; } for (mask_size = p->eqcr.pi_ring_size; mask_size > 0; mask_size >>= 1) p->eqcr.pi_mask = (p->eqcr.pi_mask<<1) + 1; eqcr_pi = qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_EQCR_PI); p->eqcr.pi = eqcr_pi & p->eqcr.pi_mask; p->eqcr.pi_vb = eqcr_pi & QB_VALID_BIT; if ((p->desc.qman_version & QMAN_REV_MASK) < QMAN_REV_5000) p->eqcr.ci = qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_EQCR_CI) & p->eqcr.pi_mask; else p->eqcr.ci = qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_EQCR_PI) & p->eqcr.pi_mask; p->eqcr.available = p->eqcr.pi_ring_size - qm_cyc_diff(p->eqcr.pi_ring_size, p->eqcr.ci & (p->eqcr.pi_mask<<1), p->eqcr.pi & (p->eqcr.pi_mask<<1)); portal_idx_map[p->desc.idx] = p; return p; } void qbman_swp_finish(struct qbman_swp *p) { #ifdef QBMAN_CHECKING QBMAN_BUG_ON(p->mc.check != swp_mc_can_start); #endif qbman_swp_sys_finish(&p->sys); portal_idx_map[p->desc.idx] = NULL; free(p); } const struct qbman_swp_desc *qbman_swp_get_desc(struct qbman_swp *p) { return &p->desc; } /**************/ /* Interrupts */ /**************/ uint32_t qbman_swp_interrupt_get_vanish(struct qbman_swp *p) { return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_ISDR); } void qbman_swp_interrupt_set_vanish(struct qbman_swp *p, uint32_t mask) { qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_ISDR, mask); } uint32_t qbman_swp_interrupt_read_status(struct qbman_swp *p) { return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_ISR); } void qbman_swp_interrupt_clear_status(struct qbman_swp *p, uint32_t mask) { qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_ISR, mask); } uint32_t qbman_swp_dqrr_thrshld_read_status(struct qbman_swp *p) { return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_DQRR_ITR); } void qbman_swp_dqrr_thrshld_write(struct qbman_swp *p, uint32_t mask) { qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_DQRR_ITR, mask); } uint32_t qbman_swp_intr_timeout_read_status(struct qbman_swp *p) { return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_ITPR); } void qbman_swp_intr_timeout_write(struct qbman_swp *p, uint32_t mask) { qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_ITPR, mask); } uint32_t qbman_swp_interrupt_get_trigger(struct qbman_swp *p) { return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_IER); } void qbman_swp_interrupt_set_trigger(struct qbman_swp *p, uint32_t mask) { qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_IER, mask); } int qbman_swp_interrupt_get_inhibit(struct qbman_swp *p) { return qbman_cinh_read(&p->sys, QBMAN_CINH_SWP_IIR); } void qbman_swp_interrupt_set_inhibit(struct qbman_swp *p, int inhibit) { qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_IIR, inhibit ? 0xffffffff : 0); } /***********************/ /* Management commands */ /***********************/ /* * Internal code common to all types of management commands. */ void *qbman_swp_mc_start(struct qbman_swp *p) { void *ret; #ifdef QBMAN_CHECKING QBMAN_BUG_ON(p->mc.check != swp_mc_can_start); #endif if ((p->desc.qman_version & QMAN_REV_MASK) < QMAN_REV_5000) ret = qbman_cena_write_start(&p->sys, QBMAN_CENA_SWP_CR); else ret = qbman_cena_write_start(&p->sys, QBMAN_CENA_SWP_CR_MEM); #ifdef QBMAN_CHECKING if (!ret) p->mc.check = swp_mc_can_submit; #endif return ret; } void qbman_swp_mc_submit(struct qbman_swp *p, void *cmd, uint8_t cmd_verb) { uint8_t *v = cmd; #ifdef QBMAN_CHECKING QBMAN_BUG_ON(!(p->mc.check != swp_mc_can_submit)); #endif /* TBD: "|=" is going to hurt performance. Need to move as many fields * out of word zero, and for those that remain, the "OR" needs to occur * at the caller side. This debug check helps to catch cases where the * caller wants to OR but has forgotten to do so. */ QBMAN_BUG_ON((*v & cmd_verb) != *v); if ((p->desc.qman_version & QMAN_REV_MASK) < QMAN_REV_5000) { dma_wmb(); *v = cmd_verb | p->mc.valid_bit; qbman_cena_write_complete(&p->sys, QBMAN_CENA_SWP_CR, cmd); clean(cmd); } else { *v = cmd_verb | p->mr.valid_bit; qbman_cena_write_complete(&p->sys, QBMAN_CENA_SWP_CR_MEM, cmd); dma_wmb(); qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_CR_RT, QMAN_RT_MODE); } #ifdef QBMAN_CHECKING p->mc.check = swp_mc_can_poll; #endif } void *qbman_swp_mc_result(struct qbman_swp *p) { uint32_t *ret, verb; #ifdef QBMAN_CHECKING QBMAN_BUG_ON(p->mc.check != swp_mc_can_poll); #endif if ((p->desc.qman_version & QMAN_REV_MASK) < QMAN_REV_5000) { qbman_cena_invalidate_prefetch(&p->sys, QBMAN_CENA_SWP_RR(p->mc.valid_bit)); ret = qbman_cena_read(&p->sys, QBMAN_CENA_SWP_RR(p->mc.valid_bit)); /* Remove the valid-bit - * command completed iff the rest is non-zero */ verb = ret[0] & ~QB_VALID_BIT; if (!verb) return NULL; p->mc.valid_bit ^= QB_VALID_BIT; } else { ret = qbman_cena_read(&p->sys, QBMAN_CENA_SWP_RR_MEM); /* Command completed if the valid bit is toggled */ if (p->mr.valid_bit != (ret[0] & QB_VALID_BIT)) return NULL; /* Remove the valid-bit - * command completed iff the rest is non-zero */ verb = ret[0] & ~QB_VALID_BIT; if (!verb) return NULL; p->mr.valid_bit ^= QB_VALID_BIT; } #ifdef QBMAN_CHECKING p->mc.check = swp_mc_can_start; #endif return ret; } /***********/ /* Enqueue */ /***********/ #define QB_ENQUEUE_CMD_OPTIONS_SHIFT 0 enum qb_enqueue_commands { enqueue_empty = 0, enqueue_response_always = 1, enqueue_rejects_to_fq = 2 }; #define QB_ENQUEUE_CMD_EC_OPTION_MASK 0x3 #define QB_ENQUEUE_CMD_ORP_ENABLE_SHIFT 2 #define QB_ENQUEUE_CMD_IRQ_ON_DISPATCH_SHIFT 3 #define QB_ENQUEUE_CMD_TARGET_TYPE_SHIFT 4 #define QB_ENQUEUE_CMD_DCA_PK_SHIFT 6 #define QB_ENQUEUE_CMD_DCA_EN_SHIFT 7 #define QB_ENQUEUE_CMD_NLIS_SHIFT 14 #define QB_ENQUEUE_CMD_IS_NESN_SHIFT 15 void qbman_eq_desc_clear(struct qbman_eq_desc *d) { memset(d, 0, sizeof(*d)); } void qbman_eq_desc_set_no_orp(struct qbman_eq_desc *d, int respond_success) { d->eq.verb &= ~(1 << QB_ENQUEUE_CMD_ORP_ENABLE_SHIFT); if (respond_success) d->eq.verb |= enqueue_response_always; else d->eq.verb |= enqueue_rejects_to_fq; } void qbman_eq_desc_set_orp(struct qbman_eq_desc *d, int respond_success, uint16_t opr_id, uint16_t seqnum, int incomplete) { d->eq.verb |= 1 << QB_ENQUEUE_CMD_ORP_ENABLE_SHIFT; if (respond_success) d->eq.verb |= enqueue_response_always; else d->eq.verb |= enqueue_rejects_to_fq; d->eq.orpid = opr_id; d->eq.seqnum = seqnum; if (incomplete) d->eq.seqnum |= 1 << QB_ENQUEUE_CMD_NLIS_SHIFT; else d->eq.seqnum &= ~(1 << QB_ENQUEUE_CMD_NLIS_SHIFT); } void qbman_eq_desc_set_orp_hole(struct qbman_eq_desc *d, uint16_t opr_id, uint16_t seqnum) { d->eq.verb |= 1 << QB_ENQUEUE_CMD_ORP_ENABLE_SHIFT; d->eq.verb &= ~QB_ENQUEUE_CMD_EC_OPTION_MASK; d->eq.orpid = opr_id; d->eq.seqnum = seqnum; d->eq.seqnum &= ~(1 << QB_ENQUEUE_CMD_NLIS_SHIFT); d->eq.seqnum &= ~(1 << QB_ENQUEUE_CMD_IS_NESN_SHIFT); } void qbman_eq_desc_set_orp_nesn(struct qbman_eq_desc *d, uint16_t opr_id, uint16_t seqnum) { d->eq.verb |= 1 << QB_ENQUEUE_CMD_ORP_ENABLE_SHIFT; d->eq.verb &= ~QB_ENQUEUE_CMD_EC_OPTION_MASK; d->eq.orpid = opr_id; d->eq.seqnum = seqnum; d->eq.seqnum &= ~(1 << QB_ENQUEUE_CMD_NLIS_SHIFT); d->eq.seqnum |= 1 << QB_ENQUEUE_CMD_IS_NESN_SHIFT; } void qbman_eq_desc_set_response(struct qbman_eq_desc *d, dma_addr_t storage_phys, int stash) { d->eq.rsp_addr = storage_phys; d->eq.wae = stash; } void qbman_eq_desc_set_token(struct qbman_eq_desc *d, uint8_t token) { d->eq.rspid = token; } void qbman_eq_desc_set_fq(struct qbman_eq_desc *d, uint32_t fqid) { d->eq.verb &= ~(1 << QB_ENQUEUE_CMD_TARGET_TYPE_SHIFT); d->eq.tgtid = fqid; } void qbman_eq_desc_set_qd(struct qbman_eq_desc *d, uint32_t qdid, uint16_t qd_bin, uint8_t qd_prio) { d->eq.verb |= 1 << QB_ENQUEUE_CMD_TARGET_TYPE_SHIFT; d->eq.tgtid = qdid; d->eq.qdbin = qd_bin; d->eq.qpri = qd_prio; } void qbman_eq_desc_set_eqdi(struct qbman_eq_desc *d, int enable) { if (enable) d->eq.verb |= 1 << QB_ENQUEUE_CMD_IRQ_ON_DISPATCH_SHIFT; else d->eq.verb &= ~(1 << QB_ENQUEUE_CMD_IRQ_ON_DISPATCH_SHIFT); } void qbman_eq_desc_set_dca(struct qbman_eq_desc *d, int enable, uint8_t dqrr_idx, int park) { if (enable) { d->eq.dca = dqrr_idx; if (park) d->eq.dca |= 1 << QB_ENQUEUE_CMD_DCA_PK_SHIFT; else d->eq.dca &= ~(1 << QB_ENQUEUE_CMD_DCA_PK_SHIFT); d->eq.dca |= 1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT; } else { d->eq.dca &= ~(1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT); } } #define EQAR_IDX(eqar) ((eqar) & 0x1f) #define EQAR_VB(eqar) ((eqar) & 0x80) #define EQAR_SUCCESS(eqar) ((eqar) & 0x100) static inline void qbman_write_eqcr_am_rt_register(struct qbman_swp *p, uint8_t idx) { if (idx < 16) qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_EQCR_AM_RT + idx * 4, QMAN_RT_MODE); else qbman_cinh_write(&p->sys, QBMAN_CINH_SWP_EQCR_AM_RT2 + (idx - 16) * 4, QMAN_RT_MODE); } static int qbman_swp_enqueue_array_mode_direct(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd) { uint32_t *p; const uint32_t *cl = qb_cl(d); uint32_t eqar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_EQAR); pr_debug("EQAR=%08x\n", eqar); if (!EQAR_SUCCESS(eqar)) return -EBUSY; p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar))); memcpy(&p[1], &cl[1], 28); memcpy(&p[8], fd, sizeof(*fd)); /* Set the verb byte, have to substitute in the valid-bit */ dma_wmb(); p[0] = cl[0] | EQAR_VB(eqar); qbman_cena_write_complete_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar))); return 0; } static int qbman_swp_enqueue_array_mode_mem_back(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd) { uint32_t *p; const uint32_t *cl = qb_cl(d); uint32_t eqar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_EQAR); pr_debug("EQAR=%08x\n", eqar); if (!EQAR_SUCCESS(eqar)) return -EBUSY; p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(EQAR_IDX(eqar))); memcpy(&p[1], &cl[1], 28); memcpy(&p[8], fd, sizeof(*fd)); /* Set the verb byte, have to substitute in the valid-bit */ p[0] = cl[0] | EQAR_VB(eqar); dma_wmb(); qbman_write_eqcr_am_rt_register(s, EQAR_IDX(eqar)); return 0; } static inline int qbman_swp_enqueue_array_mode(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd) { return qbman_swp_enqueue_array_mode_ptr(s, d, fd); } static int qbman_swp_enqueue_ring_mode_direct(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd) { uint32_t *p; const uint32_t *cl = qb_cl(d); uint32_t eqcr_ci, full_mask, half_mask; half_mask = (s->eqcr.pi_mask>>1); full_mask = s->eqcr.pi_mask; if (!s->eqcr.available) { eqcr_ci = s->eqcr.ci; s->eqcr.ci = qbman_cena_read_reg(&s->sys, QBMAN_CENA_SWP_EQCR_CI) & full_mask; s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, eqcr_ci, s->eqcr.ci); if (!s->eqcr.available) return -EBUSY; } p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(s->eqcr.pi & half_mask)); memcpy(&p[1], &cl[1], 28); memcpy(&p[8], fd, sizeof(*fd)); lwsync(); /* Set the verb byte, have to substitute in the valid-bit */ p[0] = cl[0] | s->eqcr.pi_vb; qbman_cena_write_complete_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(s->eqcr.pi & half_mask)); s->eqcr.pi++; s->eqcr.pi &= full_mask; s->eqcr.available--; if (!(s->eqcr.pi & half_mask)) s->eqcr.pi_vb ^= QB_VALID_BIT; return 0; } static int qbman_swp_enqueue_ring_mode_mem_back(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd) { uint32_t *p; const uint32_t *cl = qb_cl(d); uint32_t eqcr_ci, full_mask, half_mask; half_mask = (s->eqcr.pi_mask>>1); full_mask = s->eqcr.pi_mask; if (!s->eqcr.available) { eqcr_ci = s->eqcr.ci; s->eqcr.ci = qbman_cinh_read(&s->sys, QBMAN_CENA_SWP_EQCR_CI) & full_mask; s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, eqcr_ci, s->eqcr.ci); if (!s->eqcr.available) return -EBUSY; } p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(s->eqcr.pi & half_mask)); memcpy(&p[1], &cl[1], 28); memcpy(&p[8], fd, sizeof(*fd)); /* Set the verb byte, have to substitute in the valid-bit */ p[0] = cl[0] | s->eqcr.pi_vb; s->eqcr.pi++; s->eqcr.pi &= full_mask; s->eqcr.available--; if (!(s->eqcr.pi & half_mask)) s->eqcr.pi_vb ^= QB_VALID_BIT; dma_wmb(); qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_EQCR_PI, (QB_RT_BIT)|(s->eqcr.pi)|s->eqcr.pi_vb); return 0; } static int qbman_swp_enqueue_ring_mode(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd) { return qbman_swp_enqueue_ring_mode_ptr(s, d, fd); } int qbman_swp_enqueue(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd) { if (s->sys.eqcr_mode == qman_eqcr_vb_array) return qbman_swp_enqueue_array_mode(s, d, fd); else /* Use ring mode by default */ return qbman_swp_enqueue_ring_mode(s, d, fd); } static int qbman_swp_enqueue_multiple_direct(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, uint32_t *flags, int num_frames) { uint32_t *p = NULL; const uint32_t *cl = qb_cl(d); uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask; int i, num_enqueued = 0; uint64_t addr_cena; half_mask = (s->eqcr.pi_mask>>1); full_mask = s->eqcr.pi_mask; if (!s->eqcr.available) { eqcr_ci = s->eqcr.ci; s->eqcr.ci = qbman_cena_read_reg(&s->sys, QBMAN_CENA_SWP_EQCR_CI) & full_mask; s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, eqcr_ci, s->eqcr.ci); if (!s->eqcr.available) return 0; } eqcr_pi = s->eqcr.pi; num_enqueued = (s->eqcr.available < num_frames) ? s->eqcr.available : num_frames; s->eqcr.available -= num_enqueued; /* Fill in the EQCR ring */ for (i = 0; i < num_enqueued; i++) { p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); memcpy(&p[1], &cl[1], 28); memcpy(&p[8], &fd[i], sizeof(*fd)); eqcr_pi++; } lwsync(); /* Set the verb byte, have to substitute in the valid-bit */ eqcr_pi = s->eqcr.pi; for (i = 0; i < num_enqueued; i++) { p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); p[0] = cl[0] | s->eqcr.pi_vb; if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) { struct qbman_eq_desc *d = (struct qbman_eq_desc *)p; d->eq.dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) | ((flags[i]) & QBMAN_EQCR_DCA_IDXMASK); } eqcr_pi++; if (!(eqcr_pi & half_mask)) s->eqcr.pi_vb ^= QB_VALID_BIT; } /* Flush all the cacheline without load/store in between */ eqcr_pi = s->eqcr.pi; addr_cena = (size_t)s->sys.addr_cena; for (i = 0; i < num_enqueued; i++) { dcbf((uintptr_t)(addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask))); eqcr_pi++; } s->eqcr.pi = eqcr_pi & full_mask; return num_enqueued; } static int qbman_swp_enqueue_multiple_mem_back(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, uint32_t *flags, int num_frames) { uint32_t *p = NULL; const uint32_t *cl = qb_cl(d); uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask; int i, num_enqueued = 0; half_mask = (s->eqcr.pi_mask>>1); full_mask = s->eqcr.pi_mask; if (!s->eqcr.available) { eqcr_ci = s->eqcr.ci; s->eqcr.ci = qbman_cinh_read(&s->sys, QBMAN_CENA_SWP_EQCR_CI) & full_mask; s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, eqcr_ci, s->eqcr.ci); if (!s->eqcr.available) return 0; } eqcr_pi = s->eqcr.pi; num_enqueued = (s->eqcr.available < num_frames) ? s->eqcr.available : num_frames; s->eqcr.available -= num_enqueued; /* Fill in the EQCR ring */ for (i = 0; i < num_enqueued; i++) { p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); memcpy(&p[1], &cl[1], 28); memcpy(&p[8], &fd[i], sizeof(*fd)); eqcr_pi++; } /* Set the verb byte, have to substitute in the valid-bit */ eqcr_pi = s->eqcr.pi; for (i = 0; i < num_enqueued; i++) { p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); p[0] = cl[0] | s->eqcr.pi_vb; if (flags && (flags[i] & QBMAN_ENQUEUE_FLAG_DCA)) { struct qbman_eq_desc *d = (struct qbman_eq_desc *)p; d->eq.dca = (1 << QB_ENQUEUE_CMD_DCA_EN_SHIFT) | ((flags[i]) & QBMAN_EQCR_DCA_IDXMASK); } eqcr_pi++; if (!(eqcr_pi & half_mask)) s->eqcr.pi_vb ^= QB_VALID_BIT; } s->eqcr.pi = eqcr_pi & full_mask; dma_wmb(); qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_EQCR_PI, (QB_RT_BIT)|(s->eqcr.pi)|s->eqcr.pi_vb); return num_enqueued; } inline int qbman_swp_enqueue_multiple(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, uint32_t *flags, int num_frames) { return qbman_swp_enqueue_multiple_ptr(s, d, fd, flags, num_frames); } static int qbman_swp_enqueue_multiple_desc_direct(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, int num_frames) { uint32_t *p; const uint32_t *cl; uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask; int i, num_enqueued = 0; uint64_t addr_cena; half_mask = (s->eqcr.pi_mask>>1); full_mask = s->eqcr.pi_mask; if (!s->eqcr.available) { eqcr_ci = s->eqcr.ci; s->eqcr.ci = qbman_cena_read_reg(&s->sys, QBMAN_CENA_SWP_EQCR_CI) & full_mask; s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, eqcr_ci, s->eqcr.ci); if (!s->eqcr.available) return 0; } eqcr_pi = s->eqcr.pi; num_enqueued = (s->eqcr.available < num_frames) ? s->eqcr.available : num_frames; s->eqcr.available -= num_enqueued; /* Fill in the EQCR ring */ for (i = 0; i < num_enqueued; i++) { p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); cl = qb_cl(&d[i]); memcpy(&p[1], &cl[1], 28); memcpy(&p[8], &fd[i], sizeof(*fd)); eqcr_pi++; } lwsync(); /* Set the verb byte, have to substitute in the valid-bit */ eqcr_pi = s->eqcr.pi; for (i = 0; i < num_enqueued; i++) { p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); cl = qb_cl(&d[i]); p[0] = cl[0] | s->eqcr.pi_vb; eqcr_pi++; if (!(eqcr_pi & half_mask)) s->eqcr.pi_vb ^= QB_VALID_BIT; } /* Flush all the cacheline without load/store in between */ eqcr_pi = s->eqcr.pi; addr_cena = (size_t)s->sys.addr_cena; for (i = 0; i < num_enqueued; i++) { dcbf((uintptr_t)(addr_cena + QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask))); eqcr_pi++; } s->eqcr.pi = eqcr_pi & full_mask; return num_enqueued; } static int qbman_swp_enqueue_multiple_desc_mem_back(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, int num_frames) { uint32_t *p; const uint32_t *cl; uint32_t eqcr_ci, eqcr_pi, half_mask, full_mask; int i, num_enqueued = 0; half_mask = (s->eqcr.pi_mask>>1); full_mask = s->eqcr.pi_mask; if (!s->eqcr.available) { eqcr_ci = s->eqcr.ci; s->eqcr.ci = qbman_cinh_read(&s->sys, QBMAN_CENA_SWP_EQCR_CI) & full_mask; s->eqcr.available = qm_cyc_diff(s->eqcr.pi_ring_size, eqcr_ci, s->eqcr.ci); if (!s->eqcr.available) return 0; } eqcr_pi = s->eqcr.pi; num_enqueued = (s->eqcr.available < num_frames) ? s->eqcr.available : num_frames; s->eqcr.available -= num_enqueued; /* Fill in the EQCR ring */ for (i = 0; i < num_enqueued; i++) { p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); cl = qb_cl(&d[i]); memcpy(&p[1], &cl[1], 28); memcpy(&p[8], &fd[i], sizeof(*fd)); eqcr_pi++; } /* Set the verb byte, have to substitute in the valid-bit */ eqcr_pi = s->eqcr.pi; for (i = 0; i < num_enqueued; i++) { p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_EQCR(eqcr_pi & half_mask)); cl = qb_cl(&d[i]); p[0] = cl[0] | s->eqcr.pi_vb; eqcr_pi++; if (!(eqcr_pi & half_mask)) s->eqcr.pi_vb ^= QB_VALID_BIT; } s->eqcr.pi = eqcr_pi & full_mask; dma_wmb(); qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_EQCR_PI, (QB_RT_BIT)|(s->eqcr.pi)|s->eqcr.pi_vb); return num_enqueued; } inline int qbman_swp_enqueue_multiple_desc(struct qbman_swp *s, const struct qbman_eq_desc *d, const struct qbman_fd *fd, int num_frames) { return qbman_swp_enqueue_multiple_desc_ptr(s, d, fd, num_frames); } /*************************/ /* Static (push) dequeue */ /*************************/ void qbman_swp_push_get(struct qbman_swp *s, uint8_t channel_idx, int *enabled) { uint16_t src = (s->sdq >> QB_SDQCR_SRC_SHIFT) & QB_SDQCR_SRC_MASK; QBMAN_BUG_ON(channel_idx > 15); *enabled = src | (1 << channel_idx); } void qbman_swp_push_set(struct qbman_swp *s, uint8_t channel_idx, int enable) { uint16_t dqsrc; QBMAN_BUG_ON(channel_idx > 15); if (enable) s->sdq |= 1 << channel_idx; else s->sdq &= ~(1 << channel_idx); /* Read make the complete src map. If no channels are enabled * the SDQCR must be 0 or else QMan will assert errors */ dqsrc = (s->sdq >> QB_SDQCR_SRC_SHIFT) & QB_SDQCR_SRC_MASK; if (dqsrc != 0) qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_SDQCR, s->sdq); else qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_SDQCR, 0); } /***************************/ /* Volatile (pull) dequeue */ /***************************/ /* These should be const, eventually */ #define QB_VDQCR_VERB_DCT_SHIFT 0 #define QB_VDQCR_VERB_DT_SHIFT 2 #define QB_VDQCR_VERB_RLS_SHIFT 4 #define QB_VDQCR_VERB_WAE_SHIFT 5 #define QB_VDQCR_VERB_RAD_SHIFT 6 enum qb_pull_dt_e { qb_pull_dt_channel, qb_pull_dt_workqueue, qb_pull_dt_framequeue }; void qbman_pull_desc_clear(struct qbman_pull_desc *d) { memset(d, 0, sizeof(*d)); } void qbman_pull_desc_set_storage(struct qbman_pull_desc *d, struct qbman_result *storage, dma_addr_t storage_phys, int stash) { d->pull.rsp_addr_virt = (size_t)storage; if (!storage) { d->pull.verb &= ~(1 << QB_VDQCR_VERB_RLS_SHIFT); return; } d->pull.verb |= 1 << QB_VDQCR_VERB_RLS_SHIFT; if (stash) d->pull.verb |= 1 << QB_VDQCR_VERB_WAE_SHIFT; else d->pull.verb &= ~(1 << QB_VDQCR_VERB_WAE_SHIFT); d->pull.rsp_addr = storage_phys; } void qbman_pull_desc_set_numframes(struct qbman_pull_desc *d, uint8_t numframes) { d->pull.numf = numframes - 1; } void qbman_pull_desc_set_token(struct qbman_pull_desc *d, uint8_t token) { d->pull.tok = token; } void qbman_pull_desc_set_fq(struct qbman_pull_desc *d, uint32_t fqid) { d->pull.verb |= 1 << QB_VDQCR_VERB_DCT_SHIFT; d->pull.verb |= qb_pull_dt_framequeue << QB_VDQCR_VERB_DT_SHIFT; d->pull.dq_src = fqid; } void qbman_pull_desc_set_wq(struct qbman_pull_desc *d, uint32_t wqid, enum qbman_pull_type_e dct) { d->pull.verb |= dct << QB_VDQCR_VERB_DCT_SHIFT; d->pull.verb |= qb_pull_dt_workqueue << QB_VDQCR_VERB_DT_SHIFT; d->pull.dq_src = wqid; } void qbman_pull_desc_set_channel(struct qbman_pull_desc *d, uint32_t chid, enum qbman_pull_type_e dct) { d->pull.verb |= dct << QB_VDQCR_VERB_DCT_SHIFT; d->pull.verb |= qb_pull_dt_channel << QB_VDQCR_VERB_DT_SHIFT; d->pull.dq_src = chid; } void qbman_pull_desc_set_rad(struct qbman_pull_desc *d, int rad) { if (d->pull.verb & (1 << QB_VDQCR_VERB_RLS_SHIFT)) { if (rad) d->pull.verb |= 1 << QB_VDQCR_VERB_RAD_SHIFT; else d->pull.verb &= ~(1 << QB_VDQCR_VERB_RAD_SHIFT); } else { printf("The RAD feature is not valid when RLS = 0\n"); } } static int qbman_swp_pull_direct(struct qbman_swp *s, struct qbman_pull_desc *d) { uint32_t *p; uint32_t *cl = qb_cl(d); if (!atomic_dec_and_test(&s->vdq.busy)) { atomic_inc(&s->vdq.busy); return -EBUSY; } d->pull.tok = s->sys.idx + 1; s->vdq.storage = (void *)(size_t)d->pull.rsp_addr_virt; p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_VDQCR); memcpy(&p[1], &cl[1], 12); /* Set the verb byte, have to substitute in the valid-bit */ lwsync(); p[0] = cl[0] | s->vdq.valid_bit; s->vdq.valid_bit ^= QB_VALID_BIT; qbman_cena_write_complete_wo_shadow(&s->sys, QBMAN_CENA_SWP_VDQCR); return 0; } static int qbman_swp_pull_mem_back(struct qbman_swp *s, struct qbman_pull_desc *d) { uint32_t *p; uint32_t *cl = qb_cl(d); if (!atomic_dec_and_test(&s->vdq.busy)) { atomic_inc(&s->vdq.busy); return -EBUSY; } d->pull.tok = s->sys.idx + 1; s->vdq.storage = (void *)(size_t)d->pull.rsp_addr_virt; p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_VDQCR_MEM); memcpy(&p[1], &cl[1], 12); /* Set the verb byte, have to substitute in the valid-bit */ p[0] = cl[0] | s->vdq.valid_bit; s->vdq.valid_bit ^= QB_VALID_BIT; dma_wmb(); qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_VDQCR_RT, QMAN_RT_MODE); return 0; } inline int qbman_swp_pull(struct qbman_swp *s, struct qbman_pull_desc *d) { return qbman_swp_pull_ptr(s, d); } /****************/ /* Polling DQRR */ /****************/ #define QMAN_DQRR_PI_MASK 0xf #define QBMAN_RESULT_DQ 0x60 #define QBMAN_RESULT_FQRN 0x21 #define QBMAN_RESULT_FQRNI 0x22 #define QBMAN_RESULT_FQPN 0x24 #define QBMAN_RESULT_FQDAN 0x25 #define QBMAN_RESULT_CDAN 0x26 #define QBMAN_RESULT_CSCN_MEM 0x27 #define QBMAN_RESULT_CGCU 0x28 #define QBMAN_RESULT_BPSCN 0x29 #define QBMAN_RESULT_CSCN_WQ 0x2a #include void qbman_swp_prefetch_dqrr_next(struct qbman_swp *s) { const struct qbman_result *p; p = qbman_cena_read_wo_shadow(&s->sys, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); rte_prefetch0(p); } /* NULL return if there are no unconsumed DQRR entries. Returns a DQRR entry * only once, so repeated calls can return a sequence of DQRR entries, without * requiring they be consumed immediately or in any particular order. */ inline const struct qbman_result *qbman_swp_dqrr_next(struct qbman_swp *s) { return qbman_swp_dqrr_next_ptr(s); } const struct qbman_result *qbman_swp_dqrr_next_direct(struct qbman_swp *s) { uint32_t verb; uint32_t response_verb; uint32_t flags; const struct qbman_result *p; /* Before using valid-bit to detect if something is there, we have to * handle the case of the DQRR reset bug... */ if (s->dqrr.reset_bug) { /* We pick up new entries by cache-inhibited producer index, * which means that a non-coherent mapping would require us to * invalidate and read *only* once that PI has indicated that * there's an entry here. The first trip around the DQRR ring * will be much less efficient than all subsequent trips around * it... */ uint8_t pi = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_DQPI) & QMAN_DQRR_PI_MASK; /* there are new entries if pi != next_idx */ if (pi == s->dqrr.next_idx) return NULL; /* if next_idx is/was the last ring index, and 'pi' is * different, we can disable the workaround as all the ring * entries have now been DMA'd to so valid-bit checking is * repaired. Note: this logic needs to be based on next_idx * (which increments one at a time), rather than on pi (which * can burst and wrap-around between our snapshots of it). */ QBMAN_BUG_ON((s->dqrr.dqrr_size - 1) < 0); if (s->dqrr.next_idx == (s->dqrr.dqrr_size - 1u)) { pr_debug("DEBUG: next_idx=%d, pi=%d, clear reset bug\n", s->dqrr.next_idx, pi); s->dqrr.reset_bug = 0; } qbman_cena_invalidate_prefetch(&s->sys, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); } p = qbman_cena_read_wo_shadow(&s->sys, QBMAN_CENA_SWP_DQRR(s->dqrr.next_idx)); verb = p->dq.verb; /* If the valid-bit isn't of the expected polarity, nothing there. Note, * in the DQRR reset bug workaround, we shouldn't need to skip these * check, because we've already determined that a new entry is available * and we've invalidated the cacheline before reading it, so the * valid-bit behaviour is repaired and should tell us what we already * knew from reading PI. */ if ((verb & QB_VALID_BIT) != s->dqrr.valid_bit) return NULL; /* There's something there. Move "next_idx" attention to the next ring * entry (and prefetch it) before returning what we found. */ s->dqrr.next_idx++; if (s->dqrr.next_idx == s->dqrr.dqrr_size) { s->dqrr.next_idx = 0; s->dqrr.valid_bit ^= QB_VALID_BIT; } /* If this is the final response to a volatile dequeue command * indicate that the vdq is no longer busy */ flags = p->dq.stat; response_verb = verb & QBMAN_RESPONSE_VERB_MASK; if ((response_verb == QBMAN_RESULT_DQ) && (flags & QBMAN_DQ_STAT_VOLATILE) && (flags & QBMAN_DQ_STAT_EXPIRED)) atomic_inc(&s->vdq.busy); return p; } const struct qbman_result *qbman_swp_dqrr_next_mem_back(struct qbman_swp *s) { uint32_t verb; uint32_t response_verb; uint32_t flags; const struct qbman_result *p; p = qbman_cena_read_wo_shadow(&s->sys, QBMAN_CENA_SWP_DQRR_MEM(s->dqrr.next_idx)); verb = p->dq.verb; /* If the valid-bit isn't of the expected polarity, nothing there. Note, * in the DQRR reset bug workaround, we shouldn't need to skip these * check, because we've already determined that a new entry is available * and we've invalidated the cacheline before reading it, so the * valid-bit behaviour is repaired and should tell us what we already * knew from reading PI. */ if ((verb & QB_VALID_BIT) != s->dqrr.valid_bit) return NULL; /* There's something there. Move "next_idx" attention to the next ring * entry (and prefetch it) before returning what we found. */ s->dqrr.next_idx++; if (s->dqrr.next_idx == s->dqrr.dqrr_size) { s->dqrr.next_idx = 0; s->dqrr.valid_bit ^= QB_VALID_BIT; } /* If this is the final response to a volatile dequeue command * indicate that the vdq is no longer busy */ flags = p->dq.stat; response_verb = verb & QBMAN_RESPONSE_VERB_MASK; if ((response_verb == QBMAN_RESULT_DQ) && (flags & QBMAN_DQ_STAT_VOLATILE) && (flags & QBMAN_DQ_STAT_EXPIRED)) atomic_inc(&s->vdq.busy); return p; } /* Consume DQRR entries previously returned from qbman_swp_dqrr_next(). */ void qbman_swp_dqrr_consume(struct qbman_swp *s, const struct qbman_result *dq) { qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_DCAP, QBMAN_IDX_FROM_DQRR(dq)); } /* Consume DQRR entries previously returned from qbman_swp_dqrr_next(). */ void qbman_swp_dqrr_idx_consume(struct qbman_swp *s, uint8_t dqrr_index) { qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_DCAP, dqrr_index); } /*********************************/ /* Polling user-provided storage */ /*********************************/ int qbman_result_has_new_result(struct qbman_swp *s, struct qbman_result *dq) { if (dq->dq.tok == 0) return 0; /* * Set token to be 0 so we will detect change back to 1 * next time the looping is traversed. Const is cast away here * as we want users to treat the dequeue responses as read only. */ ((struct qbman_result *)dq)->dq.tok = 0; /* * VDQCR "no longer busy" hook - not quite the same as DQRR, because * the fact "VDQCR" shows busy doesn't mean that we hold the result * that makes it available. Eg. we may be looking at our 10th dequeue * result, having released VDQCR after the 1st result and it is now * busy due to some other command! */ if (s->vdq.storage == dq) { s->vdq.storage = NULL; atomic_inc(&s->vdq.busy); } return 1; } int qbman_check_new_result(struct qbman_result *dq) { if (dq->dq.tok == 0) return 0; /* * Set token to be 0 so we will detect change back to 1 * next time the looping is traversed. Const is cast away here * as we want users to treat the dequeue responses as read only. */ ((struct qbman_result *)dq)->dq.tok = 0; return 1; } int qbman_check_command_complete(struct qbman_result *dq) { struct qbman_swp *s; if (dq->dq.tok == 0) return 0; s = portal_idx_map[dq->dq.tok - 1]; /* * VDQCR "no longer busy" hook - not quite the same as DQRR, because * the fact "VDQCR" shows busy doesn't mean that we hold the result * that makes it available. Eg. we may be looking at our 10th dequeue * result, having released VDQCR after the 1st result and it is now * busy due to some other command! */ if (s->vdq.storage == dq) { s->vdq.storage = NULL; atomic_inc(&s->vdq.busy); } return 1; } /********************************/ /* Categorising qbman results */ /********************************/ static inline int __qbman_result_is_x(const struct qbman_result *dq, uint8_t x) { uint8_t response_verb = dq->dq.verb & QBMAN_RESPONSE_VERB_MASK; return (response_verb == x); } int qbman_result_is_DQ(const struct qbman_result *dq) { return __qbman_result_is_x(dq, QBMAN_RESULT_DQ); } int qbman_result_is_FQDAN(const struct qbman_result *dq) { return __qbman_result_is_x(dq, QBMAN_RESULT_FQDAN); } int qbman_result_is_CDAN(const struct qbman_result *dq) { return __qbman_result_is_x(dq, QBMAN_RESULT_CDAN); } int qbman_result_is_CSCN(const struct qbman_result *dq) { return __qbman_result_is_x(dq, QBMAN_RESULT_CSCN_MEM) || __qbman_result_is_x(dq, QBMAN_RESULT_CSCN_WQ); } int qbman_result_is_BPSCN(const struct qbman_result *dq) { return __qbman_result_is_x(dq, QBMAN_RESULT_BPSCN); } int qbman_result_is_CGCU(const struct qbman_result *dq) { return __qbman_result_is_x(dq, QBMAN_RESULT_CGCU); } int qbman_result_is_FQRN(const struct qbman_result *dq) { return __qbman_result_is_x(dq, QBMAN_RESULT_FQRN); } int qbman_result_is_FQRNI(const struct qbman_result *dq) { return __qbman_result_is_x(dq, QBMAN_RESULT_FQRNI); } int qbman_result_is_FQPN(const struct qbman_result *dq) { return __qbman_result_is_x(dq, QBMAN_RESULT_FQPN); } /*********************************/ /* Parsing frame dequeue results */ /*********************************/ /* These APIs assume qbman_result_is_DQ() is TRUE */ uint8_t qbman_result_DQ_flags(const struct qbman_result *dq) { return dq->dq.stat; } uint16_t qbman_result_DQ_seqnum(const struct qbman_result *dq) { return dq->dq.seqnum; } uint16_t qbman_result_DQ_odpid(const struct qbman_result *dq) { return dq->dq.oprid; } uint32_t qbman_result_DQ_fqid(const struct qbman_result *dq) { return dq->dq.fqid; } uint32_t qbman_result_DQ_byte_count(const struct qbman_result *dq) { return dq->dq.fq_byte_cnt; } uint32_t qbman_result_DQ_frame_count(const struct qbman_result *dq) { return dq->dq.fq_frm_cnt; } uint64_t qbman_result_DQ_fqd_ctx(const struct qbman_result *dq) { return dq->dq.fqd_ctx; } const struct qbman_fd *qbman_result_DQ_fd(const struct qbman_result *dq) { return (const struct qbman_fd *)&dq->dq.fd[0]; } /**************************************/ /* Parsing state-change notifications */ /**************************************/ uint8_t qbman_result_SCN_state(const struct qbman_result *scn) { return scn->scn.state; } uint32_t qbman_result_SCN_rid(const struct qbman_result *scn) { return scn->scn.rid_tok; } uint64_t qbman_result_SCN_ctx(const struct qbman_result *scn) { return scn->scn.ctx; } /*****************/ /* Parsing BPSCN */ /*****************/ uint16_t qbman_result_bpscn_bpid(const struct qbman_result *scn) { return (uint16_t)qbman_result_SCN_rid(scn) & 0x3FFF; } int qbman_result_bpscn_has_free_bufs(const struct qbman_result *scn) { return !(int)(qbman_result_SCN_state(scn) & 0x1); } int qbman_result_bpscn_is_depleted(const struct qbman_result *scn) { return (int)(qbman_result_SCN_state(scn) & 0x2); } int qbman_result_bpscn_is_surplus(const struct qbman_result *scn) { return (int)(qbman_result_SCN_state(scn) & 0x4); } uint64_t qbman_result_bpscn_ctx(const struct qbman_result *scn) { return qbman_result_SCN_ctx(scn); } /*****************/ /* Parsing CGCU */ /*****************/ uint16_t qbman_result_cgcu_cgid(const struct qbman_result *scn) { return (uint16_t)qbman_result_SCN_rid(scn) & 0xFFFF; } uint64_t qbman_result_cgcu_icnt(const struct qbman_result *scn) { return qbman_result_SCN_ctx(scn); } /******************/ /* Buffer release */ /******************/ #define QB_BR_RC_VALID_SHIFT 5 #define QB_BR_RCDI_SHIFT 6 void qbman_release_desc_clear(struct qbman_release_desc *d) { memset(d, 0, sizeof(*d)); d->br.verb = 1 << QB_BR_RC_VALID_SHIFT; } void qbman_release_desc_set_bpid(struct qbman_release_desc *d, uint16_t bpid) { d->br.bpid = bpid; } void qbman_release_desc_set_rcdi(struct qbman_release_desc *d, int enable) { if (enable) d->br.verb |= 1 << QB_BR_RCDI_SHIFT; else d->br.verb &= ~(1 << QB_BR_RCDI_SHIFT); } #define RAR_IDX(rar) ((rar) & 0x7) #define RAR_VB(rar) ((rar) & 0x80) #define RAR_SUCCESS(rar) ((rar) & 0x100) static int qbman_swp_release_direct(struct qbman_swp *s, const struct qbman_release_desc *d, const uint64_t *buffers, unsigned int num_buffers) { uint32_t *p; const uint32_t *cl = qb_cl(d); uint32_t rar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_RAR); pr_debug("RAR=%08x\n", rar); if (!RAR_SUCCESS(rar)) return -EBUSY; QBMAN_BUG_ON(!num_buffers || (num_buffers > 7)); /* Start the release command */ p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_RCR(RAR_IDX(rar))); /* Copy the caller's buffer pointers to the command */ u64_to_le32_copy(&p[2], buffers, num_buffers); /* Set the verb byte, have to substitute in the valid-bit and the * number of buffers. */ lwsync(); p[0] = cl[0] | RAR_VB(rar) | num_buffers; qbman_cena_write_complete_wo_shadow(&s->sys, QBMAN_CENA_SWP_RCR(RAR_IDX(rar))); return 0; } static int qbman_swp_release_mem_back(struct qbman_swp *s, const struct qbman_release_desc *d, const uint64_t *buffers, unsigned int num_buffers) { uint32_t *p; const uint32_t *cl = qb_cl(d); uint32_t rar = qbman_cinh_read(&s->sys, QBMAN_CINH_SWP_RAR); pr_debug("RAR=%08x\n", rar); if (!RAR_SUCCESS(rar)) return -EBUSY; QBMAN_BUG_ON(!num_buffers || (num_buffers > 7)); /* Start the release command */ p = qbman_cena_write_start_wo_shadow(&s->sys, QBMAN_CENA_SWP_RCR_MEM(RAR_IDX(rar))); /* Copy the caller's buffer pointers to the command */ u64_to_le32_copy(&p[2], buffers, num_buffers); /* Set the verb byte, have to substitute in the valid-bit and the * number of buffers. */ p[0] = cl[0] | RAR_VB(rar) | num_buffers; lwsync(); qbman_cinh_write(&s->sys, QBMAN_CINH_SWP_RCR_AM_RT + RAR_IDX(rar) * 4, QMAN_RT_MODE); return 0; } inline int qbman_swp_release(struct qbman_swp *s, const struct qbman_release_desc *d, const uint64_t *buffers, unsigned int num_buffers) { return qbman_swp_release_ptr(s, d, buffers, num_buffers); } /*******************/ /* Buffer acquires */ /*******************/ struct qbman_acquire_desc { uint8_t verb; uint8_t reserved; uint16_t bpid; uint8_t num; uint8_t reserved2[59]; }; struct qbman_acquire_rslt { uint8_t verb; uint8_t rslt; uint16_t reserved; uint8_t num; uint8_t reserved2[3]; uint64_t buf[7]; }; int qbman_swp_acquire(struct qbman_swp *s, uint16_t bpid, uint64_t *buffers, unsigned int num_buffers) { struct qbman_acquire_desc *p; struct qbman_acquire_rslt *r; if (!num_buffers || (num_buffers > 7)) return -EINVAL; /* Start the management command */ p = qbman_swp_mc_start(s); if (!p) return -EBUSY; /* Encode the caller-provided attributes */ p->bpid = bpid; p->num = num_buffers; /* Complete the management command */ r = qbman_swp_mc_complete(s, p, QBMAN_MC_ACQUIRE); if (!r) { pr_err("qbman: acquire from BPID %d failed, no response\n", bpid); return -EIO; } /* Decode the outcome */ QBMAN_BUG_ON((r->verb & QBMAN_RESPONSE_VERB_MASK) != QBMAN_MC_ACQUIRE); /* Determine success or failure */ if (r->rslt != QBMAN_MC_RSLT_OK) { pr_err("Acquire buffers from BPID 0x%x failed, code=0x%02x\n", bpid, r->rslt); return -EIO; } QBMAN_BUG_ON(r->num > num_buffers); /* Copy the acquired buffers to the caller's array */ u64_from_le32_copy(buffers, &r->buf[0], r->num); return (int)r->num; } /*****************/ /* FQ management */ /*****************/ struct qbman_alt_fq_state_desc { uint8_t verb; uint8_t reserved[3]; uint32_t fqid; uint8_t reserved2[56]; }; struct qbman_alt_fq_state_rslt { uint8_t verb; uint8_t rslt; uint8_t reserved[62]; }; #define ALT_FQ_FQID_MASK 0x00FFFFFF static int qbman_swp_alt_fq_state(struct qbman_swp *s, uint32_t fqid, uint8_t alt_fq_verb) { struct qbman_alt_fq_state_desc *p; struct qbman_alt_fq_state_rslt *r; /* Start the management command */ p = qbman_swp_mc_start(s); if (!p) return -EBUSY; p->fqid = fqid & ALT_FQ_FQID_MASK; /* Complete the management command */ r = qbman_swp_mc_complete(s, p, alt_fq_verb); if (!r) { pr_err("qbman: mgmt cmd failed, no response (verb=0x%x)\n", alt_fq_verb); return -EIO; } /* Decode the outcome */ QBMAN_BUG_ON((r->verb & QBMAN_RESPONSE_VERB_MASK) != alt_fq_verb); /* Determine success or failure */ if (r->rslt != QBMAN_MC_RSLT_OK) { pr_err("ALT FQID %d failed: verb = 0x%08x, code = 0x%02x\n", fqid, alt_fq_verb, r->rslt); return -EIO; } return 0; } int qbman_swp_fq_schedule(struct qbman_swp *s, uint32_t fqid) { return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_SCHEDULE); } int qbman_swp_fq_force(struct qbman_swp *s, uint32_t fqid) { return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_FORCE); } int qbman_swp_fq_xon(struct qbman_swp *s, uint32_t fqid) { return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_XON); } int qbman_swp_fq_xoff(struct qbman_swp *s, uint32_t fqid) { return qbman_swp_alt_fq_state(s, fqid, QBMAN_FQ_XOFF); } /**********************/ /* Channel management */ /**********************/ struct qbman_cdan_ctrl_desc { uint8_t verb; uint8_t reserved; uint16_t ch; uint8_t we; uint8_t ctrl; uint16_t reserved2; uint64_t cdan_ctx; uint8_t reserved3[48]; }; struct qbman_cdan_ctrl_rslt { uint8_t verb; uint8_t rslt; uint16_t ch; uint8_t reserved[60]; }; /* Hide "ICD" for now as we don't use it, don't set it, and don't test it, so it * would be irresponsible to expose it. */ #define CODE_CDAN_WE_EN 0x1 #define CODE_CDAN_WE_CTX 0x4 static int qbman_swp_CDAN_set(struct qbman_swp *s, uint16_t channelid, uint8_t we_mask, uint8_t cdan_en, uint64_t ctx) { struct qbman_cdan_ctrl_desc *p; struct qbman_cdan_ctrl_rslt *r; /* Start the management command */ p = qbman_swp_mc_start(s); if (!p) return -EBUSY; /* Encode the caller-provided attributes */ p->ch = channelid; p->we = we_mask; if (cdan_en) p->ctrl = 1; else p->ctrl = 0; p->cdan_ctx = ctx; /* Complete the management command */ r = qbman_swp_mc_complete(s, p, QBMAN_WQCHAN_CONFIGURE); if (!r) { pr_err("qbman: wqchan config failed, no response\n"); return -EIO; } /* Decode the outcome */ QBMAN_BUG_ON((r->verb & QBMAN_RESPONSE_VERB_MASK) != QBMAN_WQCHAN_CONFIGURE); /* Determine success or failure */ if (r->rslt != QBMAN_MC_RSLT_OK) { pr_err("CDAN cQID %d failed: code = 0x%02x\n", channelid, r->rslt); return -EIO; } return 0; } int qbman_swp_CDAN_set_context(struct qbman_swp *s, uint16_t channelid, uint64_t ctx) { return qbman_swp_CDAN_set(s, channelid, CODE_CDAN_WE_CTX, 0, ctx); } int qbman_swp_CDAN_enable(struct qbman_swp *s, uint16_t channelid) { return qbman_swp_CDAN_set(s, channelid, CODE_CDAN_WE_EN, 1, 0); } int qbman_swp_CDAN_disable(struct qbman_swp *s, uint16_t channelid) { return qbman_swp_CDAN_set(s, channelid, CODE_CDAN_WE_EN, 0, 0); } int qbman_swp_CDAN_set_context_enable(struct qbman_swp *s, uint16_t channelid, uint64_t ctx) { return qbman_swp_CDAN_set(s, channelid, CODE_CDAN_WE_EN | CODE_CDAN_WE_CTX, 1, ctx); } uint8_t qbman_get_dqrr_idx(const struct qbman_result *dqrr) { return QBMAN_IDX_FROM_DQRR(dqrr); } struct qbman_result *qbman_get_dqrr_from_idx(struct qbman_swp *s, uint8_t idx) { struct qbman_result *dq; dq = qbman_cena_read(&s->sys, QBMAN_CENA_SWP_DQRR(idx)); return dq; }