diff options
author | Minghuan Lian <Minghuan.Lian@freescale.com> | 2013-11-28 10:36:00 (GMT) |
---|---|---|
committer | Jose Rivera <German.Rivera@freescale.com> | 2014-03-17 19:38:52 (GMT) |
commit | 62f0a17ff5dfcba040175e9c1c90a39a08f26447 (patch) | |
tree | e66a50350f1a29576444e7e20387ad0b4af3886b /drivers | |
parent | 28dc7598ca17f87ade7cb525be245153284af66d (diff) | |
download | linux-fsl-qoriq-62f0a17ff5dfcba040175e9c1c90a39a08f26447.tar.xz |
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 <Minghuan.Lian@freescale.com>
Change-Id: I18a6f9056b3c630bba91f5f1dfef2eee01995926
Reviewed-on: http://git.am.freescale.net:8181/9605
Tested-by: Review Code-CDREVIEW <CDREVIEW@freescale.com>
Reviewed-by: Jose Rivera <German.Rivera@freescale.com>
Diffstat (limited to 'drivers')
-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 |
3 files changed, 200 insertions, 2 deletions
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; |