From ee930fffee469d076998274a2ca55e13dc1efb67 Mon Sep 17 00:00:00 2001
From: hc <hc@nodka.com>
Date: Fri, 10 May 2024 08:50:54 +0000
Subject: [PATCH] enable tun/tap/iptables
---
kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c | 210 ++++++++++++++++++++++++++++++++++++----------------
1 files changed, 144 insertions(+), 66 deletions(-)
diff --git a/kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c b/kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c
index 6655bed..1addd6c 100644
--- a/kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c
+++ b/kernel/drivers/pci/controller/dwc/pcie-dw-ep-rockchip.c
@@ -81,6 +81,10 @@
#define PCIE_CLIENT_HOT_RESET_CTRL 0x180
#define PCIE_CLIENT_LTSSM_STATUS 0x300
#define PCIE_CLIENT_INTR_MASK 0x24
+#define PCIE_LTSSM_APP_DLY1_EN BIT(0)
+#define PCIE_LTSSM_APP_DLY2_EN BIT(1)
+#define PCIE_LTSSM_APP_DLY1_DONE BIT(2)
+#define PCIE_LTSSM_APP_DLY2_DONE BIT(3)
#define PCIE_LTSSM_ENABLE_ENHANCE BIT(4)
#define PCIE_CLIENT_MSI_GEN_CON 0x38
@@ -90,11 +94,13 @@
#define PCIE_ELBI_LOCAL_BASE 0x200e00
#define PCIE_ELBI_APP_ELBI_INT_GEN0 0x0
-#define PCIE_ELBI_APP_ELBI_INT_GEN0_SIGIO BIT(0)
+#define PCIE_ELBI_APP_ELBI_INT_GEN0_IRQ_USER BIT(0)
#define PCIE_ELBI_APP_ELBI_INT_GEN1 0x4
#define PCIE_ELBI_LOCAL_ENABLE_OFF 0x8
+
+#define PCIE_ELBI_USER_DATA_OFF 0x10
#define PCIE_DIRECT_SPEED_CHANGE BIT(17)
@@ -106,6 +112,7 @@
#define PCIE_EP_OBJ_INFO_DRV_VERSION 0x00000001
#define PCIE_BAR_MAX_NUM 6
+#define PCIE_HOTRESET_TMOUT_US 10000
struct rockchip_pcie {
struct dw_pcie pci;
@@ -126,10 +133,14 @@
u32 ib_target_size[PCIE_BAR_MAX_NUM];
void *ib_target_base[PCIE_BAR_MAX_NUM];
struct dma_trx_obj *dma_obj;
- struct fasync_struct *async;
phys_addr_t dbi_base_physical;
struct pcie_ep_obj_info *obj_info;
enum pcie_ep_mmap_resource cur_mmap_res;
+ struct workqueue_struct *hot_rst_wq;
+ struct work_struct hot_rst_work;
+ struct mutex file_mutex;
+ DECLARE_BITMAP(virtual_id_irq_bitmap, RKEP_EP_VIRTUAL_ID_MAX);
+ wait_queue_head_t wq_head;
};
struct rockchip_pcie_misc_dev {
@@ -521,18 +532,18 @@
/* Resize BAR0 4M 32bits, BAR2 64M 64bits-pref, BAR4 1MB 32bits */
bar = BAR_0;
- dw_pcie_writel_dbi(pci, resbar_base + 0x4 + bar * 0x8, 0xfffff0);
+ dw_pcie_writel_dbi(pci, resbar_base + 0x4 + bar * 0x8, 0x40);
dw_pcie_writel_dbi(pci, resbar_base + 0x8 + bar * 0x8, 0x2c0);
rockchip_pcie_ep_set_bar_flag(rockchip, bar, PCI_BASE_ADDRESS_MEM_TYPE_32);
bar = BAR_2;
- dw_pcie_writel_dbi(pci, resbar_base + 0x4 + bar * 0x8, 0xfffff0);
+ dw_pcie_writel_dbi(pci, resbar_base + 0x4 + bar * 0x8, 0x400);
dw_pcie_writel_dbi(pci, resbar_base + 0x8 + bar * 0x8, 0x6c0);
rockchip_pcie_ep_set_bar_flag(rockchip, bar,
PCI_BASE_ADDRESS_MEM_PREFETCH | PCI_BASE_ADDRESS_MEM_TYPE_64);
bar = BAR_4;
- dw_pcie_writel_dbi(pci, resbar_base + 0x4 + bar * 0x8, 0xfffff0);
+ dw_pcie_writel_dbi(pci, resbar_base + 0x4 + bar * 0x8, 0x10);
dw_pcie_writel_dbi(pci, resbar_base + 0x8 + bar * 0x8, 0xc0);
rockchip_pcie_ep_set_bar_flag(rockchip, bar, PCI_BASE_ADDRESS_MEM_TYPE_32);
@@ -586,7 +597,8 @@
/* LTSSM EN ctrl mode */
val = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_HOT_RESET_CTRL);
- val |= PCIE_LTSSM_ENABLE_ENHANCE | (PCIE_LTSSM_ENABLE_ENHANCE << 16);
+ val |= (PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN) |
+ ((PCIE_LTSSM_ENABLE_ENHANCE | PCIE_LTSSM_APP_DLY2_EN) << 16);
rockchip_pcie_writel_apb(rockchip, val, PCIE_CLIENT_HOT_RESET_CTRL);
}
@@ -634,6 +646,49 @@
rockchip_pcie_writel_apb(rockchip, BIT(interrupt_num), PCIE_CLIENT_MSI_GEN_CON);
}
+static int rockchip_pcie_raise_irq_user(struct rockchip_pcie *rockchip, u32 index)
+{
+ if (index >= RKEP_EP_VIRTUAL_ID_MAX) {
+ dev_err(rockchip->pci.dev, "raise irq_user, virtual id %d out of range\n", index);
+
+ return -EINVAL;
+ }
+
+ mutex_lock(&rockchip->file_mutex);
+ rockchip->obj_info->irq_type_rc = OBJ_IRQ_USER;
+ rockchip->obj_info->irq_user_data_rc = index;
+ rockchip_pcie_raise_msi_irq(rockchip, PCIe_CLIENT_MSI_OBJ_IRQ);
+ mutex_unlock(&rockchip->file_mutex);
+
+ return 0;
+}
+
+static int rockchip_pcie_poll_irq_user(struct rockchip_pcie *rockchip, struct pcie_ep_obj_poll_virtual_id_cfg *cfg)
+{
+ u32 index = cfg->virtual_id;
+
+ if (index >= RKEP_EP_VIRTUAL_ID_MAX) {
+ dev_err(rockchip->pci.dev, "poll irq_user, virtual id %d out of range\n", index);
+
+ return -EINVAL;
+ }
+
+ cfg->poll_status = NSIGPOLL;
+ if (cfg->sync)
+ wait_event_interruptible(rockchip->wq_head,
+ test_bit(index, rockchip->virtual_id_irq_bitmap));
+ else
+ wait_event_interruptible_timeout(rockchip->wq_head,
+ test_bit(index, rockchip->virtual_id_irq_bitmap),
+ cfg->timeout_ms);
+ if (test_and_clear_bit(index, rockchip->virtual_id_irq_bitmap))
+ cfg->poll_status = POLL_IN;
+
+ dev_dbg(rockchip->pci.dev, "poll virtual id %d, ret=%d\n", index, cfg->poll_status);
+
+ return 0;
+}
+
static irqreturn_t rockchip_pcie_sys_irq_handler(int irq, void *arg)
{
struct rockchip_pcie *rockchip = arg;
@@ -642,15 +697,20 @@
u32 chn;
union int_status wr_status, rd_status;
union int_clear clears;
- u32 reg, val, mask;
- bool sigio = false;
+ u32 reg, mask;
/* ELBI helper, only check the valid bits, and discard the rest interrupts */
elbi_reg = dw_pcie_readl_dbi(pci, PCIE_ELBI_LOCAL_BASE + PCIE_ELBI_APP_ELBI_INT_GEN0);
- if (elbi_reg & PCIE_ELBI_APP_ELBI_INT_GEN0_SIGIO) {
- sigio = true;
- rockchip->obj_info->irq_type_ep = OBJ_IRQ_ELBI;
+ if (elbi_reg & PCIE_ELBI_APP_ELBI_INT_GEN0_IRQ_USER) {
rockchip_pcie_elbi_clear(rockchip);
+
+ if (rockchip->obj_info->irq_type_ep == OBJ_IRQ_USER) {
+ reg = rockchip->obj_info->irq_user_data_ep;
+ if (reg < RKEP_EP_VIRTUAL_ID_MAX) {
+ set_bit(reg, rockchip->virtual_id_irq_bitmap);
+ wake_up_interruptible(&rockchip->wq_head);
+ }
+ }
goto out;
}
@@ -703,24 +763,12 @@
rockchip->obj_info->irq_type_ep = OBJ_IRQ_DMA;
rockchip->obj_info->dma_status_ep.wr |= wr_status.asdword;
rockchip->obj_info->dma_status_ep.rd |= rd_status.asdword;
- sigio = true;
}
out:
- if (sigio) {
- dev_dbg(rockchip->pci.dev, "SIGIO\n");
- kill_fasync(&rockchip->async, SIGIO, POLL_IN);
- }
-
reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC);
- if (reg & BIT(2)) {
- /* Setup command register */
- val = dw_pcie_readl_dbi(pci, PCI_COMMAND);
- val &= 0xffff0000;
- val |= PCI_COMMAND_IO | PCI_COMMAND_MEMORY |
- PCI_COMMAND_MASTER | PCI_COMMAND_SERR;
- dw_pcie_writel_dbi(pci, PCI_COMMAND, val);
- }
+ if (reg & BIT(2))
+ queue_work(rockchip->hot_rst_wq, &rockchip->hot_rst_work);
rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC);
@@ -870,6 +918,23 @@
table->start.chnl = table->chn;
}
+static void rockchip_pcie_hot_rst_work(struct work_struct *work)
+{
+ struct rockchip_pcie *rockchip = container_of(work, struct rockchip_pcie, hot_rst_work);
+ u32 status;
+ int ret;
+
+ if (rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_HOT_RESET_CTRL) & PCIE_LTSSM_APP_DLY2_EN) {
+ ret = readl_poll_timeout(rockchip->apb_base + PCIE_CLIENT_LTSSM_STATUS,
+ status, ((status & 0x3F) == 0), 100, PCIE_HOTRESET_TMOUT_US);
+ if (ret)
+ dev_err(rockchip->pci.dev, "wait for detect quiet failed!\n");
+
+ rockchip_pcie_writel_apb(rockchip, (PCIE_LTSSM_APP_DLY2_DONE) | ((PCIE_LTSSM_APP_DLY2_DONE) << 16),
+ PCIE_CLIENT_HOT_RESET_CTRL);
+ }
+}
+
static int rockchip_pcie_get_dma_status(struct dma_trx_obj *obj, u8 chn, enum dma_dir dir)
{
struct rockchip_pcie *rockchip = dev_get_drvdata(obj->dev);
@@ -925,13 +990,6 @@
.link_up = rockchip_pcie_link_up,
};
-static int pcie_ep_fasync(int fd, struct file *file, int mode)
-{
- struct rockchip_pcie *rockchip = (struct rockchip_pcie *)file->private_data;
-
- return fasync_helper(fd, file, mode, &rockchip->async);
-}
-
static int pcie_ep_open(struct inode *inode, struct file *file)
{
struct miscdevice *miscdev = file->private_data;
@@ -943,35 +1001,16 @@
return 0;
}
-static int pcie_ep_release(struct inode *inode, struct file *file)
-{
- return pcie_ep_fasync(-1, file, 0);
-}
-
static long pcie_ep_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
{
struct rockchip_pcie *rockchip = (struct rockchip_pcie *)file->private_data;
- struct pcie_ep_user_data msg;
struct pcie_ep_dma_cache_cfg cfg;
void __user *uarg = (void __user *)arg;
- int i, ret;
+ struct pcie_ep_obj_poll_virtual_id_cfg poll_cfg;
enum pcie_ep_mmap_resource mmap_res;
+ int ret, index;
switch (cmd) {
- case PCIE_DMA_GET_ELBI_DATA:
- for (i = 4; i <= 6; i++)
- msg.elbi_app_user[i - 4] = dw_pcie_readl_dbi(&rockchip->pci,
- PCIE_ELBI_LOCAL_BASE + i * 4);
- for (i = 8; i <= 15; i++)
- msg.elbi_app_user[i - 5] = dw_pcie_readl_dbi(&rockchip->pci,
- PCIE_ELBI_LOCAL_BASE + i * 4);
-
- ret = copy_to_user(uarg, &msg, sizeof(msg));
- if (ret) {
- dev_err(rockchip->pci.dev, "failed to get elbi data\n");
- return -EFAULT;
- }
- break;
case PCIE_DMA_CACHE_INVALIDE:
ret = copy_from_user(&cfg, uarg, sizeof(cfg));
if (ret) {
@@ -994,18 +1033,8 @@
dw_pcie_writel_dbi(&rockchip->pci, PCIE_DMA_OFFSET + PCIE_DMA_RD_INT_MASK,
0xffffffff);
break;
- case PCIE_DMA_RAISE_MSI_OBJ_IRQ_USER:
- rockchip->obj_info->irq_type_rc = OBJ_IRQ_USER;
+ case PCIE_EP_RAISE_MSI:
rockchip_pcie_raise_msi_irq(rockchip, PCIe_CLIENT_MSI_OBJ_IRQ);
- break;
- case PCIE_EP_GET_USER_INFO:
- msg.bar0_phys_addr = rockchip->ib_target_address[0];
-
- ret = copy_to_user(uarg, &msg, sizeof(msg));
- if (ret) {
- dev_err(rockchip->pci.dev, "failed to get elbi data\n");
- return -EFAULT;
- }
break;
case PCIE_EP_SET_MMAP_RESOURCE:
ret = copy_from_user(&mmap_res, uarg, sizeof(mmap_res));
@@ -1020,6 +1049,34 @@
}
rockchip->cur_mmap_res = mmap_res;
+ break;
+ case PCIE_EP_RAISE_IRQ_USER:
+ ret = copy_from_user(&index, uarg, sizeof(index));
+ if (ret) {
+ dev_err(rockchip->pci.dev,
+ "failed to get raise irq data copy from userspace\n");
+ return -EFAULT;
+ }
+
+ ret = rockchip_pcie_raise_irq_user(rockchip, index);
+ if (ret < 0)
+ return -EFAULT;
+ break;
+ case PCIE_EP_POLL_IRQ_USER:
+ ret = copy_from_user(&poll_cfg, uarg, sizeof(poll_cfg));
+ if (ret) {
+ dev_err(rockchip->pci.dev,
+ "failed to get poll irq data copy from userspace\n");
+
+ return -EFAULT;
+ }
+
+ ret = rockchip_pcie_poll_irq_user(rockchip, &poll_cfg);
+ if (ret < 0)
+ return -EFAULT;
+
+ if (copy_to_user(uarg, &poll_cfg, sizeof(poll_cfg)))
+ return -EFAULT;
break;
default:
break;
@@ -1081,9 +1138,7 @@
static const struct file_operations pcie_ep_ops = {
.owner = THIS_MODULE,
.open = pcie_ep_open,
- .release = pcie_ep_release,
.unlocked_ioctl = pcie_ep_ioctl,
- .fasync = pcie_ep_fasync,
.mmap = pcie_ep_mmap,
};
@@ -1121,6 +1176,7 @@
struct rockchip_pcie *rockchip;
int ret;
int retry, i;
+ u32 reg;
rockchip = devm_kzalloc(dev, sizeof(*rockchip), GFP_KERNEL);
if (!rockchip)
@@ -1182,6 +1238,26 @@
rockchip_pcie_start_link(&rockchip->pci);
rockchip_pcie_devmode_update(rockchip, RKEP_MODE_KERNEL, RKEP_SMODE_LNKRDY);
+ rockchip->hot_rst_wq = create_singlethread_workqueue("rkep_hot_rst_wq");
+ if (!rockchip->hot_rst_wq) {
+ dev_err(dev, "failed to create hot_rst workqueue\n");
+ ret = -ENOMEM;
+ goto deinit_phy;
+ }
+ INIT_WORK(&rockchip->hot_rst_work, rockchip_pcie_hot_rst_work);
+
+ reg = rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_INTR_STATUS_MISC);
+ if ((reg & BIT(2)) &&
+ (rockchip_pcie_readl_apb(rockchip, PCIE_CLIENT_HOT_RESET_CTRL) & PCIE_LTSSM_APP_DLY2_EN)) {
+ rockchip_pcie_writel_apb(rockchip, PCIE_LTSSM_APP_DLY2_DONE | (PCIE_LTSSM_APP_DLY2_DONE << 16),
+ PCIE_CLIENT_HOT_RESET_CTRL);
+ dev_info(dev, "hot reset ever\n");
+ }
+ rockchip_pcie_writel_apb(rockchip, reg, PCIE_CLIENT_INTR_STATUS_MISC);
+
+ /* Enable client reset or link down interrupt */
+ rockchip_pcie_writel_apb(rockchip, 0x40000, PCIE_CLIENT_INTR_MASK);
+
for (retry = 0; retry < 10000; retry++) {
if (dw_pcie_link_up(&rockchip->pci)) {
/*
@@ -1225,12 +1301,14 @@
rockchip->dma_obj->config_dma_func = rockchip_pcie_config_dma_dwc;
rockchip->dma_obj->get_dma_status = rockchip_pcie_get_dma_status;
}
+ mutex_init(&rockchip->file_mutex);
/* Enable client ELBI interrupt */
rockchip_pcie_writel_apb(rockchip, 0x80000000, PCIE_CLIENT_INTR_MASK);
/* Enable ELBI interrupt */
rockchip_pcie_local_elbi_enable(rockchip);
+ init_waitqueue_head(&rockchip->wq_head);
ret = rockchip_pcie_request_sys_irq(rockchip, pdev);
if (ret)
goto deinit_phy;
--
Gitblit v1.6.2