misc: rockchip: pcie-rkep: Support mmap bar resource and rw config space

Change-Id: Ib97bd299cfdc3bcba40250cc1d10686fb1fc64e4
Signed-off-by: Jon Lin <jon.lin@rock-chips.com>
This commit is contained in:
Jon Lin
2023-11-10 12:06:03 +08:00
committed by Tao Huang
parent 5592509ba4
commit 694b4503a6
2 changed files with 219 additions and 53 deletions

View File

@@ -91,6 +91,10 @@ static DEFINE_MUTEX(rkep_mutex);
#define RKEP_EP_VIRTUAL_ID_MAX (8 * 4096)
#define RKEP_EP_ELBI_TIEMOUT_US 100000
#define PCIE_RK3568_RC_DBI_BASE 0xf6000000
#define PCIE_RK3588_RC_DBI_BASE 0xf5000000
#define PCIE_DBI_SIZE 0x400000
struct pcie_rkep_msix_context {
struct pci_dev *dev;
u16 msg_id;
@@ -102,12 +106,14 @@ struct pcie_rkep {
void __iomem *bar0;
void __iomem *bar2;
void __iomem *bar4;
struct miscdevice dev;
int cur_mmap_res;
struct msix_entry msix_entries[RKEP_NUM_MSIX_VECTORS];
struct pcie_rkep_msix_context msix_ctx[RKEP_NUM_MSIX_VECTORS];
struct pcie_rkep_msix_context msi_ctx[RKEP_NUM_MSI_VECTORS];
bool msi_enable;
bool msix_enable;
struct miscdevice dev;
struct dma_trx_obj *dma_obj;
struct pcie_ep_obj_info *obj_info;
struct page *user_pages; /* Allocated physical memory for user space */
@@ -275,33 +281,73 @@ static ssize_t pcie_rkep_write(struct file *file, const char __user *buf,
{
struct pcie_file *pcie_file = file->private_data;
struct pcie_rkep *pcie_rkep = pcie_file->pcie_rkep;
u32 *bar0_buf;
int loop, i = 0;
size_t raw_count = count;
struct pci_dev *dev = pcie_rkep->pdev;
unsigned int size = count;
loff_t init_off = *ppos, off = *ppos;
u8 *data;
count = (count % 4) ? (count - count % 4) : count;
if (count > BAR_0_SZ)
return -EINVAL;
bar0_buf = kzalloc(count, GFP_KERNEL);
if (!bar0_buf)
data = kzalloc(PCI_CFG_SPACE_EXP_SIZE, GFP_KERNEL);
if (!data)
return -ENOMEM;
if (copy_from_user(bar0_buf, buf, count)) {
raw_count = -EFAULT;
goto exit;
if (off > dev->cfg_size) {
kfree(data);
return 0;
}
if (off + count > dev->cfg_size) {
size = dev->cfg_size - off;
count = size;
}
for (loop = 0; loop < count / 4; loop++) {
iowrite32(bar0_buf[i], pcie_rkep->bar0 + loop * 4);
i++;
if (copy_from_user(data, buf, count)) {
kfree(data);
return -EFAULT;
}
exit:
kfree(bar0_buf);
if ((off & 1) && size) {
pci_write_config_byte(dev, off, data[off - init_off]);
off++;
size--;
}
return raw_count;
if ((off & 3) && size > 2) {
u16 val = data[off - init_off];
val |= (u16) data[off - init_off + 1] << 8;
pci_write_config_word(dev, off, val);
off += 2;
size -= 2;
}
while (size > 3) {
u32 val = data[off - init_off];
val |= (u32) data[off - init_off + 1] << 8;
val |= (u32) data[off - init_off + 2] << 16;
val |= (u32) data[off - init_off + 3] << 24;
pci_write_config_dword(dev, off, val);
off += 4;
size -= 4;
}
if (size >= 2) {
u16 val = data[off - init_off];
val |= (u16) data[off - init_off + 1] << 8;
pci_write_config_word(dev, off, val);
off += 2;
size -= 2;
}
if (size) {
pci_write_config_byte(dev, off, data[off - init_off]);
off++;
--size;
}
kfree(data);
return count;
}
static ssize_t pcie_rkep_read(struct file *file, char __user *buf,
@@ -309,33 +355,82 @@ static ssize_t pcie_rkep_read(struct file *file, char __user *buf,
{
struct pcie_file *pcie_file = file->private_data;
struct pcie_rkep *pcie_rkep = pcie_file->pcie_rkep;
u32 *bar0_buf;
int loop, i = 0;
size_t raw_count = count;
struct pci_dev *dev = pcie_rkep->pdev;
unsigned int size = count;
loff_t init_off = *ppos, off = *ppos;
u8 *data;
count = (count % 4) ? (count - count % 4) : count;
if (count > BAR_0_SZ)
return -EINVAL;
bar0_buf = kzalloc(count, GFP_ATOMIC);
if (!bar0_buf)
data = kzalloc(PCI_CFG_SPACE_EXP_SIZE, GFP_KERNEL);
if (!data)
return -ENOMEM;
for (loop = 0; loop < count / 4; loop++) {
bar0_buf[i] = ioread32(pcie_rkep->bar0 + loop * 4);
i++;
if (off > dev->cfg_size) {
kfree(data);
return 0;
}
if (off + count > dev->cfg_size) {
size = dev->cfg_size - off;
count = size;
}
if (copy_to_user(buf, bar0_buf, count)) {
raw_count = -EFAULT;
goto exit;
if ((off & 1) && size) {
u8 val;
pci_read_config_byte(dev, off, &val);
data[off - init_off] = val;
off++;
size--;
}
exit:
kfree(bar0_buf);
if ((off & 3) && size > 2) {
u16 val;
return raw_count;
pci_read_config_word(dev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
off += 2;
size -= 2;
}
while (size > 3) {
u32 val;
pci_read_config_dword(dev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
data[off - init_off + 2] = (val >> 16) & 0xff;
data[off - init_off + 3] = (val >> 24) & 0xff;
off += 4;
size -= 4;
}
if (size >= 2) {
u16 val;
pci_read_config_word(dev, off, &val);
data[off - init_off] = val & 0xff;
data[off - init_off + 1] = (val >> 8) & 0xff;
off += 2;
size -= 2;
}
if (size > 0) {
u8 val;
pci_read_config_byte(dev, off, &val);
data[off - init_off] = val;
off++;
--size;
}
if (copy_to_user(buf, data, count)) {
kfree(data);
return -EFAULT;
}
kfree(data);
return count;
}
static int pcie_rkep_mmap(struct file *file, struct vm_area_struct *vma)
@@ -343,26 +438,81 @@ static int pcie_rkep_mmap(struct file *file, struct vm_area_struct *vma)
u64 addr;
struct pcie_file *pcie_file = file->private_data;
struct pcie_rkep *pcie_rkep = pcie_file->pcie_rkep;
struct pci_dev *dev = pcie_rkep->pdev;
size_t size = vma->vm_end - vma->vm_start;
resource_size_t bar_size;
int err;
if (size > RKEP_USER_MEM_SIZE) {
dev_warn(&pcie_rkep->pdev->dev, "mmap size is out of limitation\n");
switch (pcie_rkep->cur_mmap_res) {
case PCIE_EP_MMAP_RESOURCE_RK3568_RC_DBI:
if (size > PCIE_DBI_SIZE) {
dev_warn(&pcie_rkep->pdev->dev, "dbi mmap size is out of limitation\n");
return -EINVAL;
}
addr = PCIE_RK3568_RC_DBI_BASE;
break;
case PCIE_EP_MMAP_RESOURCE_RK3588_RC_DBI:
if (size > PCIE_DBI_SIZE) {
dev_warn(&pcie_rkep->pdev->dev, "dbi mmap size is out of limitation\n");
return -EINVAL;
}
addr = PCIE_RK3588_RC_DBI_BASE;
break;
case PCIE_EP_MMAP_RESOURCE_BAR0:
bar_size = pci_resource_len(dev, 0);
if (size > bar_size) {
dev_warn(&pcie_rkep->pdev->dev, "bar0 mmap size is out of limitation\n");
return -EINVAL;
}
addr = pci_resource_start(dev, 0);
break;
case PCIE_EP_MMAP_RESOURCE_BAR2:
bar_size = pci_resource_len(dev, 2);
if (size > bar_size) {
dev_warn(&pcie_rkep->pdev->dev, "bar2 mmap size is out of limitation\n");
return -EINVAL;
}
addr = pci_resource_start(dev, 2);
break;
case PCIE_EP_MMAP_RESOURCE_BAR4:
bar_size = pci_resource_len(dev, 4);
if (size > bar_size) {
dev_warn(&pcie_rkep->pdev->dev, "bar4 mmap size is out of limitation\n");
return -EINVAL;
}
addr = pci_resource_start(dev, 4);
break;
case PCIE_EP_MMAP_RESOURCE_USER_MEM:
if (size > RKEP_USER_MEM_SIZE) {
dev_warn(&pcie_rkep->pdev->dev, "mmap size is out of limitation\n");
return -EINVAL;
}
if (!pcie_rkep->user_pages) {
dev_warn(&pcie_rkep->pdev->dev, "user_pages has not been allocated yet\n");
return -EINVAL;
}
addr = page_to_phys(pcie_rkep->user_pages);
break;
default:
dev_err(&pcie_rkep->pdev->dev, "cur mmap_res %d is unsurreport\n", pcie_rkep->cur_mmap_res);
return -EINVAL;
}
if (!pcie_rkep->user_pages) {
dev_warn(&pcie_rkep->pdev->dev, "user_pages has not been allocated yet\n");
return -EINVAL;
}
addr = page_to_phys(pcie_rkep->user_pages);
vma->vm_flags |= VM_IO;
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
vma->vm_flags |= (VM_DONTEXPAND | VM_DONTDUMP);
if (io_remap_pfn_range(vma, vma->vm_start, addr >> PAGE_SHIFT, size, vma->vm_page_prot)) {
dev_err(&pcie_rkep->pdev->dev, "io_remap_pfn_range failed\n");
if (pcie_rkep->cur_mmap_res == PCIE_EP_MMAP_RESOURCE_BAR2 ||
pcie_rkep->cur_mmap_res == PCIE_EP_MMAP_RESOURCE_USER_MEM)
vma->vm_page_prot = pgprot_writecombine(vma->vm_page_prot);
else
vma->vm_page_prot = pgprot_noncached(vma->vm_page_prot);
err = remap_pfn_range(vma, vma->vm_start,
__phys_to_pfn(addr),
size, vma->vm_page_prot);
if (err)
return -EAGAIN;
}
return 0;
}
@@ -375,6 +525,7 @@ static long pcie_rkep_ioctl(struct file *file, unsigned int cmd, unsigned long a
struct pcie_ep_dma_cache_cfg cfg;
struct pcie_ep_dma_block_req dma;
void __user *uarg = (void __user *)args;
int mmap_res;
int ret;
int index;
u64 addr;
@@ -462,6 +613,20 @@ static long pcie_rkep_ioctl(struct file *file, unsigned int cmd, unsigned long a
return -EFAULT;
}
break;
case PCIE_EP_SET_MMAP_RESOURCE:
ret = copy_from_user(&mmap_res, uarg, sizeof(mmap_res));
if (ret) {
dev_err(&pcie_rkep->pdev->dev, "failed to get copy from\n");
return -EFAULT;
}
if (mmap_res >= PCIE_EP_MMAP_RESOURCE_MAX || mmap_res < 0) {
dev_err(&pcie_rkep->pdev->dev, "mmap index %d is out of number\n", mmap_res);
return -EINVAL;
}
pcie_rkep->cur_mmap_res = mmap_res;
break;
default:
break;
}
@@ -478,7 +643,7 @@ static const struct file_operations pcie_rkep_fops = {
.mmap = pcie_rkep_mmap,
.fasync = pcie_rkep_fasync,
.release = pcie_rkep_release,
.llseek = no_llseek,
.llseek = default_llseek,
};
static inline void pcie_rkep_writel_dbi(struct pcie_rkep *pcie_rkep, u32 reg, u32 val)
@@ -985,6 +1150,7 @@ static int pcie_rkep_probe(struct pci_dev *pdev, const struct pci_device_id *id)
pcie_dw_dmatest_unregister(pcie_rkep->dma_obj);
goto err_register_obj;
}
pcie_rkep->cur_mmap_res = PCIE_EP_MMAP_RESOURCE_USER_MEM;
dev_err(&pdev->dev, "successfully allocate continuouse buffer for userspace\n");
#endif

View File

@@ -106,7 +106,7 @@ struct pcie_ep_obj_info {
#define PCIE_DMA_IRQ_MASK_ALL _IOW(PCIE_BASE, 3, int)
#define PCIE_DMA_RAISE_MSI_OBJ_IRQ_USER _IOW(PCIE_BASE, 4, int)
#define PCIE_EP_GET_USER_INFO _IOR(PCIE_BASE, 5, struct pcie_ep_user_data)
#define PCIE_EP_SET_MMAP_RESOURCE _IOW(PCIE_BASE, 6, enum pcie_ep_mmap_resource)
#define PCIE_EP_SET_MMAP_RESOURCE _IOW(PCIE_BASE, 6, int)
#define PCIE_EP_RAISE_ELBI _IOW(PCIE_BASE, 7, int)
#define PCIE_EP_REQUEST_VIRTUAL_ID _IOR(PCIE_BASE, 16, int)
#define PCIE_EP_RELEASE_VIRTUAL_ID _IOW(PCIE_BASE, 17, int)