diff options
-rw-r--r-- | arch/powerpc/sysdev/fsl_pci.h | 33 | ||||
-rw-r--r-- | drivers/vfio/fsl_pci_ep/fsl_pci_ep.c | 197 | ||||
-rw-r--r-- | drivers/vfio/fsl_pci_ep/fsl_pci_ep.h | 4 | ||||
-rw-r--r-- | drivers/vfio/fsl_pci_ep/fsl_pci_ep_vfio.c | 1 | ||||
-rw-r--r-- | include/uapi/linux/fsl_pci_ep_vfio.h | 2 |
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 |