summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
-rw-r--r--arch/powerpc/sysdev/fsl_pci.h33
-rw-r--r--drivers/vfio/fsl_pci_ep/fsl_pci_ep.c197
-rw-r--r--drivers/vfio/fsl_pci_ep/fsl_pci_ep.h4
-rw-r--r--drivers/vfio/fsl_pci_ep/fsl_pci_ep_vfio.c1
-rw-r--r--include/uapi/linux/fsl_pci_ep_vfio.h2
5 files changed, 233 insertions, 4 deletions
diff --git a/arch/powerpc/sysdev/fsl_pci.h b/arch/powerpc/sysdev/fsl_pci.h
index 878fb29..f1b39d9 100644
--- a/arch/powerpc/sysdev/fsl_pci.h
+++ b/arch/powerpc/sysdev/fsl_pci.h
@@ -32,6 +32,22 @@ struct platform_device;
#define PIWAR_WRITE_SNOOP 0x00005000
#define PIWAR_SZ_MASK 0x0000003f
+#define PAOR_LIODON_MODE 1 /* To access LIODN permission table */
+#define PAOR_MSIX_VECTOR_MODE 2 /* To access MSI-X table structure */
+#define PAOR_MSIX_PBA_MODE 3 /* To access MSI-X PBA structure */
+#define PAOR_MSIX_PF_TYPE 0 /* Access physical function MSIX */
+#define PAOR_MSIX_VF_TYPE 1 /* Access virtual function MSIX */
+#define PAOR_MSIX_TYPE_SHIFT 31
+#define PAOR_MSIX_PF_SHIFT 26
+#define PAOR_MSIX_VF_SHIFT 16
+#define PAOR_MSIX_ENTRY_IDX_SHIFT 8
+#define PAOR_MSIX_ENTRY_EIDX_SHIFT 4
+#define PAOR_MSIX_CONTROL_IDX 3
+#define PAOR_MSIX_MSG_DATA_IDX 2
+#define PAOR_MSIX_MSG_UADDR_IDX 1
+#define PAOR_MSIX_MSG_LADDR_IDX 0
+#define PCIE_MSIX_VECTOR_MAX_NUM 8
+
/* PCI/PCI Express outbound window reg */
struct pci_outbound_window_regs {
__be32 potar; /* 0x.0 - Outbound translation address register */
@@ -91,6 +107,14 @@ struct pcie_err_regs {
u8 res24[4];
};
+/* PCI Express Utility Registers */
+struct pcie_utility_regs {
+ __be32 pex_aor; /* 0x050 - PCIE address offset register */
+ u8 res_0x054[4];
+ __be32 pex_udr; /* 0x058 - PCIE upper data register */
+ __be32 pex_ldr; /* 0x05c - PCIE lower data register */
+};
+
/* PCI/PCI Express IO block registers for 85xx/86xx */
struct ccsr_pci {
__be32 config_addr; /* 0x.000 - PCI/PCIE Configuration Address Register */
@@ -105,7 +129,9 @@ struct ccsr_pci {
__be32 pex_pme_mes_disr; /* 0x.024 - PCIE PME and message disable register */
__be32 pex_pme_mes_ier; /* 0x.028 - PCIE PME and message interrupt enable register */
__be32 pex_pmcr; /* 0x.02c - PCIE power management command register */
- u8 res3[3016];
+ u8 res_030[32];
+ struct pcie_utility_regs putil;
+ u8 res_060[2968];
__be32 block_rev1; /* 0x.bf8 - PCIE Block Revision register 1 */
__be32 block_rev2; /* 0x.bfc - PCIE Block Revision register 2 */
@@ -115,7 +141,10 @@ struct ccsr_pci {
* in all of the other outbound windows.
*/
struct pci_outbound_window_regs pow[5];
- u8 res14[96];
+ u8 res_ca0[32];
+ /* 0xcc0 - 0xcdc MSI-X Trap Outbound Window Address Registers */
+ struct pci_outbound_window_regs msixow;
+ u8 res_ce0[32];
struct pci_inbound_window_regs pmit; /* 0xd00 - 0xd9c Inbound MSI */
u8 res6[96];
/* PCI/PCI Express inbound window 3-0
diff --git a/drivers/vfio/fsl_pci_ep/fsl_pci_ep.c b/drivers/vfio/fsl_pci_ep/fsl_pci_ep.c
index 45e7eec..8fda88d 100644
--- a/drivers/vfio/fsl_pci_ep/fsl_pci_ep.c
+++ b/drivers/vfio/fsl_pci_ep/fsl_pci_ep.c
@@ -47,6 +47,62 @@ LIST_HEAD(pci_ep_controllers);
static int global_phb_number; /* Global phb counter */
+u32 fsl_pci_msix_vector_read(struct pci_ep_dev *ep, int vector, int eidx)
+{
+ struct pcie_utility_regs *putil;
+ int type, vf_idx, addr, data;
+
+ putil = &ep->pf->regs->putil;
+
+ if (ep->type == PCI_EP_TYPE_PF) {
+ type = PAOR_MSIX_PF_TYPE;
+ vf_idx = 0;
+ } else {
+ type = PAOR_MSIX_VF_TYPE;
+ vf_idx = ep->idx - 1;
+ }
+
+ addr = type << PAOR_MSIX_TYPE_SHIFT |
+ ep->pf->idx << PAOR_MSIX_PF_SHIFT |
+ vf_idx << PAOR_MSIX_VF_SHIFT |
+ vector << PAOR_MSIX_ENTRY_IDX_SHIFT |
+ eidx << PAOR_MSIX_ENTRY_EIDX_SHIFT |
+ PAOR_MSIX_VECTOR_MODE;
+
+ iowrite32be(addr, &putil->pex_aor);
+ data = ioread32(&putil->pex_ldr);
+
+ return data;
+}
+
+u32 fsl_pci_msix_pba_read(struct pci_ep_dev *ep, int vector)
+{
+ struct pcie_utility_regs *putil;
+ int type, vf_idx, addr, data;
+
+ putil = &ep->pf->regs->putil;
+
+ if (ep->type == PCI_EP_TYPE_PF) {
+ type = PAOR_MSIX_PF_TYPE;
+ vf_idx = 0;
+ } else {
+ type = PAOR_MSIX_VF_TYPE;
+ vf_idx = ep->idx - 1;
+ }
+
+ addr = type << PAOR_MSIX_TYPE_SHIFT |
+ ep->pf->idx << PAOR_MSIX_PF_SHIFT |
+ vf_idx << PAOR_MSIX_VF_SHIFT |
+ vector << PAOR_MSIX_ENTRY_IDX_SHIFT |
+ (vector / 32) << PAOR_MSIX_ENTRY_EIDX_SHIFT |
+ PAOR_MSIX_PBA_MODE;
+
+ iowrite32be(addr, &putil->pex_aor);
+ data = ioread32(&putil->pex_ldr);
+
+ return data;
+}
+
static inline size_t attr_to_size(u32 attr)
{
int bits = (attr & 0x3f);
@@ -202,6 +258,29 @@ static int fsl_pci_ep_get_obwin(struct pci_ep_dev *ep,
return 0;
}
+static int fsl_pci_ep_get_msixobwin(struct pci_ep_dev *ep,
+ struct pci_ep_win *win)
+{
+ struct pci_pf_dev *pf = ep->pf;
+ struct pci_outbound_window_regs *ow_regs;
+
+
+ if (ep->type != PCI_EP_TYPE_PF)
+ return -EINVAL;
+
+ ow_regs = &pf->regs->msixow;
+
+ spin_lock(&pf->lock);
+ win->cpu_addr = ((u64)in_be32(&ow_regs->powbar)) << 12;
+ win->pci_addr = 0;
+ win->attr = in_be32(&ow_regs->powar);
+ spin_unlock(&pf->lock);
+
+ win->size = attr_to_size(win->attr);
+
+ return 0;
+}
+
static int fsl_pci_ep_get_vfibwin(struct pci_ep_dev *ep,
struct pci_ep_win *win)
{
@@ -307,7 +386,10 @@ int fsl_pci_ep_get_win(struct pci_ep_dev *ep, struct pci_ep_win *win)
break;
case PCI_EP_REGION_MEM:
ret = fsl_pci_ep_get_mem_win(ep, win);
-
+ break;
+ case PCI_EP_REGION_MSIX_OBWIN:
+ ret = fsl_pci_ep_get_msixobwin(ep, win);
+ break;
default:
ret = -EINVAL;
}
@@ -393,6 +475,32 @@ static int fsl_pci_ep_set_obwin(struct pci_ep_dev *ep,
return 0;
}
+static int fsl_pci_ep_set_msixobwin(struct pci_ep_dev *ep,
+ struct pci_ep_win *win)
+{
+ struct pci_pf_dev *pf = ep->pf;
+ struct pci_outbound_window_regs *ow_regs;
+ u32 flags = 0x8000400b; /* enable 4K MSIX outbound window */
+
+ ow_regs = &pf->regs->msixow;
+
+ if (ep->type != PCI_EP_TYPE_PF)
+ return -EINVAL;
+
+ if (win->size != FSL_PCI_EP_MSIX_OW_SIZE)
+ return -EINVAL;
+
+ spin_lock(&pf->lock);
+ out_be32(&ow_regs->powbar, win->cpu_addr >> 12);
+ if (win->attr) /* use the specific attribute */
+ out_be32(&ow_regs->powar, win->attr);
+ else /* use the default attribute */
+ out_be32(&ow_regs->powar, flags);
+ spin_unlock(&pf->lock);
+
+ return 0;
+}
+
static int fsl_pci_ep_set_vfibwin(struct pci_ep_dev *ep,
struct pci_ep_win *win)
{
@@ -471,6 +579,9 @@ int fsl_pci_ep_set_win(struct pci_ep_dev *ep, struct pci_ep_win *win)
case PCI_EP_REGION_VF_OBWIN:
ret = fsl_pci_ep_set_vfobwin(ep, win);
break;
+ case PCI_EP_REGION_MSIX_OBWIN:
+ ret = fsl_pci_ep_set_msixobwin(ep, win);
+ break;
default:
ret = -EINVAL;
}
@@ -659,6 +770,16 @@ outbound_windows_show(struct device *dev,
if (ep->type == PCI_EP_TYPE_PF) {
struct pci_pf_dev *pf = ep->pf;
+ if (pf->msix_enable) {
+ fsl_pci_ep_get_msixobwin(ep, &win);
+ str += sprintf(str, "MSIX Outbound Window:\n"
+ "\tcpu_addr:0x%016llx\n"
+ "\twin_size:0x%016llx"
+ " attribute:0x%08x\n",
+ win.cpu_addr,
+ win.size,
+ win.attr);
+ }
if (pf->vf_regs) {
for (i = 0; i < pf->vf_ow_num; i++) {
@@ -706,12 +827,43 @@ ep_type_show(struct device *dev, struct device_attribute *attr, char *buf)
ep->type == PCI_EP_TYPE_PF ? "PF" : "VF");
}
+static ssize_t
+msix_show(struct device *dev, struct device_attribute *attr, char *buf)
+{
+ struct pci_ep_dev *ep = container_of(dev, struct pci_ep_dev, dev);
+ char *str = buf;
+ int i;
+
+ if (!ep->pf->msix_enable) {
+ str += sprintf(str, "Not support MSIX\n");
+ return str - buf;
+ }
+
+ for (i = 0; i < PCIE_MSIX_VECTOR_MAX_NUM; i++) {
+ str += sprintf(str,
+ "MSIX venctor %d:\n"
+ "\tcontrol:0x%x data:0x%08x addr:0x%016llx\n",
+ i,
+ fsl_pci_msix_vector_read(ep, i,
+ PAOR_MSIX_CONTROL_IDX),
+ fsl_pci_msix_vector_read(ep, i,
+ PAOR_MSIX_MSG_DATA_IDX),
+ (u64) fsl_pci_msix_vector_read(ep, i,
+ PAOR_MSIX_MSG_UADDR_IDX) << 32 |
+ fsl_pci_msix_vector_read(ep, i,
+ PAOR_MSIX_MSG_LADDR_IDX));
+ }
+
+ return str - buf;
+}
+
struct device_attribute pci_ep_attrs[] = {
__ATTR_RO(ep_type),
__ATTR_RO(pf_idx),
__ATTR_RO(vf_idx),
__ATTR_RO(inbound_windows),
__ATTR_RO(outbound_windows),
+ __ATTR_RO(msix),
__ATTR_NULL,
};
@@ -987,7 +1139,7 @@ static int fsl_pci_pf_atmu_init(struct pci_pf_dev *pf)
struct pci_ep_win win;
int i, bits;
int win_idx = 3, start_idx = 1, end_idx = 4;
- u64 sz;
+ u64 sz, reserve_sz = 0, free_sz;
if (in_be32(&pf->regs->block_rev1) >= PCIE_IP_REV_2_2) {
win_idx = 2;
@@ -1013,6 +1165,17 @@ static int fsl_pci_pf_atmu_init(struct pci_pf_dev *pf)
win.attr = 0;
win.idx = 0;
fsl_pci_ep_set_vfobwin(ep, &win);
+
+ /* Setup VF MSIX inbound windows*/
+ if (pf->msix_enable) {
+ win.cpu_addr = 0;
+ win.pci_addr = 0;
+ win.size = FSL_PCI_EP_MSIX_IW_SIZE;
+ win.attr = FSL_PCI_MSIX_IW_ATTR |
+ (ilog2(win.size) - 1);
+ win.idx = PCI_EP_MSI_WIN_INDEX;
+ fsl_pci_ep_set_vfibwin(ep, &win);
+ }
}
/* Disable all PF windows (except powar0 since it's ignored) */
@@ -1021,14 +1184,41 @@ static int fsl_pci_pf_atmu_init(struct pci_pf_dev *pf)
for (i = start_idx; i < end_idx; i++)
out_be32(&pf->regs->piw[i].piwar, 0);
+ /* Setup PF MSIX outbound windows */
+ if (pf->msix_enable) {
+ win.cpu_addr = pf->mem_resources[0].end -
+ FSL_PCI_EP_MSIX_OW_SIZE + 1;
+ win.size = FSL_PCI_EP_MSIX_OW_SIZE;
+ win.attr = 0;
+ win.idx = 0;
+ fsl_pci_ep_set_msixobwin(ep, &win);
+ reserve_sz = FSL_PCI_EP_MSIX_OW_SIZE;
+ }
+
/* Setup PF outbound windows */
win.cpu_addr = pf->mem_resources[0].start + pf->vf_total * sz;
win.pci_addr = win.cpu_addr - pf->pci_mem_offset;
+ free_sz = pf->mem_resources[0].end - win.cpu_addr - reserve_sz + 1;
+ if (free_sz < sz) {
+ bits = ilog2(free_sz);
+ sz = 1ull << bits;
+ }
win.size = sz;
win.attr = 0;
win.idx = 1;
fsl_pci_ep_set_obwin(ep, &win);
+ /* Setup VF MSIX inbound windows*/
+ if (pf->msix_enable) {
+ win.cpu_addr = 0;
+ win.pci_addr = 0;
+ win.size = FSL_PCI_EP_MSIX_IW_SIZE;
+ win.attr = FSL_PCI_MSIX_IW_ATTR |
+ (ilog2(win.size) - 1);
+ win.idx = PCI_EP_MSI_WIN_INDEX;
+ fsl_pci_ep_set_ibwin(ep, &win);
+ }
+
return 0;
}
@@ -1160,6 +1350,9 @@ int fsl_pci_pf_setup(struct pci_bus *bus, int pf_num)
goto _err;
}
+ if (pci_find_capability(pf->pdev, PCI_CAP_ID_MSIX))
+ pf->msix_enable = true;
+
pf->pci_mem_offset = host->pci_mem_offset;
for (i = 0; i < 3; i++) {
mem_size = resource_size(&host->mem_resources[i]);
diff --git a/drivers/vfio/fsl_pci_ep/fsl_pci_ep.h b/drivers/vfio/fsl_pci_ep/fsl_pci_ep.h
index 2681ed7..a46ec92 100644
--- a/drivers/vfio/fsl_pci_ep/fsl_pci_ep.h
+++ b/drivers/vfio/fsl_pci_ep/fsl_pci_ep.h
@@ -27,6 +27,9 @@
#define PCI_EP_PF_OFFSET 0x2000
#define FSL_PCI_EP_BAR_NUM 4
#define FSL_PCI_EP_OW_NUM 5
+#define FSL_PCI_EP_MSIX_OW_SIZE (4 * 1024) /* 4k */
+#define FSL_PCI_EP_MSIX_IW_SIZE (8 * 1024) /* 8k */
+#define FSL_PCI_MSIX_IW_ATTR 0x80f44000
#define PCI_EP_WIN_INDEX_SHIFT 36
#define PCI_EP_WIN_INDEX_MASK 0xf
@@ -67,6 +70,7 @@ struct pci_pf_dev {
struct ccsr_pci __iomem *regs;
u8 iw_num;
u8 ow_num;
+ bool msix_enable;
resource_size_t pci_mem_offset;
struct resource mem_resources[3];
/* VF info */
diff --git a/drivers/vfio/fsl_pci_ep/fsl_pci_ep_vfio.c b/drivers/vfio/fsl_pci_ep/fsl_pci_ep_vfio.c
index 4285ed0..1218f2d 100644
--- a/drivers/vfio/fsl_pci_ep/fsl_pci_ep_vfio.c
+++ b/drivers/vfio/fsl_pci_ep/fsl_pci_ep_vfio.c
@@ -81,6 +81,7 @@ static long fsl_pci_ep_vfio_ioctl(void *device_data,
info.type = ep->type;
info.pf_idx = ep->pf->idx;
info.vf_idx = ep->idx;
+ info.msix_enable = ep->pf->msix_enable;
if (ep->type == PCI_EP_TYPE_PF) {
info.vf_iw_num = ep->pf->vf_iw_num;
info.vf_ow_num = ep->pf->vf_iw_num;
diff --git a/include/uapi/linux/fsl_pci_ep_vfio.h b/include/uapi/linux/fsl_pci_ep_vfio.h
index 1cf259f..8960157 100644
--- a/include/uapi/linux/fsl_pci_ep_vfio.h
+++ b/include/uapi/linux/fsl_pci_ep_vfio.h
@@ -36,6 +36,7 @@ enum PCI_EP_REGION_TYPE {
PCI_EP_REGION_REGS,
PCI_EP_REGION_CONFIG,
PCI_EP_REGION_MEM,
+ PCI_EP_REGION_MSIX_OBWIN
};
enum PCI_EP_REGION_INDEX {
@@ -72,6 +73,7 @@ struct pci_ep_info {
uint32_t ow_num;
uint32_t vf_iw_num;
uint32_t vf_ow_num;
+ bool msix_enable;
};
#endif