From 62f0a17ff5dfcba040175e9c1c90a39a08f26447 Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Thu, 28 Nov 2013 18:36:00 +0800 Subject: fsl_pci_ep: add MSIX support 1. The patch initializes MSIX trap outbound window, the application can map this window and trigger the MSIX interrupt. 2. The patch initializes MSIX inbound window which is used to store MSIX vector and PBA data. 3. Add sysfs node to display MSIX vector setting for example: # cat /sys/class/pci_ep/pci0-pf0/msix MSIX venctor 0: control:0x0 data:0x0000406c addr:0x00000000fee00000 MSIX venctor 1: control:0x0 data:0x0000407c addr:0x00000000fee00000 MSIX venctor 2: control:0x0 data:0x0000408c addr:0x00000000fee00000 MSIX venctor 3: control:0x0 data:0x0000409c addr:0x00000000fee00000 MSIX venctor 4: control:0x0 data:0x000040ac addr:0x00000000fee00000 MSIX venctor 5: control:0x0 data:0x00000000 addr:0x0000000000000000 MSIX venctor 6: control:0x0 data:0x00000000 addr:0x0000000000000000 MSIX venctor 7: control:0x0 data:0x00000000 addr:0x0000000000000000 Signed-off-by: Minghuan Lian Change-Id: I18a6f9056b3c630bba91f5f1dfef2eee01995926 Reviewed-on: http://git.am.freescale.net:8181/9605 Tested-by: Review Code-CDREVIEW Reviewed-by: Jose Rivera 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 -- cgit v0.10.2