From 28dc7598ca17f87ade7cb525be245153284af66d Mon Sep 17 00:00:00 2001 From: Minghuan Lian Date: Thu, 17 Oct 2013 15:15:17 +0800 Subject: fsl_pci_ep: add VF ATMU access support All VFs of a PF share the common inbound/outbound windows except translation registers of outbound windows. A VF can only change translation registers of outbound windows. A PF can change all ATMU of VF. The patch provides VF ATMU register definition and provides interfaces to access inbound/outbound windows. It also adds PCI_EP_REGION_MEM type to return PF's memory resource. The application can get and reassign the memory resource to VF. Signed-off-by: Minghuan Lian Change-Id: Iec877a8054ac47b64d9d94abb9bc32dc0450211e Reviewed-on: http://git.am.freescale.net:8181/9604 Tested-by: Review Code-CDREVIEW Reviewed-by: Tiefei Zang Reviewed-by: Jose Rivera diff --git a/arch/powerpc/sysdev/fsl_pci.h b/arch/powerpc/sysdev/fsl_pci.h index 2707da9..878fb29 100644 --- a/arch/powerpc/sysdev/fsl_pci.h +++ b/arch/powerpc/sysdev/fsl_pci.h @@ -144,6 +144,24 @@ struct ccsr_pci { }; +#define VF_ATMU_OFFSET 0x1000 +#define VF_OW_NUM 4 +#define VF_IW_NUM 4 + +/* PCIE VF outbound window translation address regs */ +struct vf_owta_regs { + __be32 tar; + __be32 tear; +}; + +/* PCIE VF ATMU regs */ +struct vf_atmu_regs { + struct pci_outbound_window_regs vfow[VF_OW_NUM];/* 0x1000 - 0x107c */ + struct pci_inbound_window_regs vfiw[VF_IW_NUM]; /* 0x1080 - 0x10fc */ + u8 res_1100[1792]; /* 0x1100 - 0x17fc */ + struct vf_owta_regs vfowta[VF_OW_NUM][64]; /* 0x1800 - 0x1ffc */ +}; + extern int fsl_add_bridge(struct platform_device *pdev, int is_primary); extern void fsl_pcibios_fixup_bus(struct pci_bus *bus); extern int mpc83xx_add_bridge(struct device_node *dev); diff --git a/drivers/vfio/fsl_pci_ep/fsl_pci_ep.c b/drivers/vfio/fsl_pci_ep/fsl_pci_ep.c index a29d97c..45e7eec 100644 --- a/drivers/vfio/fsl_pci_ep/fsl_pci_ep.c +++ b/drivers/vfio/fsl_pci_ep/fsl_pci_ep.c @@ -57,9 +57,9 @@ static inline size_t attr_to_size(u32 attr) return (size_t)2 << bits; } -static int fsl_pci_ep_find_bar_pos(struct pci_dev *dev, int idx) +static int fsl_pci_find_bar_pos(struct pci_dev *dev, int base, int idx) { - int i, bar32, pos = PCI_BASE_ADDRESS_0; + int i, bar32, pos = base; if (idx >= FSL_PCI_EP_BAR_NUM) return -EINVAL; @@ -75,16 +75,10 @@ static int fsl_pci_ep_find_bar_pos(struct pci_dev *dev, int idx) return pos; } -static u64 fsl_pci_ep_read_bar(struct pci_dev *dev, int idx) +static u64 fsl_pci_read_bar(struct pci_dev *dev, int pos) { u64 bar64; u32 bar32; - int pos; - - if (idx >= FSL_PCI_EP_BAR_NUM) - return 0; - - pos = fsl_pci_ep_find_bar_pos(dev, idx); pci_read_config_dword(dev, pos, &bar32); bar64 = bar32 & 0xfffffff0; @@ -97,6 +91,35 @@ static u64 fsl_pci_ep_read_bar(struct pci_dev *dev, int idx) return bar64; } +static u64 fsl_pci_pf_read_vfbar(struct pci_pf_dev *pf, int idx) +{ + struct pci_dev *pdev = pf->pdev; + int pos; + + pos = fsl_pci_find_bar_pos(pdev, pf->vf_pos + PCI_SRIOV_BAR, idx); + return fsl_pci_read_bar(pdev, pos); +} + +static u64 fsl_pci_pf_read_bar(struct pci_pf_dev *pf, int idx) +{ + struct pci_dev *pdev = pf->pdev; + int pos; + + pos = fsl_pci_find_bar_pos(pdev, PCI_BASE_ADDRESS_0, idx); + return fsl_pci_read_bar(pdev, pos); +} + +static u64 fsl_pci_ep_read_bar(struct pci_ep_dev *ep, int idx) +{ + if (idx >= ep->iw_num) + return 0; + + if (ep->type == PCI_EP_TYPE_PF) + return fsl_pci_pf_read_bar(ep->pf, idx); + else + return fsl_pci_pf_read_vfbar(ep->pf, idx); +} + static int fsl_pci_ep_get_ibwin(struct pci_ep_dev *ep, struct pci_ep_win *win) { @@ -107,19 +130,30 @@ static int fsl_pci_ep_get_ibwin(struct pci_ep_dev *ep, return -EINVAL; if (ep->type == PCI_EP_TYPE_PF) { - iw_regs = &pf->regs->piw[3 - win->idx]; + iw_regs = &pf->regs->piw[ep->iw_num - win->idx - 1]; spin_lock(&pf->lock); win->cpu_addr = ((u64)in_be32(&iw_regs->pitar)) << 12; - win->attr = in_be32(&iw_regs->piwar); win->attr = in_be32(&iw_regs->piwar); spin_unlock(&pf->lock); win->size = attr_to_size(win->attr); - win->pci_addr = fsl_pci_ep_read_bar(ep->pdev, win->idx); + win->pci_addr = fsl_pci_ep_read_bar(ep, win->idx); } else { - /*todo support VF Inbound windows */ + /* VF inbound windows */ + iw_regs = &pf->vf_regs->vfiw[ep->iw_num - win->idx - 1]; + + spin_lock(&pf->lock); + win->cpu_addr = + ((u64)in_be32(&iw_regs->pitar)) << 12; + win->attr = in_be32(&iw_regs->piwar); + spin_unlock(&pf->lock); + + win->size = attr_to_size(win->attr); + win->pci_addr = fsl_pci_ep_read_bar(ep, win->idx); + win->cpu_addr += win->size * (ep->idx - 1); + win->pci_addr += win->size * (ep->idx - 1); } return 0; @@ -147,12 +181,83 @@ static int fsl_pci_ep_get_obwin(struct pci_ep_dev *ep, win->size = attr_to_size(win->attr); } else { - /*todo support VF outbound windows */ + /* VF outbound windows */ + struct vf_owta_regs *ta; + + ow_regs = &pf->vf_regs->vfow[win->idx]; + ta = &pf->vf_regs->vfowta[win->idx][ep->idx - 1]; + + spin_lock(&pf->lock); + win->cpu_addr = ((u64)in_be32(&ow_regs->powbar)) << 12; + win->pci_addr = + ((u64)in_be32(&ta->tear)) << 44 | + ((u64)in_be32(&ta->tar)) << 12; + win->attr = in_be32(&ow_regs->powar); + spin_unlock(&pf->lock); + + win->size = attr_to_size(win->attr); + win->cpu_addr += win->size * (ep->idx - 1); } return 0; } +static int fsl_pci_ep_get_vfibwin(struct pci_ep_dev *ep, + struct pci_ep_win *win) +{ + struct pci_pf_dev *pf = ep->pf; + struct pci_inbound_window_regs *iw_regs; + + if (win->idx >= pf->vf_iw_num) + return -EINVAL; + + if (ep->type != PCI_EP_TYPE_PF) + return -EINVAL; + + /* VF inbound windows */ + iw_regs = &pf->vf_regs->vfiw[pf->vf_iw_num - win->idx - 1]; + + spin_lock(&pf->lock); + win->cpu_addr = + ((u64)in_be32(&iw_regs->pitar)) << 12; + win->attr = in_be32(&iw_regs->piwar); + spin_unlock(&pf->lock); + + win->size = attr_to_size(win->attr); + win->pci_addr = fsl_pci_pf_read_vfbar(pf, win->idx); + + return 0; +} + +static int fsl_pci_ep_get_vfobwin(struct pci_ep_dev *ep, + struct pci_ep_win *win) +{ + struct pci_pf_dev *pf = ep->pf; + struct pci_outbound_window_regs *ow_regs; + struct vf_owta_regs *ta; + + if (win->idx >= pf->vf_ow_num) + return -EINVAL; + + if (ep->type != PCI_EP_TYPE_PF) + return -EINVAL; + + ow_regs = &pf->vf_regs->vfow[win->idx]; + ta = &pf->vf_regs->vfowta[win->idx][0]; + + spin_lock(&pf->lock); + win->cpu_addr = ((u64)in_be32(&ow_regs->powbar)) << 12; + win->pci_addr = + ((u64)in_be32(&ta->tear)) << 44 | + ((u64)in_be32(&ta->tar)) << 12; + 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_reg_win(struct pci_ep_dev *ep, struct pci_ep_win *win) { @@ -163,6 +268,23 @@ static int fsl_pci_ep_get_reg_win(struct pci_ep_dev *ep, return 0; } +static int fsl_pci_ep_get_mem_win(struct pci_ep_dev *ep, + struct pci_ep_win *win) +{ + if (ep->type == PCI_EP_TYPE_PF) { + if (win->idx != 0) + goto _err; + win->cpu_addr = ep->pf->mem_resources[win->idx].start; + win->size = resource_size(&ep->pf->mem_resources[win->idx]); + win->attr = 0x80000000; /* Enabled */ + } + + return 0; +_err: + win->attr = 0; + return -EINVAL; +} + int fsl_pci_ep_get_win(struct pci_ep_dev *ep, struct pci_ep_win *win) { int ret; @@ -174,10 +296,18 @@ int fsl_pci_ep_get_win(struct pci_ep_dev *ep, struct pci_ep_win *win) case PCI_EP_REGION_OBWIN: ret = fsl_pci_ep_get_obwin(ep, win); break; + case PCI_EP_REGION_VF_IBWIN: + ret = fsl_pci_ep_get_vfibwin(ep, win); + break; + case PCI_EP_REGION_VF_OBWIN: + ret = fsl_pci_ep_get_vfobwin(ep, win); + break; case PCI_EP_REGION_REGS: ret = fsl_pci_ep_get_reg_win(ep, win); - case PCI_EP_REGION_CONFIG: - ret = 0; + break; + case PCI_EP_REGION_MEM: + ret = fsl_pci_ep_get_mem_win(ep, win); + default: ret = -EINVAL; } @@ -198,7 +328,7 @@ static int fsl_pci_ep_set_ibwin(struct pci_ep_dev *ep, int bits = ilog2(win->size); u32 piwar = PIWAR_EN | PIWAR_PF | PIWAR_TGI_LOCAL | PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP; - iw_regs = &pf->regs->piw[3 - win->idx]; + iw_regs = &pf->regs->piw[ep->iw_num - win->idx - 1]; /* Setup inbound memory window */ spin_lock(&pf->lock); out_be32(&iw_regs->pitar, win->cpu_addr >> 12); @@ -208,7 +338,13 @@ static int fsl_pci_ep_set_ibwin(struct pci_ep_dev *ep, out_be32(&iw_regs->piwar, piwar | (bits - 1)); spin_unlock(&pf->lock); } else { - /*todo support VF Inbound windows */ + /* + * All VFs use the common VF inbound windows + * So an EP(VF) device can not change VF Inbound windows + * Only PF device can call fsl_pci_ep_set_vfibwin() to + * change VF's inbound windows + */ + return -EINVAL; } return 0; @@ -242,12 +378,82 @@ static int fsl_pci_ep_set_obwin(struct pci_ep_dev *ep, out_be32(&ow_regs->powar, flags | (bits - 1)); spin_unlock(&pf->lock); } else { - /*todo support VF Outbound windows */ + /* + * For an outbound window, each VF has a translation + * register separately and other registers are shared + * by all VFs. So an EP device can only change owtar + */ + struct vf_owta_regs *ta; + + ta = &pf->vf_regs->vfowta[win->idx][ep->idx - 1]; + out_be32(&ta->tar, win->pci_addr >> 12); + out_be32(&ta->tear, win->pci_addr >> 44); } return 0; } +static int fsl_pci_ep_set_vfibwin(struct pci_ep_dev *ep, + struct pci_ep_win *win) +{ + struct pci_pf_dev *pf = ep->pf; + struct pci_inbound_window_regs *iw_regs; + int bits; + u32 piwar = PIWAR_EN | PIWAR_PF | PIWAR_TGI_LOCAL | + PIWAR_READ_SNOOP | PIWAR_WRITE_SNOOP; + + if (win->idx >= ep->iw_num) + return -EINVAL; + + /* Only PF can set VF ATMU */ + if (ep->type != PCI_EP_TYPE_PF) + return -EINVAL; + + bits = ilog2(win->size); + iw_regs = &pf->vf_regs->vfiw[ep->iw_num - win->idx - 1]; + + /* Setup inbound memory window */ + spin_lock(&pf->lock); + out_be32(&iw_regs->pitar, win->cpu_addr >> 12); + if (win->attr) /* use the specified attribute */ + out_be32(&iw_regs->piwar, win->attr); + else /* use the default attribute */ + out_be32(&iw_regs->piwar, piwar | (bits - 1)); + spin_unlock(&pf->lock); + + return 0; +} + +static int fsl_pci_ep_set_vfobwin(struct pci_ep_dev *ep, + struct pci_ep_win *win) +{ + struct pci_pf_dev *pf = ep->pf; + struct pci_outbound_window_regs *ow_regs; + int bits; + u32 flags = 0x80044000; /* enable & mem R/W */ + + if (win->idx >= ep->iw_num) + return -EINVAL; + + /* Only PF can set VF ATMU */ + if (ep->type != PCI_EP_TYPE_PF) + return -EINVAL; + + ow_regs = &pf->vf_regs->vfow[win->idx]; + + bits = min(ilog2(win->size), __ffs(win->pci_addr | win->cpu_addr)); + + spin_lock(&pf->lock); + out_be32(&ow_regs->powbar, win->cpu_addr >> 12); + if (win->attr) /* use the specified attribute */ + out_be32(&ow_regs->powar, win->attr); + else /* use the default attribute */ + out_be32(&ow_regs->powar, flags | (bits - 1)); + spin_unlock(&pf->lock); + + return 0; +} + int fsl_pci_ep_set_win(struct pci_ep_dev *ep, struct pci_ep_win *win) { int ret; @@ -259,12 +465,17 @@ int fsl_pci_ep_set_win(struct pci_ep_dev *ep, struct pci_ep_win *win) case PCI_EP_REGION_OBWIN: ret = fsl_pci_ep_set_obwin(ep, win); break; + case PCI_EP_REGION_VF_IBWIN: + ret = fsl_pci_ep_set_vfibwin(ep, win); + break; + case PCI_EP_REGION_VF_OBWIN: + ret = fsl_pci_ep_set_vfobwin(ep, win); + break; default: ret = -EINVAL; } return ret; - } static struct pci_ep_dev * @@ -399,6 +610,27 @@ inbound_windows_show(struct device *dev, win.attr); } + if (ep->type == PCI_EP_TYPE_PF) { + struct pci_pf_dev *pf = ep->pf; + + if (pf->vf_regs) { + for (i = 0; i < pf->vf_iw_num; i++) { + win.idx = i; + fsl_pci_ep_get_vfibwin(ep, &win); + str += sprintf(str, "VF Inbound Window%d:\n" + "\tcpu_addr:0x%016llx" + " pci_addr:0x%016llx\n" + "\twin_size:0x%016llx" + " attribute:0x%08x\n", + i, + win.cpu_addr, + win.pci_addr, + win.size, + win.attr); + } + } + } + return str - buf; } @@ -425,6 +657,27 @@ outbound_windows_show(struct device *dev, win.attr); } + if (ep->type == PCI_EP_TYPE_PF) { + struct pci_pf_dev *pf = ep->pf; + + if (pf->vf_regs) { + for (i = 0; i < pf->vf_ow_num; i++) { + win.idx = i; + fsl_pci_ep_get_vfobwin(ep, &win); + str += sprintf(str, "VF Outbound Window%d:\n" + "\tcpu_addr:0x%016llx" + " pci_addr:0x%016llx\n" + "\twin_size:0x%016llx" + " attribute:0x%08x\n", + i, + win.cpu_addr, + win.pci_addr, + win.size, + win.attr); + } + } + } + return str - buf; } @@ -720,9 +973,10 @@ static int fsl_pci_pf_iov_init(struct pci_pf_dev *pf) pf->vf_offset = offset; pf->vf_stride = stride; pf->vf_num = pf->vf_total; - pf->vf_iw_num = FSL_PCI_EP_BAR_NUM; - pf->vf_ow_num = FSL_PCI_EP_OW_NUM; + pf->vf_iw_num = VF_IW_NUM; + pf->vf_ow_num = VF_OW_NUM; pf->vf_pos = pos; + pf->vf_regs = (void *)pf->regs + VF_ATMU_OFFSET; return pf->vf_num; } diff --git a/drivers/vfio/fsl_pci_ep/fsl_pci_ep.h b/drivers/vfio/fsl_pci_ep/fsl_pci_ep.h index cc6c88a..2681ed7 100644 --- a/drivers/vfio/fsl_pci_ep/fsl_pci_ep.h +++ b/drivers/vfio/fsl_pci_ep/fsl_pci_ep.h @@ -79,6 +79,7 @@ struct pci_pf_dev { u16 vf_num; /* number of VFs available */ u16 vf_offset; /* first VF Routing ID offset */ u16 vf_stride; /* following VF stride */ + struct vf_atmu_regs __iomem *vf_regs; }; int fsl_pci_ep_get_win(struct pci_ep_dev *ep, struct pci_ep_win *win); 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 68a26e0..4285ed0 100644 --- a/drivers/vfio/fsl_pci_ep/fsl_pci_ep_vfio.c +++ b/drivers/vfio/fsl_pci_ep/fsl_pci_ep_vfio.c @@ -78,11 +78,17 @@ static long fsl_pci_ep_vfio_ioctl(void *device_data, info.iw_num = ep->iw_num; info.ow_num = ep->ow_num; - info.vf_iw_num = 0; - info.vf_ow_num = 0; info.type = ep->type; info.pf_idx = ep->pf->idx; info.vf_idx = ep->idx; + 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; + } else { + info.vf_iw_num = 0; + info.vf_ow_num = 0; + } + return copy_to_user((void __user *)arg, &info, sizeof(info)); } else if (cmd == VFIO_DEVICE_GET_WIN_INFO) { struct pci_ep_win win; diff --git a/include/uapi/linux/fsl_pci_ep_vfio.h b/include/uapi/linux/fsl_pci_ep_vfio.h index 7cccf85..1cf259f 100644 --- a/include/uapi/linux/fsl_pci_ep_vfio.h +++ b/include/uapi/linux/fsl_pci_ep_vfio.h @@ -35,6 +35,7 @@ enum PCI_EP_REGION_TYPE { PCI_EP_REGION_VF_OBWIN, PCI_EP_REGION_REGS, PCI_EP_REGION_CONFIG, + PCI_EP_REGION_MEM, }; enum PCI_EP_REGION_INDEX { -- cgit v0.10.2