162306a36Sopenharmony_ci/* 262306a36Sopenharmony_ci * Copyright (c) 2004, 2005 Topspin Communications. All rights reserved. 362306a36Sopenharmony_ci * Copyright (c) 2005 Mellanox Technologies. All rights reserved. 462306a36Sopenharmony_ci * 562306a36Sopenharmony_ci * This software is available to you under a choice of one of two 662306a36Sopenharmony_ci * licenses. You may choose to be licensed under the terms of the GNU 762306a36Sopenharmony_ci * General Public License (GPL) Version 2, available from the file 862306a36Sopenharmony_ci * COPYING in the main directory of this source tree, or the 962306a36Sopenharmony_ci * OpenIB.org BSD license below: 1062306a36Sopenharmony_ci * 1162306a36Sopenharmony_ci * Redistribution and use in source and binary forms, with or 1262306a36Sopenharmony_ci * without modification, are permitted provided that the following 1362306a36Sopenharmony_ci * conditions are met: 1462306a36Sopenharmony_ci * 1562306a36Sopenharmony_ci * - Redistributions of source code must retain the above 1662306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 1762306a36Sopenharmony_ci * disclaimer. 1862306a36Sopenharmony_ci * 1962306a36Sopenharmony_ci * - Redistributions in binary form must reproduce the above 2062306a36Sopenharmony_ci * copyright notice, this list of conditions and the following 2162306a36Sopenharmony_ci * disclaimer in the documentation and/or other materials 2262306a36Sopenharmony_ci * provided with the distribution. 2362306a36Sopenharmony_ci * 2462306a36Sopenharmony_ci * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 2562306a36Sopenharmony_ci * EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 2662306a36Sopenharmony_ci * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 2762306a36Sopenharmony_ci * NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS 2862306a36Sopenharmony_ci * BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN 2962306a36Sopenharmony_ci * ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN 3062306a36Sopenharmony_ci * CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 3162306a36Sopenharmony_ci * SOFTWARE. 3262306a36Sopenharmony_ci */ 3362306a36Sopenharmony_ci 3462306a36Sopenharmony_ci#include <linux/errno.h> 3562306a36Sopenharmony_ci#include <linux/interrupt.h> 3662306a36Sopenharmony_ci#include <linux/pci.h> 3762306a36Sopenharmony_ci#include <linux/slab.h> 3862306a36Sopenharmony_ci 3962306a36Sopenharmony_ci#include "mthca_dev.h" 4062306a36Sopenharmony_ci#include "mthca_cmd.h" 4162306a36Sopenharmony_ci#include "mthca_config_reg.h" 4262306a36Sopenharmony_ci 4362306a36Sopenharmony_cienum { 4462306a36Sopenharmony_ci MTHCA_NUM_ASYNC_EQE = 0x80, 4562306a36Sopenharmony_ci MTHCA_NUM_CMD_EQE = 0x80, 4662306a36Sopenharmony_ci MTHCA_NUM_SPARE_EQE = 0x80, 4762306a36Sopenharmony_ci MTHCA_EQ_ENTRY_SIZE = 0x20 4862306a36Sopenharmony_ci}; 4962306a36Sopenharmony_ci 5062306a36Sopenharmony_ci/* 5162306a36Sopenharmony_ci * Must be packed because start is 64 bits but only aligned to 32 bits. 5262306a36Sopenharmony_ci */ 5362306a36Sopenharmony_cistruct mthca_eq_context { 5462306a36Sopenharmony_ci __be32 flags; 5562306a36Sopenharmony_ci __be64 start; 5662306a36Sopenharmony_ci __be32 logsize_usrpage; 5762306a36Sopenharmony_ci __be32 tavor_pd; /* reserved for Arbel */ 5862306a36Sopenharmony_ci u8 reserved1[3]; 5962306a36Sopenharmony_ci u8 intr; 6062306a36Sopenharmony_ci __be32 arbel_pd; /* lost_count for Tavor */ 6162306a36Sopenharmony_ci __be32 lkey; 6262306a36Sopenharmony_ci u32 reserved2[2]; 6362306a36Sopenharmony_ci __be32 consumer_index; 6462306a36Sopenharmony_ci __be32 producer_index; 6562306a36Sopenharmony_ci u32 reserved3[4]; 6662306a36Sopenharmony_ci} __packed; 6762306a36Sopenharmony_ci 6862306a36Sopenharmony_ci#define MTHCA_EQ_STATUS_OK ( 0 << 28) 6962306a36Sopenharmony_ci#define MTHCA_EQ_STATUS_OVERFLOW ( 9 << 28) 7062306a36Sopenharmony_ci#define MTHCA_EQ_STATUS_WRITE_FAIL (10 << 28) 7162306a36Sopenharmony_ci#define MTHCA_EQ_OWNER_SW ( 0 << 24) 7262306a36Sopenharmony_ci#define MTHCA_EQ_OWNER_HW ( 1 << 24) 7362306a36Sopenharmony_ci#define MTHCA_EQ_FLAG_TR ( 1 << 18) 7462306a36Sopenharmony_ci#define MTHCA_EQ_FLAG_OI ( 1 << 17) 7562306a36Sopenharmony_ci#define MTHCA_EQ_STATE_ARMED ( 1 << 8) 7662306a36Sopenharmony_ci#define MTHCA_EQ_STATE_FIRED ( 2 << 8) 7762306a36Sopenharmony_ci#define MTHCA_EQ_STATE_ALWAYS_ARMED ( 3 << 8) 7862306a36Sopenharmony_ci#define MTHCA_EQ_STATE_ARBEL ( 8 << 8) 7962306a36Sopenharmony_ci 8062306a36Sopenharmony_cienum { 8162306a36Sopenharmony_ci MTHCA_EVENT_TYPE_COMP = 0x00, 8262306a36Sopenharmony_ci MTHCA_EVENT_TYPE_PATH_MIG = 0x01, 8362306a36Sopenharmony_ci MTHCA_EVENT_TYPE_COMM_EST = 0x02, 8462306a36Sopenharmony_ci MTHCA_EVENT_TYPE_SQ_DRAINED = 0x03, 8562306a36Sopenharmony_ci MTHCA_EVENT_TYPE_SRQ_QP_LAST_WQE = 0x13, 8662306a36Sopenharmony_ci MTHCA_EVENT_TYPE_SRQ_LIMIT = 0x14, 8762306a36Sopenharmony_ci MTHCA_EVENT_TYPE_CQ_ERROR = 0x04, 8862306a36Sopenharmony_ci MTHCA_EVENT_TYPE_WQ_CATAS_ERROR = 0x05, 8962306a36Sopenharmony_ci MTHCA_EVENT_TYPE_EEC_CATAS_ERROR = 0x06, 9062306a36Sopenharmony_ci MTHCA_EVENT_TYPE_PATH_MIG_FAILED = 0x07, 9162306a36Sopenharmony_ci MTHCA_EVENT_TYPE_WQ_INVAL_REQ_ERROR = 0x10, 9262306a36Sopenharmony_ci MTHCA_EVENT_TYPE_WQ_ACCESS_ERROR = 0x11, 9362306a36Sopenharmony_ci MTHCA_EVENT_TYPE_SRQ_CATAS_ERROR = 0x12, 9462306a36Sopenharmony_ci MTHCA_EVENT_TYPE_LOCAL_CATAS_ERROR = 0x08, 9562306a36Sopenharmony_ci MTHCA_EVENT_TYPE_PORT_CHANGE = 0x09, 9662306a36Sopenharmony_ci MTHCA_EVENT_TYPE_EQ_OVERFLOW = 0x0f, 9762306a36Sopenharmony_ci MTHCA_EVENT_TYPE_ECC_DETECT = 0x0e, 9862306a36Sopenharmony_ci MTHCA_EVENT_TYPE_CMD = 0x0a 9962306a36Sopenharmony_ci}; 10062306a36Sopenharmony_ci 10162306a36Sopenharmony_ci#define MTHCA_ASYNC_EVENT_MASK ((1ULL << MTHCA_EVENT_TYPE_PATH_MIG) | \ 10262306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_COMM_EST) | \ 10362306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_SQ_DRAINED) | \ 10462306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_CQ_ERROR) | \ 10562306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_WQ_CATAS_ERROR) | \ 10662306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_EEC_CATAS_ERROR) | \ 10762306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_PATH_MIG_FAILED) | \ 10862306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_WQ_INVAL_REQ_ERROR) | \ 10962306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_WQ_ACCESS_ERROR) | \ 11062306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_LOCAL_CATAS_ERROR) | \ 11162306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_PORT_CHANGE) | \ 11262306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_ECC_DETECT)) 11362306a36Sopenharmony_ci#define MTHCA_SRQ_EVENT_MASK ((1ULL << MTHCA_EVENT_TYPE_SRQ_CATAS_ERROR) | \ 11462306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_SRQ_QP_LAST_WQE) | \ 11562306a36Sopenharmony_ci (1ULL << MTHCA_EVENT_TYPE_SRQ_LIMIT)) 11662306a36Sopenharmony_ci#define MTHCA_CMD_EVENT_MASK (1ULL << MTHCA_EVENT_TYPE_CMD) 11762306a36Sopenharmony_ci 11862306a36Sopenharmony_ci#define MTHCA_EQ_DB_INC_CI (1 << 24) 11962306a36Sopenharmony_ci#define MTHCA_EQ_DB_REQ_NOT (2 << 24) 12062306a36Sopenharmony_ci#define MTHCA_EQ_DB_DISARM_CQ (3 << 24) 12162306a36Sopenharmony_ci#define MTHCA_EQ_DB_SET_CI (4 << 24) 12262306a36Sopenharmony_ci#define MTHCA_EQ_DB_ALWAYS_ARM (5 << 24) 12362306a36Sopenharmony_ci 12462306a36Sopenharmony_cistruct mthca_eqe { 12562306a36Sopenharmony_ci u8 reserved1; 12662306a36Sopenharmony_ci u8 type; 12762306a36Sopenharmony_ci u8 reserved2; 12862306a36Sopenharmony_ci u8 subtype; 12962306a36Sopenharmony_ci union { 13062306a36Sopenharmony_ci u32 raw[6]; 13162306a36Sopenharmony_ci struct { 13262306a36Sopenharmony_ci __be32 cqn; 13362306a36Sopenharmony_ci } __packed comp; 13462306a36Sopenharmony_ci struct { 13562306a36Sopenharmony_ci u16 reserved1; 13662306a36Sopenharmony_ci __be16 token; 13762306a36Sopenharmony_ci u32 reserved2; 13862306a36Sopenharmony_ci u8 reserved3[3]; 13962306a36Sopenharmony_ci u8 status; 14062306a36Sopenharmony_ci __be64 out_param; 14162306a36Sopenharmony_ci } __packed cmd; 14262306a36Sopenharmony_ci struct { 14362306a36Sopenharmony_ci __be32 qpn; 14462306a36Sopenharmony_ci } __packed qp; 14562306a36Sopenharmony_ci struct { 14662306a36Sopenharmony_ci __be32 srqn; 14762306a36Sopenharmony_ci } __packed srq; 14862306a36Sopenharmony_ci struct { 14962306a36Sopenharmony_ci __be32 cqn; 15062306a36Sopenharmony_ci u32 reserved1; 15162306a36Sopenharmony_ci u8 reserved2[3]; 15262306a36Sopenharmony_ci u8 syndrome; 15362306a36Sopenharmony_ci } __packed cq_err; 15462306a36Sopenharmony_ci struct { 15562306a36Sopenharmony_ci u32 reserved1[2]; 15662306a36Sopenharmony_ci __be32 port; 15762306a36Sopenharmony_ci } __packed port_change; 15862306a36Sopenharmony_ci } event; 15962306a36Sopenharmony_ci u8 reserved3[3]; 16062306a36Sopenharmony_ci u8 owner; 16162306a36Sopenharmony_ci} __packed; 16262306a36Sopenharmony_ci 16362306a36Sopenharmony_ci#define MTHCA_EQ_ENTRY_OWNER_SW (0 << 7) 16462306a36Sopenharmony_ci#define MTHCA_EQ_ENTRY_OWNER_HW (1 << 7) 16562306a36Sopenharmony_ci 16662306a36Sopenharmony_cistatic inline u64 async_mask(struct mthca_dev *dev) 16762306a36Sopenharmony_ci{ 16862306a36Sopenharmony_ci return dev->mthca_flags & MTHCA_FLAG_SRQ ? 16962306a36Sopenharmony_ci MTHCA_ASYNC_EVENT_MASK | MTHCA_SRQ_EVENT_MASK : 17062306a36Sopenharmony_ci MTHCA_ASYNC_EVENT_MASK; 17162306a36Sopenharmony_ci} 17262306a36Sopenharmony_ci 17362306a36Sopenharmony_cistatic inline void tavor_set_eq_ci(struct mthca_dev *dev, struct mthca_eq *eq, u32 ci) 17462306a36Sopenharmony_ci{ 17562306a36Sopenharmony_ci /* 17662306a36Sopenharmony_ci * This barrier makes sure that all updates to ownership bits 17762306a36Sopenharmony_ci * done by set_eqe_hw() hit memory before the consumer index 17862306a36Sopenharmony_ci * is updated. set_eq_ci() allows the HCA to possibly write 17962306a36Sopenharmony_ci * more EQ entries, and we want to avoid the exceedingly 18062306a36Sopenharmony_ci * unlikely possibility of the HCA writing an entry and then 18162306a36Sopenharmony_ci * having set_eqe_hw() overwrite the owner field. 18262306a36Sopenharmony_ci */ 18362306a36Sopenharmony_ci wmb(); 18462306a36Sopenharmony_ci mthca_write64(MTHCA_EQ_DB_SET_CI | eq->eqn, ci & (eq->nent - 1), 18562306a36Sopenharmony_ci dev->kar + MTHCA_EQ_DOORBELL, 18662306a36Sopenharmony_ci MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock)); 18762306a36Sopenharmony_ci} 18862306a36Sopenharmony_ci 18962306a36Sopenharmony_cistatic inline void arbel_set_eq_ci(struct mthca_dev *dev, struct mthca_eq *eq, u32 ci) 19062306a36Sopenharmony_ci{ 19162306a36Sopenharmony_ci /* See comment in tavor_set_eq_ci() above. */ 19262306a36Sopenharmony_ci wmb(); 19362306a36Sopenharmony_ci __raw_writel((__force u32) cpu_to_be32(ci), 19462306a36Sopenharmony_ci dev->eq_regs.arbel.eq_set_ci_base + eq->eqn * 8); 19562306a36Sopenharmony_ci /* We still want ordering, just not swabbing, so add a barrier */ 19662306a36Sopenharmony_ci mb(); 19762306a36Sopenharmony_ci} 19862306a36Sopenharmony_ci 19962306a36Sopenharmony_cistatic inline void set_eq_ci(struct mthca_dev *dev, struct mthca_eq *eq, u32 ci) 20062306a36Sopenharmony_ci{ 20162306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 20262306a36Sopenharmony_ci arbel_set_eq_ci(dev, eq, ci); 20362306a36Sopenharmony_ci else 20462306a36Sopenharmony_ci tavor_set_eq_ci(dev, eq, ci); 20562306a36Sopenharmony_ci} 20662306a36Sopenharmony_ci 20762306a36Sopenharmony_cistatic inline void tavor_eq_req_not(struct mthca_dev *dev, int eqn) 20862306a36Sopenharmony_ci{ 20962306a36Sopenharmony_ci mthca_write64(MTHCA_EQ_DB_REQ_NOT | eqn, 0, 21062306a36Sopenharmony_ci dev->kar + MTHCA_EQ_DOORBELL, 21162306a36Sopenharmony_ci MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock)); 21262306a36Sopenharmony_ci} 21362306a36Sopenharmony_ci 21462306a36Sopenharmony_cistatic inline void arbel_eq_req_not(struct mthca_dev *dev, u32 eqn_mask) 21562306a36Sopenharmony_ci{ 21662306a36Sopenharmony_ci writel(eqn_mask, dev->eq_regs.arbel.eq_arm); 21762306a36Sopenharmony_ci} 21862306a36Sopenharmony_ci 21962306a36Sopenharmony_cistatic inline void disarm_cq(struct mthca_dev *dev, int eqn, int cqn) 22062306a36Sopenharmony_ci{ 22162306a36Sopenharmony_ci if (!mthca_is_memfree(dev)) { 22262306a36Sopenharmony_ci mthca_write64(MTHCA_EQ_DB_DISARM_CQ | eqn, cqn, 22362306a36Sopenharmony_ci dev->kar + MTHCA_EQ_DOORBELL, 22462306a36Sopenharmony_ci MTHCA_GET_DOORBELL_LOCK(&dev->doorbell_lock)); 22562306a36Sopenharmony_ci } 22662306a36Sopenharmony_ci} 22762306a36Sopenharmony_ci 22862306a36Sopenharmony_cistatic inline struct mthca_eqe *get_eqe(struct mthca_eq *eq, u32 entry) 22962306a36Sopenharmony_ci{ 23062306a36Sopenharmony_ci unsigned long off = (entry & (eq->nent - 1)) * MTHCA_EQ_ENTRY_SIZE; 23162306a36Sopenharmony_ci return eq->page_list[off / PAGE_SIZE].buf + off % PAGE_SIZE; 23262306a36Sopenharmony_ci} 23362306a36Sopenharmony_ci 23462306a36Sopenharmony_cistatic inline struct mthca_eqe *next_eqe_sw(struct mthca_eq *eq) 23562306a36Sopenharmony_ci{ 23662306a36Sopenharmony_ci struct mthca_eqe *eqe; 23762306a36Sopenharmony_ci eqe = get_eqe(eq, eq->cons_index); 23862306a36Sopenharmony_ci return (MTHCA_EQ_ENTRY_OWNER_HW & eqe->owner) ? NULL : eqe; 23962306a36Sopenharmony_ci} 24062306a36Sopenharmony_ci 24162306a36Sopenharmony_cistatic inline void set_eqe_hw(struct mthca_eqe *eqe) 24262306a36Sopenharmony_ci{ 24362306a36Sopenharmony_ci eqe->owner = MTHCA_EQ_ENTRY_OWNER_HW; 24462306a36Sopenharmony_ci} 24562306a36Sopenharmony_ci 24662306a36Sopenharmony_cistatic void port_change(struct mthca_dev *dev, int port, int active) 24762306a36Sopenharmony_ci{ 24862306a36Sopenharmony_ci struct ib_event record; 24962306a36Sopenharmony_ci 25062306a36Sopenharmony_ci mthca_dbg(dev, "Port change to %s for port %d\n", 25162306a36Sopenharmony_ci active ? "active" : "down", port); 25262306a36Sopenharmony_ci 25362306a36Sopenharmony_ci record.device = &dev->ib_dev; 25462306a36Sopenharmony_ci record.event = active ? IB_EVENT_PORT_ACTIVE : IB_EVENT_PORT_ERR; 25562306a36Sopenharmony_ci record.element.port_num = port; 25662306a36Sopenharmony_ci 25762306a36Sopenharmony_ci ib_dispatch_event(&record); 25862306a36Sopenharmony_ci} 25962306a36Sopenharmony_ci 26062306a36Sopenharmony_cistatic int mthca_eq_int(struct mthca_dev *dev, struct mthca_eq *eq) 26162306a36Sopenharmony_ci{ 26262306a36Sopenharmony_ci struct mthca_eqe *eqe; 26362306a36Sopenharmony_ci int disarm_cqn; 26462306a36Sopenharmony_ci int eqes_found = 0; 26562306a36Sopenharmony_ci int set_ci = 0; 26662306a36Sopenharmony_ci 26762306a36Sopenharmony_ci while ((eqe = next_eqe_sw(eq))) { 26862306a36Sopenharmony_ci /* 26962306a36Sopenharmony_ci * Make sure we read EQ entry contents after we've 27062306a36Sopenharmony_ci * checked the ownership bit. 27162306a36Sopenharmony_ci */ 27262306a36Sopenharmony_ci rmb(); 27362306a36Sopenharmony_ci 27462306a36Sopenharmony_ci switch (eqe->type) { 27562306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_COMP: 27662306a36Sopenharmony_ci disarm_cqn = be32_to_cpu(eqe->event.comp.cqn) & 0xffffff; 27762306a36Sopenharmony_ci disarm_cq(dev, eq->eqn, disarm_cqn); 27862306a36Sopenharmony_ci mthca_cq_completion(dev, disarm_cqn); 27962306a36Sopenharmony_ci break; 28062306a36Sopenharmony_ci 28162306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_PATH_MIG: 28262306a36Sopenharmony_ci mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, 28362306a36Sopenharmony_ci IB_EVENT_PATH_MIG); 28462306a36Sopenharmony_ci break; 28562306a36Sopenharmony_ci 28662306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_COMM_EST: 28762306a36Sopenharmony_ci mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, 28862306a36Sopenharmony_ci IB_EVENT_COMM_EST); 28962306a36Sopenharmony_ci break; 29062306a36Sopenharmony_ci 29162306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_SQ_DRAINED: 29262306a36Sopenharmony_ci mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, 29362306a36Sopenharmony_ci IB_EVENT_SQ_DRAINED); 29462306a36Sopenharmony_ci break; 29562306a36Sopenharmony_ci 29662306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_SRQ_QP_LAST_WQE: 29762306a36Sopenharmony_ci mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, 29862306a36Sopenharmony_ci IB_EVENT_QP_LAST_WQE_REACHED); 29962306a36Sopenharmony_ci break; 30062306a36Sopenharmony_ci 30162306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_SRQ_LIMIT: 30262306a36Sopenharmony_ci mthca_srq_event(dev, be32_to_cpu(eqe->event.srq.srqn) & 0xffffff, 30362306a36Sopenharmony_ci IB_EVENT_SRQ_LIMIT_REACHED); 30462306a36Sopenharmony_ci break; 30562306a36Sopenharmony_ci 30662306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_WQ_CATAS_ERROR: 30762306a36Sopenharmony_ci mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, 30862306a36Sopenharmony_ci IB_EVENT_QP_FATAL); 30962306a36Sopenharmony_ci break; 31062306a36Sopenharmony_ci 31162306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_PATH_MIG_FAILED: 31262306a36Sopenharmony_ci mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, 31362306a36Sopenharmony_ci IB_EVENT_PATH_MIG_ERR); 31462306a36Sopenharmony_ci break; 31562306a36Sopenharmony_ci 31662306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_WQ_INVAL_REQ_ERROR: 31762306a36Sopenharmony_ci mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, 31862306a36Sopenharmony_ci IB_EVENT_QP_REQ_ERR); 31962306a36Sopenharmony_ci break; 32062306a36Sopenharmony_ci 32162306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_WQ_ACCESS_ERROR: 32262306a36Sopenharmony_ci mthca_qp_event(dev, be32_to_cpu(eqe->event.qp.qpn) & 0xffffff, 32362306a36Sopenharmony_ci IB_EVENT_QP_ACCESS_ERR); 32462306a36Sopenharmony_ci break; 32562306a36Sopenharmony_ci 32662306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_CMD: 32762306a36Sopenharmony_ci mthca_cmd_event(dev, 32862306a36Sopenharmony_ci be16_to_cpu(eqe->event.cmd.token), 32962306a36Sopenharmony_ci eqe->event.cmd.status, 33062306a36Sopenharmony_ci be64_to_cpu(eqe->event.cmd.out_param)); 33162306a36Sopenharmony_ci break; 33262306a36Sopenharmony_ci 33362306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_PORT_CHANGE: 33462306a36Sopenharmony_ci port_change(dev, 33562306a36Sopenharmony_ci (be32_to_cpu(eqe->event.port_change.port) >> 28) & 3, 33662306a36Sopenharmony_ci eqe->subtype == 0x4); 33762306a36Sopenharmony_ci break; 33862306a36Sopenharmony_ci 33962306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_CQ_ERROR: 34062306a36Sopenharmony_ci mthca_warn(dev, "CQ %s on CQN %06x\n", 34162306a36Sopenharmony_ci eqe->event.cq_err.syndrome == 1 ? 34262306a36Sopenharmony_ci "overrun" : "access violation", 34362306a36Sopenharmony_ci be32_to_cpu(eqe->event.cq_err.cqn) & 0xffffff); 34462306a36Sopenharmony_ci mthca_cq_event(dev, be32_to_cpu(eqe->event.cq_err.cqn), 34562306a36Sopenharmony_ci IB_EVENT_CQ_ERR); 34662306a36Sopenharmony_ci break; 34762306a36Sopenharmony_ci 34862306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_EQ_OVERFLOW: 34962306a36Sopenharmony_ci mthca_warn(dev, "EQ overrun on EQN %d\n", eq->eqn); 35062306a36Sopenharmony_ci break; 35162306a36Sopenharmony_ci 35262306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_EEC_CATAS_ERROR: 35362306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_SRQ_CATAS_ERROR: 35462306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_LOCAL_CATAS_ERROR: 35562306a36Sopenharmony_ci case MTHCA_EVENT_TYPE_ECC_DETECT: 35662306a36Sopenharmony_ci default: 35762306a36Sopenharmony_ci mthca_warn(dev, "Unhandled event %02x(%02x) on EQ %d\n", 35862306a36Sopenharmony_ci eqe->type, eqe->subtype, eq->eqn); 35962306a36Sopenharmony_ci break; 36062306a36Sopenharmony_ci } 36162306a36Sopenharmony_ci 36262306a36Sopenharmony_ci set_eqe_hw(eqe); 36362306a36Sopenharmony_ci ++eq->cons_index; 36462306a36Sopenharmony_ci eqes_found = 1; 36562306a36Sopenharmony_ci ++set_ci; 36662306a36Sopenharmony_ci 36762306a36Sopenharmony_ci /* 36862306a36Sopenharmony_ci * The HCA will think the queue has overflowed if we 36962306a36Sopenharmony_ci * don't tell it we've been processing events. We 37062306a36Sopenharmony_ci * create our EQs with MTHCA_NUM_SPARE_EQE extra 37162306a36Sopenharmony_ci * entries, so we must update our consumer index at 37262306a36Sopenharmony_ci * least that often. 37362306a36Sopenharmony_ci */ 37462306a36Sopenharmony_ci if (unlikely(set_ci >= MTHCA_NUM_SPARE_EQE)) { 37562306a36Sopenharmony_ci /* 37662306a36Sopenharmony_ci * Conditional on hca_type is OK here because 37762306a36Sopenharmony_ci * this is a rare case, not the fast path. 37862306a36Sopenharmony_ci */ 37962306a36Sopenharmony_ci set_eq_ci(dev, eq, eq->cons_index); 38062306a36Sopenharmony_ci set_ci = 0; 38162306a36Sopenharmony_ci } 38262306a36Sopenharmony_ci } 38362306a36Sopenharmony_ci 38462306a36Sopenharmony_ci /* 38562306a36Sopenharmony_ci * Rely on caller to set consumer index so that we don't have 38662306a36Sopenharmony_ci * to test hca_type in our interrupt handling fast path. 38762306a36Sopenharmony_ci */ 38862306a36Sopenharmony_ci return eqes_found; 38962306a36Sopenharmony_ci} 39062306a36Sopenharmony_ci 39162306a36Sopenharmony_cistatic irqreturn_t mthca_tavor_interrupt(int irq, void *dev_ptr) 39262306a36Sopenharmony_ci{ 39362306a36Sopenharmony_ci struct mthca_dev *dev = dev_ptr; 39462306a36Sopenharmony_ci u32 ecr; 39562306a36Sopenharmony_ci int i; 39662306a36Sopenharmony_ci 39762306a36Sopenharmony_ci if (dev->eq_table.clr_mask) 39862306a36Sopenharmony_ci writel(dev->eq_table.clr_mask, dev->eq_table.clr_int); 39962306a36Sopenharmony_ci 40062306a36Sopenharmony_ci ecr = readl(dev->eq_regs.tavor.ecr_base + 4); 40162306a36Sopenharmony_ci if (!ecr) 40262306a36Sopenharmony_ci return IRQ_NONE; 40362306a36Sopenharmony_ci 40462306a36Sopenharmony_ci writel(ecr, dev->eq_regs.tavor.ecr_base + 40562306a36Sopenharmony_ci MTHCA_ECR_CLR_BASE - MTHCA_ECR_BASE + 4); 40662306a36Sopenharmony_ci 40762306a36Sopenharmony_ci for (i = 0; i < MTHCA_NUM_EQ; ++i) 40862306a36Sopenharmony_ci if (ecr & dev->eq_table.eq[i].eqn_mask) { 40962306a36Sopenharmony_ci if (mthca_eq_int(dev, &dev->eq_table.eq[i])) 41062306a36Sopenharmony_ci tavor_set_eq_ci(dev, &dev->eq_table.eq[i], 41162306a36Sopenharmony_ci dev->eq_table.eq[i].cons_index); 41262306a36Sopenharmony_ci tavor_eq_req_not(dev, dev->eq_table.eq[i].eqn); 41362306a36Sopenharmony_ci } 41462306a36Sopenharmony_ci 41562306a36Sopenharmony_ci return IRQ_HANDLED; 41662306a36Sopenharmony_ci} 41762306a36Sopenharmony_ci 41862306a36Sopenharmony_cistatic irqreturn_t mthca_tavor_msi_x_interrupt(int irq, void *eq_ptr) 41962306a36Sopenharmony_ci{ 42062306a36Sopenharmony_ci struct mthca_eq *eq = eq_ptr; 42162306a36Sopenharmony_ci struct mthca_dev *dev = eq->dev; 42262306a36Sopenharmony_ci 42362306a36Sopenharmony_ci mthca_eq_int(dev, eq); 42462306a36Sopenharmony_ci tavor_set_eq_ci(dev, eq, eq->cons_index); 42562306a36Sopenharmony_ci tavor_eq_req_not(dev, eq->eqn); 42662306a36Sopenharmony_ci 42762306a36Sopenharmony_ci /* MSI-X vectors always belong to us */ 42862306a36Sopenharmony_ci return IRQ_HANDLED; 42962306a36Sopenharmony_ci} 43062306a36Sopenharmony_ci 43162306a36Sopenharmony_cistatic irqreturn_t mthca_arbel_interrupt(int irq, void *dev_ptr) 43262306a36Sopenharmony_ci{ 43362306a36Sopenharmony_ci struct mthca_dev *dev = dev_ptr; 43462306a36Sopenharmony_ci int work = 0; 43562306a36Sopenharmony_ci int i; 43662306a36Sopenharmony_ci 43762306a36Sopenharmony_ci if (dev->eq_table.clr_mask) 43862306a36Sopenharmony_ci writel(dev->eq_table.clr_mask, dev->eq_table.clr_int); 43962306a36Sopenharmony_ci 44062306a36Sopenharmony_ci for (i = 0; i < MTHCA_NUM_EQ; ++i) 44162306a36Sopenharmony_ci if (mthca_eq_int(dev, &dev->eq_table.eq[i])) { 44262306a36Sopenharmony_ci work = 1; 44362306a36Sopenharmony_ci arbel_set_eq_ci(dev, &dev->eq_table.eq[i], 44462306a36Sopenharmony_ci dev->eq_table.eq[i].cons_index); 44562306a36Sopenharmony_ci } 44662306a36Sopenharmony_ci 44762306a36Sopenharmony_ci arbel_eq_req_not(dev, dev->eq_table.arm_mask); 44862306a36Sopenharmony_ci 44962306a36Sopenharmony_ci return IRQ_RETVAL(work); 45062306a36Sopenharmony_ci} 45162306a36Sopenharmony_ci 45262306a36Sopenharmony_cistatic irqreturn_t mthca_arbel_msi_x_interrupt(int irq, void *eq_ptr) 45362306a36Sopenharmony_ci{ 45462306a36Sopenharmony_ci struct mthca_eq *eq = eq_ptr; 45562306a36Sopenharmony_ci struct mthca_dev *dev = eq->dev; 45662306a36Sopenharmony_ci 45762306a36Sopenharmony_ci mthca_eq_int(dev, eq); 45862306a36Sopenharmony_ci arbel_set_eq_ci(dev, eq, eq->cons_index); 45962306a36Sopenharmony_ci arbel_eq_req_not(dev, eq->eqn_mask); 46062306a36Sopenharmony_ci 46162306a36Sopenharmony_ci /* MSI-X vectors always belong to us */ 46262306a36Sopenharmony_ci return IRQ_HANDLED; 46362306a36Sopenharmony_ci} 46462306a36Sopenharmony_ci 46562306a36Sopenharmony_cistatic int mthca_create_eq(struct mthca_dev *dev, 46662306a36Sopenharmony_ci int nent, 46762306a36Sopenharmony_ci u8 intr, 46862306a36Sopenharmony_ci struct mthca_eq *eq) 46962306a36Sopenharmony_ci{ 47062306a36Sopenharmony_ci int npages; 47162306a36Sopenharmony_ci u64 *dma_list = NULL; 47262306a36Sopenharmony_ci dma_addr_t t; 47362306a36Sopenharmony_ci struct mthca_mailbox *mailbox; 47462306a36Sopenharmony_ci struct mthca_eq_context *eq_context; 47562306a36Sopenharmony_ci int err = -ENOMEM; 47662306a36Sopenharmony_ci int i; 47762306a36Sopenharmony_ci 47862306a36Sopenharmony_ci eq->dev = dev; 47962306a36Sopenharmony_ci eq->nent = roundup_pow_of_two(max(nent, 2)); 48062306a36Sopenharmony_ci npages = ALIGN(eq->nent * MTHCA_EQ_ENTRY_SIZE, PAGE_SIZE) / PAGE_SIZE; 48162306a36Sopenharmony_ci 48262306a36Sopenharmony_ci eq->page_list = kmalloc_array(npages, sizeof(*eq->page_list), 48362306a36Sopenharmony_ci GFP_KERNEL); 48462306a36Sopenharmony_ci if (!eq->page_list) 48562306a36Sopenharmony_ci goto err_out; 48662306a36Sopenharmony_ci 48762306a36Sopenharmony_ci for (i = 0; i < npages; ++i) 48862306a36Sopenharmony_ci eq->page_list[i].buf = NULL; 48962306a36Sopenharmony_ci 49062306a36Sopenharmony_ci dma_list = kmalloc_array(npages, sizeof(*dma_list), GFP_KERNEL); 49162306a36Sopenharmony_ci if (!dma_list) 49262306a36Sopenharmony_ci goto err_out_free; 49362306a36Sopenharmony_ci 49462306a36Sopenharmony_ci mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 49562306a36Sopenharmony_ci if (IS_ERR(mailbox)) 49662306a36Sopenharmony_ci goto err_out_free; 49762306a36Sopenharmony_ci eq_context = mailbox->buf; 49862306a36Sopenharmony_ci 49962306a36Sopenharmony_ci for (i = 0; i < npages; ++i) { 50062306a36Sopenharmony_ci eq->page_list[i].buf = dma_alloc_coherent(&dev->pdev->dev, 50162306a36Sopenharmony_ci PAGE_SIZE, &t, GFP_KERNEL); 50262306a36Sopenharmony_ci if (!eq->page_list[i].buf) 50362306a36Sopenharmony_ci goto err_out_free_pages; 50462306a36Sopenharmony_ci 50562306a36Sopenharmony_ci dma_list[i] = t; 50662306a36Sopenharmony_ci dma_unmap_addr_set(&eq->page_list[i], mapping, t); 50762306a36Sopenharmony_ci 50862306a36Sopenharmony_ci clear_page(eq->page_list[i].buf); 50962306a36Sopenharmony_ci } 51062306a36Sopenharmony_ci 51162306a36Sopenharmony_ci for (i = 0; i < eq->nent; ++i) 51262306a36Sopenharmony_ci set_eqe_hw(get_eqe(eq, i)); 51362306a36Sopenharmony_ci 51462306a36Sopenharmony_ci eq->eqn = mthca_alloc(&dev->eq_table.alloc); 51562306a36Sopenharmony_ci if (eq->eqn == -1) 51662306a36Sopenharmony_ci goto err_out_free_pages; 51762306a36Sopenharmony_ci 51862306a36Sopenharmony_ci err = mthca_mr_alloc_phys(dev, dev->driver_pd.pd_num, 51962306a36Sopenharmony_ci dma_list, PAGE_SHIFT, npages, 52062306a36Sopenharmony_ci 0, npages * PAGE_SIZE, 52162306a36Sopenharmony_ci MTHCA_MPT_FLAG_LOCAL_WRITE | 52262306a36Sopenharmony_ci MTHCA_MPT_FLAG_LOCAL_READ, 52362306a36Sopenharmony_ci &eq->mr); 52462306a36Sopenharmony_ci if (err) 52562306a36Sopenharmony_ci goto err_out_free_eq; 52662306a36Sopenharmony_ci 52762306a36Sopenharmony_ci memset(eq_context, 0, sizeof *eq_context); 52862306a36Sopenharmony_ci eq_context->flags = cpu_to_be32(MTHCA_EQ_STATUS_OK | 52962306a36Sopenharmony_ci MTHCA_EQ_OWNER_HW | 53062306a36Sopenharmony_ci MTHCA_EQ_STATE_ARMED | 53162306a36Sopenharmony_ci MTHCA_EQ_FLAG_TR); 53262306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 53362306a36Sopenharmony_ci eq_context->flags |= cpu_to_be32(MTHCA_EQ_STATE_ARBEL); 53462306a36Sopenharmony_ci 53562306a36Sopenharmony_ci eq_context->logsize_usrpage = cpu_to_be32((ffs(eq->nent) - 1) << 24); 53662306a36Sopenharmony_ci if (mthca_is_memfree(dev)) { 53762306a36Sopenharmony_ci eq_context->arbel_pd = cpu_to_be32(dev->driver_pd.pd_num); 53862306a36Sopenharmony_ci } else { 53962306a36Sopenharmony_ci eq_context->logsize_usrpage |= cpu_to_be32(dev->driver_uar.index); 54062306a36Sopenharmony_ci eq_context->tavor_pd = cpu_to_be32(dev->driver_pd.pd_num); 54162306a36Sopenharmony_ci } 54262306a36Sopenharmony_ci eq_context->intr = intr; 54362306a36Sopenharmony_ci eq_context->lkey = cpu_to_be32(eq->mr.ibmr.lkey); 54462306a36Sopenharmony_ci 54562306a36Sopenharmony_ci err = mthca_SW2HW_EQ(dev, mailbox, eq->eqn); 54662306a36Sopenharmony_ci if (err) { 54762306a36Sopenharmony_ci mthca_warn(dev, "SW2HW_EQ returned %d\n", err); 54862306a36Sopenharmony_ci goto err_out_free_mr; 54962306a36Sopenharmony_ci } 55062306a36Sopenharmony_ci 55162306a36Sopenharmony_ci kfree(dma_list); 55262306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 55362306a36Sopenharmony_ci 55462306a36Sopenharmony_ci eq->eqn_mask = swab32(1 << eq->eqn); 55562306a36Sopenharmony_ci eq->cons_index = 0; 55662306a36Sopenharmony_ci 55762306a36Sopenharmony_ci dev->eq_table.arm_mask |= eq->eqn_mask; 55862306a36Sopenharmony_ci 55962306a36Sopenharmony_ci mthca_dbg(dev, "Allocated EQ %d with %d entries\n", 56062306a36Sopenharmony_ci eq->eqn, eq->nent); 56162306a36Sopenharmony_ci 56262306a36Sopenharmony_ci return err; 56362306a36Sopenharmony_ci 56462306a36Sopenharmony_ci err_out_free_mr: 56562306a36Sopenharmony_ci mthca_free_mr(dev, &eq->mr); 56662306a36Sopenharmony_ci 56762306a36Sopenharmony_ci err_out_free_eq: 56862306a36Sopenharmony_ci mthca_free(&dev->eq_table.alloc, eq->eqn); 56962306a36Sopenharmony_ci 57062306a36Sopenharmony_ci err_out_free_pages: 57162306a36Sopenharmony_ci for (i = 0; i < npages; ++i) 57262306a36Sopenharmony_ci if (eq->page_list[i].buf) 57362306a36Sopenharmony_ci dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, 57462306a36Sopenharmony_ci eq->page_list[i].buf, 57562306a36Sopenharmony_ci dma_unmap_addr(&eq->page_list[i], 57662306a36Sopenharmony_ci mapping)); 57762306a36Sopenharmony_ci 57862306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 57962306a36Sopenharmony_ci 58062306a36Sopenharmony_ci err_out_free: 58162306a36Sopenharmony_ci kfree(eq->page_list); 58262306a36Sopenharmony_ci kfree(dma_list); 58362306a36Sopenharmony_ci 58462306a36Sopenharmony_ci err_out: 58562306a36Sopenharmony_ci return err; 58662306a36Sopenharmony_ci} 58762306a36Sopenharmony_ci 58862306a36Sopenharmony_cistatic void mthca_free_eq(struct mthca_dev *dev, 58962306a36Sopenharmony_ci struct mthca_eq *eq) 59062306a36Sopenharmony_ci{ 59162306a36Sopenharmony_ci struct mthca_mailbox *mailbox; 59262306a36Sopenharmony_ci int err; 59362306a36Sopenharmony_ci int npages = (eq->nent * MTHCA_EQ_ENTRY_SIZE + PAGE_SIZE - 1) / 59462306a36Sopenharmony_ci PAGE_SIZE; 59562306a36Sopenharmony_ci int i; 59662306a36Sopenharmony_ci 59762306a36Sopenharmony_ci mailbox = mthca_alloc_mailbox(dev, GFP_KERNEL); 59862306a36Sopenharmony_ci if (IS_ERR(mailbox)) 59962306a36Sopenharmony_ci return; 60062306a36Sopenharmony_ci 60162306a36Sopenharmony_ci err = mthca_HW2SW_EQ(dev, mailbox, eq->eqn); 60262306a36Sopenharmony_ci if (err) 60362306a36Sopenharmony_ci mthca_warn(dev, "HW2SW_EQ returned %d\n", err); 60462306a36Sopenharmony_ci 60562306a36Sopenharmony_ci dev->eq_table.arm_mask &= ~eq->eqn_mask; 60662306a36Sopenharmony_ci 60762306a36Sopenharmony_ci if (0) { 60862306a36Sopenharmony_ci mthca_dbg(dev, "Dumping EQ context %02x:\n", eq->eqn); 60962306a36Sopenharmony_ci for (i = 0; i < sizeof (struct mthca_eq_context) / 4; ++i) { 61062306a36Sopenharmony_ci if (i % 4 == 0) 61162306a36Sopenharmony_ci printk("[%02x] ", i * 4); 61262306a36Sopenharmony_ci printk(" %08x", be32_to_cpup(mailbox->buf + i * 4)); 61362306a36Sopenharmony_ci if ((i + 1) % 4 == 0) 61462306a36Sopenharmony_ci printk("\n"); 61562306a36Sopenharmony_ci } 61662306a36Sopenharmony_ci } 61762306a36Sopenharmony_ci 61862306a36Sopenharmony_ci mthca_free_mr(dev, &eq->mr); 61962306a36Sopenharmony_ci for (i = 0; i < npages; ++i) 62062306a36Sopenharmony_ci dma_free_coherent(&dev->pdev->dev, PAGE_SIZE, 62162306a36Sopenharmony_ci eq->page_list[i].buf, 62262306a36Sopenharmony_ci dma_unmap_addr(&eq->page_list[i], mapping)); 62362306a36Sopenharmony_ci 62462306a36Sopenharmony_ci kfree(eq->page_list); 62562306a36Sopenharmony_ci mthca_free_mailbox(dev, mailbox); 62662306a36Sopenharmony_ci} 62762306a36Sopenharmony_ci 62862306a36Sopenharmony_cistatic void mthca_free_irqs(struct mthca_dev *dev) 62962306a36Sopenharmony_ci{ 63062306a36Sopenharmony_ci int i; 63162306a36Sopenharmony_ci 63262306a36Sopenharmony_ci if (dev->eq_table.have_irq) 63362306a36Sopenharmony_ci free_irq(dev->pdev->irq, dev); 63462306a36Sopenharmony_ci for (i = 0; i < MTHCA_NUM_EQ; ++i) 63562306a36Sopenharmony_ci if (dev->eq_table.eq[i].have_irq) { 63662306a36Sopenharmony_ci free_irq(dev->eq_table.eq[i].msi_x_vector, 63762306a36Sopenharmony_ci dev->eq_table.eq + i); 63862306a36Sopenharmony_ci dev->eq_table.eq[i].have_irq = 0; 63962306a36Sopenharmony_ci } 64062306a36Sopenharmony_ci} 64162306a36Sopenharmony_ci 64262306a36Sopenharmony_cistatic int mthca_map_reg(struct mthca_dev *dev, 64362306a36Sopenharmony_ci unsigned long offset, unsigned long size, 64462306a36Sopenharmony_ci void __iomem **map) 64562306a36Sopenharmony_ci{ 64662306a36Sopenharmony_ci phys_addr_t base = pci_resource_start(dev->pdev, 0); 64762306a36Sopenharmony_ci 64862306a36Sopenharmony_ci *map = ioremap(base + offset, size); 64962306a36Sopenharmony_ci if (!*map) 65062306a36Sopenharmony_ci return -ENOMEM; 65162306a36Sopenharmony_ci 65262306a36Sopenharmony_ci return 0; 65362306a36Sopenharmony_ci} 65462306a36Sopenharmony_ci 65562306a36Sopenharmony_cistatic int mthca_map_eq_regs(struct mthca_dev *dev) 65662306a36Sopenharmony_ci{ 65762306a36Sopenharmony_ci if (mthca_is_memfree(dev)) { 65862306a36Sopenharmony_ci /* 65962306a36Sopenharmony_ci * We assume that the EQ arm and EQ set CI registers 66062306a36Sopenharmony_ci * fall within the first BAR. We can't trust the 66162306a36Sopenharmony_ci * values firmware gives us, since those addresses are 66262306a36Sopenharmony_ci * valid on the HCA's side of the PCI bus but not 66362306a36Sopenharmony_ci * necessarily the host side. 66462306a36Sopenharmony_ci */ 66562306a36Sopenharmony_ci if (mthca_map_reg(dev, (pci_resource_len(dev->pdev, 0) - 1) & 66662306a36Sopenharmony_ci dev->fw.arbel.clr_int_base, MTHCA_CLR_INT_SIZE, 66762306a36Sopenharmony_ci &dev->clr_base)) { 66862306a36Sopenharmony_ci mthca_err(dev, "Couldn't map interrupt clear register, " 66962306a36Sopenharmony_ci "aborting.\n"); 67062306a36Sopenharmony_ci return -ENOMEM; 67162306a36Sopenharmony_ci } 67262306a36Sopenharmony_ci 67362306a36Sopenharmony_ci /* 67462306a36Sopenharmony_ci * Add 4 because we limit ourselves to EQs 0 ... 31, 67562306a36Sopenharmony_ci * so we only need the low word of the register. 67662306a36Sopenharmony_ci */ 67762306a36Sopenharmony_ci if (mthca_map_reg(dev, ((pci_resource_len(dev->pdev, 0) - 1) & 67862306a36Sopenharmony_ci dev->fw.arbel.eq_arm_base) + 4, 4, 67962306a36Sopenharmony_ci &dev->eq_regs.arbel.eq_arm)) { 68062306a36Sopenharmony_ci mthca_err(dev, "Couldn't map EQ arm register, aborting.\n"); 68162306a36Sopenharmony_ci iounmap(dev->clr_base); 68262306a36Sopenharmony_ci return -ENOMEM; 68362306a36Sopenharmony_ci } 68462306a36Sopenharmony_ci 68562306a36Sopenharmony_ci if (mthca_map_reg(dev, (pci_resource_len(dev->pdev, 0) - 1) & 68662306a36Sopenharmony_ci dev->fw.arbel.eq_set_ci_base, 68762306a36Sopenharmony_ci MTHCA_EQ_SET_CI_SIZE, 68862306a36Sopenharmony_ci &dev->eq_regs.arbel.eq_set_ci_base)) { 68962306a36Sopenharmony_ci mthca_err(dev, "Couldn't map EQ CI register, aborting.\n"); 69062306a36Sopenharmony_ci iounmap(dev->eq_regs.arbel.eq_arm); 69162306a36Sopenharmony_ci iounmap(dev->clr_base); 69262306a36Sopenharmony_ci return -ENOMEM; 69362306a36Sopenharmony_ci } 69462306a36Sopenharmony_ci } else { 69562306a36Sopenharmony_ci if (mthca_map_reg(dev, MTHCA_CLR_INT_BASE, MTHCA_CLR_INT_SIZE, 69662306a36Sopenharmony_ci &dev->clr_base)) { 69762306a36Sopenharmony_ci mthca_err(dev, "Couldn't map interrupt clear register, " 69862306a36Sopenharmony_ci "aborting.\n"); 69962306a36Sopenharmony_ci return -ENOMEM; 70062306a36Sopenharmony_ci } 70162306a36Sopenharmony_ci 70262306a36Sopenharmony_ci if (mthca_map_reg(dev, MTHCA_ECR_BASE, 70362306a36Sopenharmony_ci MTHCA_ECR_SIZE + MTHCA_ECR_CLR_SIZE, 70462306a36Sopenharmony_ci &dev->eq_regs.tavor.ecr_base)) { 70562306a36Sopenharmony_ci mthca_err(dev, "Couldn't map ecr register, " 70662306a36Sopenharmony_ci "aborting.\n"); 70762306a36Sopenharmony_ci iounmap(dev->clr_base); 70862306a36Sopenharmony_ci return -ENOMEM; 70962306a36Sopenharmony_ci } 71062306a36Sopenharmony_ci } 71162306a36Sopenharmony_ci 71262306a36Sopenharmony_ci return 0; 71362306a36Sopenharmony_ci 71462306a36Sopenharmony_ci} 71562306a36Sopenharmony_ci 71662306a36Sopenharmony_cistatic void mthca_unmap_eq_regs(struct mthca_dev *dev) 71762306a36Sopenharmony_ci{ 71862306a36Sopenharmony_ci if (mthca_is_memfree(dev)) { 71962306a36Sopenharmony_ci iounmap(dev->eq_regs.arbel.eq_set_ci_base); 72062306a36Sopenharmony_ci iounmap(dev->eq_regs.arbel.eq_arm); 72162306a36Sopenharmony_ci iounmap(dev->clr_base); 72262306a36Sopenharmony_ci } else { 72362306a36Sopenharmony_ci iounmap(dev->eq_regs.tavor.ecr_base); 72462306a36Sopenharmony_ci iounmap(dev->clr_base); 72562306a36Sopenharmony_ci } 72662306a36Sopenharmony_ci} 72762306a36Sopenharmony_ci 72862306a36Sopenharmony_ciint mthca_map_eq_icm(struct mthca_dev *dev, u64 icm_virt) 72962306a36Sopenharmony_ci{ 73062306a36Sopenharmony_ci int ret; 73162306a36Sopenharmony_ci 73262306a36Sopenharmony_ci /* 73362306a36Sopenharmony_ci * We assume that mapping one page is enough for the whole EQ 73462306a36Sopenharmony_ci * context table. This is fine with all current HCAs, because 73562306a36Sopenharmony_ci * we only use 32 EQs and each EQ uses 32 bytes of context 73662306a36Sopenharmony_ci * memory, or 1 KB total. 73762306a36Sopenharmony_ci */ 73862306a36Sopenharmony_ci dev->eq_table.icm_virt = icm_virt; 73962306a36Sopenharmony_ci dev->eq_table.icm_page = alloc_page(GFP_HIGHUSER); 74062306a36Sopenharmony_ci if (!dev->eq_table.icm_page) 74162306a36Sopenharmony_ci return -ENOMEM; 74262306a36Sopenharmony_ci dev->eq_table.icm_dma = 74362306a36Sopenharmony_ci dma_map_page(&dev->pdev->dev, dev->eq_table.icm_page, 0, 74462306a36Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 74562306a36Sopenharmony_ci if (dma_mapping_error(&dev->pdev->dev, dev->eq_table.icm_dma)) { 74662306a36Sopenharmony_ci __free_page(dev->eq_table.icm_page); 74762306a36Sopenharmony_ci return -ENOMEM; 74862306a36Sopenharmony_ci } 74962306a36Sopenharmony_ci 75062306a36Sopenharmony_ci ret = mthca_MAP_ICM_page(dev, dev->eq_table.icm_dma, icm_virt); 75162306a36Sopenharmony_ci if (ret) { 75262306a36Sopenharmony_ci dma_unmap_page(&dev->pdev->dev, dev->eq_table.icm_dma, 75362306a36Sopenharmony_ci PAGE_SIZE, DMA_BIDIRECTIONAL); 75462306a36Sopenharmony_ci __free_page(dev->eq_table.icm_page); 75562306a36Sopenharmony_ci } 75662306a36Sopenharmony_ci 75762306a36Sopenharmony_ci return ret; 75862306a36Sopenharmony_ci} 75962306a36Sopenharmony_ci 76062306a36Sopenharmony_civoid mthca_unmap_eq_icm(struct mthca_dev *dev) 76162306a36Sopenharmony_ci{ 76262306a36Sopenharmony_ci mthca_UNMAP_ICM(dev, dev->eq_table.icm_virt, 1); 76362306a36Sopenharmony_ci dma_unmap_page(&dev->pdev->dev, dev->eq_table.icm_dma, PAGE_SIZE, 76462306a36Sopenharmony_ci DMA_BIDIRECTIONAL); 76562306a36Sopenharmony_ci __free_page(dev->eq_table.icm_page); 76662306a36Sopenharmony_ci} 76762306a36Sopenharmony_ci 76862306a36Sopenharmony_ciint mthca_init_eq_table(struct mthca_dev *dev) 76962306a36Sopenharmony_ci{ 77062306a36Sopenharmony_ci int err; 77162306a36Sopenharmony_ci u8 intr; 77262306a36Sopenharmony_ci int i; 77362306a36Sopenharmony_ci 77462306a36Sopenharmony_ci err = mthca_alloc_init(&dev->eq_table.alloc, 77562306a36Sopenharmony_ci dev->limits.num_eqs, 77662306a36Sopenharmony_ci dev->limits.num_eqs - 1, 77762306a36Sopenharmony_ci dev->limits.reserved_eqs); 77862306a36Sopenharmony_ci if (err) 77962306a36Sopenharmony_ci return err; 78062306a36Sopenharmony_ci 78162306a36Sopenharmony_ci err = mthca_map_eq_regs(dev); 78262306a36Sopenharmony_ci if (err) 78362306a36Sopenharmony_ci goto err_out_free; 78462306a36Sopenharmony_ci 78562306a36Sopenharmony_ci if (dev->mthca_flags & MTHCA_FLAG_MSI_X) { 78662306a36Sopenharmony_ci dev->eq_table.clr_mask = 0; 78762306a36Sopenharmony_ci } else { 78862306a36Sopenharmony_ci dev->eq_table.clr_mask = 78962306a36Sopenharmony_ci swab32(1 << (dev->eq_table.inta_pin & 31)); 79062306a36Sopenharmony_ci dev->eq_table.clr_int = dev->clr_base + 79162306a36Sopenharmony_ci (dev->eq_table.inta_pin < 32 ? 4 : 0); 79262306a36Sopenharmony_ci } 79362306a36Sopenharmony_ci 79462306a36Sopenharmony_ci dev->eq_table.arm_mask = 0; 79562306a36Sopenharmony_ci 79662306a36Sopenharmony_ci intr = dev->eq_table.inta_pin; 79762306a36Sopenharmony_ci 79862306a36Sopenharmony_ci err = mthca_create_eq(dev, dev->limits.num_cqs + MTHCA_NUM_SPARE_EQE, 79962306a36Sopenharmony_ci (dev->mthca_flags & MTHCA_FLAG_MSI_X) ? 128 : intr, 80062306a36Sopenharmony_ci &dev->eq_table.eq[MTHCA_EQ_COMP]); 80162306a36Sopenharmony_ci if (err) 80262306a36Sopenharmony_ci goto err_out_unmap; 80362306a36Sopenharmony_ci 80462306a36Sopenharmony_ci err = mthca_create_eq(dev, MTHCA_NUM_ASYNC_EQE + MTHCA_NUM_SPARE_EQE, 80562306a36Sopenharmony_ci (dev->mthca_flags & MTHCA_FLAG_MSI_X) ? 129 : intr, 80662306a36Sopenharmony_ci &dev->eq_table.eq[MTHCA_EQ_ASYNC]); 80762306a36Sopenharmony_ci if (err) 80862306a36Sopenharmony_ci goto err_out_comp; 80962306a36Sopenharmony_ci 81062306a36Sopenharmony_ci err = mthca_create_eq(dev, MTHCA_NUM_CMD_EQE + MTHCA_NUM_SPARE_EQE, 81162306a36Sopenharmony_ci (dev->mthca_flags & MTHCA_FLAG_MSI_X) ? 130 : intr, 81262306a36Sopenharmony_ci &dev->eq_table.eq[MTHCA_EQ_CMD]); 81362306a36Sopenharmony_ci if (err) 81462306a36Sopenharmony_ci goto err_out_async; 81562306a36Sopenharmony_ci 81662306a36Sopenharmony_ci if (dev->mthca_flags & MTHCA_FLAG_MSI_X) { 81762306a36Sopenharmony_ci static const char *eq_name[] = { 81862306a36Sopenharmony_ci [MTHCA_EQ_COMP] = DRV_NAME "-comp", 81962306a36Sopenharmony_ci [MTHCA_EQ_ASYNC] = DRV_NAME "-async", 82062306a36Sopenharmony_ci [MTHCA_EQ_CMD] = DRV_NAME "-cmd" 82162306a36Sopenharmony_ci }; 82262306a36Sopenharmony_ci 82362306a36Sopenharmony_ci for (i = 0; i < MTHCA_NUM_EQ; ++i) { 82462306a36Sopenharmony_ci snprintf(dev->eq_table.eq[i].irq_name, 82562306a36Sopenharmony_ci IB_DEVICE_NAME_MAX, 82662306a36Sopenharmony_ci "%s@pci:%s", eq_name[i], 82762306a36Sopenharmony_ci pci_name(dev->pdev)); 82862306a36Sopenharmony_ci err = request_irq(dev->eq_table.eq[i].msi_x_vector, 82962306a36Sopenharmony_ci mthca_is_memfree(dev) ? 83062306a36Sopenharmony_ci mthca_arbel_msi_x_interrupt : 83162306a36Sopenharmony_ci mthca_tavor_msi_x_interrupt, 83262306a36Sopenharmony_ci 0, dev->eq_table.eq[i].irq_name, 83362306a36Sopenharmony_ci dev->eq_table.eq + i); 83462306a36Sopenharmony_ci if (err) 83562306a36Sopenharmony_ci goto err_out_cmd; 83662306a36Sopenharmony_ci dev->eq_table.eq[i].have_irq = 1; 83762306a36Sopenharmony_ci } 83862306a36Sopenharmony_ci } else { 83962306a36Sopenharmony_ci snprintf(dev->eq_table.eq[0].irq_name, IB_DEVICE_NAME_MAX, 84062306a36Sopenharmony_ci DRV_NAME "@pci:%s", pci_name(dev->pdev)); 84162306a36Sopenharmony_ci err = request_irq(dev->pdev->irq, 84262306a36Sopenharmony_ci mthca_is_memfree(dev) ? 84362306a36Sopenharmony_ci mthca_arbel_interrupt : 84462306a36Sopenharmony_ci mthca_tavor_interrupt, 84562306a36Sopenharmony_ci IRQF_SHARED, dev->eq_table.eq[0].irq_name, dev); 84662306a36Sopenharmony_ci if (err) 84762306a36Sopenharmony_ci goto err_out_cmd; 84862306a36Sopenharmony_ci dev->eq_table.have_irq = 1; 84962306a36Sopenharmony_ci } 85062306a36Sopenharmony_ci 85162306a36Sopenharmony_ci err = mthca_MAP_EQ(dev, async_mask(dev), 85262306a36Sopenharmony_ci 0, dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn); 85362306a36Sopenharmony_ci if (err) 85462306a36Sopenharmony_ci mthca_warn(dev, "MAP_EQ for async EQ %d failed (%d)\n", 85562306a36Sopenharmony_ci dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn, err); 85662306a36Sopenharmony_ci 85762306a36Sopenharmony_ci err = mthca_MAP_EQ(dev, MTHCA_CMD_EVENT_MASK, 85862306a36Sopenharmony_ci 0, dev->eq_table.eq[MTHCA_EQ_CMD].eqn); 85962306a36Sopenharmony_ci if (err) 86062306a36Sopenharmony_ci mthca_warn(dev, "MAP_EQ for cmd EQ %d failed (%d)\n", 86162306a36Sopenharmony_ci dev->eq_table.eq[MTHCA_EQ_CMD].eqn, err); 86262306a36Sopenharmony_ci 86362306a36Sopenharmony_ci for (i = 0; i < MTHCA_NUM_EQ; ++i) 86462306a36Sopenharmony_ci if (mthca_is_memfree(dev)) 86562306a36Sopenharmony_ci arbel_eq_req_not(dev, dev->eq_table.eq[i].eqn_mask); 86662306a36Sopenharmony_ci else 86762306a36Sopenharmony_ci tavor_eq_req_not(dev, dev->eq_table.eq[i].eqn); 86862306a36Sopenharmony_ci 86962306a36Sopenharmony_ci return 0; 87062306a36Sopenharmony_ci 87162306a36Sopenharmony_cierr_out_cmd: 87262306a36Sopenharmony_ci mthca_free_irqs(dev); 87362306a36Sopenharmony_ci mthca_free_eq(dev, &dev->eq_table.eq[MTHCA_EQ_CMD]); 87462306a36Sopenharmony_ci 87562306a36Sopenharmony_cierr_out_async: 87662306a36Sopenharmony_ci mthca_free_eq(dev, &dev->eq_table.eq[MTHCA_EQ_ASYNC]); 87762306a36Sopenharmony_ci 87862306a36Sopenharmony_cierr_out_comp: 87962306a36Sopenharmony_ci mthca_free_eq(dev, &dev->eq_table.eq[MTHCA_EQ_COMP]); 88062306a36Sopenharmony_ci 88162306a36Sopenharmony_cierr_out_unmap: 88262306a36Sopenharmony_ci mthca_unmap_eq_regs(dev); 88362306a36Sopenharmony_ci 88462306a36Sopenharmony_cierr_out_free: 88562306a36Sopenharmony_ci mthca_alloc_cleanup(&dev->eq_table.alloc); 88662306a36Sopenharmony_ci return err; 88762306a36Sopenharmony_ci} 88862306a36Sopenharmony_ci 88962306a36Sopenharmony_civoid mthca_cleanup_eq_table(struct mthca_dev *dev) 89062306a36Sopenharmony_ci{ 89162306a36Sopenharmony_ci int i; 89262306a36Sopenharmony_ci 89362306a36Sopenharmony_ci mthca_free_irqs(dev); 89462306a36Sopenharmony_ci 89562306a36Sopenharmony_ci mthca_MAP_EQ(dev, async_mask(dev), 89662306a36Sopenharmony_ci 1, dev->eq_table.eq[MTHCA_EQ_ASYNC].eqn); 89762306a36Sopenharmony_ci mthca_MAP_EQ(dev, MTHCA_CMD_EVENT_MASK, 89862306a36Sopenharmony_ci 1, dev->eq_table.eq[MTHCA_EQ_CMD].eqn); 89962306a36Sopenharmony_ci 90062306a36Sopenharmony_ci for (i = 0; i < MTHCA_NUM_EQ; ++i) 90162306a36Sopenharmony_ci mthca_free_eq(dev, &dev->eq_table.eq[i]); 90262306a36Sopenharmony_ci 90362306a36Sopenharmony_ci mthca_unmap_eq_regs(dev); 90462306a36Sopenharmony_ci 90562306a36Sopenharmony_ci mthca_alloc_cleanup(&dev->eq_table.alloc); 90662306a36Sopenharmony_ci} 907