/** * SPRD ep device driver in host side for Spreadtrum SoCs * * Copyright (C) 2018 Spreadtrum Co., Ltd. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License version 2 of * the License as published by the Free Software Foundation. * * This program is used to control ep device driver in host side for * Spreadtrum SoCs. */ #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include #include "../include/pcie-rc-sprd.h" #include "../include/sprd_pcie_ep_device.h" #include "../include/sipc_big_to_little.h" #include "../include/sprd_pcie_resource.h" #include "../include/sipc.h" #include "../include/sipa.h" #include "../sipa/sipa_core.h" #define VERSION_NUMBER "V1.1.8" #define QUECTEL_SPRD_PCIE_VERSION "Quectel_Linux&Android_SPRD_PCIE_Driver_"VERSION_NUMBER #define DRV_MODULE_NAME "sprd-pcie-ep-device" #define CONFIG_SPRD_IPA_PCIE_WORKROUND enum dev_pci_barno { BAR_0 = 0, BAR_1, BAR_2, BAR_3, BAR_4, BAR_5, BAR_CNT }; #define MAX_SUPPORT_IRQ 32 #define IPA_HW_IRQ_CNT 4 #define IPA_HW_IRQ_BASE 16 #define IPA_HW_IRQ_BASE_DEFECT 0 #define REQUEST_BASE_IRQ (IPA_HW_IRQ_BASE + IPA_HW_IRQ_CNT) #define REQUEST_BASE_IRQ_DEFECT 16 #ifdef CONFIG_SPRD_IPA_PCIE_WORKROUND /* the bar0 and the bar1 are used for ipa */ #define IPA_MEM_BAR BAR_0 #define IPA_REG_BAR BAR_1 #define IPA_TFT_BAR BAR_3 #define BAR_MIN BAR_2 #else #define BAR_MIN BAR_0 #endif /* the bar4 and the bar5 are specail bars */ #define BAR_MAX BAR_4 #define PCI_VENDOR_ID_SPRD 0x16c3 #define PCI_DEVICE_ID_SPRD_ORCA 0xabcd #define PCI_CLASS_ID_SPRD_ORCA 0x80d00 /* Parameters for the waiting for iATU enabled routine */ #define LINK_WAIT_MAX_IATU_RETRIES 5 #define LINK_WAIT_IATU_MIN 9000 #define LINK_WAIT_IATU_MAX 10000 /* ep config bar bar4 , can config ep iatu reg and door bell reg */ #define EP_CFG_BAR BAR_4 #define DOOR_BELL_BASE 0x00000 #define IATU_REG_BASE 0x10000 #define DOOR_BELL_ENABLE 0x10 #define DOOR_BELL_STATUS 0x14 /* used 0x18 & 0x1c to save the smem base & size. */ #define DOOR_BELL_SMEMBASE 0x18 #define DOOR_BELL_SMEMSIZE 0x1C /* one bit can indicate one irq , if stauts[i] & enable[i] , irq = i */ #define DOOR_BELL_IRQ_VALUE(irq) BIT((irq)) #define DOOR_BELL_IRQ_CNT 32 #define IATU_MAX_REGION 8 #define PCIE_ATU_VIEWPORT 0x900 #define PCIE_ATU_CR1 0x904 #define PCIE_ATU_CR2 0x908 #define PCIE_ATU_LOWER_BASE 0x90c #define PCIE_ATU_UPPER_BASE 0x910 #define PCIE_ATU_LIMIT 0x914 #define PCIE_ATU_LOWER_TARGET 0x918 #define PCIE_ATU_UPPER_TARGET 0x91c #define PCIE_ATU_REGION_INBOUND BIT(31) #define PCIE_ATU_ENABLE BIT(31) #define PCIE_ATU_BAR_MODE_ENABLE BIT(30) #define PCIE_ATU_TYPE_MEM 0x0 #define PCIE_ATU_UNR_REGION_CTRL1 0x00 #define PCIE_ATU_UNR_REGION_CTRL2 0x04 #define PCIE_ATU_UNR_LOWER_BASE 0x08 #define PCIE_ATU_UNR_UPPER_BASE 0x0c #define PCIE_ATU_UNR_LIMIT 0x10 #define PCIE_ATU_UNR_LOWER_TARGET 0x14 #define PCIE_ATU_UNR_UPPER_TARGET 0x18 /* bar4 + 0x10000 has map to ep base + 0x18000 ((0x3 << 15)) */ #define PCIE_ATU_IB_REGION(region) (((region) << 9) | (0x1 << 8)) #define PCIE_ATU_OB_REGION(region) ((region) << 9) #define PCIE_SAVE_REGION_NUM (IATU_MAX_REGION * 2) #define PCIE_SAVE_REG_NUM 8 #define PCIE_LEGACY_CLEAR_BASE 0x29000000 #define PCIE_LEGACY_CLEAR_REG 0x2c #define PCIE_LEGACY_CLEAR_MASK BIT(0) #define BIT_SET_OFFSET 0x1000 #define BIT_CLR_OFFSET 0x2000 struct sprd_ep_dev_notify { void (*notify)(int event, void *data); void *data; }; struct sprd_pci_ep_dev { struct pci_dev *pdev; void __iomem *cfg_base; /* ep config bar base in rc */ spinlock_t irq_lock; /* irq spinlock */ spinlock_t bar_lock; /* bar spinlock */ spinlock_t set_irq_lock; /* set irq spinlock */ spinlock_t set_bar_lock; /* set bar spinlock */ unsigned long bar_res; u32 base_irq; u32 ipa_base_irq; u32 bak_irq_status; u8 iatu_unroll_enabled; u8 ep; u8 irq_cnt; bool no_msi; bool need_backup; void __iomem *legacy_addr; struct resource *bar[BAR_CNT]; void __iomem *bar_vir[BAR_MAX]; void __iomem *cpu_vir[BAR_MAX]; dma_addr_t src_addr[BAR_MAX]; dma_addr_t target_addr[BAR_MAX]; size_t map_size[BAR_MAX]; int event; struct work_struct notify_work; }; struct sprd_pci_ep_dev_save { bool save_succ; unsigned long bar_res; void __iomem *bar_vir[BAR_MAX]; void __iomem *cpu_vir[BAR_MAX]; dma_addr_t src_addr[BAR_MAX]; dma_addr_t target_addr[BAR_MAX]; size_t map_size[BAR_MAX]; u32 doorbell_enable; u32 doorbell_status; void __iomem *cfg_base; u32 save_reg[PCIE_SAVE_REGION_NUM][PCIE_SAVE_REG_NUM]; }; static void __iomem *g_irq_addr[PCIE_EP_NR]; static struct sprd_pci_ep_dev_save g_ep_save[PCIE_EP_NR]; static struct sprd_pci_ep_dev *g_ep_dev[PCIE_EP_NR]; static irq_handler_t ep_dev_handler[PCIE_EP_NR][PCIE_MSI_MAX_IRQ]; static void *ep_dev_handler_data[PCIE_EP_NR][PCIE_MSI_MAX_IRQ]; static struct sprd_ep_dev_notify g_ep_dev_notify[PCIE_EP_NR]; static int sprd_ep_dev_get_bar(int ep); static int sprd_ep_dev_put_bar(int ep, int bar); static int sprd_ep_dev_adjust_region(struct sprd_pci_ep_dev *ep_dev, int bar, dma_addr_t *cpu_addr_ptr, size_t *size_ptr, dma_addr_t *offset_ptr); static int sprd_ep_dev_just_map_bar(struct sprd_pci_ep_dev *ep_dev, int bar, dma_addr_t cpu_addr, size_t size); static int sprd_ep_dev_just_unmap_bar(struct sprd_pci_ep_dev *ep_dev, int bar); static void __iomem *sprd_ep_dev_map_bar(int ep, int bar, dma_addr_t cpu_addr, size_t size); static int sprd_ep_dev_unmap_bar(int ep, int bar); static void sprd_pci_ep_dev_backup(struct sprd_pci_ep_dev *ep_dev); /* //Quectel Remove static void sprd_pcie_iommu_init(struct device *dev) { struct iommu_domain *domain = NULL; unsigned long pg_size; dma_addr_t start, end, addr; u32 base, size; int ret = 0; domain = iommu_get_domain_for_dev(dev); if(!domain) { dev_info(dev, "sprd_pcie_iommu_init domian null"); return; } pg_size = 1UL << __ffs(domain->pgsize_bitmap); smem_get_area(SIPC_ID_MINIAP, &base, &size); start = ALIGN(base, pg_size); end = ALIGN(base + size, pg_size); for (addr = start; addr < end; addr += pg_size) { phys_addr_t phys_addr; phys_addr = iommu_iova_to_phys(domain, addr); if (phys_addr) { dev_info(dev, "sprd_pcie_iommu_init iova:%d have been used", (u32)addr); continue; } ret = iommu_map(domain, addr, addr, pg_size, IOMMU_READ | IOMMU_WRITE); if (ret) { dev_info(dev, "sprd_pcie_iommu_init iommu_map failed"); return; } } } */ #if (LINUX_VERSION_CODE < KERNEL_VERSION( 3,11,0 )) int pci_enable_msi_range(struct pci_dev *dev, int minvec, int maxvec) { int nvec = maxvec; int rc; if (maxvec < minvec) return -ERANGE; do { rc = pci_enable_msi_block(dev, nvec); if (rc < 0) { return rc; } else if (rc > 0) { if (rc < minvec) return -ENOSPC; nvec = rc; } } while (rc); return nvec; } #endif int sprd_ep_dev_register_notify(int ep, void (*notify)(int event, void *data), void *data) { struct sprd_ep_dev_notify *dev_notify; if (ep >= PCIE_EP_NR) return -EINVAL; dev_notify = &g_ep_dev_notify[ep]; dev_notify->notify = notify; dev_notify->data = data; return 0; } EXPORT_SYMBOL_GPL(sprd_ep_dev_register_notify); int sprd_ep_dev_unregister_notify(int ep) { struct sprd_ep_dev_notify *notify; if (ep >= PCIE_EP_NR) return -EINVAL; notify = &g_ep_dev_notify[ep]; notify->notify = NULL; notify->data = NULL; return 0; } EXPORT_SYMBOL_GPL(sprd_ep_dev_unregister_notify); int sprd_ep_dev_set_irq_addr(int ep, void __iomem *irq_addr) { if (ep >= PCIE_EP_NR) return -EINVAL; g_irq_addr[ep] = irq_addr; return 0; } EXPORT_SYMBOL_GPL(sprd_ep_dev_set_irq_addr); int sprd_ep_dev_register_irq_handler(int ep, int irq, irq_handler_t handler, void *data) { struct sprd_pci_ep_dev *ep_dev; if (ep >= PCIE_EP_NR || irq >= PCIE_MSI_MAX_IRQ) return -EINVAL; ep_dev_handler[ep][irq] = handler; ep_dev_handler_data[ep][irq] = data; ep_dev = g_ep_dev[ep]; if (handler && ep_dev && (BIT(irq) & ep_dev->bak_irq_status)) { ep_dev->bak_irq_status &= ~BIT(irq); handler(irq, data); } return 0; } EXPORT_SYMBOL_GPL(sprd_ep_dev_register_irq_handler); int sprd_ep_dev_unregister_irq_handler(int ep, int irq) { if (ep < PCIE_EP_NR && irq < PCIE_MSI_MAX_IRQ) { ep_dev_handler[ep][irq] = NULL; ep_dev_handler_data[ep][irq] = NULL; return 0; } return -EINVAL; } EXPORT_SYMBOL_GPL(sprd_ep_dev_unregister_irq_handler); int sprd_ep_dev_register_irq_handler_ex(int ep, int from_irq, int to_irq, irq_handler_t handler, void *data) { int i, ret; for (i = from_irq; i < to_irq + 1; i++) { ret = sprd_ep_dev_register_irq_handler(ep, i, handler, data); if (ret) return ret; } return 0; } int sprd_ep_dev_unregister_irq_handler_ex(int ep, int from_irq, int to_irq) { int i, ret; for (i = from_irq; i < to_irq + 1; i++) { ret = sprd_ep_dev_unregister_irq_handler(ep, i); if (ret) return ret; } return 0; } void __iomem *sprd_ep_map_memory(int ep, phys_addr_t cpu_addr, size_t size) { int bar; void __iomem *bar_addr; bar = sprd_ep_dev_get_bar(ep); if (bar < 0) { pr_err("%s: get bar err = %d\n", __func__, bar); return NULL; }else{ pr_err("%s: get bar = %d\n", __func__, bar); } bar_addr = sprd_ep_dev_map_bar(ep, bar, cpu_addr, size); if (!bar_addr) { pr_err("%s: map bar = %d err!\n", __func__, bar); sprd_ep_dev_put_bar(ep, bar); return NULL; } return bar_addr; } EXPORT_SYMBOL_GPL(sprd_ep_map_memory); void sprd_ep_unmap_memory(int ep, const void __iomem *bar_addr) { int bar; struct sprd_pci_ep_dev *ep_dev; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return; ep_dev = g_ep_dev[ep]; for (bar = 0; bar < BAR_MAX; bar++) { if (bar_addr == ep_dev->cpu_vir[bar]) { sprd_ep_dev_unmap_bar(ep, bar); sprd_ep_dev_put_bar(ep, bar); break; } } } EXPORT_SYMBOL_GPL(sprd_ep_unmap_memory); #ifdef CONFIG_SPRD_SIPA phys_addr_t sprd_ep_ipa_map(int type, phys_addr_t target_addr, size_t size) { int bar, ep = PCIE_EP_MODEM; dma_addr_t offset; struct sprd_pci_ep_dev *ep_dev; struct pci_dev *pdev; struct device *dev; struct resource *res; ep_dev = g_ep_dev[ep]; if (!ep_dev) return 0; ep_dev = g_ep_dev[ep]; pdev = ep_dev->pdev; dev = &pdev->dev; #ifdef CONFIG_SPRD_IPA_PCIE_WORKROUND if(type == PCIE_IPA_TYPE_REG){ bar = IPA_REG_BAR; }else if(type == PCIE_IPA_TYPE_TFT_REG){ bar = IPA_TFT_BAR; }else{ bar = IPA_MEM_BAR; } pr_info("%s: get bar = %d\n", __func__, bar); #else bar = sprd_ep_dev_get_bar(ep); if (bar < 0) { dev_err(dev, "ep: ipa map, get bar err = %d\n", bar); return 0; } #endif res = &pdev->resource[bar]; dev_info(dev, "ep: ipa map type=%d, addr=0x%lx, size=0x%lx\n", type, (unsigned long)target_addr, (unsigned long)size); /* 1st, adjust the map region */ if (sprd_ep_dev_adjust_region(ep_dev, bar, &target_addr, &size, &offset)) return 0; /* than, map bar */ if (sprd_ep_dev_just_map_bar(ep_dev, bar, target_addr, size)) return 0; /* save for unmap */ ep_dev->src_addr[bar] = res->start + offset; ep_dev->target_addr[bar] = target_addr; ep_dev->map_size[bar] = size; /* return the cpu phy address */ return res->start + offset; } int sprd_ep_ipa_unmap(int type, const phys_addr_t cpu_addr) { int bar, ep = PCIE_EP_MODEM; bool find_bar = false; struct sprd_pci_ep_dev *ep_dev; struct pci_dev *pdev; struct resource *res; ep_dev = g_ep_dev[ep]; if (!ep_dev) return -EINVAL; pdev = ep_dev->pdev; res = &pdev->resource[bar]; dev_info(&pdev->dev, "ep: ipa unmap cpu_addr=0x%lx\n", (unsigned long)cpu_addr); #ifdef CONFIG_SPRD_IPA_PCIE_WORKROUND if(type == PCIE_IPA_TYPE_REG){ bar = IPA_REG_BAR; }else if(type == PCIE_IPA_TYPE_TFT_REG){ bar = IPA_TFT_BAR; }else{ bar = IPA_MEM_BAR; } if (ep_dev->src_addr[bar] == cpu_addr) find_bar = true; #else for (bar = 0; bar < BAR_MAX; bar++) { if (cpu_addr == ep_dev->src_addr[bar]) { find_bar = true; break; } } #endif if (!find_bar) { dev_err(&pdev->dev, "ep: ipa unmap can't find bar!"); return -EINVAL; } ep_dev->target_addr[bar] = 0; ep_dev->target_addr[bar] = 0; ep_dev->map_size[bar] = 0; return sprd_ep_dev_just_unmap_bar(ep_dev, bar); } #endif int sprd_ep_dev_raise_irq(int ep, int irq) { struct pci_dev *pdev; struct device *dev; struct sprd_pci_ep_dev *ep_dev; void __iomem *base; u32 value; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return -ENODEV; ep_dev = g_ep_dev[ep]; pdev = ep_dev->pdev; dev = &pdev->dev; dev_dbg(dev, "ep: raise, ep=%d, irq=%d\n", ep, irq); if (irq >= DOOR_BELL_IRQ_CNT) { dev_err(&pdev->dev, "raise err, irq=%d\n", irq); return -EINVAL; } spin_lock(&ep_dev->set_irq_lock); base = ep_dev->cfg_base + DOOR_BELL_BASE; value = readl_relaxed(base + DOOR_BELL_STATUS); writel_relaxed(value | DOOR_BELL_IRQ_VALUE(irq), base + DOOR_BELL_STATUS); spin_unlock(&ep_dev->set_irq_lock); return 0; } EXPORT_SYMBOL_GPL(sprd_ep_dev_raise_irq); int sprd_ep_dev_clear_doolbell_irq(int ep, int irq) { struct pci_dev *pdev; struct device *dev; struct sprd_pci_ep_dev *ep_dev; void __iomem *base; u32 value; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return -ENODEV; ep_dev = g_ep_dev[ep]; pdev = ep_dev->pdev; dev = &pdev->dev; dev_dbg(dev, "ep: clear doorbell, ep=%d, irq=%d\n", ep, irq); if (irq >= DOOR_BELL_IRQ_CNT) return -EINVAL; spin_lock(&ep_dev->set_irq_lock); base = ep_dev->cfg_base + DOOR_BELL_BASE; value = readl_relaxed(base + DOOR_BELL_STATUS); if (value & DOOR_BELL_IRQ_VALUE(irq)) writel_relaxed(value & (~DOOR_BELL_IRQ_VALUE(irq)), base + DOOR_BELL_STATUS); spin_unlock(&ep_dev->set_irq_lock); return 0; } int sprd_ep_dev_set_backup(int ep) { struct sprd_pci_ep_dev *ep_dev; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return -ENODEV; ep_dev = g_ep_dev[ep]; ep_dev->need_backup = true; /* backup once immediately. */ sprd_pci_ep_dev_backup(ep_dev); return 0; } int sprd_ep_dev_clear_backup(int ep) { struct sprd_pci_ep_dev *ep_dev; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return -ENODEV; ep_dev = g_ep_dev[ep]; ep_dev->need_backup = false; return 0; } int sprd_ep_dev_pass_smem(int ep, u32 base, u32 size) { struct sprd_pci_ep_dev *ep_dev; void __iomem *reg_base; struct pci_dev *pdev; struct device *dev; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return -ENODEV; ep_dev = g_ep_dev[ep]; pdev = ep_dev->pdev; dev = &pdev->dev; dev_info(&pdev->dev, "pass_smem, base=0x%x,size=0x%x\n", base, size); reg_base = ep_dev->cfg_base + DOOR_BELL_BASE; writel_relaxed(base, reg_base + DOOR_BELL_SMEMBASE); writel_relaxed(size, reg_base + DOOR_BELL_SMEMSIZE); return 0; } static inline u32 sprd_pci_ep_iatu_readl(struct sprd_pci_ep_dev *ep_dev, u32 offset) { return readl_relaxed(ep_dev->cfg_base + IATU_REG_BASE + offset); } static inline void sprd_pci_ep_iatu_writel(struct sprd_pci_ep_dev *ep_dev, u32 offset, u32 value) { writel_relaxed(value, ep_dev->cfg_base + IATU_REG_BASE + offset); } static int sprd_ep_dev_get_bar(int ep) { int bar; int ret = -EBUSY; struct sprd_pci_ep_dev *ep_dev; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return -ENODEV; ep_dev = g_ep_dev[ep]; spin_lock(&ep_dev->bar_lock); for (bar = BAR_MIN; bar < BAR_MAX; bar++) { if (ep_dev->bar[bar] && !test_bit(bar, &ep_dev->bar_res)) { set_bit(bar, &ep_dev->bar_res); ret = bar; break; } } spin_unlock(&ep_dev->bar_lock); return ret; } static int sprd_ep_dev_put_bar(int ep, int bar) { int ret = -ENODEV; struct sprd_pci_ep_dev *ep_dev; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return -ENODEV; ep_dev = g_ep_dev[ep]; spin_lock(&ep_dev->bar_lock); if (test_and_clear_bit(bar, &ep_dev->bar_res)) ret = bar; spin_unlock(&ep_dev->bar_lock); return ret; } static int sprd_ep_dev_unr_set_bar(struct sprd_pci_ep_dev *ep_dev, int bar, dma_addr_t cpu_addr, size_t size) { u32 retries, val; struct pci_dev *pdev = ep_dev->pdev; spin_lock(&ep_dev->set_bar_lock); /* bar n use region n to map, map to bar match mode */ sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_IB_REGION(bar) + PCIE_ATU_UNR_LOWER_TARGET, lower_32_bits(cpu_addr)); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_IB_REGION(bar) + PCIE_ATU_UNR_UPPER_TARGET, upper_32_bits(cpu_addr)); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_IB_REGION(bar) + PCIE_ATU_UNR_REGION_CTRL1, PCIE_ATU_TYPE_MEM); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_IB_REGION(bar) + PCIE_ATU_UNR_REGION_CTRL2, PCIE_ATU_ENABLE | PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); spin_unlock(&ep_dev->set_bar_lock); /* * Make sure ATU enable takes effect before any subsequent config * and I/O accesses. */ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { val = sprd_pci_ep_iatu_readl(ep_dev, PCIE_ATU_IB_REGION(bar) + PCIE_ATU_UNR_REGION_CTRL2); if (val & PCIE_ATU_ENABLE) return 0; dev_dbg(&pdev->dev, "ep: unr set bar[%d], var = 0x%x\n", bar, val); /* wait a moment for polling ep atu enable bit */ usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); } return -EINVAL; } static int sprd_ep_dev_unr_clear_bar(struct sprd_pci_ep_dev *ep_dev, int bar) { struct pci_dev *pdev = ep_dev->pdev; dev_dbg(&pdev->dev, "ep: unr clear map bar=%d\n", bar); spin_lock(&ep_dev->set_bar_lock); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_IB_REGION(bar) + PCIE_ATU_UNR_REGION_CTRL2, (u32)(~PCIE_ATU_ENABLE)); spin_unlock(&ep_dev->set_bar_lock); return 0; } static int sprd_ep_dev_adjust_region(struct sprd_pci_ep_dev *ep_dev, int bar, dma_addr_t *cpu_addr_ptr, size_t *size_ptr, dma_addr_t *offset_ptr) { dma_addr_t cpu_addr, base, offset; resource_size_t bar_size, size; struct pci_dev *pdev = ep_dev->pdev; struct resource *res = &pdev->resource[bar]; size = (resource_size_t)*size_ptr; cpu_addr = *cpu_addr_ptr; bar_size = resource_size(res); /* size must align with page */ size = PAGE_ALIGN(size); /* base must be divisible by bar size for bar match mode */ base = cpu_addr / bar_size * bar_size; offset = cpu_addr - base; size += PAGE_ALIGN(offset); /* size must < bar size */ if (size > bar_size) { dev_err(&pdev->dev, "bar[%d]:size=0x%lx > 0x%lx\n", bar, (unsigned long)size, (unsigned long)bar_size); return -EINVAL; } dev_dbg(&pdev->dev, "bar[%d]: base=0x%lx,size=0x%lx,offset=0x%lx\n", bar, (unsigned long)base, (unsigned long)size, (unsigned long)offset); *size_ptr = (size_t)size; *offset_ptr = offset; *cpu_addr_ptr = base; return 0; } static int sprd_ep_dev_just_map_bar(struct sprd_pci_ep_dev *ep_dev, int bar, dma_addr_t cpu_addr, size_t size) { u32 retries, val; struct pci_dev *pdev; struct device *dev; pdev = ep_dev->pdev; dev = &pdev->dev; dev_dbg(dev, "ep: map bar=%d, addr=0x%lx, size=0x%lx\n", bar, (unsigned long)cpu_addr, (unsigned long)size); if (ep_dev->iatu_unroll_enabled) return sprd_ep_dev_unr_set_bar(ep_dev, bar, cpu_addr, size); spin_lock(&ep_dev->set_bar_lock); /* bar n use region n to map, map to bar match mode */ sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | bar); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_LOWER_TARGET, lower_32_bits(cpu_addr)); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_UPPER_TARGET, upper_32_bits(cpu_addr)); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_CR1, PCIE_ATU_TYPE_MEM); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_CR2, PCIE_ATU_ENABLE | PCIE_ATU_BAR_MODE_ENABLE | (bar << 8)); spin_unlock(&ep_dev->set_bar_lock); /* * Make sure ATU enable takes effect * before any subsequent config and I/O accesses. */ for (retries = 0; retries < LINK_WAIT_MAX_IATU_RETRIES; retries++) { val = sprd_pci_ep_iatu_readl(ep_dev, PCIE_ATU_CR2); if (val & PCIE_ATU_ENABLE) return 0; /* wait a moment for polling ep atu enable bit */ usleep_range(LINK_WAIT_IATU_MIN, LINK_WAIT_IATU_MAX); } return -EINVAL; } static int sprd_ep_dev_just_unmap_bar(struct sprd_pci_ep_dev *ep_dev, int bar) { struct pci_dev *pdev; struct device *dev; pdev = ep_dev->pdev; dev = &pdev->dev; dev_dbg(dev, "ep: unmap bar = %d\n", bar); if (ep_dev->iatu_unroll_enabled) return sprd_ep_dev_unr_clear_bar(ep_dev, bar); spin_lock(&ep_dev->set_bar_lock); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_VIEWPORT, PCIE_ATU_REGION_INBOUND | bar); sprd_pci_ep_iatu_writel(ep_dev, PCIE_ATU_CR2, (u32)(~PCIE_ATU_ENABLE)); spin_unlock(&ep_dev->set_bar_lock); return 0; } static void __iomem *sprd_ep_dev_map_bar(int ep, int bar, dma_addr_t cpu_addr, size_t size) { resource_size_t offset; struct pci_dev *pdev; struct device *dev; struct sprd_pci_ep_dev *ep_dev; void __iomem *bar_vir; struct resource *res; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return NULL; ep_dev = g_ep_dev[ep]; pdev = ep_dev->pdev; dev = &pdev->dev; /* bar is be used */ if (ep_dev->bar_vir[bar]) { dev_err(dev, "ep: bar[%d] is used!", bar); return NULL; } /* 1st, adjust the map region */ if (sprd_ep_dev_adjust_region(ep_dev, bar, &cpu_addr, &size, &offset)) return NULL; /* than, ioremap, if map failed, no need to set bar */ res = &pdev->resource[bar]; #ifndef ioremap_nocache #define ioremap_nocache ioremap #endif bar_vir = ioremap_nocache(res->start, size); if (!bar_vir) { dev_err(dev, "ep: map error, bar=%d, addr=0x%lx, size=0x%lx\n", bar, (unsigned long)cpu_addr, (unsigned long)size); return NULL; } if (sprd_ep_dev_just_map_bar(ep_dev, bar, cpu_addr, size)) { dev_err(dev, "ep: map bar =%d\n", bar); iounmap(ep_dev->bar_vir[bar]); return NULL; } ep_dev->bar_vir[bar] = (void __iomem *)bar_vir; ep_dev->cpu_vir[bar] = (void __iomem *)(bar_vir + offset); ep_dev->src_addr[bar] = res->start + offset; ep_dev->target_addr[bar] = cpu_addr; ep_dev->map_size[bar] = size; return ep_dev->cpu_vir[bar]; } static int sprd_ep_dev_unmap_bar(int ep, int bar) { struct pci_dev *pdev; struct device *dev; struct sprd_pci_ep_dev *ep_dev; if (ep >= PCIE_EP_NR || !g_ep_dev[ep]) return -ENODEV; ep_dev = g_ep_dev[ep]; pdev = ep_dev->pdev; dev = &pdev->dev; dev_info(dev, "ep: unmap bar = %d\n", bar); if (!ep_dev->bar_vir[bar]) return -ENODEV; sprd_ep_dev_just_unmap_bar(ep_dev, bar); iounmap(ep_dev->bar_vir[bar]); ep_dev->bar_vir[bar] = NULL; ep_dev->cpu_vir[bar] = NULL; ep_dev->src_addr[bar] = 0; ep_dev->target_addr[bar] = 0; ep_dev->map_size[bar] = 0; return 0; } static void sprd_pci_ep_dev_clear_legacy_irq(struct sprd_pci_ep_dev *ep_dev) { if (!ep_dev->legacy_addr) return; writel_relaxed(PCIE_LEGACY_CLEAR_MASK, ep_dev->legacy_addr + BIT_CLR_OFFSET + PCIE_LEGACY_CLEAR_REG); } static irqreturn_t sprd_pci_ep_dev_irqhandler(int irq, void *dev_ptr) { struct sprd_pci_ep_dev *ep_dev = dev_ptr; struct pci_dev *pdev = ep_dev->pdev; struct device *dev = &pdev->dev; irq_handler_t handler; u32 i, j, value; int ipa_irq; if (ep_dev->no_msi) { /* clear irq */ sprd_pci_ep_dev_clear_legacy_irq(ep_dev); /* workaroud for IPA */ handler = ep_dev_handler[ep_dev->ep][PCIE_MSI_IPA]; if (handler) handler(irq, ep_dev_handler_data[ep_dev->ep][PCIE_MSI_IPA]); value = BL_READL(g_irq_addr[ep_dev->ep]); dev_dbg(dev, "ep: irq handler. irq = 0x%x, base=%d\n", value, ep_dev->base_irq); for (i = 0; i < 32; i++) { if (value & BIT(i)) { /* clear iqr bit*/ value = BL_READL(g_irq_addr[ep_dev->ep]); value &= ~(BIT(i)); BL_WRITEL(value,g_irq_addr[ep_dev->ep]); j = i - ep_dev->base_irq; if (j >= PCIE_MSI_MAX_IRQ) continue; handler = ep_dev_handler[ep_dev->ep][j]; if (handler) handler(irq, ep_dev_handler_data[ep_dev->ep][j]); else ep_dev->bak_irq_status |= BIT(j); } } return IRQ_HANDLED; } dev_dbg(dev, "ep: irq handler. irq = %d\n", irq); /* for ipa hw irq. */ ipa_irq = irq - (pdev->irq + ep_dev->ipa_base_irq); if (ipa_irq >= 0 && ipa_irq < IPA_HW_IRQ_CNT) { handler = ep_dev_handler[ep_dev->ep][PCIE_MSI_IPA]; if (handler) handler(ipa_irq, ep_dev_handler_data[ep_dev->ep][PCIE_MSI_IPA]); else ep_dev->bak_irq_status |= BIT(PCIE_MSI_IPA); return IRQ_HANDLED; } irq -= (pdev->irq + ep_dev->base_irq); if (irq >= PCIE_MSI_MAX_IRQ || irq < 0) { dev_err(dev, "ep: error, irq = %d", irq); return IRQ_HANDLED; } handler = ep_dev_handler[ep_dev->ep][irq]; if (handler) handler(irq, ep_dev_handler_data[ep_dev->ep][irq]); else ep_dev->bak_irq_status |= BIT(irq); return IRQ_HANDLED; } static void sprd_pci_ep_save_reg(struct sprd_pci_ep_dev *ep_dev) { int i, j; u32 (*save_reg)[PCIE_SAVE_REG_NUM]; static struct sprd_pci_ep_dev_save *ep_save; ep_save = &g_ep_save[ep_dev->ep]; save_reg = ep_save->save_reg; for (i = 0; i < PCIE_SAVE_REGION_NUM; i += 2) { for (j = 0; j < PCIE_SAVE_REG_NUM; j++) { save_reg[i][j] = sprd_pci_ep_iatu_readl(ep_dev, PCIE_ATU_OB_REGION(i) + j * sizeof(u32)); save_reg[i + 1][j] = sprd_pci_ep_iatu_readl(ep_dev, PCIE_ATU_IB_REGION(i) + j * sizeof(u32)); } } ep_save->doorbell_enable = sprd_pci_ep_iatu_readl(ep_dev, DOOR_BELL_BASE + DOOR_BELL_ENABLE); ep_save->doorbell_status = sprd_pci_ep_iatu_readl(ep_dev, DOOR_BELL_BASE + DOOR_BELL_STATUS); ep_save->cfg_base = ep_dev->cfg_base; ep_save->save_succ = true; } static void sprd_pci_ep_dev_backup(struct sprd_pci_ep_dev *ep_dev) { struct pci_dev *pdev = ep_dev->pdev; struct device *dev = &pdev->dev; struct sprd_pci_ep_dev_save *ep_save; int i; ep_save = &g_ep_save[ep_dev->ep]; /* save some member */ ep_save->bar_res = ep_dev->bar_res; for (i = 0; i < BAR_MAX; i++) { if (!ep_dev->src_addr[i]) continue; dev_info(dev, "ep: backup bar=%d, addr=0x%lx, size=0x%lx\n", i, (unsigned long)ep_save->target_addr[i], (unsigned long)ep_save->map_size[i]); ep_save->bar_vir[i] = ep_dev->bar_vir[i]; ep_save->cpu_vir[i] = ep_dev->cpu_vir[i]; ep_save->src_addr[i] = ep_dev->src_addr[i]; ep_save->target_addr[i] = ep_dev->target_addr[i]; ep_save->map_size[i] = ep_dev->map_size[i]; } /* save ep reg */ sprd_pci_ep_save_reg(ep_dev); } static void sprd_pci_ep_dev_restore(struct sprd_pci_ep_dev *ep_dev) { struct pci_dev *pdev = ep_dev->pdev; struct device *dev = &pdev->dev; struct sprd_pci_ep_dev_save *ep_save; int i; ep_save = &g_ep_save[ep_dev->ep]; /* save some member */ ep_dev->bar_res = ep_save->bar_res; for (i = 0; i < BAR_MAX; i++) { if (!ep_save->src_addr[i]) continue; ep_dev->bar_vir[i] = ep_save->bar_vir[i]; ep_dev->cpu_vir[i] = ep_save->cpu_vir[i]; ep_dev->src_addr[i] = ep_save->src_addr[i]; ep_dev->target_addr[i] = ep_save->target_addr[i]; ep_dev->map_size[i] = ep_save->map_size[i]; dev_info(dev, "ep: restore bar=%d, addr=0x%lx, size=0x%lx\n", i, (unsigned long)ep_dev->target_addr[i], (unsigned long)ep_dev->map_size[i]); if (sprd_ep_dev_just_map_bar(ep_dev, i, ep_dev->target_addr[i], ep_dev->map_size[i])) dev_err(dev, "ep: restore map err i = %d.\n", i); } } static void sprd_pci_ep_notify_fn(struct work_struct *work) { struct sprd_ep_dev_notify *notify; struct sprd_pci_ep_dev *ep_dev = container_of(work, struct sprd_pci_ep_dev, notify_work); notify = &g_ep_dev_notify[ep_dev->ep]; if (notify->notify) notify->notify(ep_dev->event, notify->data); } static int sprd_pci_ep_dev_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { int i, err, irq_cnt = 0; u32 val; enum dev_pci_barno bar; struct device *dev = &pdev->dev; struct sprd_pci_ep_dev *ep_dev; struct resource *res; dev_info(dev, "ep: probe\n"); //sprd_pcie_iommu_init(dev); //Quectel Remove ep_dev = devm_kzalloc(dev, sizeof(*ep_dev), GFP_KERNEL); spin_lock_init(&ep_dev->set_bar_lock); spin_lock_init(&ep_dev->bar_lock); spin_lock_init(&ep_dev->set_irq_lock); if (!ep_dev) return -ENOMEM; ep_dev->pdev = pdev; if (ent->device == PCI_DEVICE_ID_SPRD_ORCA) ep_dev->ep = PCIE_EP_MODEM; else { dev_err(dev, "ep: Cannot support ep device = 0x%x\n", ent->device); return -EINVAL; } err = pci_enable_device(pdev); if (err) { dev_err(dev, "ep: Cannot enable PCI device\n"); return err; } err = pci_request_regions(pdev, DRV_MODULE_NAME); if (err) { dev_err(dev, "ep: Cannot obtain PCI resources\n"); goto err_disable_pdev; } pci_set_master(pdev); if(dma_set_mask(dev, DMA_BIT_MASK(32))) { dev_warn(dev, "ep: No suitable DMA available\n"); } #if defined(CONFIG_PCI_IRQ_MSI) && defined(CONFIG_PCI_MSI) irq_cnt = pci_alloc_irq_vectors(pdev, 1, MAX_SUPPORT_IRQ, PCI_IRQ_MSI); //irq_cnt = pci_enable_msi_range(pdev, 1, MAX_SUPPORT_IRQ); dev_info(dev, "pci alloc msi irq cnt = %d\n", irq_cnt); #endif if (sprd_pcie_is_defective_chip()) { ep_dev->base_irq = REQUEST_BASE_IRQ_DEFECT; ep_dev->ipa_base_irq = IPA_HW_IRQ_BASE_DEFECT; } else { ep_dev->base_irq = REQUEST_BASE_IRQ; ep_dev->ipa_base_irq = IPA_HW_IRQ_BASE; } for (bar = BAR_0; bar <= BAR_5; bar++) { res = &pdev->resource[bar]; dev_info(dev, "ep: BAR[%d] %pR\n", bar, res); /* only save mem bar */ if (resource_type(res) == IORESOURCE_MEM) ep_dev->bar[bar] = res; } ep_dev->cfg_base = pci_ioremap_bar(pdev, EP_CFG_BAR); if (!ep_dev->cfg_base) { dev_err(dev, "ep: failed to map cfg bar.\n"); err = -ENOMEM; goto err_disable_msi; } /* clear all 32 bit door bell */ writel_relaxed(0x0, ep_dev->cfg_base + DOOR_BELL_BASE + DOOR_BELL_STATUS); pci_set_drvdata(pdev, ep_dev); pci_read_config_dword(ep_dev->pdev, PCIE_ATU_VIEWPORT, &val); /* * this atu view port reg is 0xffffffff means that the ep device * doesn't support atu view port, we need unroll iatu registers */ dev_info(dev, "ep: atu_view_port val = 0x%x", val); ep_dev->iatu_unroll_enabled = val == 0xffffffff; /* default , PCIE_EP_PROBE */ ep_dev->event = PCIE_EP_PROBE; g_ep_dev[ep_dev->ep] = ep_dev; if (!ep_dev->bar[BAR_1] || !ep_dev->bar[BAR_3]) { /* only 2 bar, set PCIE_EP_PROBE_BEFORE_SPLIT_BAR */ ep_dev->event = PCIE_EP_PROBE_BEFORE_SPLIT_BAR; dev_info(dev, "ep:bar not ready, wait the next probe!"); } /* restore all the config */ if (ep_dev->event == PCIE_EP_PROBE) sprd_pci_ep_dev_restore(ep_dev); /* start notify work */ INIT_WORK(&ep_dev->notify_work, sprd_pci_ep_notify_fn); schedule_work(&ep_dev->notify_work); if (irq_cnt < MAX_SUPPORT_IRQ) { err = irq_cnt < 0 ? irq_cnt : -EINVAL; ep_dev->no_msi = true; dev_info(dev, "ep: failed to get MSI, err=%d, irq=%d\n", err, pdev->irq); ep_dev->legacy_addr = sprd_ep_map_memory(ep_dev->ep, PCIE_LEGACY_CLEAR_BASE, 0x4000); /* request legacy irq */ err = devm_request_irq(dev, pdev->irq, sprd_pci_ep_dev_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, ep_dev); if (err) dev_warn(dev, "ep: failed to request legacy %d\n", pdev->irq); } else { ep_dev->irq_cnt = irq_cnt; dev_info(dev, "ep: request IRQ = %d, cnt =%d\n", pdev->irq, ep_dev->irq_cnt); /* request msi irq */ for (i = ep_dev->base_irq; i < ep_dev->base_irq + PCIE_MSI_MAX_IRQ; i++) { err = devm_request_irq(dev, pdev->irq + i, sprd_pci_ep_dev_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, ep_dev); if (err) dev_warn(dev, "ep: failed to request IRQ %d for MSI %d\n", pdev->irq + i, i + 1); } #ifdef CONFIG_SPRD_IPA_PCIE_WORKROUND for (i = ep_dev->ipa_base_irq; i < ep_dev->ipa_base_irq + IPA_HW_IRQ_CNT; i++) { err = devm_request_irq(dev, pdev->irq + i, sprd_pci_ep_dev_irqhandler, IRQF_SHARED, DRV_MODULE_NAME, ep_dev); if (!err) sprd_pcie_teardown_msi_irq(pdev->irq + i); } #endif } #ifndef SPRD_PCIE_USE_DTS sipa_module_init(dev); sipa_eth_init(); sipa_dummy_init(); #endif return 0; err_disable_msi: pci_disable_msi(pdev); pci_release_regions(pdev); err_disable_pdev: pci_disable_device(pdev); return err; } static void sprd_pci_ep_dev_remove(struct pci_dev *pdev) { u32 i; struct sprd_ep_dev_notify *notify; struct sprd_pci_ep_dev *ep_dev = pci_get_drvdata(pdev); struct sipa_core *ctrl = sipa_get_ctrl_pointer(); dev_info(&pdev->dev, "ep: remove\n"); ctrl->remote_ready = false; spipe_device_down(); spool_device_down(); cancel_work_sync(&ep_dev->notify_work); /* first notify PCIE_EP_REMOVE */ notify = &g_ep_dev_notify[ep_dev->ep]; if (notify->notify) notify->notify(PCIE_EP_REMOVE, notify->data); /* back up some config before remove */ if (ep_dev->need_backup) sprd_pci_ep_dev_backup(ep_dev); if (ep_dev->no_msi) { devm_free_irq(&pdev->dev, pdev->irq, ep_dev); } else { for (i = ep_dev->base_irq; i < ep_dev->base_irq + PCIE_MSI_MAX_IRQ; i++) devm_free_irq(&pdev->dev, pdev->irq + i, ep_dev); #ifdef CONFIG_SPRD_IPA_PCIE_WORKROUND for (i = ep_dev->ipa_base_irq; i < ep_dev->ipa_base_irq + IPA_HW_IRQ_CNT; i++) devm_free_irq(&pdev->dev, pdev->irq + i, ep_dev); #endif } pci_disable_msi(pdev); #ifndef SPRD_PCIE_USE_DTS sipa_dummy_exit(); sipa_eth_exit(); sipa_module_exit(); #endif if (ep_dev->legacy_addr) { sprd_ep_unmap_memory(ep_dev->ep, ep_dev->legacy_addr); ep_dev->legacy_addr = NULL; dev_info(&ep_dev->pdev->dev, "set ep_dev->legacy_addr = %lx\n", (long)ep_dev->legacy_addr); } if (ep_dev->cfg_base) { iounmap(ep_dev->cfg_base); ep_dev->cfg_base = NULL; } pci_release_regions(pdev); pci_disable_device(pdev); g_ep_dev[ep_dev->ep] = NULL; ep_dev->bar_res = 0; } static const struct pci_device_id sprd_pci_ep_dev_tbl[] = { { PCI_DEVICE(PCI_VENDOR_ID_SPRD, PCI_DEVICE_ID_SPRD_ORCA) }, { } }; MODULE_DEVICE_TABLE(pci, sprd_pci_ep_dev_tbl); #ifdef CONFIG_PM_SLEEP static int sprd_pci_ep_suspend(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); int rc; dev_info(dev, "suspend\n"); /* Exec pci PCI_D3cold one time */ if (pdev->current_state != PCI_D0) { dev_info(dev, "done for pm %d\n", pdev->current_state); return 0; } /* * TODO: The HAL will ask the shared memory layer whether D3 is allowed. */ /* Save the PCI configuration space of a device before suspending. */ rc = pci_save_state(pdev); if (rc) { dev_err(dev, "pci_save_state error=%d\n", rc); return rc; } /* Set the power state of a PCI device. * Transition a device to a new power state, using the device's PCI PM * registers. */ rc = pci_set_power_state(pdev, PCI_D3cold); if (rc) { dev_err(dev, "pci_set_power_state error=%d\n", rc); return rc; } return 0; } static int sprd_pci_ep_resume(struct device *dev) { struct pci_dev *pdev = to_pci_dev(dev); int rc; dev_info(dev, "resume\n"); /* Set the power state of a PCI device. */ rc = pci_set_power_state(pdev, PCI_D0); if (rc) { dev_err(dev, "pci_set_power_state error=%d\n", rc); return rc; } /* Restore the saved state of a PCI device. */ pci_restore_state(pdev); /* TODO: The HAL shall inform that the device is active. */ return 0; } #endif /* CONFIG_PM_SLEEP */ static const struct dev_pm_ops sprd_pci_ep_pm = { SET_SYSTEM_SLEEP_PM_OPS(sprd_pci_ep_suspend, sprd_pci_ep_resume) }; static struct pci_driver sprd_pci_ep_dev_driver = { .name = DRV_MODULE_NAME, .id_table = sprd_pci_ep_dev_tbl, .probe = sprd_pci_ep_dev_probe, .remove = sprd_pci_ep_dev_remove, .driver = { .pm = &sprd_pci_ep_pm, } }; //module_pci_driver(sprd_pci_ep_dev_driver); #if defined(CONFIG_DEBUG_FS) static void sprd_pci_ep_dev_save_show(struct seq_file *m, struct sprd_pci_ep_dev_save *ep_save, int ep) { u32 i; seq_printf(m, "ep-save-%d configs:\n", ep); seq_printf(m, "bar_res = 0x%lx\n", ep_save->bar_res); for (i = 0; i < BAR_MAX; i++) { seq_printf(m, "src_addr[%d] = 0x%lx\n", i, (unsigned long)ep_save->src_addr[i]); seq_printf(m, "target_addr[%d] = 0x%lx\n", i, (unsigned long)ep_save->target_addr[i]); seq_printf(m, "map_size[%d] = 0x%lx\n", i, (unsigned long)ep_save->map_size[i]); } } static void sprd_pci_ep_dev_config_show(struct seq_file *m, struct sprd_pci_ep_dev *ep_dev) { u32 i; void __iomem *base; seq_printf(m, "ep-%d configs:\n", ep_dev->ep); /* doorbell regs */ seq_puts(m, "door bell regs:\n"); base = ep_dev->cfg_base + DOOR_BELL_BASE; seq_printf(m, "irq_enable = 0x%08x\n irq_status = 0x%08x\n", readl_relaxed(base + DOOR_BELL_ENABLE), readl_relaxed(base + DOOR_BELL_STATUS)); /* iatu reg regs */ seq_puts(m, "iatu regs reg:\n"); for (i = 0; i < IATU_MAX_REGION * 2; i++) { base = ep_dev->cfg_base + IATU_REG_BASE + i * 100; seq_printf(m, "IATU[%d]:\n", i); seq_printf(m, "0x%p: 0x%08x 0x%08x 0x%08x 0x%08x\n", base, readl_relaxed(base + 0x0), readl_relaxed(base + 0x4), readl_relaxed(base + 0x8), readl_relaxed(base + 0xc)); base += 0x10; seq_printf(m, "0x%p: 0x%08x 0x%08x 0x%08x 0x%08x\n", base, readl_relaxed(base + 0x0), readl_relaxed(base + 0x4), readl_relaxed(base + 0x8), readl_relaxed(base + 0x10)); } } static void sprd_pci_ep_dev_backup_show(struct seq_file *m, struct sprd_pci_ep_dev_save *ep_save, int ep) { int i; u32 (*save_reg)[PCIE_SAVE_REG_NUM]; void __iomem *base; save_reg = ep_save->save_reg; seq_printf(m, "ep-%d backup configs:\n", ep); /* doorbell regs */ seq_puts(m, "door bell regs:\n"); seq_printf(m, "irq_enable = 0x%08x\n irq_status = 0x%08x\n", ep_save->doorbell_enable, ep_save->doorbell_status); /* iatu reg regs */ seq_puts(m, "iatu regs reg:\n"); for (i = 0; i < PCIE_SAVE_REGION_NUM; i++) { seq_printf(m, "IATU[%d]:\n", i); base = ep_save->cfg_base + IATU_REG_BASE + i * 100; seq_printf(m, "0x%p: 0x%08x 0x%08x 0x%08x 0x%08x\n", base, save_reg[i][0], save_reg[i][1], save_reg[i][2], save_reg[i][3]); base += 0x10; seq_printf(m, "0x%p: 0x%08x 0x%08x 0x%08x 0x%08x\n", base, save_reg[i][4], save_reg[i][5], save_reg[i][6], save_reg[i][7]); } } static int sprd_pci_ep_dev_show(struct seq_file *m, void *unused) { u32 i; struct sprd_pci_ep_dev *ep_dev; struct sprd_pci_ep_dev_save *ep_save; for (i = 0; i < PCIE_EP_NR; i++) { /* ep_save configus */ ep_save = &g_ep_save[i]; ep_dev = g_ep_dev[i]; if (!ep_dev && !ep_save->save_succ) continue; if (ep_save) sprd_pci_ep_dev_save_show(m, ep_save, i); if (ep_dev) sprd_pci_ep_dev_config_show(m, ep_dev); else sprd_pci_ep_dev_backup_show(m, ep_save, i); } return 0; } static int sprd_pci_ep_dev_open(struct inode *inode, struct file *file) { return single_open(file, sprd_pci_ep_dev_show, NULL); } static const struct file_operations sprd_pci_ep_dev_fops = { .owner = THIS_MODULE, .open = sprd_pci_ep_dev_open, .read = seq_read, .llseek = seq_lseek, .release = single_release, }; static struct dentry *g_ep_debugfs_root; static int sprd_pci_ep_dev_init_debugfs(void) { struct dentry *g_ep_debugfs_root = debugfs_create_dir("ep_dev", NULL); if (!g_ep_debugfs_root) return -ENXIO; debugfs_create_file("ep", 0444, g_ep_debugfs_root, NULL, &sprd_pci_ep_dev_fops); return 0; } static void sprd_pci_ep_dev_remove_debugfs(void) { debugfs_remove_recursive(g_ep_debugfs_root); } #endif static int __init sprd_pci_ep_dev_init(void) { pr_info("%s %s\n", __func__, QUECTEL_SPRD_PCIE_VERSION); #if defined(CONFIG_DEBUG_FS) sprd_pci_ep_dev_init_debugfs(); sipc_init_debugfs(); #endif sipc_init(); spipe_init(); spool_init(); modem_power_manager_init(); return pci_register_driver(&sprd_pci_ep_dev_driver); } static void __exit sprd_pci_ep_dev_exit(void) { spipe_exit(); spool_exit(); modem_power_manager_exit(); pci_unregister_driver(&sprd_pci_ep_dev_driver); #if defined(CONFIG_DEBUG_FS) sprd_pci_ep_dev_remove_debugfs(); #endif sipc_exit(); } module_init(sprd_pci_ep_dev_init); module_exit(sprd_pci_ep_dev_exit); MODULE_DESCRIPTION("SPRD PCI EP DEVICE HOST DRIVER"); MODULE_AUTHOR("Wenping Zhou "); MODULE_LICENSE("GPL v2"); MODULE_VERSION(QUECTEL_SPRD_PCIE_VERSION);