diff options
Diffstat (limited to 'drivers')
152 files changed, 4865 insertions, 1977 deletions
diff --git a/drivers/acpi/nfit/core.c b/drivers/acpi/nfit/core.c index ae035b93da08..3eb56b77cb6d 100644 --- a/drivers/acpi/nfit/core.c +++ b/drivers/acpi/nfit/core.c @@ -2637,7 +2637,7 @@ static int acpi_nfit_register_region(struct acpi_nfit_desc *acpi_desc, if (ndr_desc->target_node == NUMA_NO_NODE) { ndr_desc->target_node = phys_to_target_node(spa->address); dev_info(acpi_desc->dev, "changing target node from %d to %d for nfit region [%pa-%pa]", - NUMA_NO_NODE, ndr_desc->numa_node, &res.start, &res.end); + NUMA_NO_NODE, ndr_desc->target_node, &res.start, &res.end); } /* diff --git a/drivers/base/memory.c b/drivers/base/memory.c index 5c6c1d6bb59f..6d84a02cfa5d 100644 --- a/drivers/base/memory.c +++ b/drivers/base/memory.c @@ -769,21 +769,22 @@ static struct zone *early_node_zone_for_memory_block(struct memory_block *mem, #ifdef CONFIG_NUMA /** - * memory_block_add_nid() - Indicate that system RAM falling into this memory - * block device (partially) belongs to the given node. + * memory_block_add_nid_early() - Indicate that early system RAM falling into + * this memory block device (partially) belongs + * to the given node. * @mem: The memory block device. * @nid: The node id. - * @context: The memory initialization context. * - * Indicate that system RAM falling into this memory block (partially) belongs - * to the given node. If the context indicates ("early") that we are adding the - * node during node device subsystem initialization, this will also properly - * set/adjust mem->zone based on the zone ranges of the given node. + * Indicate that early system RAM falling into this memory block (partially) + * belongs to the given node. This will also properly set/adjust mem->zone based + * on the zone ranges of the given node. + * + * Memory hotplug handles this on memory block creation, where we can only have + * a single nid span a memory block. */ -void memory_block_add_nid(struct memory_block *mem, int nid, - enum meminit_context context) +void memory_block_add_nid_early(struct memory_block *mem, int nid) { - if (context == MEMINIT_EARLY && mem->nid != nid) { + if (mem->nid != nid) { /* * For early memory we have to determine the zone when setting * the node id and handle multiple nodes spanning a single @@ -797,19 +798,18 @@ void memory_block_add_nid(struct memory_block *mem, int nid, mem->zone = early_node_zone_for_memory_block(mem, nid); else mem->zone = NULL; + /* + * If this memory block spans multiple nodes, we only indicate + * the last processed node. If we span multiple nodes (not applicable + * to hotplugged memory), zone == NULL will prohibit memory offlining + * and consequently unplug. + */ + mem->nid = nid; } - - /* - * If this memory block spans multiple nodes, we only indicate - * the last processed node. If we span multiple nodes (not applicable - * to hotplugged memory), zone == NULL will prohibit memory offlining - * and consequently unplug. - */ - mem->nid = nid; } #endif -static int add_memory_block(unsigned long block_id, unsigned long state, +static int add_memory_block(unsigned long block_id, int nid, unsigned long state, struct vmem_altmap *altmap, struct memory_group *group) { @@ -827,7 +827,7 @@ static int add_memory_block(unsigned long block_id, unsigned long state, mem->start_section_nr = block_id * sections_per_block; mem->state = state; - mem->nid = NUMA_NO_NODE; + mem->nid = nid; mem->altmap = altmap; INIT_LIST_HEAD(&mem->group_next); @@ -854,13 +854,6 @@ static int add_memory_block(unsigned long block_id, unsigned long state, return 0; } -static int add_hotplug_memory_block(unsigned long block_id, - struct vmem_altmap *altmap, - struct memory_group *group) -{ - return add_memory_block(block_id, MEM_OFFLINE, altmap, group); -} - static void remove_memory_block(struct memory_block *memory) { if (WARN_ON_ONCE(memory->dev.bus != &memory_subsys)) @@ -886,7 +879,7 @@ static void remove_memory_block(struct memory_block *memory) * Called under device_hotplug_lock. */ int create_memory_block_devices(unsigned long start, unsigned long size, - struct vmem_altmap *altmap, + int nid, struct vmem_altmap *altmap, struct memory_group *group) { const unsigned long start_block_id = pfn_to_block_id(PFN_DOWN(start)); @@ -900,7 +893,7 @@ int create_memory_block_devices(unsigned long start, unsigned long size, return -EINVAL; for (block_id = start_block_id; block_id != end_block_id; block_id++) { - ret = add_hotplug_memory_block(block_id, altmap, group); + ret = add_memory_block(block_id, nid, MEM_OFFLINE, altmap, group); if (ret) break; } @@ -1005,7 +998,7 @@ void __init memory_dev_init(void) continue; block_id = memory_block_id(nr); - ret = add_memory_block(block_id, MEM_ONLINE, NULL, NULL); + ret = add_memory_block(block_id, NUMA_NO_NODE, MEM_ONLINE, NULL, NULL); if (ret) { panic("%s() failed to add memory block: %d\n", __func__, ret); diff --git a/drivers/base/node.c b/drivers/base/node.c index 3e2329ccb618..83aeb0518e1d 100644 --- a/drivers/base/node.c +++ b/drivers/base/node.c @@ -819,13 +819,10 @@ int unregister_cpu_under_node(unsigned int cpu, unsigned int nid) #ifdef CONFIG_MEMORY_HOTPLUG static void do_register_memory_block_under_node(int nid, - struct memory_block *mem_blk, - enum meminit_context context) + struct memory_block *mem_blk) { int ret; - memory_block_add_nid(mem_blk, nid, context); - ret = sysfs_create_link_nowarn(&node_devices[nid]->dev.kobj, &mem_blk->dev.kobj, kobject_name(&mem_blk->dev.kobj)); @@ -853,7 +850,7 @@ static int register_mem_block_under_node_hotplug(struct memory_block *mem_blk, { int nid = *(int *)arg; - do_register_memory_block_under_node(nid, mem_blk, MEMINIT_HOTPLUG); + do_register_memory_block_under_node(nid, mem_blk); return 0; } @@ -893,7 +890,8 @@ static void register_memory_blocks_under_nodes(void) if (!mem) continue; - do_register_memory_block_under_node(nid, mem, MEMINIT_EARLY); + memory_block_add_nid_early(mem, nid); + do_register_memory_block_under_node(nid, mem); put_device(&mem->dev); } diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index 05c7c7d9e5a4..b8a74b1798ba 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -450,7 +450,7 @@ config MILBEAUT_XDMAC config MMP_PDMA tristate "MMP PDMA support" - depends on ARCH_MMP || ARCH_PXA || COMPILE_TEST + depends on ARCH_MMP || ARCH_PXA || ARCH_SPACEMIT || COMPILE_TEST select DMA_ENGINE help Support the MMP PDMA engine for PXA and MMP platform. diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index b43255f914f3..8e5f7defa6b6 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -584,6 +584,25 @@ dw_edma_device_prep_interleaved_dma(struct dma_chan *dchan, return dw_edma_device_transfer(&xfer); } +static void dw_hdma_set_callback_result(struct virt_dma_desc *vd, + enum dmaengine_tx_result result) +{ + u32 residue = 0; + struct dw_edma_desc *desc; + struct dmaengine_result *res; + + if (!vd->tx.callback_result) + return; + + desc = vd2dw_edma_desc(vd); + if (desc) + residue = desc->alloc_sz - desc->xfer_sz; + + res = &vd->tx_result; + res->result = result; + res->residue = residue; +} + static void dw_edma_done_interrupt(struct dw_edma_chan *chan) { struct dw_edma_desc *desc; @@ -597,6 +616,8 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) case EDMA_REQ_NONE: desc = vd2dw_edma_desc(vd); if (!desc->chunks_alloc) { + dw_hdma_set_callback_result(vd, + DMA_TRANS_NOERROR); list_del(&vd->node); vchan_cookie_complete(vd); } @@ -633,6 +654,7 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) spin_lock_irqsave(&chan->vc.lock, flags); vd = vchan_next_desc(&chan->vc); if (vd) { + dw_hdma_set_callback_result(vd, DMA_TRANS_ABORTED); list_del(&vd->node); vchan_cookie_complete(vd); } diff --git a/drivers/dma/idxd/defaults.c b/drivers/dma/idxd/defaults.c index c607ae8dd12c..2bbbcd02a0da 100644 --- a/drivers/dma/idxd/defaults.c +++ b/drivers/dma/idxd/defaults.c @@ -36,12 +36,10 @@ int idxd_load_iaa_device_defaults(struct idxd_device *idxd) group->num_wqs++; /* set name to "iaa_crypto" */ - memset(wq->name, 0, WQ_NAME_SIZE + 1); - strscpy(wq->name, "iaa_crypto", WQ_NAME_SIZE + 1); + strscpy_pad(wq->name, "iaa_crypto"); /* set driver_name to "crypto" */ - memset(wq->driver_name, 0, DRIVER_NAME_SIZE + 1); - strscpy(wq->driver_name, "crypto", DRIVER_NAME_SIZE + 1); + strscpy_pad(wq->driver_name, "crypto"); engine = idxd->engines[0]; diff --git a/drivers/dma/idxd/init.c b/drivers/dma/idxd/init.c index 8c4725ad1f64..2acc34b3daff 100644 --- a/drivers/dma/idxd/init.c +++ b/drivers/dma/idxd/init.c @@ -80,6 +80,8 @@ static struct pci_device_id idxd_pci_tbl[] = { { PCI_DEVICE_DATA(INTEL, IAA_DMR, &idxd_driver_data[IDXD_TYPE_IAX]) }, /* IAA PTL platforms */ { PCI_DEVICE_DATA(INTEL, IAA_PTL, &idxd_driver_data[IDXD_TYPE_IAX]) }, + /* IAA WCL platforms */ + { PCI_DEVICE_DATA(INTEL, IAA_WCL, &idxd_driver_data[IDXD_TYPE_IAX]) }, { 0, } }; MODULE_DEVICE_TABLE(pci, idxd_pci_tbl); diff --git a/drivers/dma/idxd/registers.h b/drivers/dma/idxd/registers.h index 02bab136385e..8dc2e8bca779 100644 --- a/drivers/dma/idxd/registers.h +++ b/drivers/dma/idxd/registers.h @@ -14,6 +14,7 @@ #define PCI_DEVICE_ID_INTEL_DSA_DMR 0x1212 #define PCI_DEVICE_ID_INTEL_IAA_DMR 0x1216 #define PCI_DEVICE_ID_INTEL_IAA_PTL 0xb02d +#define PCI_DEVICE_ID_INTEL_IAA_WCL 0xfd2d #define DEVICE_VERSION_1 0x100 #define DEVICE_VERSION_2 0x200 diff --git a/drivers/dma/imx-sdma.c b/drivers/dma/imx-sdma.c index 02a85d6f1bea..ed9e56de5a9b 100644 --- a/drivers/dma/imx-sdma.c +++ b/drivers/dma/imx-sdma.c @@ -256,7 +256,7 @@ struct sdma_script_start_addrs { /* End of v3 array */ union { s32 v3_end; s32 mcu_2_zqspi_addr; }; /* End of v4 array */ - s32 v4_end[0]; + s32 v4_end[]; }; /* diff --git a/drivers/dma/mmp_pdma.c b/drivers/dma/mmp_pdma.c index a95d31103d30..d07229a74886 100644 --- a/drivers/dma/mmp_pdma.c +++ b/drivers/dma/mmp_pdma.c @@ -15,6 +15,8 @@ #include <linux/device.h> #include <linux/platform_data/mmp_dma.h> #include <linux/dmapool.h> +#include <linux/clk.h> +#include <linux/reset.h> #include <linux/of_dma.h> #include <linux/of.h> @@ -23,9 +25,12 @@ #define DCSR 0x0000 #define DALGN 0x00a0 #define DINT 0x00f0 -#define DDADR 0x0200 +#define DDADR(n) (0x0200 + ((n) << 4)) #define DSADR(n) (0x0204 + ((n) << 4)) #define DTADR(n) (0x0208 + ((n) << 4)) +#define DDADRH(n) (0x0300 + ((n) << 4)) +#define DSADRH(n) (0x0304 + ((n) << 4)) +#define DTADRH(n) (0x0308 + ((n) << 4)) #define DCMD 0x020c #define DCSR_RUN BIT(31) /* Run Bit (read / write) */ @@ -42,6 +47,7 @@ #define DCSR_EORSTOPEN BIT(26) /* STOP on an EOR */ #define DCSR_SETCMPST BIT(25) /* Set Descriptor Compare Status */ #define DCSR_CLRCMPST BIT(24) /* Clear Descriptor Compare Status */ +#define DCSR_LPAEEN BIT(21) /* Long Physical Address Extension Enable */ #define DCSR_CMPST BIT(10) /* The Descriptor Compare Status */ #define DCSR_EORINTR BIT(9) /* The end of Receive */ @@ -74,6 +80,16 @@ struct mmp_pdma_desc_hw { u32 dsadr; /* DSADR value for the current transfer */ u32 dtadr; /* DTADR value for the current transfer */ u32 dcmd; /* DCMD value for the current transfer */ + /* + * The following 32-bit words are only used in the 64-bit, ie. + * LPAE (Long Physical Address Extension) mode. + * They are used to specify the high 32 bits of the descriptor's + * addresses. + */ + u32 ddadrh; /* High 32-bit of DDADR */ + u32 dsadrh; /* High 32-bit of DSADR */ + u32 dtadrh; /* High 32-bit of DTADR */ + u32 rsvd; /* reserved */ } __aligned(32); struct mmp_pdma_desc_sw { @@ -118,12 +134,55 @@ struct mmp_pdma_phy { struct mmp_pdma_chan *vchan; }; +/** + * struct mmp_pdma_ops - Operations for the MMP PDMA controller + * + * Hardware Register Operations (read/write hardware registers): + * @write_next_addr: Function to program address of next descriptor into + * DDADR/DDADRH + * @read_src_addr: Function to read the source address from DSADR/DSADRH + * @read_dst_addr: Function to read the destination address from DTADR/DTADRH + * + * Descriptor Memory Operations (manipulate descriptor structs in memory): + * @set_desc_next_addr: Function to set next descriptor address in descriptor + * @set_desc_src_addr: Function to set the source address in descriptor + * @set_desc_dst_addr: Function to set the destination address in descriptor + * @get_desc_src_addr: Function to get the source address from descriptor + * @get_desc_dst_addr: Function to get the destination address from descriptor + * + * Controller Configuration: + * @run_bits: Control bits in DCSR register for channel start/stop + * @dma_mask: DMA addressing capability of controller. 0 to use OF/platform + * settings, or explicit mask like DMA_BIT_MASK(32/64) + */ +struct mmp_pdma_ops { + /* Hardware Register Operations */ + void (*write_next_addr)(struct mmp_pdma_phy *phy, dma_addr_t addr); + u64 (*read_src_addr)(struct mmp_pdma_phy *phy); + u64 (*read_dst_addr)(struct mmp_pdma_phy *phy); + + /* Descriptor Memory Operations */ + void (*set_desc_next_addr)(struct mmp_pdma_desc_hw *desc, + dma_addr_t addr); + void (*set_desc_src_addr)(struct mmp_pdma_desc_hw *desc, + dma_addr_t addr); + void (*set_desc_dst_addr)(struct mmp_pdma_desc_hw *desc, + dma_addr_t addr); + u64 (*get_desc_src_addr)(const struct mmp_pdma_desc_hw *desc); + u64 (*get_desc_dst_addr)(const struct mmp_pdma_desc_hw *desc); + + /* Controller Configuration */ + u32 run_bits; + u64 dma_mask; +}; + struct mmp_pdma_device { int dma_channels; void __iomem *base; struct device *dev; struct dma_device device; struct mmp_pdma_phy *phy; + const struct mmp_pdma_ops *ops; spinlock_t phy_lock; /* protect alloc/free phy channels */ }; @@ -136,24 +195,112 @@ struct mmp_pdma_device { #define to_mmp_pdma_dev(dmadev) \ container_of(dmadev, struct mmp_pdma_device, device) -static int mmp_pdma_config_write(struct dma_chan *dchan, - struct dma_slave_config *cfg, - enum dma_transfer_direction direction); +/* For 32-bit PDMA */ +static void write_next_addr_32(struct mmp_pdma_phy *phy, dma_addr_t addr) +{ + writel(addr, phy->base + DDADR(phy->idx)); +} + +static u64 read_src_addr_32(struct mmp_pdma_phy *phy) +{ + return readl(phy->base + DSADR(phy->idx)); +} + +static u64 read_dst_addr_32(struct mmp_pdma_phy *phy) +{ + return readl(phy->base + DTADR(phy->idx)); +} + +static void set_desc_next_addr_32(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->ddadr = addr; +} + +static void set_desc_src_addr_32(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->dsadr = addr; +} + +static void set_desc_dst_addr_32(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->dtadr = addr; +} + +static u64 get_desc_src_addr_32(const struct mmp_pdma_desc_hw *desc) +{ + return desc->dsadr; +} + +static u64 get_desc_dst_addr_32(const struct mmp_pdma_desc_hw *desc) +{ + return desc->dtadr; +} + +/* For 64-bit PDMA */ +static void write_next_addr_64(struct mmp_pdma_phy *phy, dma_addr_t addr) +{ + writel(lower_32_bits(addr), phy->base + DDADR(phy->idx)); + writel(upper_32_bits(addr), phy->base + DDADRH(phy->idx)); +} + +static u64 read_src_addr_64(struct mmp_pdma_phy *phy) +{ + u32 low = readl(phy->base + DSADR(phy->idx)); + u32 high = readl(phy->base + DSADRH(phy->idx)); + + return ((u64)high << 32) | low; +} -static void set_desc(struct mmp_pdma_phy *phy, dma_addr_t addr) +static u64 read_dst_addr_64(struct mmp_pdma_phy *phy) { - u32 reg = (phy->idx << 4) + DDADR; + u32 low = readl(phy->base + DTADR(phy->idx)); + u32 high = readl(phy->base + DTADRH(phy->idx)); - writel(addr, phy->base + reg); + return ((u64)high << 32) | low; } +static void set_desc_next_addr_64(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->ddadr = lower_32_bits(addr); + desc->ddadrh = upper_32_bits(addr); +} + +static void set_desc_src_addr_64(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->dsadr = lower_32_bits(addr); + desc->dsadrh = upper_32_bits(addr); +} + +static void set_desc_dst_addr_64(struct mmp_pdma_desc_hw *desc, dma_addr_t addr) +{ + desc->dtadr = lower_32_bits(addr); + desc->dtadrh = upper_32_bits(addr); +} + +static u64 get_desc_src_addr_64(const struct mmp_pdma_desc_hw *desc) +{ + return ((u64)desc->dsadrh << 32) | desc->dsadr; +} + +static u64 get_desc_dst_addr_64(const struct mmp_pdma_desc_hw *desc) +{ + return ((u64)desc->dtadrh << 32) | desc->dtadr; +} + +static int mmp_pdma_config_write(struct dma_chan *dchan, + struct dma_slave_config *cfg, + enum dma_transfer_direction direction); + static void enable_chan(struct mmp_pdma_phy *phy) { u32 reg, dalgn; + struct mmp_pdma_device *pdev; if (!phy->vchan) return; + pdev = to_mmp_pdma_dev(phy->vchan->chan.device); + reg = DRCMR(phy->vchan->drcmr); writel(DRCMR_MAPVLD | phy->idx, phy->base + reg); @@ -165,18 +312,29 @@ static void enable_chan(struct mmp_pdma_phy *phy) writel(dalgn, phy->base + DALGN); reg = (phy->idx << 2) + DCSR; - writel(readl(phy->base + reg) | DCSR_RUN, phy->base + reg); + writel(readl(phy->base + reg) | pdev->ops->run_bits, + phy->base + reg); } static void disable_chan(struct mmp_pdma_phy *phy) { - u32 reg; + u32 reg, dcsr; if (!phy) return; reg = (phy->idx << 2) + DCSR; - writel(readl(phy->base + reg) & ~DCSR_RUN, phy->base + reg); + dcsr = readl(phy->base + reg); + + if (phy->vchan) { + struct mmp_pdma_device *pdev; + + pdev = to_mmp_pdma_dev(phy->vchan->chan.device); + writel(dcsr & ~pdev->ops->run_bits, phy->base + reg); + } else { + /* If no vchan, just clear the RUN bit */ + writel(dcsr & ~DCSR_RUN, phy->base + reg); + } } static int clear_chan_irq(struct mmp_pdma_phy *phy) @@ -295,6 +453,7 @@ static void mmp_pdma_free_phy(struct mmp_pdma_chan *pchan) static void start_pending_queue(struct mmp_pdma_chan *chan) { struct mmp_pdma_desc_sw *desc; + struct mmp_pdma_device *pdev = to_mmp_pdma_dev(chan->chan.device); /* still in running, irq will start the pending list */ if (!chan->idle) { @@ -329,7 +488,7 @@ static void start_pending_queue(struct mmp_pdma_chan *chan) * Program the descriptor's address into the DMA controller, * then start the DMA transaction */ - set_desc(chan->phy, desc->async_tx.phys); + pdev->ops->write_next_addr(chan->phy, desc->async_tx.phys); enable_chan(chan->phy); chan->idle = false; } @@ -445,15 +604,14 @@ mmp_pdma_prep_memcpy(struct dma_chan *dchan, size_t len, unsigned long flags) { struct mmp_pdma_chan *chan; + struct mmp_pdma_device *pdev; struct mmp_pdma_desc_sw *first = NULL, *prev = NULL, *new; size_t copy = 0; - if (!dchan) - return NULL; - - if (!len) + if (!dchan || !len) return NULL; + pdev = to_mmp_pdma_dev(dchan->device); chan = to_mmp_pdma_chan(dchan); chan->byte_align = false; @@ -476,13 +634,14 @@ mmp_pdma_prep_memcpy(struct dma_chan *dchan, chan->byte_align = true; new->desc.dcmd = chan->dcmd | (DCMD_LENGTH & copy); - new->desc.dsadr = dma_src; - new->desc.dtadr = dma_dst; + pdev->ops->set_desc_src_addr(&new->desc, dma_src); + pdev->ops->set_desc_dst_addr(&new->desc, dma_dst); if (!first) first = new; else - prev->desc.ddadr = new->async_tx.phys; + pdev->ops->set_desc_next_addr(&prev->desc, + new->async_tx.phys); new->async_tx.cookie = 0; async_tx_ack(&new->async_tx); @@ -526,6 +685,7 @@ mmp_pdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, unsigned long flags, void *context) { struct mmp_pdma_chan *chan = to_mmp_pdma_chan(dchan); + struct mmp_pdma_device *pdev = to_mmp_pdma_dev(dchan->device); struct mmp_pdma_desc_sw *first = NULL, *prev = NULL, *new = NULL; size_t len, avail; struct scatterlist *sg; @@ -557,17 +717,18 @@ mmp_pdma_prep_slave_sg(struct dma_chan *dchan, struct scatterlist *sgl, new->desc.dcmd = chan->dcmd | (DCMD_LENGTH & len); if (dir == DMA_MEM_TO_DEV) { - new->desc.dsadr = addr; + pdev->ops->set_desc_src_addr(&new->desc, addr); new->desc.dtadr = chan->dev_addr; } else { new->desc.dsadr = chan->dev_addr; - new->desc.dtadr = addr; + pdev->ops->set_desc_dst_addr(&new->desc, addr); } if (!first) first = new; else - prev->desc.ddadr = new->async_tx.phys; + pdev->ops->set_desc_next_addr(&prev->desc, + new->async_tx.phys); new->async_tx.cookie = 0; async_tx_ack(&new->async_tx); @@ -607,12 +768,15 @@ mmp_pdma_prep_dma_cyclic(struct dma_chan *dchan, unsigned long flags) { struct mmp_pdma_chan *chan; + struct mmp_pdma_device *pdev; struct mmp_pdma_desc_sw *first = NULL, *prev = NULL, *new; dma_addr_t dma_src, dma_dst; if (!dchan || !len || !period_len) return NULL; + pdev = to_mmp_pdma_dev(dchan->device); + /* the buffer length must be a multiple of period_len */ if (len % period_len != 0) return NULL; @@ -649,13 +813,14 @@ mmp_pdma_prep_dma_cyclic(struct dma_chan *dchan, new->desc.dcmd = (chan->dcmd | DCMD_ENDIRQEN | (DCMD_LENGTH & period_len)); - new->desc.dsadr = dma_src; - new->desc.dtadr = dma_dst; + pdev->ops->set_desc_src_addr(&new->desc, dma_src); + pdev->ops->set_desc_dst_addr(&new->desc, dma_dst); if (!first) first = new; else - prev->desc.ddadr = new->async_tx.phys; + pdev->ops->set_desc_next_addr(&prev->desc, + new->async_tx.phys); new->async_tx.cookie = 0; async_tx_ack(&new->async_tx); @@ -676,7 +841,7 @@ mmp_pdma_prep_dma_cyclic(struct dma_chan *dchan, first->async_tx.cookie = -EBUSY; /* make the cyclic link */ - new->desc.ddadr = first->async_tx.phys; + pdev->ops->set_desc_next_addr(&new->desc, first->async_tx.phys); chan->cyclic_first = first; return &first->async_tx; @@ -762,7 +927,9 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan, dma_cookie_t cookie) { struct mmp_pdma_desc_sw *sw; - u32 curr, residue = 0; + struct mmp_pdma_device *pdev = to_mmp_pdma_dev(chan->chan.device); + u64 curr; + u32 residue = 0; bool passed = false; bool cyclic = chan->cyclic_first != NULL; @@ -774,17 +941,18 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan, return 0; if (chan->dir == DMA_DEV_TO_MEM) - curr = readl(chan->phy->base + DTADR(chan->phy->idx)); + curr = pdev->ops->read_dst_addr(chan->phy); else - curr = readl(chan->phy->base + DSADR(chan->phy->idx)); + curr = pdev->ops->read_src_addr(chan->phy); list_for_each_entry(sw, &chan->chain_running, node) { - u32 start, end, len; + u64 start, end; + u32 len; if (chan->dir == DMA_DEV_TO_MEM) - start = sw->desc.dtadr; + start = pdev->ops->get_desc_dst_addr(&sw->desc); else - start = sw->desc.dsadr; + start = pdev->ops->get_desc_src_addr(&sw->desc); len = sw->desc.dcmd & DCMD_LENGTH; end = start + len; @@ -800,7 +968,7 @@ static unsigned int mmp_pdma_residue(struct mmp_pdma_chan *chan, if (passed) { residue += len; } else if (curr >= start && curr <= end) { - residue += end - curr; + residue += (u32)(end - curr); passed = true; } @@ -994,9 +1162,42 @@ static int mmp_pdma_chan_init(struct mmp_pdma_device *pdev, int idx, int irq) return 0; } +static const struct mmp_pdma_ops marvell_pdma_v1_ops = { + .write_next_addr = write_next_addr_32, + .read_src_addr = read_src_addr_32, + .read_dst_addr = read_dst_addr_32, + .set_desc_next_addr = set_desc_next_addr_32, + .set_desc_src_addr = set_desc_src_addr_32, + .set_desc_dst_addr = set_desc_dst_addr_32, + .get_desc_src_addr = get_desc_src_addr_32, + .get_desc_dst_addr = get_desc_dst_addr_32, + .run_bits = (DCSR_RUN), + .dma_mask = 0, /* let OF/platform set DMA mask */ +}; + +static const struct mmp_pdma_ops spacemit_k1_pdma_ops = { + .write_next_addr = write_next_addr_64, + .read_src_addr = read_src_addr_64, + .read_dst_addr = read_dst_addr_64, + .set_desc_next_addr = set_desc_next_addr_64, + .set_desc_src_addr = set_desc_src_addr_64, + .set_desc_dst_addr = set_desc_dst_addr_64, + .get_desc_src_addr = get_desc_src_addr_64, + .get_desc_dst_addr = get_desc_dst_addr_64, + .run_bits = (DCSR_RUN | DCSR_LPAEEN), + .dma_mask = DMA_BIT_MASK(64), /* force 64-bit DMA addr capability */ +}; + static const struct of_device_id mmp_pdma_dt_ids[] = { - { .compatible = "marvell,pdma-1.0", }, - {} + { + .compatible = "marvell,pdma-1.0", + .data = &marvell_pdma_v1_ops + }, { + .compatible = "spacemit,k1-pdma", + .data = &spacemit_k1_pdma_ops + }, { + /* sentinel */ + } }; MODULE_DEVICE_TABLE(of, mmp_pdma_dt_ids); @@ -1019,6 +1220,8 @@ static int mmp_pdma_probe(struct platform_device *op) { struct mmp_pdma_device *pdev; struct mmp_dma_platdata *pdata = dev_get_platdata(&op->dev); + struct clk *clk; + struct reset_control *rst; int i, ret, irq = 0; int dma_channels = 0, irq_num = 0; const enum dma_slave_buswidth widths = @@ -1037,6 +1240,19 @@ static int mmp_pdma_probe(struct platform_device *op) if (IS_ERR(pdev->base)) return PTR_ERR(pdev->base); + clk = devm_clk_get_optional_enabled(pdev->dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + rst = devm_reset_control_get_optional_exclusive_deasserted(pdev->dev, + NULL); + if (IS_ERR(rst)) + return PTR_ERR(rst); + + pdev->ops = of_device_get_match_data(&op->dev); + if (!pdev->ops) + return -ENODEV; + if (pdev->dev->of_node) { /* Parse new and deprecated dma-channels properties */ if (of_property_read_u32(pdev->dev->of_node, "dma-channels", @@ -1098,7 +1314,10 @@ static int mmp_pdma_probe(struct platform_device *op) pdev->device.directions = BIT(DMA_MEM_TO_DEV) | BIT(DMA_DEV_TO_MEM); pdev->device.residue_granularity = DMA_RESIDUE_GRANULARITY_DESCRIPTOR; - if (pdev->dev->coherent_dma_mask) + /* Set DMA mask based on ops->dma_mask, or OF/platform */ + if (pdev->ops->dma_mask) + dma_set_mask(pdev->dev, pdev->ops->dma_mask); + else if (pdev->dev->coherent_dma_mask) dma_set_mask(pdev->dev, pdev->dev->coherent_dma_mask); else dma_set_mask(pdev->dev, DMA_BIT_MASK(64)); diff --git a/drivers/dma/mv_xor.c b/drivers/dma/mv_xor.c index 1fdcb0f5c9e7..5e8386296046 100644 --- a/drivers/dma/mv_xor.c +++ b/drivers/dma/mv_xor.c @@ -1013,7 +1013,7 @@ static int mv_xor_channel_remove(struct mv_xor_chan *mv_chan) dma_async_device_unregister(&mv_chan->dmadev); - dma_free_coherent(dev, MV_XOR_POOL_SIZE, + dma_free_wc(dev, MV_XOR_POOL_SIZE, mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool); dma_unmap_single(dev, mv_chan->dummy_src_addr, MV_XOR_MIN_BYTE_COUNT, DMA_FROM_DEVICE); @@ -1163,7 +1163,7 @@ mv_xor_channel_add(struct mv_xor_device *xordev, err_free_irq: free_irq(mv_chan->irq, mv_chan); err_free_dma: - dma_free_coherent(&pdev->dev, MV_XOR_POOL_SIZE, + dma_free_wc(&pdev->dev, MV_XOR_POOL_SIZE, mv_chan->dma_desc_pool_virt, mv_chan->dma_desc_pool); err_unmap_dst: dma_unmap_single(dma_dev->dev, mv_chan->dummy_dst_addr, diff --git a/drivers/dma/ppc4xx/adma.c b/drivers/dma/ppc4xx/adma.c index 9d2a5a967a99..61500ad7c850 100644 --- a/drivers/dma/ppc4xx/adma.c +++ b/drivers/dma/ppc4xx/adma.c @@ -874,7 +874,7 @@ static int ppc440spe_dma2_pq_slot_count(dma_addr_t *srcs, pr_err("%s: src_cnt=%d, state=%d, addr_count=%d, order=%lld\n", __func__, src_cnt, state, addr_count, order); for (i = 0; i < src_cnt; i++) - pr_err("\t[%d] 0x%llx \n", i, srcs[i]); + pr_err("\t[%d] 0x%llx\n", i, srcs[i]); BUG(); } @@ -3636,7 +3636,7 @@ static void ppc440spe_adma_issue_pending(struct dma_chan *chan) ppc440spe_chan = to_ppc440spe_adma_chan(chan); dev_dbg(ppc440spe_chan->device->common.dev, - "ppc440spe adma%d: %s %d \n", ppc440spe_chan->device->id, + "ppc440spe adma%d: %s %d\n", ppc440spe_chan->device->id, __func__, ppc440spe_chan->pending); if (ppc440spe_chan->pending) { diff --git a/drivers/dma/sh/shdma-base.c b/drivers/dma/sh/shdma-base.c index 6b4fce453c85..834741adadaa 100644 --- a/drivers/dma/sh/shdma-base.c +++ b/drivers/dma/sh/shdma-base.c @@ -129,12 +129,25 @@ static dma_cookie_t shdma_tx_submit(struct dma_async_tx_descriptor *tx) const struct shdma_ops *ops = sdev->ops; dev_dbg(schan->dev, "Bring up channel %d\n", schan->id); - /* - * TODO: .xfer_setup() might fail on some platforms. - * Make it int then, on error remove chunks from the - * queue again - */ - ops->setup_xfer(schan, schan->slave_id); + + ret = ops->setup_xfer(schan, schan->slave_id); + if (ret < 0) { + dev_err(schan->dev, "setup_xfer failed: %d\n", ret); + + /* Remove chunks from the queue and mark them as idle */ + list_for_each_entry_safe(chunk, c, &schan->ld_queue, node) { + if (chunk->cookie == cookie) { + chunk->mark = DESC_IDLE; + list_move(&chunk->node, &schan->ld_free); + } + } + + schan->pm_state = SHDMA_PM_ESTABLISHED; + ret = pm_runtime_put(schan->dev); + + spin_unlock_irq(&schan->chan_lock); + return ret; + } if (schan->pm_state == SHDMA_PM_PENDING) shdma_chan_xfer_ld_queue(schan); diff --git a/drivers/dma/sh/shdmac.c b/drivers/dma/sh/shdmac.c index 093e449e19ee..603e15102e45 100644 --- a/drivers/dma/sh/shdmac.c +++ b/drivers/dma/sh/shdmac.c @@ -300,21 +300,30 @@ static bool sh_dmae_channel_busy(struct shdma_chan *schan) return dmae_is_busy(sh_chan); } -static void sh_dmae_setup_xfer(struct shdma_chan *schan, - int slave_id) +static int sh_dmae_setup_xfer(struct shdma_chan *schan, int slave_id) { struct sh_dmae_chan *sh_chan = container_of(schan, struct sh_dmae_chan, shdma_chan); + int ret = 0; if (slave_id >= 0) { const struct sh_dmae_slave_config *cfg = sh_chan->config; - dmae_set_dmars(sh_chan, cfg->mid_rid); - dmae_set_chcr(sh_chan, cfg->chcr); + ret = dmae_set_dmars(sh_chan, cfg->mid_rid); + if (ret < 0) + goto END; + + ret = dmae_set_chcr(sh_chan, cfg->chcr); + if (ret < 0) + goto END; + } else { dmae_init(sh_chan); } + +END: + return ret; } /* diff --git a/drivers/dma/xilinx/xilinx_dma.c b/drivers/dma/xilinx/xilinx_dma.c index a34d8f0ceed8..fabff602065f 100644 --- a/drivers/dma/xilinx/xilinx_dma.c +++ b/drivers/dma/xilinx/xilinx_dma.c @@ -2173,6 +2173,99 @@ error: } /** + * xilinx_dma_prep_peripheral_dma_vec - prepare descriptors for a DMA_SLAVE + * transaction from DMA vectors + * @dchan: DMA channel + * @vecs: Array of DMA vectors that should be transferred + * @nb: number of entries in @vecs + * @direction: DMA direction + * @flags: transfer ack flags + * + * Return: Async transaction descriptor on success and NULL on failure + */ +static struct dma_async_tx_descriptor *xilinx_dma_prep_peripheral_dma_vec( + struct dma_chan *dchan, const struct dma_vec *vecs, size_t nb, + enum dma_transfer_direction direction, unsigned long flags) +{ + struct xilinx_dma_chan *chan = to_xilinx_chan(dchan); + struct xilinx_dma_tx_descriptor *desc; + struct xilinx_axidma_tx_segment *segment, *head, *prev = NULL; + size_t copy; + size_t sg_used; + unsigned int i; + + if (!is_slave_direction(direction) || direction != chan->direction) + return NULL; + + desc = xilinx_dma_alloc_tx_descriptor(chan); + if (!desc) + return NULL; + + dma_async_tx_descriptor_init(&desc->async_tx, &chan->common); + desc->async_tx.tx_submit = xilinx_dma_tx_submit; + + /* Build transactions using information from DMA vectors */ + for (i = 0; i < nb; i++) { + sg_used = 0; + + /* Loop until the entire dma_vec entry is used */ + while (sg_used < vecs[i].len) { + struct xilinx_axidma_desc_hw *hw; + + /* Get a free segment */ + segment = xilinx_axidma_alloc_tx_segment(chan); + if (!segment) + goto error; + + /* + * Calculate the maximum number of bytes to transfer, + * making sure it is less than the hw limit + */ + copy = xilinx_dma_calc_copysize(chan, vecs[i].len, + sg_used); + hw = &segment->hw; + + /* Fill in the descriptor */ + xilinx_axidma_buf(chan, hw, vecs[i].addr, sg_used, 0); + hw->control = copy; + + if (prev) + prev->hw.next_desc = segment->phys; + + prev = segment; + sg_used += copy; + + /* + * Insert the segment into the descriptor segments + * list. + */ + list_add_tail(&segment->node, &desc->segments); + } + } + + head = list_first_entry(&desc->segments, struct xilinx_axidma_tx_segment, node); + desc->async_tx.phys = head->phys; + + /* For the last DMA_MEM_TO_DEV transfer, set EOP */ + if (chan->direction == DMA_MEM_TO_DEV) { + segment->hw.control |= XILINX_DMA_BD_SOP; + segment = list_last_entry(&desc->segments, + struct xilinx_axidma_tx_segment, + node); + segment->hw.control |= XILINX_DMA_BD_EOP; + } + + if (chan->xdev->has_axistream_connected) + desc->async_tx.metadata_ops = &xilinx_dma_metadata_ops; + + return &desc->async_tx; + +error: + xilinx_dma_free_tx_descriptor(chan, desc); + return NULL; +} + +/** * xilinx_dma_prep_slave_sg - prepare descriptors for a DMA_SLAVE transaction * @dchan: DMA channel * @sgl: scatterlist to transfer to/from @@ -3180,6 +3273,7 @@ static int xilinx_dma_probe(struct platform_device *pdev) xdev->common.device_config = xilinx_dma_device_config; if (xdev->dma_config->dmatype == XDMA_TYPE_AXIDMA) { dma_cap_set(DMA_CYCLIC, xdev->common.cap_mask); + xdev->common.device_prep_peripheral_dma_vec = xilinx_dma_prep_peripheral_dma_vec; xdev->common.device_prep_slave_sg = xilinx_dma_prep_slave_sg; xdev->common.device_prep_dma_cyclic = xilinx_dma_prep_dma_cyclic; diff --git a/drivers/dma/xilinx/zynqmp_dma.c b/drivers/dma/xilinx/zynqmp_dma.c index d05fc5fcc77d..f7e584de4335 100644 --- a/drivers/dma/xilinx/zynqmp_dma.c +++ b/drivers/dma/xilinx/zynqmp_dma.c @@ -1173,9 +1173,9 @@ static void zynqmp_dma_remove(struct platform_device *pdev) dma_async_device_unregister(&zdev->common); zynqmp_dma_chan_remove(zdev->chan); - pm_runtime_disable(zdev->dev); - if (!pm_runtime_enabled(zdev->dev)) + if (pm_runtime_active(zdev->dev)) zynqmp_dma_runtime_suspend(zdev->dev); + pm_runtime_disable(zdev->dev); } static const struct of_device_id zynqmp_dma_of_match[] = { @@ -1193,6 +1193,7 @@ static struct platform_driver zynqmp_dma_driver = { }, .probe = zynqmp_dma_probe, .remove = zynqmp_dma_remove, + .shutdown = zynqmp_dma_remove, }; module_platform_driver(zynqmp_dma_driver); diff --git a/drivers/misc/pci_endpoint_test.c b/drivers/misc/pci_endpoint_test.c index 1c156a3f845e..1c0fd185114f 100644 --- a/drivers/misc/pci_endpoint_test.c +++ b/drivers/misc/pci_endpoint_test.c @@ -436,7 +436,11 @@ static int pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, { struct pci_dev *pdev = test->pdev; u32 val; - int ret; + int irq; + + irq = pci_irq_vector(pdev, msi_num - 1); + if (irq < 0) + return irq; pci_endpoint_test_writel(test, PCI_ENDPOINT_TEST_IRQ_TYPE, msix ? PCITEST_IRQ_TYPE_MSIX : @@ -450,11 +454,7 @@ static int pci_endpoint_test_msi_irq(struct pci_endpoint_test *test, if (!val) return -ETIMEDOUT; - ret = pci_irq_vector(pdev, msi_num - 1); - if (ret < 0) - return ret; - - if (ret != test->last_irq) + if (irq != test->last_irq) return -EIO; return 0; @@ -937,7 +937,7 @@ static long pci_endpoint_test_ioctl(struct file *file, unsigned int cmd, switch (cmd) { case PCITEST_BAR: bar = arg; - if (bar > BAR_5) + if (bar <= NO_BAR || bar > BAR_5) goto ret; if (is_am654_pci_dev(pdev) && bar == BAR_0) goto ret; @@ -1020,8 +1020,6 @@ static int pci_endpoint_test_probe(struct pci_dev *pdev, if (!test) return -ENOMEM; - test->test_reg_bar = 0; - test->alignment = 0; test->pdev = pdev; test->irq_type = PCITEST_IRQ_TYPE_UNDEFINED; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c index d7cdea8f604d..91e7b38143ea 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_83xx_hw.c @@ -4215,7 +4215,6 @@ static pci_ers_result_t qlcnic_83xx_io_slot_reset(struct pci_dev *pdev) struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); int err = 0; - pdev->error_state = pci_channel_io_normal; err = pci_enable_device(pdev); if (err) goto disconnect; diff --git a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c index 53cdd36c4123..e051d8c7a28d 100644 --- a/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c +++ b/drivers/net/ethernet/qlogic/qlcnic/qlcnic_main.c @@ -3766,8 +3766,6 @@ static int qlcnic_attach_func(struct pci_dev *pdev) struct qlcnic_adapter *adapter = pci_get_drvdata(pdev); struct net_device *netdev = adapter->netdev; - pdev->error_state = pci_channel_io_normal; - err = pci_enable_device(pdev); if (err) return err; diff --git a/drivers/net/ethernet/sfc/efx_common.c b/drivers/net/ethernet/sfc/efx_common.c index 5a14d94163b1..e8fdbb62d872 100644 --- a/drivers/net/ethernet/sfc/efx_common.c +++ b/drivers/net/ethernet/sfc/efx_common.c @@ -1258,9 +1258,6 @@ out: /* For simplicity and reliability, we always require a slot reset and try to * reset the hardware when a pci error affecting the device is detected. - * We leave both the link_reset and mmio_enabled callback unimplemented: - * with our request for slot reset the mmio_enabled callback will never be - * called, and the link_reset callback is not used by AER or EEH mechanisms. */ const struct pci_error_handlers efx_err_handlers = { .error_detected = efx_io_error_detected, diff --git a/drivers/net/ethernet/sfc/falcon/efx.c b/drivers/net/ethernet/sfc/falcon/efx.c index d19fbf8732ff..6ea41f6c9ef5 100644 --- a/drivers/net/ethernet/sfc/falcon/efx.c +++ b/drivers/net/ethernet/sfc/falcon/efx.c @@ -3127,9 +3127,6 @@ out: /* For simplicity and reliability, we always require a slot reset and try to * reset the hardware when a pci error affecting the device is detected. - * We leave both the link_reset and mmio_enabled callback unimplemented: - * with our request for slot reset the mmio_enabled callback will never be - * called, and the link_reset callback is not used by AER or EEH mechanisms. */ static const struct pci_error_handlers ef4_err_handlers = { .error_detected = ef4_io_error_detected, diff --git a/drivers/net/ethernet/sfc/siena/efx_common.c b/drivers/net/ethernet/sfc/siena/efx_common.c index a0966f879664..35036cc902fe 100644 --- a/drivers/net/ethernet/sfc/siena/efx_common.c +++ b/drivers/net/ethernet/sfc/siena/efx_common.c @@ -1285,9 +1285,6 @@ out: /* For simplicity and reliability, we always require a slot reset and try to * reset the hardware when a pci error affecting the device is detected. - * We leave both the link_reset and mmio_enabled callback unimplemented: - * with our request for slot reset the mmio_enabled callback will never be - * called, and the link_reset callback is not used by AER or EEH mechanisms. */ const struct pci_error_handlers efx_siena_err_handlers = { .error_detected = efx_io_error_detected, diff --git a/drivers/nvdimm/badrange.c b/drivers/nvdimm/badrange.c index ee478ccde7c6..36c626db459a 100644 --- a/drivers/nvdimm/badrange.c +++ b/drivers/nvdimm/badrange.c @@ -278,8 +278,7 @@ void nvdimm_badblocks_populate(struct nd_region *nd_region, } nvdimm_bus = walk_to_nvdimm_bus(&nd_region->dev); - nvdimm_bus_lock(&nvdimm_bus->dev); + guard(nvdimm_bus)(&nvdimm_bus->dev); badblocks_populate(&nvdimm_bus->badrange, bb, range); - nvdimm_bus_unlock(&nvdimm_bus->dev); } EXPORT_SYMBOL_GPL(nvdimm_badblocks_populate); diff --git a/drivers/nvdimm/btt_devs.c b/drivers/nvdimm/btt_devs.c index 497fd434a6a1..b3279b86bbfd 100644 --- a/drivers/nvdimm/btt_devs.c +++ b/drivers/nvdimm/btt_devs.c @@ -50,14 +50,12 @@ static ssize_t sector_size_store(struct device *dev, struct nd_btt *nd_btt = to_nd_btt(dev); ssize_t rc; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); rc = nd_size_select_store(dev, buf, &nd_btt->lbasize, btt_lbasize_supported); dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf, buf[len - 1] == '\n' ? "" : "\n"); - nvdimm_bus_unlock(dev); - device_unlock(dev); return rc ? rc : len; } @@ -93,13 +91,10 @@ static ssize_t namespace_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_btt *nd_btt = to_nd_btt(dev); - ssize_t rc; - nvdimm_bus_lock(dev); - rc = sprintf(buf, "%s\n", nd_btt->ndns + guard(nvdimm_bus)(dev); + return sprintf(buf, "%s\n", nd_btt->ndns ? dev_name(&nd_btt->ndns->dev) : ""); - nvdimm_bus_unlock(dev); - return rc; } static ssize_t namespace_store(struct device *dev, @@ -108,13 +103,11 @@ static ssize_t namespace_store(struct device *dev, struct nd_btt *nd_btt = to_nd_btt(dev); ssize_t rc; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); rc = nd_namespace_store(dev, &nd_btt->ndns, buf, len); dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf, buf[len - 1] == '\n' ? "" : "\n"); - nvdimm_bus_unlock(dev); - device_unlock(dev); return rc; } @@ -351,9 +344,8 @@ int nd_btt_probe(struct device *dev, struct nd_namespace_common *ndns) return -ENODEV; } - nvdimm_bus_lock(&ndns->dev); - btt_dev = __nd_btt_create(nd_region, 0, NULL, ndns); - nvdimm_bus_unlock(&ndns->dev); + scoped_guard(nvdimm_bus, &ndns->dev) + btt_dev = __nd_btt_create(nd_region, 0, NULL, ndns); if (!btt_dev) return -ENOMEM; btt_sb = devm_kzalloc(dev, sizeof(*btt_sb), GFP_KERNEL); diff --git a/drivers/nvdimm/bus.c b/drivers/nvdimm/bus.c index 0ccf4a9e523a..87178a53ff9c 100644 --- a/drivers/nvdimm/bus.c +++ b/drivers/nvdimm/bus.c @@ -5,7 +5,7 @@ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt #include <linux/libnvdimm.h> #include <linux/sched/mm.h> -#include <linux/vmalloc.h> +#include <linux/slab.h> #include <linux/uaccess.h> #include <linux/module.h> #include <linux/blkdev.h> @@ -13,7 +13,6 @@ #include <linux/async.h> #include <linux/ndctl.h> #include <linux/sched.h> -#include <linux/slab.h> #include <linux/cpu.h> #include <linux/fs.h> #include <linux/io.h> @@ -64,17 +63,15 @@ static struct module *to_bus_provider(struct device *dev) static void nvdimm_bus_probe_start(struct nvdimm_bus *nvdimm_bus) { - nvdimm_bus_lock(&nvdimm_bus->dev); + guard(nvdimm_bus)(&nvdimm_bus->dev); nvdimm_bus->probe_active++; - nvdimm_bus_unlock(&nvdimm_bus->dev); } static void nvdimm_bus_probe_end(struct nvdimm_bus *nvdimm_bus) { - nvdimm_bus_lock(&nvdimm_bus->dev); + guard(nvdimm_bus)(&nvdimm_bus->dev); if (--nvdimm_bus->probe_active == 0) wake_up(&nvdimm_bus->wait); - nvdimm_bus_unlock(&nvdimm_bus->dev); } static int nvdimm_bus_probe(struct device *dev) @@ -1031,14 +1028,12 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, unsigned int cmd = _IOC_NR(ioctl_cmd); struct device *dev = &nvdimm_bus->dev; void __user *p = (void __user *) arg; - char *out_env = NULL, *in_env = NULL; const char *cmd_name, *dimm_name; u32 in_len = 0, out_len = 0; unsigned int func = cmd; unsigned long cmd_mask; struct nd_cmd_pkg pkg; int rc, i, cmd_rc; - void *buf = NULL; u64 buf_len = 0; if (nvdimm) { @@ -1097,7 +1092,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, } /* process an input envelope */ - in_env = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL); + char *in_env __free(kfree) = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL); if (!in_env) return -ENOMEM; for (i = 0; i < desc->in_num; i++) { @@ -1107,17 +1102,14 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, if (in_size == UINT_MAX) { dev_err(dev, "%s:%s unknown input size cmd: %s field: %d\n", __func__, dimm_name, cmd_name, i); - rc = -ENXIO; - goto out; + return -ENXIO; } if (in_len < ND_CMD_MAX_ENVELOPE) copy = min_t(u32, ND_CMD_MAX_ENVELOPE - in_len, in_size); else copy = 0; - if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) { - rc = -EFAULT; - goto out; - } + if (copy && copy_from_user(&in_env[in_len], p + in_len, copy)) + return -EFAULT; in_len += in_size; } @@ -1129,11 +1121,9 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, } /* process an output envelope */ - out_env = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL); - if (!out_env) { - rc = -ENOMEM; - goto out; - } + char *out_env __free(kfree) = kzalloc(ND_CMD_MAX_ENVELOPE, GFP_KERNEL); + if (!out_env) + return -ENOMEM; for (i = 0; i < desc->out_num; i++) { u32 out_size = nd_cmd_out_size(nvdimm, cmd, desc, i, @@ -1143,8 +1133,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, if (out_size == UINT_MAX) { dev_dbg(dev, "%s unknown output size cmd: %s field: %d\n", dimm_name, cmd_name, i); - rc = -EFAULT; - goto out; + return -EFAULT; } if (out_len < ND_CMD_MAX_ENVELOPE) copy = min_t(u32, ND_CMD_MAX_ENVELOPE - out_len, out_size); @@ -1152,8 +1141,7 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, copy = 0; if (copy && copy_from_user(&out_env[out_len], p + in_len + out_len, copy)) { - rc = -EFAULT; - goto out; + return -EFAULT; } out_len += out_size; } @@ -1162,30 +1150,25 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, if (buf_len > ND_IOCTL_MAX_BUFLEN) { dev_dbg(dev, "%s cmd: %s buf_len: %llu > %d\n", dimm_name, cmd_name, buf_len, ND_IOCTL_MAX_BUFLEN); - rc = -EINVAL; - goto out; + return -EINVAL; } - buf = vmalloc(buf_len); - if (!buf) { - rc = -ENOMEM; - goto out; - } + void *buf __free(kvfree) = kvzalloc(buf_len, GFP_KERNEL); + if (!buf) + return -ENOMEM; - if (copy_from_user(buf, p, buf_len)) { - rc = -EFAULT; - goto out; - } + if (copy_from_user(buf, p, buf_len)) + return -EFAULT; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); rc = nd_cmd_clear_to_send(nvdimm_bus, nvdimm, func, buf); if (rc) - goto out_unlock; + return rc; rc = nd_desc->ndctl(nd_desc, nvdimm, cmd, buf, buf_len, &cmd_rc); if (rc < 0) - goto out_unlock; + return rc; if (!nvdimm && cmd == ND_CMD_CLEAR_ERROR && cmd_rc >= 0) { struct nd_cmd_clear_error *clear_err = buf; @@ -1195,16 +1178,9 @@ static int __nd_ioctl(struct nvdimm_bus *nvdimm_bus, struct nvdimm *nvdimm, } if (copy_to_user(p, buf, buf_len)) - rc = -EFAULT; + return -EFAULT; -out_unlock: - nvdimm_bus_unlock(dev); - device_unlock(dev); -out: - kfree(in_env); - kfree(out_env); - vfree(buf); - return rc; + return 0; } enum nd_ioctl_mode { diff --git a/drivers/nvdimm/claim.c b/drivers/nvdimm/claim.c index 51614651d2e7..309cd2cddb0e 100644 --- a/drivers/nvdimm/claim.c +++ b/drivers/nvdimm/claim.c @@ -34,11 +34,10 @@ void nd_detach_ndns(struct device *dev, if (!ndns) return; - get_device(&ndns->dev); - nvdimm_bus_lock(&ndns->dev); + + struct device *ndev __free(put_device) = get_device(&ndns->dev); + guard(nvdimm_bus)(ndev); __nd_detach_ndns(dev, _ndns); - nvdimm_bus_unlock(&ndns->dev); - put_device(&ndns->dev); } bool __nd_attach_ndns(struct device *dev, struct nd_namespace_common *attach, diff --git a/drivers/nvdimm/core.c b/drivers/nvdimm/core.c index eaa796629c27..5ba204113fe1 100644 --- a/drivers/nvdimm/core.c +++ b/drivers/nvdimm/core.c @@ -141,9 +141,8 @@ static void nvdimm_map_put(void *data) struct nvdimm_map *nvdimm_map = data; struct nvdimm_bus *nvdimm_bus = nvdimm_map->nvdimm_bus; - nvdimm_bus_lock(&nvdimm_bus->dev); + guard(nvdimm_bus)(&nvdimm_bus->dev); kref_put(&nvdimm_map->kref, nvdimm_map_release); - nvdimm_bus_unlock(&nvdimm_bus->dev); } /** @@ -158,13 +157,13 @@ void *devm_nvdimm_memremap(struct device *dev, resource_size_t offset, { struct nvdimm_map *nvdimm_map; - nvdimm_bus_lock(dev); - nvdimm_map = find_nvdimm_map(dev, offset); - if (!nvdimm_map) - nvdimm_map = alloc_nvdimm_map(dev, offset, size, flags); - else - kref_get(&nvdimm_map->kref); - nvdimm_bus_unlock(dev); + scoped_guard(nvdimm_bus, dev) { + nvdimm_map = find_nvdimm_map(dev, offset); + if (!nvdimm_map) + nvdimm_map = alloc_nvdimm_map(dev, offset, size, flags); + else + kref_get(&nvdimm_map->kref); + } if (!nvdimm_map) return NULL; diff --git a/drivers/nvdimm/dax_devs.c b/drivers/nvdimm/dax_devs.c index 37b743acbb7b..ba4c409ede65 100644 --- a/drivers/nvdimm/dax_devs.c +++ b/drivers/nvdimm/dax_devs.c @@ -104,12 +104,12 @@ int nd_dax_probe(struct device *dev, struct nd_namespace_common *ndns) return -ENODEV; } - nvdimm_bus_lock(&ndns->dev); - nd_dax = nd_dax_alloc(nd_region); - dax_dev = nd_dax_devinit(nd_dax, ndns); - nvdimm_bus_unlock(&ndns->dev); - if (!dax_dev) - return -ENOMEM; + scoped_guard(nvdimm_bus, &ndns->dev) { + nd_dax = nd_dax_alloc(nd_region); + dax_dev = nd_dax_devinit(nd_dax, ndns); + if (!dax_dev) + return -ENOMEM; + } pfn_sb = devm_kmalloc(dev, sizeof(*pfn_sb), GFP_KERNEL); nd_pfn = &nd_dax->nd_pfn; nd_pfn->pfn_sb = pfn_sb; diff --git a/drivers/nvdimm/dimm.c b/drivers/nvdimm/dimm.c index 91d9163ee303..2f6c26cc6a3e 100644 --- a/drivers/nvdimm/dimm.c +++ b/drivers/nvdimm/dimm.c @@ -117,9 +117,8 @@ static void nvdimm_remove(struct device *dev) { struct nvdimm_drvdata *ndd = dev_get_drvdata(dev); - nvdimm_bus_lock(dev); - dev_set_drvdata(dev, NULL); - nvdimm_bus_unlock(dev); + scoped_guard(nvdimm_bus, dev) + dev_set_drvdata(dev, NULL); put_ndd(ndd); } diff --git a/drivers/nvdimm/dimm_devs.c b/drivers/nvdimm/dimm_devs.c index 21498d461fde..e1349ef5f8fd 100644 --- a/drivers/nvdimm/dimm_devs.c +++ b/drivers/nvdimm/dimm_devs.c @@ -226,10 +226,10 @@ void nvdimm_drvdata_release(struct kref *kref) struct resource *res, *_r; dev_dbg(dev, "trace\n"); - nvdimm_bus_lock(dev); - for_each_dpa_resource_safe(ndd, res, _r) - nvdimm_free_dpa(ndd, res); - nvdimm_bus_unlock(dev); + scoped_guard(nvdimm_bus, dev) { + for_each_dpa_resource_safe(ndd, res, _r) + nvdimm_free_dpa(ndd, res); + } kvfree(ndd->data); kfree(ndd); @@ -319,23 +319,20 @@ static DEVICE_ATTR_RO(state); static ssize_t __available_slots_show(struct nvdimm_drvdata *ndd, char *buf) { struct device *dev; - ssize_t rc; u32 nfree; if (!ndd) return -ENXIO; dev = ndd->dev; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); nfree = nd_label_nfree(ndd); if (nfree - 1 > nfree) { dev_WARN_ONCE(dev, 1, "we ate our last label?\n"); nfree = 0; } else nfree--; - rc = sprintf(buf, "%d\n", nfree); - nvdimm_bus_unlock(dev); - return rc; + return sprintf(buf, "%d\n", nfree); } static ssize_t available_slots_show(struct device *dev, @@ -388,21 +385,15 @@ static ssize_t security_store(struct device *dev, struct device_attribute *attr, const char *buf, size_t len) { - ssize_t rc; - /* * Require all userspace triggered security management to be * done while probing is idle and the DIMM is not in active use * in any region. */ - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); - rc = nvdimm_security_store(dev, buf, len); - nvdimm_bus_unlock(dev); - device_unlock(dev); - - return rc; + return nvdimm_security_store(dev, buf, len); } static DEVICE_ATTR_RW(security); @@ -454,9 +445,8 @@ static ssize_t result_show(struct device *dev, struct device_attribute *attr, ch if (!nvdimm->fw_ops) return -EOPNOTSUPP; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); result = nvdimm->fw_ops->activate_result(nvdimm); - nvdimm_bus_unlock(dev); switch (result) { case NVDIMM_FWA_RESULT_NONE: @@ -483,9 +473,8 @@ static ssize_t activate_show(struct device *dev, struct device_attribute *attr, if (!nvdimm->fw_ops) return -EOPNOTSUPP; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); state = nvdimm->fw_ops->activate_state(nvdimm); - nvdimm_bus_unlock(dev); switch (state) { case NVDIMM_FWA_IDLE: @@ -516,9 +505,8 @@ static ssize_t activate_store(struct device *dev, struct device_attribute *attr, else return -EINVAL; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); rc = nvdimm->fw_ops->arm(nvdimm, arg); - nvdimm_bus_unlock(dev); if (rc < 0) return rc; @@ -545,9 +533,8 @@ static umode_t nvdimm_firmware_visible(struct kobject *kobj, struct attribute *a if (!nvdimm->fw_ops) return 0; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); cap = nd_desc->fw_ops->capability(nd_desc); - nvdimm_bus_unlock(dev); if (cap < NVDIMM_FWA_CAP_QUIESCE) return 0; @@ -641,11 +628,10 @@ void nvdimm_delete(struct nvdimm *nvdimm) bool dev_put = false; /* We are shutting down. Make state frozen artificially. */ - nvdimm_bus_lock(dev); - set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags); - if (test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags)) - dev_put = true; - nvdimm_bus_unlock(dev); + scoped_guard(nvdimm_bus, dev) { + set_bit(NVDIMM_SECURITY_FROZEN, &nvdimm->sec.flags); + dev_put = test_and_clear_bit(NDD_WORK_PENDING, &nvdimm->flags); + } cancel_delayed_work_sync(&nvdimm->dwork); if (dev_put) put_device(dev); diff --git a/drivers/nvdimm/namespace_devs.c b/drivers/nvdimm/namespace_devs.c index 55cfbf1e0a95..a5edcacfe46d 100644 --- a/drivers/nvdimm/namespace_devs.c +++ b/drivers/nvdimm/namespace_devs.c @@ -264,15 +264,13 @@ static ssize_t alt_name_store(struct device *dev, struct nd_region *nd_region = to_nd_region(dev->parent); ssize_t rc; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); rc = __alt_name_store(dev, buf, len); if (rc >= 0) rc = nd_namespace_label_update(nd_region, dev); dev_dbg(dev, "%s(%zd)\n", rc < 0 ? "fail " : "", rc); - nvdimm_bus_unlock(dev); - device_unlock(dev); return rc < 0 ? rc : len; } @@ -849,8 +847,8 @@ static ssize_t size_store(struct device *dev, if (rc) return rc; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); rc = __size_store(dev, val); if (rc >= 0) @@ -866,9 +864,6 @@ static ssize_t size_store(struct device *dev, dev_dbg(dev, "%llx %s (%d)\n", val, rc < 0 ? "fail" : "success", rc); - nvdimm_bus_unlock(dev); - device_unlock(dev); - return rc < 0 ? rc : len; } @@ -891,13 +886,8 @@ resource_size_t __nvdimm_namespace_capacity(struct nd_namespace_common *ndns) resource_size_t nvdimm_namespace_capacity(struct nd_namespace_common *ndns) { - resource_size_t size; - - nvdimm_bus_lock(&ndns->dev); - size = __nvdimm_namespace_capacity(ndns); - nvdimm_bus_unlock(&ndns->dev); - - return size; + guard(nvdimm_bus)(&ndns->dev); + return __nvdimm_namespace_capacity(ndns); } EXPORT_SYMBOL(nvdimm_namespace_capacity); @@ -1044,8 +1034,8 @@ static ssize_t uuid_store(struct device *dev, } else return -ENXIO; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); if (to_ndns(dev)->claim) rc = -EBUSY; @@ -1059,8 +1049,6 @@ static ssize_t uuid_store(struct device *dev, kfree(uuid); dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf, buf[len - 1] == '\n' ? "" : "\n"); - nvdimm_bus_unlock(dev); - device_unlock(dev); return rc < 0 ? rc : len; } @@ -1119,20 +1107,30 @@ static ssize_t sector_size_store(struct device *dev, } else return -ENXIO; - device_lock(dev); - nvdimm_bus_lock(dev); - if (to_ndns(dev)->claim) - rc = -EBUSY; - if (rc >= 0) - rc = nd_size_select_store(dev, buf, lbasize, supported); - if (rc >= 0) - rc = nd_namespace_label_update(nd_region, dev); - dev_dbg(dev, "result: %zd %s: %s%s", rc, rc < 0 ? "tried" : "wrote", + guard(device)(dev); + guard(nvdimm_bus)(dev); + if (to_ndns(dev)->claim) { + dev_dbg(dev, "namespace %s already claimed\n", dev_name(dev)); + return -EBUSY; + } + + rc = nd_size_select_store(dev, buf, lbasize, supported); + if (rc < 0) { + dev_dbg(dev, "size select fail: %zd tried: %s%s", rc, buf, buf[len - 1] == '\n' ? "" : "\n"); - nvdimm_bus_unlock(dev); - device_unlock(dev); + return rc; + } + + rc = nd_namespace_label_update(nd_region, dev); + if (rc < 0) { + dev_dbg(dev, "label update fail: %zd tried: %s%s", + rc, buf, buf[len - 1] == '\n' ? "" : "\n"); + return rc; + } + + dev_dbg(dev, "wrote: %s%s", buf, buf[len - 1] == '\n' ? "" : "\n"); - return rc ? rc : len; + return len; } static DEVICE_ATTR_RW(sector_size); @@ -1145,7 +1143,7 @@ static ssize_t dpa_extents_show(struct device *dev, int count = 0, i; u32 flags = 0; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); if (is_namespace_pmem(dev)) { struct nd_namespace_pmem *nspm = to_nd_namespace_pmem(dev); @@ -1154,7 +1152,7 @@ static ssize_t dpa_extents_show(struct device *dev, } if (!uuid) - goto out; + return sprintf(buf, "%d\n", count); nd_label_gen_id(&label_id, uuid, flags); for (i = 0; i < nd_region->ndr_mappings; i++) { @@ -1166,8 +1164,6 @@ static ssize_t dpa_extents_show(struct device *dev, if (strcmp(res->name, label_id.id) == 0) count++; } - out: - nvdimm_bus_unlock(dev); return sprintf(buf, "%d\n", count); } @@ -1279,15 +1275,13 @@ static ssize_t holder_class_store(struct device *dev, struct nd_region *nd_region = to_nd_region(dev->parent); int rc; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); rc = __holder_class_store(dev, buf); if (rc >= 0) rc = nd_namespace_label_update(nd_region, dev); dev_dbg(dev, "%s(%d)\n", rc < 0 ? "fail " : "", rc); - nvdimm_bus_unlock(dev); - device_unlock(dev); return rc < 0 ? rc : len; } @@ -1983,7 +1977,7 @@ static struct device **scan_labels(struct nd_region *nd_region) } dev_dbg(&nd_region->dev, "discovered %d namespace%s\n", count, - count == 1 ? "" : "s"); + str_plural(count)); if (count == 0) { struct nd_namespace_pmem *nspm; @@ -2152,31 +2146,38 @@ out: nd_region); } -int nd_region_register_namespaces(struct nd_region *nd_region, int *err) +static int create_relevant_namespaces(struct nd_region *nd_region, int *type, + struct device ***devs) { - struct device **devs = NULL; - int i, rc = 0, type; + int rc; - *err = 0; - nvdimm_bus_lock(&nd_region->dev); + guard(nvdimm_bus)(&nd_region->dev); rc = init_active_labels(nd_region); - if (rc) { - nvdimm_bus_unlock(&nd_region->dev); + if (rc) return rc; - } - type = nd_region_to_nstype(nd_region); - switch (type) { + *type = nd_region_to_nstype(nd_region); + switch (*type) { case ND_DEVICE_NAMESPACE_IO: - devs = create_namespace_io(nd_region); + *devs = create_namespace_io(nd_region); break; case ND_DEVICE_NAMESPACE_PMEM: - devs = create_namespaces(nd_region); - break; - default: + *devs = create_namespaces(nd_region); break; } - nvdimm_bus_unlock(&nd_region->dev); + + return 0; +} + +int nd_region_register_namespaces(struct nd_region *nd_region, int *err) +{ + struct device **devs = NULL; + int i, rc = 0, type; + + *err = 0; + rc = create_relevant_namespaces(nd_region, &type, &devs); + if (rc) + return rc; if (!devs) return -ENODEV; diff --git a/drivers/nvdimm/nd.h b/drivers/nvdimm/nd.h index cc5c8f3f81e8..b199eea3260e 100644 --- a/drivers/nvdimm/nd.h +++ b/drivers/nvdimm/nd.h @@ -632,6 +632,9 @@ u64 nd_region_interleave_set_cookie(struct nd_region *nd_region, u64 nd_region_interleave_set_altcookie(struct nd_region *nd_region); void nvdimm_bus_lock(struct device *dev); void nvdimm_bus_unlock(struct device *dev); +DEFINE_GUARD(nvdimm_bus, struct device *, + if (_T) nvdimm_bus_lock(_T), if (_T) nvdimm_bus_unlock(_T)); + bool is_nvdimm_bus_locked(struct device *dev); void nvdimm_check_and_set_ro(struct gendisk *disk); void nvdimm_drvdata_release(struct kref *kref); diff --git a/drivers/nvdimm/pfn_devs.c b/drivers/nvdimm/pfn_devs.c index 8f3e816e805d..42b172fc5576 100644 --- a/drivers/nvdimm/pfn_devs.c +++ b/drivers/nvdimm/pfn_devs.c @@ -56,30 +56,26 @@ static ssize_t mode_store(struct device *dev, { struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); ssize_t rc = 0; + size_t n = len - 1; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); if (dev->driver) - rc = -EBUSY; - else { - size_t n = len - 1; - - if (strncmp(buf, "pmem\n", n) == 0 - || strncmp(buf, "pmem", n) == 0) { - nd_pfn->mode = PFN_MODE_PMEM; - } else if (strncmp(buf, "ram\n", n) == 0 - || strncmp(buf, "ram", n) == 0) - nd_pfn->mode = PFN_MODE_RAM; - else if (strncmp(buf, "none\n", n) == 0 - || strncmp(buf, "none", n) == 0) - nd_pfn->mode = PFN_MODE_NONE; - else - rc = -EINVAL; - } + return -EBUSY; + + if (strncmp(buf, "pmem\n", n) == 0 + || strncmp(buf, "pmem", n) == 0) { + nd_pfn->mode = PFN_MODE_PMEM; + } else if (strncmp(buf, "ram\n", n) == 0 + || strncmp(buf, "ram", n) == 0) + nd_pfn->mode = PFN_MODE_RAM; + else if (strncmp(buf, "none\n", n) == 0 + || strncmp(buf, "none", n) == 0) + nd_pfn->mode = PFN_MODE_NONE; + else + rc = -EINVAL; dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf, buf[len - 1] == '\n' ? "" : "\n"); - nvdimm_bus_unlock(dev); - device_unlock(dev); return rc ? rc : len; } @@ -125,14 +121,12 @@ static ssize_t align_store(struct device *dev, unsigned long aligns[MAX_NVDIMM_ALIGN] = { [0] = 0, }; ssize_t rc; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); rc = nd_size_select_store(dev, buf, &nd_pfn->align, nd_pfn_supported_alignments(aligns)); dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf, buf[len - 1] == '\n' ? "" : "\n"); - nvdimm_bus_unlock(dev); - device_unlock(dev); return rc ? rc : len; } @@ -168,13 +162,10 @@ static ssize_t namespace_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); - ssize_t rc; - nvdimm_bus_lock(dev); - rc = sprintf(buf, "%s\n", nd_pfn->ndns + guard(nvdimm_bus)(dev); + return sprintf(buf, "%s\n", nd_pfn->ndns ? dev_name(&nd_pfn->ndns->dev) : ""); - nvdimm_bus_unlock(dev); - return rc; } static ssize_t namespace_store(struct device *dev, @@ -183,13 +174,11 @@ static ssize_t namespace_store(struct device *dev, struct nd_pfn *nd_pfn = to_nd_pfn_safe(dev); ssize_t rc; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); rc = nd_namespace_store(dev, &nd_pfn->ndns, buf, len); dev_dbg(dev, "result: %zd wrote: %s%s", rc, buf, buf[len - 1] == '\n' ? "" : "\n"); - nvdimm_bus_unlock(dev); - device_unlock(dev); return rc; } @@ -639,10 +628,10 @@ int nd_pfn_probe(struct device *dev, struct nd_namespace_common *ndns) return -ENODEV; } - nvdimm_bus_lock(&ndns->dev); - nd_pfn = nd_pfn_alloc(nd_region); - pfn_dev = nd_pfn_devinit(nd_pfn, ndns); - nvdimm_bus_unlock(&ndns->dev); + scoped_guard(nvdimm_bus, &ndns->dev) { + nd_pfn = nd_pfn_alloc(nd_region); + pfn_dev = nd_pfn_devinit(nd_pfn, ndns); + } if (!pfn_dev) return -ENOMEM; pfn_sb = devm_kmalloc(dev, sizeof(*pfn_sb), GFP_KERNEL); diff --git a/drivers/nvdimm/region.c b/drivers/nvdimm/region.c index 88dc062af5f8..cd9b52040d7b 100644 --- a/drivers/nvdimm/region.c +++ b/drivers/nvdimm/region.c @@ -70,7 +70,7 @@ static int nd_region_probe(struct device *dev) * "<async-registered>/<total>" namespace count. */ dev_err(dev, "failed to register %d namespace%s, continuing...\n", - err, err == 1 ? "" : "s"); + err, str_plural(err)); return 0; } @@ -87,13 +87,13 @@ static void nd_region_remove(struct device *dev) device_for_each_child(dev, NULL, child_unregister); /* flush attribute readers and disable */ - nvdimm_bus_lock(dev); - nd_region->ns_seed = NULL; - nd_region->btt_seed = NULL; - nd_region->pfn_seed = NULL; - nd_region->dax_seed = NULL; - dev_set_drvdata(dev, NULL); - nvdimm_bus_unlock(dev); + scoped_guard(nvdimm_bus, dev) { + nd_region->ns_seed = NULL; + nd_region->btt_seed = NULL; + nd_region->pfn_seed = NULL; + nd_region->dax_seed = NULL; + dev_set_drvdata(dev, NULL); + } /* * Note, this assumes device_lock() context to not race diff --git a/drivers/nvdimm/region_devs.c b/drivers/nvdimm/region_devs.c index de1ee5ebc851..a5ceaf5db595 100644 --- a/drivers/nvdimm/region_devs.c +++ b/drivers/nvdimm/region_devs.c @@ -102,31 +102,44 @@ out: return 0; } -int nd_region_activate(struct nd_region *nd_region) +static int get_flush_data(struct nd_region *nd_region, size_t *size, int *num_flush) { - int i, j, rc, num_flush = 0; - struct nd_region_data *ndrd; - struct device *dev = &nd_region->dev; size_t flush_data_size = sizeof(void *); + int _num_flush = 0; + int i; - nvdimm_bus_lock(&nd_region->dev); + guard(nvdimm_bus)(&nd_region->dev); for (i = 0; i < nd_region->ndr_mappings; i++) { struct nd_mapping *nd_mapping = &nd_region->mapping[i]; struct nvdimm *nvdimm = nd_mapping->nvdimm; - if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) { - nvdimm_bus_unlock(&nd_region->dev); + if (test_bit(NDD_SECURITY_OVERWRITE, &nvdimm->flags)) return -EBUSY; - } /* at least one null hint slot per-dimm for the "no-hint" case */ flush_data_size += sizeof(void *); - num_flush = min_not_zero(num_flush, nvdimm->num_flush); + _num_flush = min_not_zero(_num_flush, nvdimm->num_flush); if (!nvdimm->num_flush) continue; flush_data_size += nvdimm->num_flush * sizeof(void *); } - nvdimm_bus_unlock(&nd_region->dev); + + *size = flush_data_size; + *num_flush = _num_flush; + + return 0; +} + +int nd_region_activate(struct nd_region *nd_region) +{ + int i, j, rc, num_flush; + struct nd_region_data *ndrd; + struct device *dev = &nd_region->dev; + size_t flush_data_size; + + rc = get_flush_data(nd_region, &flush_data_size, &num_flush); + if (rc) + return rc; rc = nd_region_invalidate_memregion(nd_region); if (rc) @@ -327,8 +340,8 @@ static ssize_t set_cookie_show(struct device *dev, * the v1.1 namespace label cookie definition. To read all this * data we need to wait for probing to settle. */ - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); if (nd_region->ndr_mappings) { struct nd_mapping *nd_mapping = &nd_region->mapping[0]; @@ -343,8 +356,6 @@ static ssize_t set_cookie_show(struct device *dev, nsindex)); } } - nvdimm_bus_unlock(dev); - device_unlock(dev); if (rc) return rc; @@ -393,7 +404,6 @@ static ssize_t available_size_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_region *nd_region = to_nd_region(dev); - unsigned long long available = 0; /* * Flush in-flight updates and grab a snapshot of the available @@ -401,14 +411,11 @@ static ssize_t available_size_show(struct device *dev, * memory nvdimm_bus_lock() is dropped, but that's userspace's * problem to not race itself. */ - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); - available = nd_region_available_dpa(nd_region); - nvdimm_bus_unlock(dev); - device_unlock(dev); - return sprintf(buf, "%llu\n", available); + return sprintf(buf, "%llu\n", nd_region_available_dpa(nd_region)); } static DEVICE_ATTR_RO(available_size); @@ -416,16 +423,12 @@ static ssize_t max_available_extent_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_region *nd_region = to_nd_region(dev); - unsigned long long available = 0; - device_lock(dev); - nvdimm_bus_lock(dev); + guard(device)(dev); + guard(nvdimm_bus)(dev); wait_nvdimm_bus_probe_idle(dev); - available = nd_region_allocatable_dpa(nd_region); - nvdimm_bus_unlock(dev); - device_unlock(dev); - return sprintf(buf, "%llu\n", available); + return sprintf(buf, "%llu\n", nd_region_allocatable_dpa(nd_region)); } static DEVICE_ATTR_RO(max_available_extent); @@ -433,16 +436,12 @@ static ssize_t init_namespaces_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_region_data *ndrd = dev_get_drvdata(dev); - ssize_t rc; - nvdimm_bus_lock(dev); - if (ndrd) - rc = sprintf(buf, "%d/%d\n", ndrd->ns_active, ndrd->ns_count); - else - rc = -ENXIO; - nvdimm_bus_unlock(dev); + guard(nvdimm_bus)(dev); + if (!ndrd) + return -ENXIO; - return rc; + return sprintf(buf, "%d/%d\n", ndrd->ns_active, ndrd->ns_count); } static DEVICE_ATTR_RO(init_namespaces); @@ -450,15 +449,12 @@ static ssize_t namespace_seed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_region *nd_region = to_nd_region(dev); - ssize_t rc; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); if (nd_region->ns_seed) - rc = sprintf(buf, "%s\n", dev_name(nd_region->ns_seed)); - else - rc = sprintf(buf, "\n"); - nvdimm_bus_unlock(dev); - return rc; + return sprintf(buf, "%s\n", dev_name(nd_region->ns_seed)); + + return sprintf(buf, "\n"); } static DEVICE_ATTR_RO(namespace_seed); @@ -466,16 +462,12 @@ static ssize_t btt_seed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_region *nd_region = to_nd_region(dev); - ssize_t rc; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); if (nd_region->btt_seed) - rc = sprintf(buf, "%s\n", dev_name(nd_region->btt_seed)); - else - rc = sprintf(buf, "\n"); - nvdimm_bus_unlock(dev); + return sprintf(buf, "%s\n", dev_name(nd_region->btt_seed)); - return rc; + return sprintf(buf, "\n"); } static DEVICE_ATTR_RO(btt_seed); @@ -483,16 +475,12 @@ static ssize_t pfn_seed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_region *nd_region = to_nd_region(dev); - ssize_t rc; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); if (nd_region->pfn_seed) - rc = sprintf(buf, "%s\n", dev_name(nd_region->pfn_seed)); - else - rc = sprintf(buf, "\n"); - nvdimm_bus_unlock(dev); + return sprintf(buf, "%s\n", dev_name(nd_region->pfn_seed)); - return rc; + return sprintf(buf, "\n"); } static DEVICE_ATTR_RO(pfn_seed); @@ -500,16 +488,12 @@ static ssize_t dax_seed_show(struct device *dev, struct device_attribute *attr, char *buf) { struct nd_region *nd_region = to_nd_region(dev); - ssize_t rc; - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); if (nd_region->dax_seed) - rc = sprintf(buf, "%s\n", dev_name(nd_region->dax_seed)); - else - rc = sprintf(buf, "\n"); - nvdimm_bus_unlock(dev); + return sprintf(buf, "%s\n", dev_name(nd_region->dax_seed)); - return rc; + return sprintf(buf, "\n"); } static DEVICE_ATTR_RO(dax_seed); @@ -581,9 +565,8 @@ static ssize_t align_store(struct device *dev, * times ensure it does not change for the duration of the * allocation. */ - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); nd_region->align = val; - nvdimm_bus_unlock(dev); return len; } @@ -890,7 +873,7 @@ void nd_mapping_free_labels(struct nd_mapping *nd_mapping) */ void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev) { - nvdimm_bus_lock(dev); + guard(nvdimm_bus)(dev); if (nd_region->ns_seed == dev) { nd_region_create_ns_seed(nd_region); } else if (is_nd_btt(dev)) { @@ -915,7 +898,6 @@ void nd_region_advance_seeds(struct nd_region *nd_region, struct device *dev) if (nd_region->ns_seed == &nd_dax->nd_pfn.ndns->dev) nd_region_create_ns_seed(nd_region); } - nvdimm_bus_unlock(dev); } /** diff --git a/drivers/nvdimm/security.c b/drivers/nvdimm/security.c index a03e3c45f297..4adce8c38870 100644 --- a/drivers/nvdimm/security.c +++ b/drivers/nvdimm/security.c @@ -219,12 +219,9 @@ static int __nvdimm_security_unlock(struct nvdimm *nvdimm) int nvdimm_security_unlock(struct device *dev) { struct nvdimm *nvdimm = to_nvdimm(dev); - int rc; - nvdimm_bus_lock(dev); - rc = __nvdimm_security_unlock(nvdimm); - nvdimm_bus_unlock(dev); - return rc; + guard(nvdimm_bus)(dev); + return __nvdimm_security_unlock(nvdimm); } static int check_security_state(struct nvdimm *nvdimm) @@ -490,9 +487,8 @@ void nvdimm_security_overwrite_query(struct work_struct *work) struct nvdimm *nvdimm = container_of(work, typeof(*nvdimm), dwork.work); - nvdimm_bus_lock(&nvdimm->dev); + guard(nvdimm_bus)(&nvdimm->dev); __nvdimm_security_overwrite_query(nvdimm); - nvdimm_bus_unlock(&nvdimm->dev); } #define OPS \ diff --git a/drivers/pci/bus.c b/drivers/pci/bus.c index b77fd30bbfd9..f26aec6ff588 100644 --- a/drivers/pci/bus.c +++ b/drivers/pci/bus.c @@ -204,6 +204,9 @@ static int pci_bus_alloc_from_region(struct pci_bus *bus, struct resource *res, if (!r) continue; + if (r->flags & (IORESOURCE_UNSET|IORESOURCE_DISABLED)) + continue; + /* type_mask must match */ if ((res->flags ^ r->flags) & type_mask) continue; @@ -361,11 +364,15 @@ void pci_bus_add_device(struct pci_dev *dev) * before PCI client drivers. */ pdev = of_find_device_by_node(dn); - if (pdev && of_pci_supply_present(dn)) { - if (!device_link_add(&dev->dev, &pdev->dev, - DL_FLAG_AUTOREMOVE_CONSUMER)) - pci_err(dev, "failed to add device link to power control device %s\n", - pdev->name); + if (pdev) { + if (of_pci_supply_present(dn)) { + if (!device_link_add(&dev->dev, &pdev->dev, + DL_FLAG_AUTOREMOVE_CONSUMER)) { + pci_err(dev, "failed to add device link to power control device %s\n", + pdev->name); + } + } + put_device(&pdev->dev); } if (!dn || of_device_is_available(dn)) diff --git a/drivers/pci/controller/cadence/Kconfig b/drivers/pci/controller/cadence/Kconfig index 666e16b6367f..02a639e55fd8 100644 --- a/drivers/pci/controller/cadence/Kconfig +++ b/drivers/pci/controller/cadence/Kconfig @@ -42,6 +42,15 @@ config PCIE_CADENCE_PLAT_EP endpoint mode. This PCIe controller may be embedded into many different vendors SoCs. +config PCIE_SG2042_HOST + tristate "Sophgo SG2042 PCIe controller (host mode)" + depends on OF && (ARCH_SOPHGO || COMPILE_TEST) + select PCIE_CADENCE_HOST + help + Say Y here if you want to support the Sophgo SG2042 PCIe platform + controller in host mode. Sophgo SG2042 PCIe controller uses Cadence + PCIe core. + config PCI_J721E tristate select PCIE_CADENCE_HOST if PCI_J721E_HOST != n @@ -67,4 +76,5 @@ config PCI_J721E_EP Say Y here if you want to support the TI J721E PCIe platform controller in endpoint mode. TI J721E PCIe controller uses Cadence PCIe core. + endmenu diff --git a/drivers/pci/controller/cadence/Makefile b/drivers/pci/controller/cadence/Makefile index 9bac5fb2f13d..5e23f8539ecc 100644 --- a/drivers/pci/controller/cadence/Makefile +++ b/drivers/pci/controller/cadence/Makefile @@ -4,3 +4,4 @@ obj-$(CONFIG_PCIE_CADENCE_HOST) += pcie-cadence-host.o obj-$(CONFIG_PCIE_CADENCE_EP) += pcie-cadence-ep.o obj-$(CONFIG_PCIE_CADENCE_PLAT) += pcie-cadence-plat.o obj-$(CONFIG_PCI_J721E) += pci-j721e.o +obj-$(CONFIG_PCIE_SG2042_HOST) += pcie-sg2042.o diff --git a/drivers/pci/controller/cadence/pci-j721e.c b/drivers/pci/controller/cadence/pci-j721e.c index 6c93f39d0288..5bc5ab20aa6d 100644 --- a/drivers/pci/controller/cadence/pci-j721e.c +++ b/drivers/pci/controller/cadence/pci-j721e.c @@ -284,6 +284,25 @@ static int j721e_pcie_ctrl_init(struct j721e_pcie *pcie) if (!ret) offset = args.args[0]; + /* + * The PCIe Controller's registers have different "reset-values" + * depending on the "strap" settings programmed into the PCIEn_CTRL + * register within the CTRL_MMR memory-mapped register space. + * The registers latch onto a "reset-value" based on the "strap" + * settings sampled after the PCIe Controller is powered on. + * To ensure that the "reset-values" are sampled accurately, power + * off the PCIe Controller before programming the "strap" settings + * and power it on after that. The runtime PM APIs namely + * pm_runtime_put_sync() and pm_runtime_get_sync() will decrement and + * increment the usage counter respectively, causing GENPD to power off + * and power on the PCIe Controller. + */ + ret = pm_runtime_put_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to power off PCIe Controller\n"); + return ret; + } + ret = j721e_pcie_set_mode(pcie, syscon, offset); if (ret < 0) { dev_err(dev, "Failed to set pci mode\n"); @@ -302,6 +321,12 @@ static int j721e_pcie_ctrl_init(struct j721e_pcie *pcie) return ret; } + ret = pm_runtime_get_sync(dev); + if (ret < 0) { + dev_err(dev, "Failed to power on PCIe Controller\n"); + return ret; + } + /* Enable ACSPCIE refclk output if the optional property exists */ syscon = syscon_regmap_lookup_by_phandle_optional(node, "ti,syscon-acspcie-proxy-ctrl"); @@ -440,6 +465,7 @@ static const struct of_device_id of_j721e_pcie_match[] = { }, {}, }; +MODULE_DEVICE_TABLE(of, of_j721e_pcie_match); static int j721e_pcie_probe(struct platform_device *pdev) { @@ -549,7 +575,7 @@ static int j721e_pcie_probe(struct platform_device *pdev) ret = j721e_pcie_ctrl_init(pcie); if (ret < 0) { - dev_err_probe(dev, ret, "pm_runtime_get_sync failed\n"); + dev_err_probe(dev, ret, "j721e_pcie_ctrl_init failed\n"); goto err_get_sync; } diff --git a/drivers/pci/controller/cadence/pcie-cadence-ep.c b/drivers/pci/controller/cadence/pcie-cadence-ep.c index 77c5a19b2ab1..1eac012a8226 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-ep.c +++ b/drivers/pci/controller/cadence/pcie-cadence-ep.c @@ -21,12 +21,13 @@ static u8 cdns_pcie_get_fn_from_vfn(struct cdns_pcie *pcie, u8 fn, u8 vfn) { - u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; u32 first_vf_offset, stride; + u16 cap; if (vfn == 0) return fn; + cap = cdns_pcie_find_ext_capability(pcie, PCI_EXT_CAP_ID_SRIOV); first_vf_offset = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_SRIOV_VF_OFFSET); stride = cdns_pcie_ep_fn_readw(pcie, fn, cap + PCI_SRIOV_VF_STRIDE); fn = fn + first_vf_offset + ((vfn - 1) * stride); @@ -38,10 +39,11 @@ static int cdns_pcie_ep_write_header(struct pci_epc *epc, u8 fn, u8 vfn, struct pci_epf_header *hdr) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - u32 cap = CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET; struct cdns_pcie *pcie = &ep->pcie; u32 reg; + u16 cap; + cap = cdns_pcie_find_ext_capability(pcie, PCI_EXT_CAP_ID_SRIOV); if (vfn > 1) { dev_err(&epc->dev, "Only Virtual Function #1 has deviceID\n"); return -EINVAL; @@ -227,9 +229,10 @@ static int cdns_pcie_ep_set_msi(struct pci_epc *epc, u8 fn, u8 vfn, u8 nr_irqs) struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; u8 mmc = order_base_2(nr_irqs); - u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; u16 flags; + u8 cap; + cap = cdns_pcie_find_capability(pcie, PCI_CAP_ID_MSI); fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn); /* @@ -249,9 +252,10 @@ static int cdns_pcie_ep_get_msi(struct pci_epc *epc, u8 fn, u8 vfn) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; - u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; u16 flags, mme; + u8 cap; + cap = cdns_pcie_find_capability(pcie, PCI_CAP_ID_MSIX); fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn); /* Validate that the MSI feature is actually enabled. */ @@ -272,9 +276,10 @@ static int cdns_pcie_ep_get_msix(struct pci_epc *epc, u8 func_no, u8 vfunc_no) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; - u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; u32 val, reg; + u8 cap; + cap = cdns_pcie_find_capability(pcie, PCI_CAP_ID_MSIX); func_no = cdns_pcie_get_fn_from_vfn(pcie, func_no, vfunc_no); reg = cap + PCI_MSIX_FLAGS; @@ -292,9 +297,10 @@ static int cdns_pcie_ep_set_msix(struct pci_epc *epc, u8 fn, u8 vfn, { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); struct cdns_pcie *pcie = &ep->pcie; - u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; u32 val, reg; + u8 cap; + cap = cdns_pcie_find_capability(pcie, PCI_CAP_ID_MSIX); fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn); reg = cap + PCI_MSIX_FLAGS; @@ -380,11 +386,11 @@ static int cdns_pcie_ep_send_msi_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn, u8 interrupt_num) { struct cdns_pcie *pcie = &ep->pcie; - u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; u16 flags, mme, data, data_mask; - u8 msi_count; u64 pci_addr, pci_addr_mask = 0xff; + u8 msi_count, cap; + cap = cdns_pcie_find_capability(pcie, PCI_CAP_ID_MSI); fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn); /* Check whether the MSI feature has been enabled by the PCI host. */ @@ -432,14 +438,14 @@ static int cdns_pcie_ep_map_msi_irq(struct pci_epc *epc, u8 fn, u8 vfn, u32 *msi_addr_offset) { struct cdns_pcie_ep *ep = epc_get_drvdata(epc); - u32 cap = CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET; struct cdns_pcie *pcie = &ep->pcie; u64 pci_addr, pci_addr_mask = 0xff; u16 flags, mme, data, data_mask; - u8 msi_count; + u8 msi_count, cap; int ret; int i; + cap = cdns_pcie_find_capability(pcie, PCI_CAP_ID_MSI); fn = cdns_pcie_get_fn_from_vfn(pcie, fn, vfn); /* Check whether the MSI feature has been enabled by the PCI host. */ @@ -482,16 +488,16 @@ static int cdns_pcie_ep_map_msi_irq(struct pci_epc *epc, u8 fn, u8 vfn, static int cdns_pcie_ep_send_msix_irq(struct cdns_pcie_ep *ep, u8 fn, u8 vfn, u16 interrupt_num) { - u32 cap = CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET; u32 tbl_offset, msg_data, reg; struct cdns_pcie *pcie = &ep->pcie; struct pci_epf_msix_tbl *msix_tbl; struct cdns_pcie_epf *epf; u64 pci_addr_mask = 0xff; u64 msg_addr; + u8 bir, cap; u16 flags; - u8 bir; + cap = cdns_pcie_find_capability(pcie, PCI_CAP_ID_MSIX); epf = &ep->epf[fn]; if (vfn > 0) epf = &epf->epf[vfn - 1]; @@ -565,7 +571,9 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) int max_epfs = sizeof(epc->function_num_map) * 8; int ret, epf, last_fn; u32 reg, value; + u8 cap; + cap = cdns_pcie_find_capability(pcie, PCI_CAP_ID_EXP); /* * BIT(0) is hardwired to 1, hence function 0 is always enabled * and can't be disabled anyway. @@ -589,12 +597,10 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) continue; value = cdns_pcie_ep_fn_readl(pcie, epf, - CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET + - PCI_EXP_DEVCAP); + cap + PCI_EXP_DEVCAP); value &= ~PCI_EXP_DEVCAP_FLR; cdns_pcie_ep_fn_writel(pcie, epf, - CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET + - PCI_EXP_DEVCAP, value); + cap + PCI_EXP_DEVCAP, value); } } @@ -608,14 +614,12 @@ static int cdns_pcie_ep_start(struct pci_epc *epc) } static const struct pci_epc_features cdns_pcie_epc_vf_features = { - .linkup_notifier = false, .msi_capable = true, .msix_capable = true, .align = 65536, }; static const struct pci_epc_features cdns_pcie_epc_features = { - .linkup_notifier = false, .msi_capable = true, .msix_capable = true, .align = 256, diff --git a/drivers/pci/controller/cadence/pcie-cadence-host.c b/drivers/pci/controller/cadence/pcie-cadence-host.c index 59a4631de79f..fffd63d6665e 100644 --- a/drivers/pci/controller/cadence/pcie-cadence-host.c +++ b/drivers/pci/controller/cadence/pcie-cadence-host.c @@ -531,7 +531,7 @@ static int cdns_pcie_host_init_address_translation(struct cdns_pcie_rc *rc) cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_PCI_ADDR1(0), addr1); cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(0), desc1); - if (pcie->ops->cpu_addr_fixup) + if (pcie->ops && pcie->ops->cpu_addr_fixup) cpu_addr = pcie->ops->cpu_addr_fixup(pcie, cpu_addr); addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(12) | diff --git a/drivers/pci/controller/cadence/pcie-cadence.c b/drivers/pci/controller/cadence/pcie-cadence.c index 70a19573440e..bd683d0fecb2 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.c +++ b/drivers/pci/controller/cadence/pcie-cadence.c @@ -8,6 +8,20 @@ #include <linux/of.h> #include "pcie-cadence.h" +#include "../../pci.h" + +u8 cdns_pcie_find_capability(struct cdns_pcie *pcie, u8 cap) +{ + return PCI_FIND_NEXT_CAP(cdns_pcie_read_cfg, PCI_CAPABILITY_LIST, + cap, pcie); +} +EXPORT_SYMBOL_GPL(cdns_pcie_find_capability); + +u16 cdns_pcie_find_ext_capability(struct cdns_pcie *pcie, u8 cap) +{ + return PCI_FIND_NEXT_EXT_CAP(cdns_pcie_read_cfg, 0, cap, pcie); +} +EXPORT_SYMBOL_GPL(cdns_pcie_find_ext_capability); void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie) { @@ -92,7 +106,7 @@ void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn, cdns_pcie_writel(pcie, CDNS_PCIE_AT_OB_REGION_DESC1(r), desc1); /* Set the CPU address */ - if (pcie->ops->cpu_addr_fixup) + if (pcie->ops && pcie->ops->cpu_addr_fixup) cpu_addr = pcie->ops->cpu_addr_fixup(pcie, cpu_addr); addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(nbits) | @@ -123,7 +137,7 @@ void cdns_pcie_set_outbound_region_for_normal_msg(struct cdns_pcie *pcie, } /* Set the CPU address */ - if (pcie->ops->cpu_addr_fixup) + if (pcie->ops && pcie->ops->cpu_addr_fixup) cpu_addr = pcie->ops->cpu_addr_fixup(pcie, cpu_addr); addr0 = CDNS_PCIE_AT_OB_REGION_CPU_ADDR0_NBITS(17) | diff --git a/drivers/pci/controller/cadence/pcie-cadence.h b/drivers/pci/controller/cadence/pcie-cadence.h index 1d81c4bf6c6d..e2a853d2c0ab 100644 --- a/drivers/pci/controller/cadence/pcie-cadence.h +++ b/drivers/pci/controller/cadence/pcie-cadence.h @@ -125,11 +125,6 @@ */ #define CDNS_PCIE_EP_FUNC_BASE(fn) (((fn) << 12) & GENMASK(19, 12)) -#define CDNS_PCIE_EP_FUNC_MSI_CAP_OFFSET 0x90 -#define CDNS_PCIE_EP_FUNC_MSIX_CAP_OFFSET 0xb0 -#define CDNS_PCIE_EP_FUNC_DEV_CAP_OFFSET 0xc0 -#define CDNS_PCIE_EP_FUNC_SRIOV_CAP_OFFSET 0x200 - /* * Endpoint PF Registers */ @@ -367,6 +362,37 @@ static inline u32 cdns_pcie_readl(struct cdns_pcie *pcie, u32 reg) return readl(pcie->reg_base + reg); } +static inline u16 cdns_pcie_readw(struct cdns_pcie *pcie, u32 reg) +{ + return readw(pcie->reg_base + reg); +} + +static inline u8 cdns_pcie_readb(struct cdns_pcie *pcie, u32 reg) +{ + return readb(pcie->reg_base + reg); +} + +static inline int cdns_pcie_read_cfg_byte(struct cdns_pcie *pcie, int where, + u8 *val) +{ + *val = cdns_pcie_readb(pcie, where); + return PCIBIOS_SUCCESSFUL; +} + +static inline int cdns_pcie_read_cfg_word(struct cdns_pcie *pcie, int where, + u16 *val) +{ + *val = cdns_pcie_readw(pcie, where); + return PCIBIOS_SUCCESSFUL; +} + +static inline int cdns_pcie_read_cfg_dword(struct cdns_pcie *pcie, int where, + u32 *val) +{ + *val = cdns_pcie_readl(pcie, where); + return PCIBIOS_SUCCESSFUL; +} + static inline u32 cdns_pcie_read_sz(void __iomem *addr, int size) { void __iomem *aligned_addr = PTR_ALIGN_DOWN(addr, 0x4); @@ -468,7 +494,7 @@ static inline u32 cdns_pcie_ep_fn_readl(struct cdns_pcie *pcie, u8 fn, u32 reg) static inline int cdns_pcie_start_link(struct cdns_pcie *pcie) { - if (pcie->ops->start_link) + if (pcie->ops && pcie->ops->start_link) return pcie->ops->start_link(pcie); return 0; @@ -476,13 +502,13 @@ static inline int cdns_pcie_start_link(struct cdns_pcie *pcie) static inline void cdns_pcie_stop_link(struct cdns_pcie *pcie) { - if (pcie->ops->stop_link) + if (pcie->ops && pcie->ops->stop_link) pcie->ops->stop_link(pcie); } static inline bool cdns_pcie_link_up(struct cdns_pcie *pcie) { - if (pcie->ops->link_up) + if (pcie->ops && pcie->ops->link_up) return pcie->ops->link_up(pcie); return true; @@ -536,6 +562,9 @@ static inline void cdns_pcie_ep_disable(struct cdns_pcie_ep *ep) } #endif +u8 cdns_pcie_find_capability(struct cdns_pcie *pcie, u8 cap); +u16 cdns_pcie_find_ext_capability(struct cdns_pcie *pcie, u8 cap); + void cdns_pcie_detect_quiet_min_delay_set(struct cdns_pcie *pcie); void cdns_pcie_set_outbound_region(struct cdns_pcie *pcie, u8 busnr, u8 fn, diff --git a/drivers/pci/controller/cadence/pcie-sg2042.c b/drivers/pci/controller/cadence/pcie-sg2042.c new file mode 100644 index 000000000000..a077b28d4894 --- /dev/null +++ b/drivers/pci/controller/cadence/pcie-sg2042.c @@ -0,0 +1,134 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * pcie-sg2042 - PCIe controller driver for Sophgo SG2042 SoC + * + * Copyright (C) 2025 Sophgo Technology Inc. + * Copyright (C) 2025 Chen Wang <unicorn_wang@outlook.com> + */ + +#include <linux/mod_devicetable.h> +#include <linux/pci.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> + +#include "pcie-cadence.h" + +/* + * SG2042 only supports 4-byte aligned access, so for the rootbus (i.e. to + * read/write the Root Port itself, read32/write32 is required. For + * non-rootbus (i.e. to read/write the PCIe peripheral registers, supports + * 1/2/4 byte aligned access, so directly using read/write should be fine. + */ + +static struct pci_ops sg2042_pcie_root_ops = { + .map_bus = cdns_pci_map_bus, + .read = pci_generic_config_read32, + .write = pci_generic_config_write32, +}; + +static struct pci_ops sg2042_pcie_child_ops = { + .map_bus = cdns_pci_map_bus, + .read = pci_generic_config_read, + .write = pci_generic_config_write, +}; + +static int sg2042_pcie_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct pci_host_bridge *bridge; + struct cdns_pcie *pcie; + struct cdns_pcie_rc *rc; + int ret; + + bridge = devm_pci_alloc_host_bridge(dev, sizeof(*rc)); + if (!bridge) + return dev_err_probe(dev, -ENOMEM, "Failed to alloc host bridge!\n"); + + bridge->ops = &sg2042_pcie_root_ops; + bridge->child_ops = &sg2042_pcie_child_ops; + + rc = pci_host_bridge_priv(bridge); + pcie = &rc->pcie; + pcie->dev = dev; + + platform_set_drvdata(pdev, pcie); + + pm_runtime_set_active(dev); + pm_runtime_no_callbacks(dev); + devm_pm_runtime_enable(dev); + + ret = cdns_pcie_init_phy(dev, pcie); + if (ret) + return dev_err_probe(dev, ret, "Failed to init phy!\n"); + + ret = cdns_pcie_host_setup(rc); + if (ret) { + dev_err_probe(dev, ret, "Failed to setup host!\n"); + cdns_pcie_disable_phy(pcie); + return ret; + } + + return 0; +} + +static void sg2042_pcie_remove(struct platform_device *pdev) +{ + struct cdns_pcie *pcie = platform_get_drvdata(pdev); + struct device *dev = &pdev->dev; + struct cdns_pcie_rc *rc; + + rc = container_of(pcie, struct cdns_pcie_rc, pcie); + cdns_pcie_host_disable(rc); + + cdns_pcie_disable_phy(pcie); + + pm_runtime_disable(dev); +} + +static int sg2042_pcie_suspend_noirq(struct device *dev) +{ + struct cdns_pcie *pcie = dev_get_drvdata(dev); + + cdns_pcie_disable_phy(pcie); + + return 0; +} + +static int sg2042_pcie_resume_noirq(struct device *dev) +{ + struct cdns_pcie *pcie = dev_get_drvdata(dev); + int ret; + + ret = cdns_pcie_enable_phy(pcie); + if (ret) { + dev_err(dev, "failed to enable PHY\n"); + return ret; + } + + return 0; +} + +static DEFINE_NOIRQ_DEV_PM_OPS(sg2042_pcie_pm_ops, + sg2042_pcie_suspend_noirq, + sg2042_pcie_resume_noirq); + +static const struct of_device_id sg2042_pcie_of_match[] = { + { .compatible = "sophgo,sg2042-pcie-host" }, + {}, +}; +MODULE_DEVICE_TABLE(of, sg2042_pcie_of_match); + +static struct platform_driver sg2042_pcie_driver = { + .driver = { + .name = "sg2042-pcie", + .of_match_table = sg2042_pcie_of_match, + .pm = pm_sleep_ptr(&sg2042_pcie_pm_ops), + }, + .probe = sg2042_pcie_probe, + .remove = sg2042_pcie_remove, +}; +module_platform_driver(sg2042_pcie_driver); + +MODULE_LICENSE("GPL"); +MODULE_DESCRIPTION("PCIe controller driver for SG2042 SoCs"); +MODULE_AUTHOR("Chen Wang <unicorn_wang@outlook.com>"); diff --git a/drivers/pci/controller/dwc/Kconfig b/drivers/pci/controller/dwc/Kconfig index ff6b6d9e18ec..349d4657393c 100644 --- a/drivers/pci/controller/dwc/Kconfig +++ b/drivers/pci/controller/dwc/Kconfig @@ -20,6 +20,7 @@ config PCIE_DW_HOST bool select PCIE_DW select IRQ_MSI_LIB + select PCI_HOST_COMMON config PCIE_DW_EP bool @@ -298,6 +299,7 @@ config PCIE_QCOM select CRC8 select PCIE_QCOM_COMMON select PCI_HOST_COMMON + select PCI_PWRCTRL_SLOT help Say Y here to enable PCIe controller support on Qualcomm SoCs. The PCIe controller uses the DesignWare core plus Qualcomm-specific @@ -422,6 +424,30 @@ config PCIE_SPEAR13XX help Say Y here if you want PCIe support on SPEAr13XX SoCs. +config PCIE_STM32_HOST + tristate "STMicroelectronics STM32MP25 PCIe Controller (host mode)" + depends on ARCH_STM32 || COMPILE_TEST + depends on PCI_MSI + select PCIE_DW_HOST + help + Enables Root Complex (RC) support for the DesignWare core based PCIe + controller found in STM32MP25 SoC. + + This driver can also be built as a module. If so, the module + will be called pcie-stm32. + +config PCIE_STM32_EP + tristate "STMicroelectronics STM32MP25 PCIe Controller (endpoint mode)" + depends on ARCH_STM32 || COMPILE_TEST + depends on PCI_ENDPOINT + select PCIE_DW_EP + help + Enables Endpoint (EP) support for the DesignWare core based PCIe + controller found in STM32MP25 SoC. + + This driver can also be built as a module. If so, the module + will be called pcie-stm32-ep. + config PCI_DRA7XX tristate diff --git a/drivers/pci/controller/dwc/Makefile b/drivers/pci/controller/dwc/Makefile index 6919d27798d1..7ae28f3b0fb3 100644 --- a/drivers/pci/controller/dwc/Makefile +++ b/drivers/pci/controller/dwc/Makefile @@ -31,6 +31,8 @@ obj-$(CONFIG_PCIE_UNIPHIER) += pcie-uniphier.o obj-$(CONFIG_PCIE_UNIPHIER_EP) += pcie-uniphier-ep.o obj-$(CONFIG_PCIE_VISCONTI_HOST) += pcie-visconti.o obj-$(CONFIG_PCIE_RCAR_GEN4) += pcie-rcar-gen4.o +obj-$(CONFIG_PCIE_STM32_HOST) += pcie-stm32.o +obj-$(CONFIG_PCIE_STM32_EP) += pcie-stm32-ep.o # The following drivers are for devices that use the generic ACPI # pci_root.c driver but don't support standard ECAM config access. diff --git a/drivers/pci/controller/dwc/pci-dra7xx.c b/drivers/pci/controller/dwc/pci-dra7xx.c index f97f5266d196..01cfd9aeb0b8 100644 --- a/drivers/pci/controller/dwc/pci-dra7xx.c +++ b/drivers/pci/controller/dwc/pci-dra7xx.c @@ -426,7 +426,6 @@ static int dra7xx_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static const struct pci_epc_features dra7xx_pcie_epc_features = { .linkup_notifier = true, .msi_capable = true, - .msix_capable = false, }; static const struct pci_epc_features* diff --git a/drivers/pci/controller/dwc/pci-exynos.c b/drivers/pci/controller/dwc/pci-exynos.c index 1f0e98d07109..0bb7d4f5d784 100644 --- a/drivers/pci/controller/dwc/pci-exynos.c +++ b/drivers/pci/controller/dwc/pci-exynos.c @@ -53,7 +53,6 @@ struct exynos_pcie { struct dw_pcie pci; - void __iomem *elbi_base; struct clk_bulk_data *clks; struct phy *phy; struct regulator_bulk_data supplies[2]; @@ -71,73 +70,78 @@ static u32 exynos_pcie_readl(void __iomem *base, u32 reg) static void exynos_pcie_sideband_dbi_w_mode(struct exynos_pcie *ep, bool on) { + struct dw_pcie *pci = &ep->pci; u32 val; - val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_AWMISC); + val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_AWMISC); if (on) val |= PCIE_ELBI_SLV_DBI_ENABLE; else val &= ~PCIE_ELBI_SLV_DBI_ENABLE; - exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_AWMISC); + exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_AWMISC); } static void exynos_pcie_sideband_dbi_r_mode(struct exynos_pcie *ep, bool on) { + struct dw_pcie *pci = &ep->pci; u32 val; - val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_SLV_ARMISC); + val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_SLV_ARMISC); if (on) val |= PCIE_ELBI_SLV_DBI_ENABLE; else val &= ~PCIE_ELBI_SLV_DBI_ENABLE; - exynos_pcie_writel(ep->elbi_base, val, PCIE_ELBI_SLV_ARMISC); + exynos_pcie_writel(pci->elbi_base, val, PCIE_ELBI_SLV_ARMISC); } static void exynos_pcie_assert_core_reset(struct exynos_pcie *ep) { + struct dw_pcie *pci = &ep->pci; u32 val; - val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET); + val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET); val &= ~PCIE_CORE_RESET_ENABLE; - exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET); - exynos_pcie_writel(ep->elbi_base, 0, PCIE_STICKY_RESET); - exynos_pcie_writel(ep->elbi_base, 0, PCIE_NONSTICKY_RESET); + exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET); + exynos_pcie_writel(pci->elbi_base, 0, PCIE_STICKY_RESET); + exynos_pcie_writel(pci->elbi_base, 0, PCIE_NONSTICKY_RESET); } static void exynos_pcie_deassert_core_reset(struct exynos_pcie *ep) { + struct dw_pcie *pci = &ep->pci; u32 val; - val = exynos_pcie_readl(ep->elbi_base, PCIE_CORE_RESET); + val = exynos_pcie_readl(pci->elbi_base, PCIE_CORE_RESET); val |= PCIE_CORE_RESET_ENABLE; - exynos_pcie_writel(ep->elbi_base, val, PCIE_CORE_RESET); - exynos_pcie_writel(ep->elbi_base, 1, PCIE_STICKY_RESET); - exynos_pcie_writel(ep->elbi_base, 1, PCIE_NONSTICKY_RESET); - exynos_pcie_writel(ep->elbi_base, 1, PCIE_APP_INIT_RESET); - exynos_pcie_writel(ep->elbi_base, 0, PCIE_APP_INIT_RESET); + exynos_pcie_writel(pci->elbi_base, val, PCIE_CORE_RESET); + exynos_pcie_writel(pci->elbi_base, 1, PCIE_STICKY_RESET); + exynos_pcie_writel(pci->elbi_base, 1, PCIE_NONSTICKY_RESET); + exynos_pcie_writel(pci->elbi_base, 1, PCIE_APP_INIT_RESET); + exynos_pcie_writel(pci->elbi_base, 0, PCIE_APP_INIT_RESET); } static int exynos_pcie_start_link(struct dw_pcie *pci) { - struct exynos_pcie *ep = to_exynos_pcie(pci); u32 val; - val = exynos_pcie_readl(ep->elbi_base, PCIE_SW_WAKE); + val = exynos_pcie_readl(pci->elbi_base, PCIE_SW_WAKE); val &= ~PCIE_BUS_EN; - exynos_pcie_writel(ep->elbi_base, val, PCIE_SW_WAKE); + exynos_pcie_writel(pci->elbi_base, val, PCIE_SW_WAKE); /* assert LTSSM enable */ - exynos_pcie_writel(ep->elbi_base, PCIE_ELBI_LTSSM_ENABLE, + exynos_pcie_writel(pci->elbi_base, PCIE_ELBI_LTSSM_ENABLE, PCIE_APP_LTSSM_ENABLE); return 0; } static void exynos_pcie_clear_irq_pulse(struct exynos_pcie *ep) { - u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_IRQ_PULSE); + struct dw_pcie *pci = &ep->pci; - exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_PULSE); + u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_IRQ_PULSE); + + exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_PULSE); } static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) @@ -150,12 +154,14 @@ static irqreturn_t exynos_pcie_irq_handler(int irq, void *arg) static void exynos_pcie_enable_irq_pulse(struct exynos_pcie *ep) { + struct dw_pcie *pci = &ep->pci; + u32 val = IRQ_INTA_ASSERT | IRQ_INTB_ASSERT | IRQ_INTC_ASSERT | IRQ_INTD_ASSERT; - exynos_pcie_writel(ep->elbi_base, val, PCIE_IRQ_EN_PULSE); - exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_LEVEL); - exynos_pcie_writel(ep->elbi_base, 0, PCIE_IRQ_EN_SPECIAL); + exynos_pcie_writel(pci->elbi_base, val, PCIE_IRQ_EN_PULSE); + exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_LEVEL); + exynos_pcie_writel(pci->elbi_base, 0, PCIE_IRQ_EN_SPECIAL); } static u32 exynos_pcie_read_dbi(struct dw_pcie *pci, void __iomem *base, @@ -211,8 +217,7 @@ static struct pci_ops exynos_pci_ops = { static bool exynos_pcie_link_up(struct dw_pcie *pci) { - struct exynos_pcie *ep = to_exynos_pcie(pci); - u32 val = exynos_pcie_readl(ep->elbi_base, PCIE_ELBI_RDLH_LINKUP); + u32 val = exynos_pcie_readl(pci->elbi_base, PCIE_ELBI_RDLH_LINKUP); return val & PCIE_ELBI_XMLH_LINKUP; } @@ -295,11 +300,6 @@ static int exynos_pcie_probe(struct platform_device *pdev) if (IS_ERR(ep->phy)) return PTR_ERR(ep->phy); - /* External Local Bus interface (ELBI) registers */ - ep->elbi_base = devm_platform_ioremap_resource_byname(pdev, "elbi"); - if (IS_ERR(ep->elbi_base)) - return PTR_ERR(ep->elbi_base); - ret = devm_clk_bulk_get_all_enabled(dev, &ep->clks); if (ret < 0) return ret; diff --git a/drivers/pci/controller/dwc/pci-imx6.c b/drivers/pci/controller/dwc/pci-imx6.c index 80e48746bbaf..4668fc9648bf 100644 --- a/drivers/pci/controller/dwc/pci-imx6.c +++ b/drivers/pci/controller/dwc/pci-imx6.c @@ -1387,9 +1387,7 @@ static int imx_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features imx8m_pcie_epc_features = { - .linkup_notifier = false, .msi_capable = true, - .msix_capable = false, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = SZ_256, }, @@ -1398,9 +1396,7 @@ static const struct pci_epc_features imx8m_pcie_epc_features = { }; static const struct pci_epc_features imx8q_pcie_epc_features = { - .linkup_notifier = false, .msi_capable = true, - .msix_capable = false, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_5] = { .type = BAR_RESERVED, }, @@ -1745,6 +1741,10 @@ static int imx_pcie_probe(struct platform_device *pdev) pci->max_link_speed = 1; of_property_read_u32(node, "fsl,max-link-speed", &pci->max_link_speed); + ret = devm_regulator_get_enable_optional(&pdev->dev, "vpcie3v3aux"); + if (ret < 0 && ret != -ENODEV) + return dev_err_probe(dev, ret, "failed to enable Vaux supply\n"); + imx_pcie->vpcie = devm_regulator_get_optional(&pdev->dev, "vpcie"); if (IS_ERR(imx_pcie->vpcie)) { if (PTR_ERR(imx_pcie->vpcie) != -ENODEV) diff --git a/drivers/pci/controller/dwc/pci-keystone.c b/drivers/pci/controller/dwc/pci-keystone.c index 2b2632e513b5..eb00aa380722 100644 --- a/drivers/pci/controller/dwc/pci-keystone.c +++ b/drivers/pci/controller/dwc/pci-keystone.c @@ -960,7 +960,6 @@ static int ks_pcie_am654_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features ks_pcie_am654_epc_features = { - .linkup_notifier = false, .msi_capable = true, .msix_capable = true, .bar[BAR_0] = { .type = BAR_RESERVED, }, @@ -1201,8 +1200,8 @@ static int ks_pcie_probe(struct platform_device *pdev) if (irq < 0) return irq; - ret = request_irq(irq, ks_pcie_err_irq_handler, IRQF_SHARED, - "ks-pcie-error-irq", ks_pcie); + ret = devm_request_irq(dev, irq, ks_pcie_err_irq_handler, IRQF_SHARED, + "ks-pcie-error-irq", ks_pcie); if (ret < 0) { dev_err(dev, "failed to request error IRQ %d\n", irq); @@ -1213,11 +1212,11 @@ static int ks_pcie_probe(struct platform_device *pdev) if (ret) num_lanes = 1; - phy = devm_kzalloc(dev, sizeof(*phy) * num_lanes, GFP_KERNEL); + phy = devm_kcalloc(dev, num_lanes, sizeof(*phy), GFP_KERNEL); if (!phy) return -ENOMEM; - link = devm_kzalloc(dev, sizeof(*link) * num_lanes, GFP_KERNEL); + link = devm_kcalloc(dev, num_lanes, sizeof(*link), GFP_KERNEL); if (!link) return -ENOMEM; diff --git a/drivers/pci/controller/dwc/pcie-al.c b/drivers/pci/controller/dwc/pcie-al.c index 643115f74092..345c281c74fe 100644 --- a/drivers/pci/controller/dwc/pcie-al.c +++ b/drivers/pci/controller/dwc/pcie-al.c @@ -352,6 +352,7 @@ static int al_pcie_probe(struct platform_device *pdev) return -ENOENT; } al_pcie->ecam_size = resource_size(ecam_res); + pci->pp.native_ecam = true; controller_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "controller"); diff --git a/drivers/pci/controller/dwc/pcie-amd-mdb.c b/drivers/pci/controller/dwc/pcie-amd-mdb.c index 9f7251a16d32..3c6e837465bb 100644 --- a/drivers/pci/controller/dwc/pcie-amd-mdb.c +++ b/drivers/pci/controller/dwc/pcie-amd-mdb.c @@ -18,6 +18,7 @@ #include <linux/resource.h> #include <linux/types.h> +#include "../../pci.h" #include "pcie-designware.h" #define AMD_MDB_TLP_IR_STATUS_MISC 0x4C0 @@ -56,6 +57,7 @@ * @slcr: MDB System Level Control and Status Register (SLCR) base * @intx_domain: INTx IRQ domain pointer * @mdb_domain: MDB IRQ domain pointer + * @perst_gpio: GPIO descriptor for PERST# signal handling * @intx_irq: INTx IRQ interrupt number */ struct amd_mdb_pcie { @@ -63,6 +65,7 @@ struct amd_mdb_pcie { void __iomem *slcr; struct irq_domain *intx_domain; struct irq_domain *mdb_domain; + struct gpio_desc *perst_gpio; int intx_irq; }; @@ -284,7 +287,7 @@ static int amd_mdb_pcie_init_irq_domains(struct amd_mdb_pcie *pcie, struct device_node *pcie_intc_node; int err; - pcie_intc_node = of_get_next_child(node, NULL); + pcie_intc_node = of_get_child_by_name(node, "interrupt-controller"); if (!pcie_intc_node) { dev_err(dev, "No PCIe Intc node found\n"); return -ENODEV; @@ -402,6 +405,28 @@ static int amd_mdb_setup_irq(struct amd_mdb_pcie *pcie, return 0; } +static int amd_mdb_parse_pcie_port(struct amd_mdb_pcie *pcie) +{ + struct device *dev = pcie->pci.dev; + struct device_node *pcie_port_node __maybe_unused; + + /* + * This platform currently supports only one Root Port, so the loop + * will execute only once. + * TODO: Enhance the driver to handle multiple Root Ports in the future. + */ + for_each_child_of_node_with_prefix(dev->of_node, pcie_port_node, "pcie") { + pcie->perst_gpio = devm_fwnode_gpiod_get(dev, of_fwnode_handle(pcie_port_node), + "reset", GPIOD_OUT_HIGH, NULL); + if (IS_ERR(pcie->perst_gpio)) + return dev_err_probe(dev, PTR_ERR(pcie->perst_gpio), + "Failed to request reset GPIO\n"); + return 0; + } + + return -ENODEV; +} + static int amd_mdb_add_pcie_port(struct amd_mdb_pcie *pcie, struct platform_device *pdev) { @@ -426,6 +451,12 @@ static int amd_mdb_add_pcie_port(struct amd_mdb_pcie *pcie, pp->ops = &amd_mdb_pcie_host_ops; + if (pcie->perst_gpio) { + mdelay(PCIE_T_PVPERL_MS); + gpiod_set_value_cansleep(pcie->perst_gpio, 0); + mdelay(PCIE_RESET_CONFIG_WAIT_MS); + } + err = dw_pcie_host_init(pp); if (err) { dev_err(dev, "Failed to initialize host, err=%d\n", err); @@ -444,6 +475,7 @@ static int amd_mdb_pcie_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct amd_mdb_pcie *pcie; struct dw_pcie *pci; + int ret; pcie = devm_kzalloc(dev, sizeof(*pcie), GFP_KERNEL); if (!pcie) @@ -454,6 +486,24 @@ static int amd_mdb_pcie_probe(struct platform_device *pdev) platform_set_drvdata(pdev, pcie); + ret = amd_mdb_parse_pcie_port(pcie); + /* + * If amd_mdb_parse_pcie_port returns -ENODEV, it indicates that the + * PCIe Bridge node was not found in the device tree. This is not + * considered a fatal error and will trigger a fallback where the + * reset GPIO is acquired directly from the PCIe Host Bridge node. + */ + if (ret) { + if (ret != -ENODEV) + return ret; + + pcie->perst_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_HIGH); + if (IS_ERR(pcie->perst_gpio)) + return dev_err_probe(dev, PTR_ERR(pcie->perst_gpio), + "Failed to request reset GPIO\n"); + } + return amd_mdb_add_pcie_port(pcie, pdev); } diff --git a/drivers/pci/controller/dwc/pcie-artpec6.c b/drivers/pci/controller/dwc/pcie-artpec6.c index 234c8cbcae3a..f4a136ee2daf 100644 --- a/drivers/pci/controller/dwc/pcie-artpec6.c +++ b/drivers/pci/controller/dwc/pcie-artpec6.c @@ -370,9 +370,7 @@ static int artpec6_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features artpec6_pcie_epc_features = { - .linkup_notifier = false, .msi_capable = true, - .msix_capable = false, }; static const struct pci_epc_features * diff --git a/drivers/pci/controller/dwc/pcie-designware-ep.c b/drivers/pci/controller/dwc/pcie-designware-ep.c index 0ae54a94809b..7f2112c2fb21 100644 --- a/drivers/pci/controller/dwc/pcie-designware-ep.c +++ b/drivers/pci/controller/dwc/pcie-designware-ep.c @@ -69,37 +69,10 @@ void dw_pcie_ep_reset_bar(struct dw_pcie *pci, enum pci_barno bar) } EXPORT_SYMBOL_GPL(dw_pcie_ep_reset_bar); -static u8 __dw_pcie_ep_find_next_cap(struct dw_pcie_ep *ep, u8 func_no, - u8 cap_ptr, u8 cap) -{ - u8 cap_id, next_cap_ptr; - u16 reg; - - if (!cap_ptr) - return 0; - - reg = dw_pcie_ep_readw_dbi(ep, func_no, cap_ptr); - cap_id = (reg & 0x00ff); - - if (cap_id > PCI_CAP_ID_MAX) - return 0; - - if (cap_id == cap) - return cap_ptr; - - next_cap_ptr = (reg & 0xff00) >> 8; - return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); -} - static u8 dw_pcie_ep_find_capability(struct dw_pcie_ep *ep, u8 func_no, u8 cap) { - u8 next_cap_ptr; - u16 reg; - - reg = dw_pcie_ep_readw_dbi(ep, func_no, PCI_CAPABILITY_LIST); - next_cap_ptr = (reg & 0x00ff); - - return __dw_pcie_ep_find_next_cap(ep, func_no, next_cap_ptr, cap); + return PCI_FIND_NEXT_CAP(dw_pcie_ep_read_cfg, PCI_CAPABILITY_LIST, + cap, ep, func_no); } /** diff --git a/drivers/pci/controller/dwc/pcie-designware-host.c b/drivers/pci/controller/dwc/pcie-designware-host.c index 952f8594b501..20c9333bcb1c 100644 --- a/drivers/pci/controller/dwc/pcie-designware-host.c +++ b/drivers/pci/controller/dwc/pcie-designware-host.c @@ -8,6 +8,7 @@ * Author: Jingoo Han <jg1.han@samsung.com> */ +#include <linux/align.h> #include <linux/iopoll.h> #include <linux/irqchip/chained_irq.h> #include <linux/irqchip/irq-msi-lib.h> @@ -32,6 +33,8 @@ static struct pci_ops dw_child_pcie_ops; MSI_FLAG_PCI_MSIX | \ MSI_GENERIC_FLAGS_MASK) +#define IS_256MB_ALIGNED(x) IS_ALIGNED(x, SZ_256M) + static const struct msi_parent_ops dw_pcie_msi_parent_ops = { .required_flags = DW_PCIE_MSI_FLAGS_REQUIRED, .supported_flags = DW_PCIE_MSI_FLAGS_SUPPORTED, @@ -413,6 +416,95 @@ static void dw_pcie_host_request_msg_tlp_res(struct dw_pcie_rp *pp) } } +static int dw_pcie_config_ecam_iatu(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct dw_pcie_ob_atu_cfg atu = {0}; + resource_size_t bus_range_max; + struct resource_entry *bus; + int ret; + + bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS); + + /* + * Root bus under the host bridge doesn't require any iATU configuration + * as DBI region will be used to access root bus config space. + * Immediate bus under Root Bus, needs type 0 iATU configuration and + * remaining buses need type 1 iATU configuration. + */ + atu.index = 0; + atu.type = PCIE_ATU_TYPE_CFG0; + atu.parent_bus_addr = pp->cfg0_base + SZ_1M; + /* 1MiB is to cover 1 (bus) * 32 (devices) * 8 (functions) */ + atu.size = SZ_1M; + atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE; + ret = dw_pcie_prog_outbound_atu(pci, &atu); + if (ret) + return ret; + + bus_range_max = resource_size(bus->res); + + if (bus_range_max < 2) + return 0; + + /* Configure remaining buses in type 1 iATU configuration */ + atu.index = 1; + atu.type = PCIE_ATU_TYPE_CFG1; + atu.parent_bus_addr = pp->cfg0_base + SZ_2M; + atu.size = (SZ_1M * bus_range_max) - SZ_2M; + atu.ctrl2 = PCIE_ATU_CFG_SHIFT_MODE_ENABLE; + + return dw_pcie_prog_outbound_atu(pci, &atu); +} + +static int dw_pcie_create_ecam_window(struct dw_pcie_rp *pp, struct resource *res) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct device *dev = pci->dev; + struct resource_entry *bus; + + bus = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS); + if (!bus) + return -ENODEV; + + pp->cfg = pci_ecam_create(dev, res, bus->res, &pci_generic_ecam_ops); + if (IS_ERR(pp->cfg)) + return PTR_ERR(pp->cfg); + + pci->dbi_base = pp->cfg->win; + pci->dbi_phys_addr = res->start; + + return 0; +} + +static bool dw_pcie_ecam_enabled(struct dw_pcie_rp *pp, struct resource *config_res) +{ + struct resource *bus_range; + u64 nr_buses; + + /* Vendor glue drivers may implement their own ECAM mechanism */ + if (pp->native_ecam) + return false; + + /* + * PCIe spec r6.0, sec 7.2.2 mandates the base address used for ECAM to + * be aligned on a 2^(n+20) byte boundary, where n is the number of bits + * used for representing 'bus' in BDF. Since the DWC cores always use 8 + * bits for representing 'bus', the base address has to be aligned to + * 2^28 byte boundary, which is 256 MiB. + */ + if (!IS_256MB_ALIGNED(config_res->start)) + return false; + + bus_range = resource_list_first_type(&pp->bridge->windows, IORESOURCE_BUS)->res; + if (!bus_range) + return false; + + nr_buses = resource_size(config_res) >> PCIE_ECAM_BUS_SHIFT; + + return nr_buses >= resource_size(bus_range); +} + static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); @@ -422,10 +514,6 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp) struct resource *res; int ret; - ret = dw_pcie_get_resources(pci); - if (ret) - return ret; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "config"); if (!res) { dev_err(dev, "Missing \"config\" reg space\n"); @@ -435,9 +523,32 @@ static int dw_pcie_host_get_resources(struct dw_pcie_rp *pp) pp->cfg0_size = resource_size(res); pp->cfg0_base = res->start; - pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pp->va_cfg0_base)) - return PTR_ERR(pp->va_cfg0_base); + pp->ecam_enabled = dw_pcie_ecam_enabled(pp, res); + if (pp->ecam_enabled) { + ret = dw_pcie_create_ecam_window(pp, res); + if (ret) + return ret; + + pp->bridge->ops = (struct pci_ops *)&pci_generic_ecam_ops.pci_ops; + pp->bridge->sysdata = pp->cfg; + pp->cfg->priv = pp; + } else { + pp->va_cfg0_base = devm_pci_remap_cfg_resource(dev, res); + if (IS_ERR(pp->va_cfg0_base)) + return PTR_ERR(pp->va_cfg0_base); + + /* Set default bus ops */ + pp->bridge->ops = &dw_pcie_ops; + pp->bridge->child_ops = &dw_child_pcie_ops; + pp->bridge->sysdata = pp; + } + + ret = dw_pcie_get_resources(pci); + if (ret) { + if (pp->cfg) + pci_ecam_free(pp->cfg); + return ret; + } /* Get the I/O range from DT */ win = resource_list_first_type(&pp->bridge->windows, IORESOURCE_IO); @@ -476,14 +587,10 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (ret) return ret; - /* Set default bus ops */ - bridge->ops = &dw_pcie_ops; - bridge->child_ops = &dw_child_pcie_ops; - if (pp->ops->init) { ret = pp->ops->init(pp); if (ret) - return ret; + goto err_free_ecam; } if (pci_msi_enabled()) { @@ -525,6 +632,14 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) if (ret) goto err_free_msi; + if (pp->ecam_enabled) { + ret = dw_pcie_config_ecam_iatu(pp); + if (ret) { + dev_err(dev, "Failed to configure iATU in ECAM mode\n"); + goto err_free_msi; + } + } + /* * Allocate the resource for MSG TLP before programming the iATU * outbound window in dw_pcie_setup_rc(). Since the allocation depends @@ -560,8 +675,6 @@ int dw_pcie_host_init(struct dw_pcie_rp *pp) /* Ignore errors, the link may come up later */ dw_pcie_wait_for_link(pci); - bridge->sysdata = pp; - ret = pci_host_probe(bridge); if (ret) goto err_stop_link; @@ -587,6 +700,10 @@ err_deinit_host: if (pp->ops->deinit) pp->ops->deinit(pp); +err_free_ecam: + if (pp->cfg) + pci_ecam_free(pp->cfg); + return ret; } EXPORT_SYMBOL_GPL(dw_pcie_host_init); @@ -609,6 +726,9 @@ void dw_pcie_host_deinit(struct dw_pcie_rp *pp) if (pp->ops->deinit) pp->ops->deinit(pp); + + if (pp->cfg) + pci_ecam_free(pp->cfg); } EXPORT_SYMBOL_GPL(dw_pcie_host_deinit); diff --git a/drivers/pci/controller/dwc/pcie-designware-plat.c b/drivers/pci/controller/dwc/pcie-designware-plat.c index 771b9d9be077..12f41886c65d 100644 --- a/drivers/pci/controller/dwc/pcie-designware-plat.c +++ b/drivers/pci/controller/dwc/pcie-designware-plat.c @@ -61,7 +61,6 @@ static int dw_plat_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features dw_plat_pcie_epc_features = { - .linkup_notifier = false, .msi_capable = true, .msix_capable = true, }; diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index 89aad5a08928..c644216995f6 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -167,6 +167,14 @@ int dw_pcie_get_resources(struct dw_pcie *pci) } } + /* ELBI is an optional resource */ + res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); + if (res) { + pci->elbi_base = devm_ioremap_resource(pci->dev, res); + if (IS_ERR(pci->elbi_base)) + return PTR_ERR(pci->elbi_base); + } + /* LLDD is supposed to manually switch the clocks and resets state */ if (dw_pcie_cap_is(pci, REQ_RES)) { ret = dw_pcie_get_clocks(pci); @@ -213,83 +221,16 @@ void dw_pcie_version_detect(struct dw_pcie *pci) pci->type = ver; } -/* - * These interfaces resemble the pci_find_*capability() interfaces, but these - * are for configuring host controllers, which are bridges *to* PCI devices but - * are not PCI devices themselves. - */ -static u8 __dw_pcie_find_next_cap(struct dw_pcie *pci, u8 cap_ptr, - u8 cap) -{ - u8 cap_id, next_cap_ptr; - u16 reg; - - if (!cap_ptr) - return 0; - - reg = dw_pcie_readw_dbi(pci, cap_ptr); - cap_id = (reg & 0x00ff); - - if (cap_id > PCI_CAP_ID_MAX) - return 0; - - if (cap_id == cap) - return cap_ptr; - - next_cap_ptr = (reg & 0xff00) >> 8; - return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); -} - u8 dw_pcie_find_capability(struct dw_pcie *pci, u8 cap) { - u8 next_cap_ptr; - u16 reg; - - reg = dw_pcie_readw_dbi(pci, PCI_CAPABILITY_LIST); - next_cap_ptr = (reg & 0x00ff); - - return __dw_pcie_find_next_cap(pci, next_cap_ptr, cap); + return PCI_FIND_NEXT_CAP(dw_pcie_read_cfg, PCI_CAPABILITY_LIST, cap, + pci); } EXPORT_SYMBOL_GPL(dw_pcie_find_capability); -static u16 dw_pcie_find_next_ext_capability(struct dw_pcie *pci, u16 start, - u8 cap) -{ - u32 header; - int ttl; - int pos = PCI_CFG_SPACE_SIZE; - - /* minimum 8 bytes per capability */ - ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; - - if (start) - pos = start; - - header = dw_pcie_readl_dbi(pci, pos); - /* - * If we have no capabilities, this is indicated by cap ID, - * cap version and next pointer all being 0. - */ - if (header == 0) - return 0; - - while (ttl-- > 0) { - if (PCI_EXT_CAP_ID(header) == cap && pos != start) - return pos; - - pos = PCI_EXT_CAP_NEXT(header); - if (pos < PCI_CFG_SPACE_SIZE) - break; - - header = dw_pcie_readl_dbi(pci, pos); - } - - return 0; -} - u16 dw_pcie_find_ext_capability(struct dw_pcie *pci, u8 cap) { - return dw_pcie_find_next_ext_capability(pci, 0, cap); + return PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, 0, cap, pci); } EXPORT_SYMBOL_GPL(dw_pcie_find_ext_capability); @@ -302,8 +243,8 @@ static u16 __dw_pcie_find_vsec_capability(struct dw_pcie *pci, u16 vendor_id, if (vendor_id != dw_pcie_readw_dbi(pci, PCI_VENDOR_ID)) return 0; - while ((vsec = dw_pcie_find_next_ext_capability(pci, vsec, - PCI_EXT_CAP_ID_VNDR))) { + while ((vsec = PCI_FIND_NEXT_EXT_CAP(dw_pcie_read_cfg, vsec, + PCI_EXT_CAP_ID_VNDR, pci))) { header = dw_pcie_readl_dbi(pci, vsec + PCI_VNDR_HEADER); if (PCI_VNDR_HEADER_ID(header) == vsec_id) return vsec; @@ -567,7 +508,7 @@ int dw_pcie_prog_outbound_atu(struct dw_pcie *pci, val = dw_pcie_enable_ecrc(val); dw_pcie_writel_atu_ob(pci, atu->index, PCIE_ATU_REGION_CTRL1, val); - val = PCIE_ATU_ENABLE; + val = PCIE_ATU_ENABLE | atu->ctrl2; if (atu->type == PCIE_ATU_TYPE_MSG) { /* The data-less messages only for now */ val |= PCIE_ATU_INHIBIT_PAYLOAD | atu->code; @@ -841,6 +782,9 @@ static void dw_pcie_link_set_max_link_width(struct dw_pcie *pci, u32 num_lanes) case 8: plc |= PORT_LINK_MODE_8_LANES; break; + case 16: + plc |= PORT_LINK_MODE_16_LANES; + break; default: dev_err(pci->dev, "num-lanes %u: invalid value\n", num_lanes); return; @@ -1045,9 +989,7 @@ static int dw_pcie_edma_irq_verify(struct dw_pcie *pci) char name[15]; int ret; - if (pci->edma.nr_irqs == 1) - return 0; - else if (pci->edma.nr_irqs > 1) + if (pci->edma.nr_irqs > 1) return pci->edma.nr_irqs != ch_cnt ? -EINVAL : 0; ret = platform_get_irq_byname_optional(pdev, "dma"); diff --git a/drivers/pci/controller/dwc/pcie-designware.h b/drivers/pci/controller/dwc/pcie-designware.h index 00f52d472dcd..e995f692a1ec 100644 --- a/drivers/pci/controller/dwc/pcie-designware.h +++ b/drivers/pci/controller/dwc/pcie-designware.h @@ -20,6 +20,7 @@ #include <linux/irq.h> #include <linux/msi.h> #include <linux/pci.h> +#include <linux/pci-ecam.h> #include <linux/reset.h> #include <linux/pci-epc.h> @@ -90,6 +91,7 @@ #define PORT_LINK_MODE_2_LANES PORT_LINK_MODE(0x3) #define PORT_LINK_MODE_4_LANES PORT_LINK_MODE(0x7) #define PORT_LINK_MODE_8_LANES PORT_LINK_MODE(0xf) +#define PORT_LINK_MODE_16_LANES PORT_LINK_MODE(0x1f) #define PCIE_PORT_LANE_SKEW 0x714 #define PORT_LANE_SKEW_INSERT_MASK GENMASK(23, 0) @@ -123,7 +125,6 @@ #define GEN3_RELATED_OFF_GEN3_EQ_DISABLE BIT(16) #define GEN3_RELATED_OFF_RATE_SHADOW_SEL_SHIFT 24 #define GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK GENMASK(25, 24) -#define GEN3_RELATED_OFF_RATE_SHADOW_SEL_16_0GT 0x1 #define GEN3_EQ_CONTROL_OFF 0x8A8 #define GEN3_EQ_CONTROL_OFF_FB_MODE GENMASK(3, 0) @@ -134,8 +135,8 @@ #define GEN3_EQ_FB_MODE_DIR_CHANGE_OFF 0x8AC #define GEN3_EQ_FMDC_T_MIN_PHASE23 GENMASK(4, 0) #define GEN3_EQ_FMDC_N_EVALS GENMASK(9, 5) -#define GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA GENMASK(13, 10) -#define GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA GENMASK(17, 14) +#define GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA GENMASK(13, 10) +#define GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA GENMASK(17, 14) #define PCIE_PORT_MULTI_LANE_CTRL 0x8C0 #define PORT_MLTI_UPCFG_SUPPORT BIT(7) @@ -169,6 +170,7 @@ #define PCIE_ATU_REGION_CTRL2 0x004 #define PCIE_ATU_ENABLE BIT(31) #define PCIE_ATU_BAR_MODE_ENABLE BIT(30) +#define PCIE_ATU_CFG_SHIFT_MODE_ENABLE BIT(28) #define PCIE_ATU_INHIBIT_PAYLOAD BIT(22) #define PCIE_ATU_FUNC_NUM_MATCH_EN BIT(19) #define PCIE_ATU_LOWER_BASE 0x008 @@ -387,6 +389,7 @@ struct dw_pcie_ob_atu_cfg { u8 func_no; u8 code; u8 routing; + u32 ctrl2; u64 parent_bus_addr; u64 pci_addr; u64 size; @@ -425,6 +428,9 @@ struct dw_pcie_rp { struct resource *msg_res; bool use_linkup_irq; struct pci_eq_presets presets; + struct pci_config_window *cfg; + bool ecam_enabled; + bool native_ecam; }; struct dw_pcie_ep_ops { @@ -492,6 +498,7 @@ struct dw_pcie { resource_size_t dbi_phys_addr; void __iomem *dbi_base2; void __iomem *atu_base; + void __iomem *elbi_base; resource_size_t atu_phys_addr; size_t atu_size; resource_size_t parent_bus_offset; @@ -609,6 +616,27 @@ static inline void dw_pcie_writel_dbi2(struct dw_pcie *pci, u32 reg, u32 val) dw_pcie_write_dbi2(pci, reg, 0x4, val); } +static inline int dw_pcie_read_cfg_byte(struct dw_pcie *pci, int where, + u8 *val) +{ + *val = dw_pcie_readb_dbi(pci, where); + return PCIBIOS_SUCCESSFUL; +} + +static inline int dw_pcie_read_cfg_word(struct dw_pcie *pci, int where, + u16 *val) +{ + *val = dw_pcie_readw_dbi(pci, where); + return PCIBIOS_SUCCESSFUL; +} + +static inline int dw_pcie_read_cfg_dword(struct dw_pcie *pci, int where, + u32 *val) +{ + *val = dw_pcie_readl_dbi(pci, where); + return PCIBIOS_SUCCESSFUL; +} + static inline unsigned int dw_pcie_ep_get_dbi_offset(struct dw_pcie_ep *ep, u8 func_no) { @@ -674,6 +702,27 @@ static inline u8 dw_pcie_ep_readb_dbi(struct dw_pcie_ep *ep, u8 func_no, return dw_pcie_ep_read_dbi(ep, func_no, reg, 0x1); } +static inline int dw_pcie_ep_read_cfg_byte(struct dw_pcie_ep *ep, u8 func_no, + int where, u8 *val) +{ + *val = dw_pcie_ep_readb_dbi(ep, func_no, where); + return PCIBIOS_SUCCESSFUL; +} + +static inline int dw_pcie_ep_read_cfg_word(struct dw_pcie_ep *ep, u8 func_no, + int where, u16 *val) +{ + *val = dw_pcie_ep_readw_dbi(ep, func_no, where); + return PCIBIOS_SUCCESSFUL; +} + +static inline int dw_pcie_ep_read_cfg_dword(struct dw_pcie_ep *ep, u8 func_no, + int where, u32 *val) +{ + *val = dw_pcie_ep_readl_dbi(ep, func_no, where); + return PCIBIOS_SUCCESSFUL; +} + static inline unsigned int dw_pcie_ep_get_dbi2_offset(struct dw_pcie_ep *ep, u8 func_no) { diff --git a/drivers/pci/controller/dwc/pcie-dw-rockchip.c b/drivers/pci/controller/dwc/pcie-dw-rockchip.c index 5d7f6f544942..3e2752c7dd09 100644 --- a/drivers/pci/controller/dwc/pcie-dw-rockchip.c +++ b/drivers/pci/controller/dwc/pcie-dw-rockchip.c @@ -331,7 +331,6 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3568 = { .linkup_notifier = true, .msi_capable = true, .msix_capable = true, - .intx_capable = false, .align = SZ_64K, .bar[BAR_0] = { .type = BAR_RESIZABLE, }, .bar[BAR_1] = { .type = BAR_RESIZABLE, }, @@ -352,7 +351,6 @@ static const struct pci_epc_features rockchip_pcie_epc_features_rk3588 = { .linkup_notifier = true, .msi_capable = true, .msix_capable = true, - .intx_capable = false, .align = SZ_64K, .bar[BAR_0] = { .type = BAR_RESIZABLE, }, .bar[BAR_1] = { .type = BAR_RESIZABLE, }, diff --git a/drivers/pci/controller/dwc/pcie-keembay.c b/drivers/pci/controller/dwc/pcie-keembay.c index 67dd3337b447..60e74ac782af 100644 --- a/drivers/pci/controller/dwc/pcie-keembay.c +++ b/drivers/pci/controller/dwc/pcie-keembay.c @@ -309,7 +309,6 @@ static int keembay_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features keembay_pcie_epc_features = { - .linkup_notifier = false, .msi_capable = true, .msix_capable = true, .bar[BAR_0] = { .only_64bit = true, }, diff --git a/drivers/pci/controller/dwc/pcie-qcom-common.c b/drivers/pci/controller/dwc/pcie-qcom-common.c index 3aad19b56da8..01c5387e53bf 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-common.c +++ b/drivers/pci/controller/dwc/pcie-qcom-common.c @@ -8,9 +8,11 @@ #include "pcie-designware.h" #include "pcie-qcom-common.h" -void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci) +void qcom_pcie_common_set_equalization(struct dw_pcie *pci) { + struct device *dev = pci->dev; u32 reg; + u16 speed; /* * GEN3_RELATED_OFF register is repurposed to apply equalization @@ -19,32 +21,40 @@ void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci) * determines the data rate for which these equalization settings are * applied. */ - reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); - reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL; - reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK; - reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, - GEN3_RELATED_OFF_RATE_SHADOW_SEL_16_0GT); - dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg); - reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF); - reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 | - GEN3_EQ_FMDC_N_EVALS | - GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA | - GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA); - reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) | - FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) | - FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CUSROR_DELTA, 0x5) | - FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CUSROR_DELTA, 0x5); - dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg); + for (speed = PCIE_SPEED_8_0GT; speed <= pcie_link_speed[pci->max_link_speed]; speed++) { + if (speed > PCIE_SPEED_32_0GT) { + dev_warn(dev, "Skipped equalization settings for unsupported data rate\n"); + break; + } - reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); - reg &= ~(GEN3_EQ_CONTROL_OFF_FB_MODE | - GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE | - GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL | - GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC); - dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, reg); + reg = dw_pcie_readl_dbi(pci, GEN3_RELATED_OFF); + reg &= ~GEN3_RELATED_OFF_GEN3_ZRXDC_NONCOMPL; + reg &= ~GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK; + reg |= FIELD_PREP(GEN3_RELATED_OFF_RATE_SHADOW_SEL_MASK, + speed - PCIE_SPEED_8_0GT); + dw_pcie_writel_dbi(pci, GEN3_RELATED_OFF, reg); + + reg = dw_pcie_readl_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF); + reg &= ~(GEN3_EQ_FMDC_T_MIN_PHASE23 | + GEN3_EQ_FMDC_N_EVALS | + GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA | + GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA); + reg |= FIELD_PREP(GEN3_EQ_FMDC_T_MIN_PHASE23, 0x1) | + FIELD_PREP(GEN3_EQ_FMDC_N_EVALS, 0xd) | + FIELD_PREP(GEN3_EQ_FMDC_MAX_PRE_CURSOR_DELTA, 0x5) | + FIELD_PREP(GEN3_EQ_FMDC_MAX_POST_CURSOR_DELTA, 0x5); + dw_pcie_writel_dbi(pci, GEN3_EQ_FB_MODE_DIR_CHANGE_OFF, reg); + + reg = dw_pcie_readl_dbi(pci, GEN3_EQ_CONTROL_OFF); + reg &= ~(GEN3_EQ_CONTROL_OFF_FB_MODE | + GEN3_EQ_CONTROL_OFF_PHASE23_EXIT_MODE | + GEN3_EQ_CONTROL_OFF_FOM_INC_INITIAL_EVAL | + GEN3_EQ_CONTROL_OFF_PSET_REQ_VEC); + dw_pcie_writel_dbi(pci, GEN3_EQ_CONTROL_OFF, reg); + } } -EXPORT_SYMBOL_GPL(qcom_pcie_common_set_16gt_equalization); +EXPORT_SYMBOL_GPL(qcom_pcie_common_set_equalization); void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci) { diff --git a/drivers/pci/controller/dwc/pcie-qcom-common.h b/drivers/pci/controller/dwc/pcie-qcom-common.h index 7d88d29e4766..7f5ca2fd9a72 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-common.h +++ b/drivers/pci/controller/dwc/pcie-qcom-common.h @@ -8,7 +8,7 @@ struct dw_pcie; -void qcom_pcie_common_set_16gt_equalization(struct dw_pcie *pci); +void qcom_pcie_common_set_equalization(struct dw_pcie *pci); void qcom_pcie_common_set_16gt_lane_margining(struct dw_pcie *pci); #endif diff --git a/drivers/pci/controller/dwc/pcie-qcom-ep.c b/drivers/pci/controller/dwc/pcie-qcom-ep.c index bf7c6ac0f3e3..f1bc0ac81a92 100644 --- a/drivers/pci/controller/dwc/pcie-qcom-ep.c +++ b/drivers/pci/controller/dwc/pcie-qcom-ep.c @@ -179,7 +179,6 @@ struct qcom_pcie_ep_cfg { * struct qcom_pcie_ep - Qualcomm PCIe Endpoint Controller * @pci: Designware PCIe controller struct * @parf: Qualcomm PCIe specific PARF register base - * @elbi: Designware PCIe specific ELBI register base * @mmio: MMIO register base * @perst_map: PERST regmap * @mmio_res: MMIO region resource @@ -202,7 +201,6 @@ struct qcom_pcie_ep { struct dw_pcie pci; void __iomem *parf; - void __iomem *elbi; void __iomem *mmio; struct regmap *perst_map; struct resource *mmio_res; @@ -267,10 +265,9 @@ static void qcom_pcie_ep_configure_tcsr(struct qcom_pcie_ep *pcie_ep) static bool qcom_pcie_dw_link_up(struct dw_pcie *pci) { - struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci); u32 reg; - reg = readl_relaxed(pcie_ep->elbi + ELBI_SYS_STTS); + reg = readl_relaxed(pci->elbi_base + ELBI_SYS_STTS); return reg & XMLH_LINK_UP; } @@ -294,16 +291,15 @@ static void qcom_pcie_dw_stop_link(struct dw_pcie *pci) static void qcom_pcie_dw_write_dbi2(struct dw_pcie *pci, void __iomem *base, u32 reg, size_t size, u32 val) { - struct qcom_pcie_ep *pcie_ep = to_pcie_ep(pci); int ret; - writel(1, pcie_ep->elbi + ELBI_CS2_ENABLE); + writel(1, pci->elbi_base + ELBI_CS2_ENABLE); ret = dw_pcie_write(pci->dbi_base2 + reg, size, val); if (ret) dev_err(pci->dev, "Failed to write DBI2 register (0x%x): %d\n", reg, ret); - writel(0, pcie_ep->elbi + ELBI_CS2_ENABLE); + writel(0, pci->elbi_base + ELBI_CS2_ENABLE); } static void qcom_pcie_ep_icc_update(struct qcom_pcie_ep *pcie_ep) @@ -511,10 +507,10 @@ static int qcom_pcie_perst_deassert(struct dw_pcie *pci) goto err_disable_resources; } - if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) { - qcom_pcie_common_set_16gt_equalization(pci); + qcom_pcie_common_set_equalization(pci); + + if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) qcom_pcie_common_set_16gt_lane_margining(pci); - } /* * The physical address of the MMIO region which is exposed as the BAR @@ -583,11 +579,6 @@ static int qcom_pcie_ep_get_io_resources(struct platform_device *pdev, return PTR_ERR(pci->dbi_base); pci->dbi_base2 = pci->dbi_base; - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "elbi"); - pcie_ep->elbi = devm_pci_remap_cfg_resource(dev, res); - if (IS_ERR(pcie_ep->elbi)) - return PTR_ERR(pcie_ep->elbi); - pcie_ep->mmio_res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mmio"); if (!pcie_ep->mmio_res) { @@ -831,7 +822,6 @@ static void qcom_pcie_ep_init_debugfs(struct qcom_pcie_ep *pcie_ep) static const struct pci_epc_features qcom_pcie_epc_features = { .linkup_notifier = true, .msi_capable = true, - .msix_capable = false, .align = SZ_4K, .bar[BAR_0] = { .only_64bit = true, }, .bar[BAR_1] = { .type = BAR_RESERVED, }, @@ -874,7 +864,6 @@ static int qcom_pcie_ep_probe(struct platform_device *pdev) pcie_ep->pci.dev = dev; pcie_ep->pci.ops = &pci_ops; pcie_ep->pci.ep.ops = &pci_ep_ops; - pcie_ep->pci.edma.nr_irqs = 1; pcie_ep->cfg = of_device_get_match_data(dev); if (pcie_ep->cfg && pcie_ep->cfg->hdma_support) { diff --git a/drivers/pci/controller/dwc/pcie-qcom.c b/drivers/pci/controller/dwc/pcie-qcom.c index 294babe1816e..805edbbfe7eb 100644 --- a/drivers/pci/controller/dwc/pcie-qcom.c +++ b/drivers/pci/controller/dwc/pcie-qcom.c @@ -55,6 +55,7 @@ #define PARF_AXI_MSTR_WR_ADDR_HALT_V2 0x1a8 #define PARF_Q2A_FLUSH 0x1ac #define PARF_LTSSM 0x1b0 +#define PARF_SLV_DBI_ELBI 0x1b4 #define PARF_INT_ALL_STATUS 0x224 #define PARF_INT_ALL_CLEAR 0x228 #define PARF_INT_ALL_MASK 0x22c @@ -64,6 +65,16 @@ #define PARF_DBI_BASE_ADDR_V2_HI 0x354 #define PARF_SLV_ADDR_SPACE_SIZE_V2 0x358 #define PARF_SLV_ADDR_SPACE_SIZE_V2_HI 0x35c +#define PARF_BLOCK_SLV_AXI_WR_BASE 0x360 +#define PARF_BLOCK_SLV_AXI_WR_BASE_HI 0x364 +#define PARF_BLOCK_SLV_AXI_WR_LIMIT 0x368 +#define PARF_BLOCK_SLV_AXI_WR_LIMIT_HI 0x36c +#define PARF_BLOCK_SLV_AXI_RD_BASE 0x370 +#define PARF_BLOCK_SLV_AXI_RD_BASE_HI 0x374 +#define PARF_BLOCK_SLV_AXI_RD_LIMIT 0x378 +#define PARF_BLOCK_SLV_AXI_RD_LIMIT_HI 0x37c +#define PARF_ECAM_BASE 0x380 +#define PARF_ECAM_BASE_HI 0x384 #define PARF_NO_SNOOP_OVERRIDE 0x3d4 #define PARF_ATU_BASE_ADDR 0x634 #define PARF_ATU_BASE_ADDR_HI 0x638 @@ -87,6 +98,7 @@ /* PARF_SYS_CTRL register fields */ #define MAC_PHY_POWERDOWN_IN_P2_D_MUX_EN BIT(29) +#define PCIE_ECAM_BLOCKER_EN BIT(26) #define MST_WAKEUP_EN BIT(13) #define SLV_WAKEUP_EN BIT(12) #define MSTR_ACLK_CGC_DIS BIT(10) @@ -134,6 +146,9 @@ /* PARF_LTSSM register fields */ #define LTSSM_EN BIT(8) +/* PARF_SLV_DBI_ELBI */ +#define SLV_DBI_ELBI_ADDR_BASE GENMASK(11, 0) + /* PARF_INT_ALL_{STATUS/CLEAR/MASK} register fields */ #define PARF_INT_ALL_LINK_UP BIT(13) #define PARF_INT_MSI_DEV_0_7 GENMASK(30, 23) @@ -247,7 +262,6 @@ struct qcom_pcie_ops { int (*get_resources)(struct qcom_pcie *pcie); int (*init)(struct qcom_pcie *pcie); int (*post_init)(struct qcom_pcie *pcie); - void (*host_post_init)(struct qcom_pcie *pcie); void (*deinit)(struct qcom_pcie *pcie); void (*ltssm_enable)(struct qcom_pcie *pcie); int (*config_sid)(struct qcom_pcie *pcie); @@ -276,11 +290,8 @@ struct qcom_pcie_port { struct qcom_pcie { struct dw_pcie *pci; void __iomem *parf; /* DT parf */ - void __iomem *elbi; /* DT elbi */ void __iomem *mhi; union qcom_pcie_resources res; - struct phy *phy; - struct gpio_desc *reset; struct icc_path *icc_mem; struct icc_path *icc_cpu; const struct qcom_pcie_cfg *cfg; @@ -297,11 +308,8 @@ static void qcom_perst_assert(struct qcom_pcie *pcie, bool assert) struct qcom_pcie_port *port; int val = assert ? 1 : 0; - if (list_empty(&pcie->ports)) - gpiod_set_value_cansleep(pcie->reset, val); - else - list_for_each_entry(port, &pcie->ports, list) - gpiod_set_value_cansleep(port->reset, val); + list_for_each_entry(port, &pcie->ports, list) + gpiod_set_value_cansleep(port->reset, val); usleep_range(PERST_DELAY_US, PERST_DELAY_US + 500); } @@ -318,14 +326,55 @@ static void qcom_ep_reset_deassert(struct qcom_pcie *pcie) qcom_perst_assert(pcie, false); } +static void qcom_pci_config_ecam(struct dw_pcie_rp *pp) +{ + struct dw_pcie *pci = to_dw_pcie_from_pp(pp); + struct qcom_pcie *pcie = to_qcom_pcie(pci); + u64 addr, addr_end; + u32 val; + + writel_relaxed(lower_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE); + writel_relaxed(upper_32_bits(pci->dbi_phys_addr), pcie->parf + PARF_ECAM_BASE_HI); + + /* + * The only device on the root bus is a single Root Port. If we try to + * access any devices other than Device/Function 00.0 on Bus 0, the TLP + * will go outside of the controller to the PCI bus. But with CFG Shift + * Feature (ECAM) enabled in iATU, there is no guarantee that the + * response is going to be all F's. Hence, to make sure that the + * requester gets all F's response for accesses other than the Root + * Port, configure iATU to block the transactions starting from + * function 1 of the root bus to the end of the root bus (i.e., from + * dbi_base + 4KB to dbi_base + 1MB). + */ + addr = pci->dbi_phys_addr + SZ_4K; + writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE); + writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_WR_BASE_HI); + + writel_relaxed(lower_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE); + writel_relaxed(upper_32_bits(addr), pcie->parf + PARF_BLOCK_SLV_AXI_RD_BASE_HI); + + addr_end = pci->dbi_phys_addr + SZ_1M - 1; + + writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT); + writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_WR_LIMIT_HI); + + writel_relaxed(lower_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT); + writel_relaxed(upper_32_bits(addr_end), pcie->parf + PARF_BLOCK_SLV_AXI_RD_LIMIT_HI); + + val = readl_relaxed(pcie->parf + PARF_SYS_CTRL); + val |= PCIE_ECAM_BLOCKER_EN; + writel_relaxed(val, pcie->parf + PARF_SYS_CTRL); +} + static int qcom_pcie_start_link(struct dw_pcie *pci) { struct qcom_pcie *pcie = to_qcom_pcie(pci); - if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) { - qcom_pcie_common_set_16gt_equalization(pci); + qcom_pcie_common_set_equalization(pci); + + if (pcie_link_speed[pci->max_link_speed] == PCIE_SPEED_16_0GT) qcom_pcie_common_set_16gt_lane_margining(pci); - } /* Enable Link Training state machine */ if (pcie->cfg->ops->ltssm_enable) @@ -414,12 +463,17 @@ static void qcom_pcie_configure_dbi_atu_base(struct qcom_pcie *pcie) static void qcom_pcie_2_1_0_ltssm_enable(struct qcom_pcie *pcie) { + struct dw_pcie *pci = pcie->pci; u32 val; + if (!pci->elbi_base) { + dev_err(pci->dev, "ELBI is not present\n"); + return; + } /* enable link training */ - val = readl(pcie->elbi + ELBI_SYS_CTRL); + val = readl(pci->elbi_base + ELBI_SYS_CTRL); val |= ELBI_SYS_CTRL_LT_ENABLE; - writel(val, pcie->elbi + ELBI_SYS_CTRL); + writel(val, pci->elbi_base + ELBI_SYS_CTRL); } static int qcom_pcie_get_resources_2_1_0(struct qcom_pcie *pcie) @@ -1040,25 +1094,6 @@ static int qcom_pcie_post_init_2_7_0(struct qcom_pcie *pcie) return 0; } -static int qcom_pcie_enable_aspm(struct pci_dev *pdev, void *userdata) -{ - /* - * Downstream devices need to be in D0 state before enabling PCI PM - * substates. - */ - pci_set_power_state_locked(pdev, PCI_D0); - pci_enable_link_state_locked(pdev, PCIE_LINK_STATE_ALL); - - return 0; -} - -static void qcom_pcie_host_post_init_2_7_0(struct qcom_pcie *pcie) -{ - struct dw_pcie_rp *pp = &pcie->pci->pp; - - pci_walk_bus(pp->bridge->bus, qcom_pcie_enable_aspm, NULL); -} - static void qcom_pcie_deinit_2_7_0(struct qcom_pcie *pcie) { struct qcom_pcie_resources_2_7_0 *res = &pcie->res.v2_7_0; @@ -1253,63 +1288,39 @@ static bool qcom_pcie_link_up(struct dw_pcie *pci) return val & PCI_EXP_LNKSTA_DLLLA; } -static void qcom_pcie_phy_exit(struct qcom_pcie *pcie) -{ - struct qcom_pcie_port *port; - - if (list_empty(&pcie->ports)) - phy_exit(pcie->phy); - else - list_for_each_entry(port, &pcie->ports, list) - phy_exit(port->phy); -} - static void qcom_pcie_phy_power_off(struct qcom_pcie *pcie) { struct qcom_pcie_port *port; - if (list_empty(&pcie->ports)) { - phy_power_off(pcie->phy); - } else { - list_for_each_entry(port, &pcie->ports, list) - phy_power_off(port->phy); - } + list_for_each_entry(port, &pcie->ports, list) + phy_power_off(port->phy); } static int qcom_pcie_phy_power_on(struct qcom_pcie *pcie) { struct qcom_pcie_port *port; - int ret = 0; + int ret; - if (list_empty(&pcie->ports)) { - ret = phy_set_mode_ext(pcie->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); + list_for_each_entry(port, &pcie->ports, list) { + ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); if (ret) return ret; - ret = phy_power_on(pcie->phy); - if (ret) + ret = phy_power_on(port->phy); + if (ret) { + qcom_pcie_phy_power_off(pcie); return ret; - } else { - list_for_each_entry(port, &pcie->ports, list) { - ret = phy_set_mode_ext(port->phy, PHY_MODE_PCIE, PHY_MODE_PCIE_RC); - if (ret) - return ret; - - ret = phy_power_on(port->phy); - if (ret) { - qcom_pcie_phy_power_off(pcie); - return ret; - } } } - return ret; + return 0; } static int qcom_pcie_host_init(struct dw_pcie_rp *pp) { struct dw_pcie *pci = to_dw_pcie_from_pp(pp); struct qcom_pcie *pcie = to_qcom_pcie(pci); + u16 offset; int ret; qcom_ep_reset_assert(pcie); @@ -1318,6 +1329,17 @@ static int qcom_pcie_host_init(struct dw_pcie_rp *pp) if (ret) return ret; + if (pp->ecam_enabled) { + /* + * Override ELBI when ECAM is enabled, as when ECAM is enabled, + * ELBI moves under the 'config' space. + */ + offset = FIELD_GET(SLV_DBI_ELBI_ADDR_BASE, readl(pcie->parf + PARF_SLV_DBI_ELBI)); + pci->elbi_base = pci->dbi_base + offset; + + qcom_pci_config_ecam(pp); + } + ret = qcom_pcie_phy_power_on(pcie); if (ret) goto err_deinit; @@ -1358,19 +1380,9 @@ static void qcom_pcie_host_deinit(struct dw_pcie_rp *pp) pcie->cfg->ops->deinit(pcie); } -static void qcom_pcie_host_post_init(struct dw_pcie_rp *pp) -{ - struct dw_pcie *pci = to_dw_pcie_from_pp(pp); - struct qcom_pcie *pcie = to_qcom_pcie(pci); - - if (pcie->cfg->ops->host_post_init) - pcie->cfg->ops->host_post_init(pcie); -} - static const struct dw_pcie_host_ops qcom_pcie_dw_ops = { .init = qcom_pcie_host_init, .deinit = qcom_pcie_host_deinit, - .post_init = qcom_pcie_host_post_init, }; /* Qcom IP rev.: 2.1.0 Synopsys IP rev.: 4.01a */ @@ -1432,7 +1444,6 @@ static const struct qcom_pcie_ops ops_1_9_0 = { .get_resources = qcom_pcie_get_resources_2_7_0, .init = qcom_pcie_init_2_7_0, .post_init = qcom_pcie_post_init_2_7_0, - .host_post_init = qcom_pcie_host_post_init_2_7_0, .deinit = qcom_pcie_deinit_2_7_0, .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, .config_sid = qcom_pcie_config_sid_1_9_0, @@ -1443,7 +1454,6 @@ static const struct qcom_pcie_ops ops_1_21_0 = { .get_resources = qcom_pcie_get_resources_2_7_0, .init = qcom_pcie_init_2_7_0, .post_init = qcom_pcie_post_init_2_7_0, - .host_post_init = qcom_pcie_host_post_init_2_7_0, .deinit = qcom_pcie_deinit_2_7_0, .ltssm_enable = qcom_pcie_2_3_2_ltssm_enable, }; @@ -1740,6 +1750,8 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie) int ret = -ENOENT; for_each_available_child_of_node_scoped(dev->of_node, of_port) { + if (!of_node_is_type(of_port, "pci")) + continue; ret = qcom_pcie_parse_port(pcie, of_port); if (ret) goto err_port_del; @@ -1748,8 +1760,10 @@ static int qcom_pcie_parse_ports(struct qcom_pcie *pcie) return ret; err_port_del: - list_for_each_entry_safe(port, tmp, &pcie->ports, list) + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + phy_exit(port->phy); list_del(&port->list); + } return ret; } @@ -1757,20 +1771,32 @@ err_port_del: static int qcom_pcie_parse_legacy_binding(struct qcom_pcie *pcie) { struct device *dev = pcie->pci->dev; + struct qcom_pcie_port *port; + struct gpio_desc *reset; + struct phy *phy; int ret; - pcie->phy = devm_phy_optional_get(dev, "pciephy"); - if (IS_ERR(pcie->phy)) - return PTR_ERR(pcie->phy); + phy = devm_phy_optional_get(dev, "pciephy"); + if (IS_ERR(phy)) + return PTR_ERR(phy); - pcie->reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); - if (IS_ERR(pcie->reset)) - return PTR_ERR(pcie->reset); + reset = devm_gpiod_get_optional(dev, "perst", GPIOD_OUT_HIGH); + if (IS_ERR(reset)) + return PTR_ERR(reset); - ret = phy_init(pcie->phy); + ret = phy_init(phy); if (ret) return ret; + port = devm_kzalloc(dev, sizeof(*port), GFP_KERNEL); + if (!port) + return -ENOMEM; + + port->reset = reset; + port->phy = phy; + INIT_LIST_HEAD(&port->list); + list_add_tail(&port->list, &pcie->ports); + return 0; } @@ -1861,12 +1887,6 @@ static int qcom_pcie_probe(struct platform_device *pdev) goto err_pm_runtime_put; } - pcie->elbi = devm_platform_ioremap_resource_byname(pdev, "elbi"); - if (IS_ERR(pcie->elbi)) { - ret = PTR_ERR(pcie->elbi); - goto err_pm_runtime_put; - } - /* MHI region is optional */ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mhi"); if (res) { @@ -1984,9 +2004,10 @@ static int qcom_pcie_probe(struct platform_device *pdev) err_host_deinit: dw_pcie_host_deinit(pp); err_phy_exit: - qcom_pcie_phy_exit(pcie); - list_for_each_entry_safe(port, tmp, &pcie->ports, list) + list_for_each_entry_safe(port, tmp, &pcie->ports, list) { + phy_exit(port->phy); list_del(&port->list); + } err_pm_runtime_put: pm_runtime_put(dev); pm_runtime_disable(dev); diff --git a/drivers/pci/controller/dwc/pcie-rcar-gen4.c b/drivers/pci/controller/dwc/pcie-rcar-gen4.c index 18055807a4f5..80778917d2dd 100644 --- a/drivers/pci/controller/dwc/pcie-rcar-gen4.c +++ b/drivers/pci/controller/dwc/pcie-rcar-gen4.c @@ -182,8 +182,17 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar) return ret; } - if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) + if (!reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc)) { reset_control_assert(dw->core_rsts[DW_PCIE_PWR_RST].rstc); + /* + * R-Car V4H Reference Manual R19UH0186EJ0130 Rev.1.30 Apr. + * 21, 2025 page 585 Figure 9.3.2 Software Reset flow (B) + * indicates that for peripherals in HSC domain, after + * reset has been asserted by writing a matching reset bit + * into register SRCR, it is mandatory to wait 1ms. + */ + fsleep(1000); + } val = readl(rcar->base + PCIEMSR0); if (rcar->drvdata->mode == DW_PCIE_RC_TYPE) { @@ -204,6 +213,19 @@ static int rcar_gen4_pcie_common_init(struct rcar_gen4_pcie *rcar) if (ret) goto err_unprepare; + /* + * Assure the reset is latched and the core is ready for DBI access. + * On R-Car V4H, the PCIe reset is asynchronous and does not take + * effect immediately, but needs a short time to complete. In case + * DBI access happens in that short time, that access generates an + * SError. To make sure that condition can never happen, read back the + * state of the reset, which should turn the asynchronous reset into + * synchronous one, and wait a little over 1ms to add additional + * safety margin. + */ + reset_control_status(dw->core_rsts[DW_PCIE_PWR_RST].rstc); + fsleep(1000); + if (rcar->drvdata->additional_common_init) rcar->drvdata->additional_common_init(rcar); @@ -398,9 +420,7 @@ static int rcar_gen4_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, } static const struct pci_epc_features rcar_gen4_pcie_epc_features = { - .linkup_notifier = false, .msi_capable = true, - .msix_capable = false, .bar[BAR_1] = { .type = BAR_RESERVED, }, .bar[BAR_3] = { .type = BAR_RESERVED, }, .bar[BAR_4] = { .type = BAR_FIXED, .fixed_size = 256 }, @@ -701,7 +721,7 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(23, 22), BIT(22)); rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(18, 16), GENMASK(17, 16)); rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(7, 6), BIT(6)); - rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(2, 0), GENMASK(11, 0)); + rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x148, GENMASK(2, 0), GENMASK(1, 0)); rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x1d4, GENMASK(16, 15), GENMASK(16, 15)); rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x514, BIT(26), BIT(26)); rcar_gen4_pcie_phy_reg_update_bits(rcar, 0x0f8, BIT(16), 0); @@ -711,7 +731,7 @@ static int rcar_gen4_pcie_ltssm_control(struct rcar_gen4_pcie *rcar, bool enable val &= ~APP_HOLD_PHY_RST; writel(val, rcar->base + PCIERSTCTRL1); - ret = readl_poll_timeout(rcar->phy_base + 0x0f8, val, !(val & BIT(18)), 100, 10000); + ret = readl_poll_timeout(rcar->phy_base + 0x0f8, val, val & BIT(18), 100, 10000); if (ret < 0) return ret; diff --git a/drivers/pci/controller/dwc/pcie-stm32-ep.c b/drivers/pci/controller/dwc/pcie-stm32-ep.c new file mode 100644 index 000000000000..3400c7cd2d88 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-stm32-ep.c @@ -0,0 +1,364 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics STM32MP25 PCIe endpoint driver. + * + * Copyright (C) 2025 STMicroelectronics + * Author: Christian Bruel <christian.bruel@foss.st.com> + */ + +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/of_platform.h> +#include <linux/of_gpio.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include "pcie-designware.h" +#include "pcie-stm32.h" + +struct stm32_pcie { + struct dw_pcie pci; + struct regmap *regmap; + struct reset_control *rst; + struct phy *phy; + struct clk *clk; + struct gpio_desc *perst_gpio; + unsigned int perst_irq; +}; + +static void stm32_pcie_ep_init(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) + dw_pcie_ep_reset_bar(pci, bar); +} + +static int stm32_pcie_enable_link(struct dw_pcie *pci) +{ + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, + STM32MP25_PCIECR_LTSSM_EN, + STM32MP25_PCIECR_LTSSM_EN); + + return dw_pcie_wait_for_link(pci); +} + +static void stm32_pcie_disable_link(struct dw_pcie *pci) +{ + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, STM32MP25_PCIECR_LTSSM_EN, 0); +} + +static int stm32_pcie_start_link(struct dw_pcie *pci) +{ + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + int ret; + + dev_dbg(pci->dev, "Enable link\n"); + + ret = stm32_pcie_enable_link(pci); + if (ret) { + dev_err(pci->dev, "PCIe cannot establish link: %d\n", ret); + return ret; + } + + enable_irq(stm32_pcie->perst_irq); + + return 0; +} + +static void stm32_pcie_stop_link(struct dw_pcie *pci) +{ + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + + dev_dbg(pci->dev, "Disable link\n"); + + disable_irq(stm32_pcie->perst_irq); + + stm32_pcie_disable_link(pci); +} + +static int stm32_pcie_raise_irq(struct dw_pcie_ep *ep, u8 func_no, + unsigned int type, u16 interrupt_num) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + + switch (type) { + case PCI_IRQ_INTX: + return dw_pcie_ep_raise_intx_irq(ep, func_no); + case PCI_IRQ_MSI: + return dw_pcie_ep_raise_msi_irq(ep, func_no, interrupt_num); + default: + dev_err(pci->dev, "UNKNOWN IRQ type\n"); + return -EINVAL; + } +} + +static const struct pci_epc_features stm32_pcie_epc_features = { + .msi_capable = true, + .align = SZ_64K, +}; + +static const struct pci_epc_features* +stm32_pcie_get_features(struct dw_pcie_ep *ep) +{ + return &stm32_pcie_epc_features; +} + +static const struct dw_pcie_ep_ops stm32_pcie_ep_ops = { + .init = stm32_pcie_ep_init, + .raise_irq = stm32_pcie_raise_irq, + .get_features = stm32_pcie_get_features, +}; + +static const struct dw_pcie_ops dw_pcie_ops = { + .start_link = stm32_pcie_start_link, + .stop_link = stm32_pcie_stop_link, +}; + +static int stm32_pcie_enable_resources(struct stm32_pcie *stm32_pcie) +{ + int ret; + + ret = phy_init(stm32_pcie->phy); + if (ret) + return ret; + + ret = clk_prepare_enable(stm32_pcie->clk); + if (ret) + phy_exit(stm32_pcie->phy); + + return ret; +} + +static void stm32_pcie_disable_resources(struct stm32_pcie *stm32_pcie) +{ + clk_disable_unprepare(stm32_pcie->clk); + + phy_exit(stm32_pcie->phy); +} + +static void stm32_pcie_perst_assert(struct dw_pcie *pci) +{ + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + struct dw_pcie_ep *ep = &stm32_pcie->pci.ep; + struct device *dev = pci->dev; + + dev_dbg(dev, "PERST asserted by host\n"); + + pci_epc_deinit_notify(ep->epc); + + stm32_pcie_disable_resources(stm32_pcie); + + pm_runtime_put_sync(dev); +} + +static void stm32_pcie_perst_deassert(struct dw_pcie *pci) +{ + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + struct device *dev = pci->dev; + struct dw_pcie_ep *ep = &pci->ep; + int ret; + + dev_dbg(dev, "PERST de-asserted by host\n"); + + ret = pm_runtime_resume_and_get(dev); + if (ret < 0) { + dev_err(dev, "Failed to resume runtime PM: %d\n", ret); + return; + } + + ret = stm32_pcie_enable_resources(stm32_pcie); + if (ret) { + dev_err(dev, "Failed to enable resources: %d\n", ret); + goto err_pm_put_sync; + } + + /* + * Reprogram the configuration space registers here because the DBI + * registers were reset by the PHY RCC during phy_init(). + */ + ret = dw_pcie_ep_init_registers(ep); + if (ret) { + dev_err(dev, "Failed to complete initialization: %d\n", ret); + goto err_disable_resources; + } + + pci_epc_init_notify(ep->epc); + + return; + +err_disable_resources: + stm32_pcie_disable_resources(stm32_pcie); + +err_pm_put_sync: + pm_runtime_put_sync(dev); +} + +static irqreturn_t stm32_pcie_ep_perst_irq_thread(int irq, void *data) +{ + struct stm32_pcie *stm32_pcie = data; + struct dw_pcie *pci = &stm32_pcie->pci; + u32 perst; + + perst = gpiod_get_value(stm32_pcie->perst_gpio); + if (perst) + stm32_pcie_perst_assert(pci); + else + stm32_pcie_perst_deassert(pci); + + irq_set_irq_type(gpiod_to_irq(stm32_pcie->perst_gpio), + (perst ? IRQF_TRIGGER_HIGH : IRQF_TRIGGER_LOW)); + + return IRQ_HANDLED; +} + +static int stm32_add_pcie_ep(struct stm32_pcie *stm32_pcie, + struct platform_device *pdev) +{ + struct dw_pcie_ep *ep = &stm32_pcie->pci.ep; + struct device *dev = &pdev->dev; + int ret; + + ret = regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, + STM32MP25_PCIECR_TYPE_MASK, + STM32MP25_PCIECR_EP); + if (ret) + return ret; + + reset_control_assert(stm32_pcie->rst); + reset_control_deassert(stm32_pcie->rst); + + ep->ops = &stm32_pcie_ep_ops; + + ret = dw_pcie_ep_init(ep); + if (ret) { + dev_err(dev, "Failed to initialize ep: %d\n", ret); + return ret; + } + + ret = stm32_pcie_enable_resources(stm32_pcie); + if (ret) { + dev_err(dev, "Failed to enable resources: %d\n", ret); + dw_pcie_ep_deinit(ep); + return ret; + } + + return 0; +} + +static int stm32_pcie_probe(struct platform_device *pdev) +{ + struct stm32_pcie *stm32_pcie; + struct device *dev = &pdev->dev; + int ret; + + stm32_pcie = devm_kzalloc(dev, sizeof(*stm32_pcie), GFP_KERNEL); + if (!stm32_pcie) + return -ENOMEM; + + stm32_pcie->pci.dev = dev; + stm32_pcie->pci.ops = &dw_pcie_ops; + + stm32_pcie->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg"); + if (IS_ERR(stm32_pcie->regmap)) + return dev_err_probe(dev, PTR_ERR(stm32_pcie->regmap), + "No syscfg specified\n"); + + stm32_pcie->phy = devm_phy_get(dev, NULL); + if (IS_ERR(stm32_pcie->phy)) + return dev_err_probe(dev, PTR_ERR(stm32_pcie->phy), + "failed to get pcie-phy\n"); + + stm32_pcie->clk = devm_clk_get(dev, NULL); + if (IS_ERR(stm32_pcie->clk)) + return dev_err_probe(dev, PTR_ERR(stm32_pcie->clk), + "Failed to get PCIe clock source\n"); + + stm32_pcie->rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(stm32_pcie->rst)) + return dev_err_probe(dev, PTR_ERR(stm32_pcie->rst), + "Failed to get PCIe reset\n"); + + stm32_pcie->perst_gpio = devm_gpiod_get(dev, "reset", GPIOD_IN); + if (IS_ERR(stm32_pcie->perst_gpio)) + return dev_err_probe(dev, PTR_ERR(stm32_pcie->perst_gpio), + "Failed to get reset GPIO\n"); + + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE); + if (ret) + return ret; + + platform_set_drvdata(pdev, stm32_pcie); + + pm_runtime_get_noresume(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret < 0) { + pm_runtime_put_noidle(&pdev->dev); + return dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); + } + + stm32_pcie->perst_irq = gpiod_to_irq(stm32_pcie->perst_gpio); + + /* Will be enabled in start_link when device is initialized. */ + irq_set_status_flags(stm32_pcie->perst_irq, IRQ_NOAUTOEN); + + ret = devm_request_threaded_irq(dev, stm32_pcie->perst_irq, NULL, + stm32_pcie_ep_perst_irq_thread, + IRQF_TRIGGER_HIGH | IRQF_ONESHOT, + "perst_irq", stm32_pcie); + if (ret) { + pm_runtime_put_noidle(&pdev->dev); + return dev_err_probe(dev, ret, "Failed to request PERST IRQ\n"); + } + + ret = stm32_add_pcie_ep(stm32_pcie, pdev); + if (ret) + pm_runtime_put_noidle(&pdev->dev); + + return ret; +} + +static void stm32_pcie_remove(struct platform_device *pdev) +{ + struct stm32_pcie *stm32_pcie = platform_get_drvdata(pdev); + struct dw_pcie *pci = &stm32_pcie->pci; + struct dw_pcie_ep *ep = &pci->ep; + + dw_pcie_stop_link(pci); + + pci_epc_deinit_notify(ep->epc); + dw_pcie_ep_deinit(ep); + + stm32_pcie_disable_resources(stm32_pcie); + + pm_runtime_put_sync(&pdev->dev); +} + +static const struct of_device_id stm32_pcie_ep_of_match[] = { + { .compatible = "st,stm32mp25-pcie-ep" }, + {}, +}; + +static struct platform_driver stm32_pcie_ep_driver = { + .probe = stm32_pcie_probe, + .remove = stm32_pcie_remove, + .driver = { + .name = "stm32-ep-pcie", + .of_match_table = stm32_pcie_ep_of_match, + }, +}; + +module_platform_driver(stm32_pcie_ep_driver); + +MODULE_AUTHOR("Christian Bruel <christian.bruel@foss.st.com>"); +MODULE_DESCRIPTION("STM32MP25 PCIe Endpoint Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, stm32_pcie_ep_of_match); diff --git a/drivers/pci/controller/dwc/pcie-stm32.c b/drivers/pci/controller/dwc/pcie-stm32.c new file mode 100644 index 000000000000..96a5fb893af4 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-stm32.c @@ -0,0 +1,358 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * STMicroelectronics STM32MP25 PCIe root complex driver. + * + * Copyright (C) 2025 STMicroelectronics + * Author: Christian Bruel <christian.bruel@foss.st.com> + */ + +#include <linux/clk.h> +#include <linux/mfd/syscon.h> +#include <linux/of_platform.h> +#include <linux/phy/phy.h> +#include <linux/pinctrl/consumer.h> +#include <linux/platform_device.h> +#include <linux/pm_runtime.h> +#include <linux/pm_wakeirq.h> +#include <linux/regmap.h> +#include <linux/reset.h> +#include "pcie-designware.h" +#include "pcie-stm32.h" +#include "../../pci.h" + +struct stm32_pcie { + struct dw_pcie pci; + struct regmap *regmap; + struct reset_control *rst; + struct phy *phy; + struct clk *clk; + struct gpio_desc *perst_gpio; + struct gpio_desc *wake_gpio; +}; + +static void stm32_pcie_deassert_perst(struct stm32_pcie *stm32_pcie) +{ + if (stm32_pcie->perst_gpio) { + msleep(PCIE_T_PVPERL_MS); + gpiod_set_value(stm32_pcie->perst_gpio, 0); + } + + msleep(PCIE_RESET_CONFIG_WAIT_MS); +} + +static void stm32_pcie_assert_perst(struct stm32_pcie *stm32_pcie) +{ + gpiod_set_value(stm32_pcie->perst_gpio, 1); +} + +static int stm32_pcie_start_link(struct dw_pcie *pci) +{ + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + + return regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, + STM32MP25_PCIECR_LTSSM_EN, + STM32MP25_PCIECR_LTSSM_EN); +} + +static void stm32_pcie_stop_link(struct dw_pcie *pci) +{ + struct stm32_pcie *stm32_pcie = to_stm32_pcie(pci); + + regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, + STM32MP25_PCIECR_LTSSM_EN, 0); +} + +static int stm32_pcie_suspend_noirq(struct device *dev) +{ + struct stm32_pcie *stm32_pcie = dev_get_drvdata(dev); + int ret; + + ret = dw_pcie_suspend_noirq(&stm32_pcie->pci); + if (ret) + return ret; + + stm32_pcie_assert_perst(stm32_pcie); + + clk_disable_unprepare(stm32_pcie->clk); + + if (!device_wakeup_path(dev)) + phy_exit(stm32_pcie->phy); + + return pinctrl_pm_select_sleep_state(dev); +} + +static int stm32_pcie_resume_noirq(struct device *dev) +{ + struct stm32_pcie *stm32_pcie = dev_get_drvdata(dev); + int ret; + + /* + * The core clock is gated with CLKREQ# from the COMBOPHY REFCLK, + * thus if no device is present, must deassert it with a GPIO from + * pinctrl pinmux before accessing the DBI registers. + */ + ret = pinctrl_pm_select_init_state(dev); + if (ret) { + dev_err(dev, "Failed to activate pinctrl pm state: %d\n", ret); + return ret; + } + + if (!device_wakeup_path(dev)) { + ret = phy_init(stm32_pcie->phy); + if (ret) { + pinctrl_pm_select_default_state(dev); + return ret; + } + } + + ret = clk_prepare_enable(stm32_pcie->clk); + if (ret) + goto err_phy_exit; + + stm32_pcie_deassert_perst(stm32_pcie); + + ret = dw_pcie_resume_noirq(&stm32_pcie->pci); + if (ret) + goto err_disable_clk; + + pinctrl_pm_select_default_state(dev); + + return 0; + +err_disable_clk: + stm32_pcie_assert_perst(stm32_pcie); + clk_disable_unprepare(stm32_pcie->clk); + +err_phy_exit: + phy_exit(stm32_pcie->phy); + pinctrl_pm_select_default_state(dev); + + return ret; +} + +static const struct dev_pm_ops stm32_pcie_pm_ops = { + NOIRQ_SYSTEM_SLEEP_PM_OPS(stm32_pcie_suspend_noirq, + stm32_pcie_resume_noirq) +}; + +static const struct dw_pcie_host_ops stm32_pcie_host_ops = { +}; + +static const struct dw_pcie_ops dw_pcie_ops = { + .start_link = stm32_pcie_start_link, + .stop_link = stm32_pcie_stop_link +}; + +static int stm32_add_pcie_port(struct stm32_pcie *stm32_pcie) +{ + struct device *dev = stm32_pcie->pci.dev; + unsigned int wake_irq; + int ret; + + ret = phy_set_mode(stm32_pcie->phy, PHY_MODE_PCIE); + if (ret) + return ret; + + ret = phy_init(stm32_pcie->phy); + if (ret) + return ret; + + ret = regmap_update_bits(stm32_pcie->regmap, SYSCFG_PCIECR, + STM32MP25_PCIECR_TYPE_MASK, + STM32MP25_PCIECR_RC); + if (ret) + goto err_phy_exit; + + stm32_pcie_deassert_perst(stm32_pcie); + + if (stm32_pcie->wake_gpio) { + wake_irq = gpiod_to_irq(stm32_pcie->wake_gpio); + ret = dev_pm_set_dedicated_wake_irq(dev, wake_irq); + if (ret) { + dev_err(dev, "Failed to enable wakeup irq %d\n", ret); + goto err_assert_perst; + } + irq_set_irq_type(wake_irq, IRQ_TYPE_EDGE_FALLING); + } + + return 0; + +err_assert_perst: + stm32_pcie_assert_perst(stm32_pcie); + +err_phy_exit: + phy_exit(stm32_pcie->phy); + + return ret; +} + +static void stm32_remove_pcie_port(struct stm32_pcie *stm32_pcie) +{ + dev_pm_clear_wake_irq(stm32_pcie->pci.dev); + + stm32_pcie_assert_perst(stm32_pcie); + + phy_exit(stm32_pcie->phy); +} + +static int stm32_pcie_parse_port(struct stm32_pcie *stm32_pcie) +{ + struct device *dev = stm32_pcie->pci.dev; + struct device_node *root_port; + + root_port = of_get_next_available_child(dev->of_node, NULL); + + stm32_pcie->phy = devm_of_phy_get(dev, root_port, NULL); + if (IS_ERR(stm32_pcie->phy)) { + of_node_put(root_port); + return dev_err_probe(dev, PTR_ERR(stm32_pcie->phy), + "Failed to get pcie-phy\n"); + } + + stm32_pcie->perst_gpio = devm_fwnode_gpiod_get(dev, of_fwnode_handle(root_port), + "reset", GPIOD_OUT_HIGH, NULL); + if (IS_ERR(stm32_pcie->perst_gpio)) { + if (PTR_ERR(stm32_pcie->perst_gpio) != -ENOENT) { + of_node_put(root_port); + return dev_err_probe(dev, PTR_ERR(stm32_pcie->perst_gpio), + "Failed to get reset GPIO\n"); + } + stm32_pcie->perst_gpio = NULL; + } + + stm32_pcie->wake_gpio = devm_fwnode_gpiod_get(dev, of_fwnode_handle(root_port), + "wake", GPIOD_IN, NULL); + + if (IS_ERR(stm32_pcie->wake_gpio)) { + if (PTR_ERR(stm32_pcie->wake_gpio) != -ENOENT) { + of_node_put(root_port); + return dev_err_probe(dev, PTR_ERR(stm32_pcie->wake_gpio), + "Failed to get wake GPIO\n"); + } + stm32_pcie->wake_gpio = NULL; + } + + of_node_put(root_port); + + return 0; +} + +static int stm32_pcie_probe(struct platform_device *pdev) +{ + struct stm32_pcie *stm32_pcie; + struct device *dev = &pdev->dev; + int ret; + + stm32_pcie = devm_kzalloc(dev, sizeof(*stm32_pcie), GFP_KERNEL); + if (!stm32_pcie) + return -ENOMEM; + + stm32_pcie->pci.dev = dev; + stm32_pcie->pci.ops = &dw_pcie_ops; + stm32_pcie->pci.pp.ops = &stm32_pcie_host_ops; + + stm32_pcie->regmap = syscon_regmap_lookup_by_compatible("st,stm32mp25-syscfg"); + if (IS_ERR(stm32_pcie->regmap)) + return dev_err_probe(dev, PTR_ERR(stm32_pcie->regmap), + "No syscfg specified\n"); + + stm32_pcie->clk = devm_clk_get(dev, NULL); + if (IS_ERR(stm32_pcie->clk)) + return dev_err_probe(dev, PTR_ERR(stm32_pcie->clk), + "Failed to get PCIe clock source\n"); + + stm32_pcie->rst = devm_reset_control_get_exclusive(dev, NULL); + if (IS_ERR(stm32_pcie->rst)) + return dev_err_probe(dev, PTR_ERR(stm32_pcie->rst), + "Failed to get PCIe reset\n"); + + ret = stm32_pcie_parse_port(stm32_pcie); + if (ret) + return ret; + + platform_set_drvdata(pdev, stm32_pcie); + + ret = stm32_add_pcie_port(stm32_pcie); + if (ret) + return ret; + + reset_control_assert(stm32_pcie->rst); + reset_control_deassert(stm32_pcie->rst); + + ret = clk_prepare_enable(stm32_pcie->clk); + if (ret) { + dev_err(dev, "Core clock enable failed %d\n", ret); + goto err_remove_port; + } + + ret = pm_runtime_set_active(dev); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to activate runtime PM\n"); + goto err_disable_clk; + } + + pm_runtime_no_callbacks(dev); + + ret = devm_pm_runtime_enable(dev); + if (ret < 0) { + dev_err_probe(dev, ret, "Failed to enable runtime PM\n"); + goto err_disable_clk; + } + + ret = dw_pcie_host_init(&stm32_pcie->pci.pp); + if (ret) + goto err_disable_clk; + + if (stm32_pcie->wake_gpio) + device_init_wakeup(dev, true); + + return 0; + +err_disable_clk: + clk_disable_unprepare(stm32_pcie->clk); + +err_remove_port: + stm32_remove_pcie_port(stm32_pcie); + + return ret; +} + +static void stm32_pcie_remove(struct platform_device *pdev) +{ + struct stm32_pcie *stm32_pcie = platform_get_drvdata(pdev); + struct dw_pcie_rp *pp = &stm32_pcie->pci.pp; + + if (stm32_pcie->wake_gpio) + device_init_wakeup(&pdev->dev, false); + + dw_pcie_host_deinit(pp); + + clk_disable_unprepare(stm32_pcie->clk); + + stm32_remove_pcie_port(stm32_pcie); + + pm_runtime_put_noidle(&pdev->dev); +} + +static const struct of_device_id stm32_pcie_of_match[] = { + { .compatible = "st,stm32mp25-pcie-rc" }, + {}, +}; + +static struct platform_driver stm32_pcie_driver = { + .probe = stm32_pcie_probe, + .remove = stm32_pcie_remove, + .driver = { + .name = "stm32-pcie", + .of_match_table = stm32_pcie_of_match, + .pm = &stm32_pcie_pm_ops, + .probe_type = PROBE_PREFER_ASYNCHRONOUS, + }, +}; + +module_platform_driver(stm32_pcie_driver); + +MODULE_AUTHOR("Christian Bruel <christian.bruel@foss.st.com>"); +MODULE_DESCRIPTION("STM32MP25 PCIe Controller driver"); +MODULE_LICENSE("GPL"); +MODULE_DEVICE_TABLE(of, stm32_pcie_of_match); diff --git a/drivers/pci/controller/dwc/pcie-stm32.h b/drivers/pci/controller/dwc/pcie-stm32.h new file mode 100644 index 000000000000..09d39f04e469 --- /dev/null +++ b/drivers/pci/controller/dwc/pcie-stm32.h @@ -0,0 +1,16 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * ST PCIe driver definitions for STM32-MP25 SoC + * + * Copyright (C) 2025 STMicroelectronics - All Rights Reserved + * Author: Christian Bruel <christian.bruel@foss.st.com> + */ + +#define to_stm32_pcie(x) dev_get_drvdata((x)->dev) + +#define STM32MP25_PCIECR_TYPE_MASK GENMASK(11, 8) +#define STM32MP25_PCIECR_EP 0 +#define STM32MP25_PCIECR_LTSSM_EN BIT(2) +#define STM32MP25_PCIECR_RC BIT(10) + +#define SYSCFG_PCIECR 0x6000 diff --git a/drivers/pci/controller/dwc/pcie-tegra194.c b/drivers/pci/controller/dwc/pcie-tegra194.c index 4f26086f25da..10e74458e667 100644 --- a/drivers/pci/controller/dwc/pcie-tegra194.c +++ b/drivers/pci/controller/dwc/pcie-tegra194.c @@ -1214,6 +1214,7 @@ static int tegra_pcie_bpmp_set_ctrl_state(struct tegra_pcie_dw *pcie, struct mrq_uphy_response resp; struct tegra_bpmp_message msg; struct mrq_uphy_request req; + int err; /* * Controller-5 doesn't need to have its state set by BPMP-FW in @@ -1236,7 +1237,13 @@ static int tegra_pcie_bpmp_set_ctrl_state(struct tegra_pcie_dw *pcie, msg.rx.data = &resp; msg.rx.size = sizeof(resp); - return tegra_bpmp_transfer(pcie->bpmp, &msg); + err = tegra_bpmp_transfer(pcie->bpmp, &msg); + if (err) + return err; + if (msg.rx.ret) + return -EINVAL; + + return 0; } static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie, @@ -1245,6 +1252,7 @@ static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie, struct mrq_uphy_response resp; struct tegra_bpmp_message msg; struct mrq_uphy_request req; + int err; memset(&req, 0, sizeof(req)); memset(&resp, 0, sizeof(resp)); @@ -1264,13 +1272,19 @@ static int tegra_pcie_bpmp_set_pll_state(struct tegra_pcie_dw *pcie, msg.rx.data = &resp; msg.rx.size = sizeof(resp); - return tegra_bpmp_transfer(pcie->bpmp, &msg); + err = tegra_bpmp_transfer(pcie->bpmp, &msg); + if (err) + return err; + if (msg.rx.ret) + return -EINVAL; + + return 0; } static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie) { struct dw_pcie_rp *pp = &pcie->pci.pp; - struct pci_bus *child, *root_bus = NULL; + struct pci_bus *child, *root_port_bus = NULL; struct pci_dev *pdev; /* @@ -1283,19 +1297,19 @@ static void tegra_pcie_downstream_dev_to_D0(struct tegra_pcie_dw *pcie) */ list_for_each_entry(child, &pp->bridge->bus->children, node) { - /* Bring downstream devices to D0 if they are not already in */ if (child->parent == pp->bridge->bus) { - root_bus = child; + root_port_bus = child; break; } } - if (!root_bus) { - dev_err(pcie->dev, "Failed to find downstream devices\n"); + if (!root_port_bus) { + dev_err(pcie->dev, "Failed to find downstream bus of Root Port\n"); return; } - list_for_each_entry(pdev, &root_bus->devices, bus_list) { + /* Bring downstream devices to D0 if they are not already in */ + list_for_each_entry(pdev, &root_port_bus->devices, bus_list) { if (PCI_SLOT(pdev->devfn) == 0) { if (pci_set_power_state(pdev, PCI_D0)) dev_err(pcie->dev, @@ -1722,9 +1736,9 @@ static void pex_ep_event_pex_rst_assert(struct tegra_pcie_dw *pcie) ret); } - ret = tegra_pcie_bpmp_set_pll_state(pcie, false); + ret = tegra_pcie_bpmp_set_ctrl_state(pcie, false); if (ret) - dev_err(pcie->dev, "Failed to turn off UPHY: %d\n", ret); + dev_err(pcie->dev, "Failed to disable controller: %d\n", ret); pcie->ep_state = EP_STATE_DISABLED; dev_dbg(pcie->dev, "Uninitialization of endpoint is completed\n"); @@ -1941,6 +1955,15 @@ static irqreturn_t tegra_pcie_ep_pex_rst_irq(int irq, void *arg) return IRQ_HANDLED; } +static void tegra_pcie_ep_init(struct dw_pcie_ep *ep) +{ + struct dw_pcie *pci = to_dw_pcie_from_ep(ep); + enum pci_barno bar; + + for (bar = 0; bar < PCI_STD_NUM_BARS; bar++) + dw_pcie_ep_reset_bar(pci, bar); +}; + static int tegra_pcie_ep_raise_intx_irq(struct tegra_pcie_dw *pcie, u16 irq) { /* Tegra194 supports only INTA */ @@ -1955,10 +1978,10 @@ static int tegra_pcie_ep_raise_intx_irq(struct tegra_pcie_dw *pcie, u16 irq) static int tegra_pcie_ep_raise_msi_irq(struct tegra_pcie_dw *pcie, u16 irq) { - if (unlikely(irq > 31)) + if (unlikely(irq > 32)) return -EINVAL; - appl_writel(pcie, BIT(irq), APPL_MSI_CTRL_1); + appl_writel(pcie, BIT(irq - 1), APPL_MSI_CTRL_1); return 0; } @@ -1998,8 +2021,7 @@ static int tegra_pcie_ep_raise_irq(struct dw_pcie_ep *ep, u8 func_no, static const struct pci_epc_features tegra_pcie_epc_features = { .linkup_notifier = true, - .msi_capable = false, - .msix_capable = false, + .msi_capable = true, .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = SZ_1M, .only_64bit = true, }, .bar[BAR_1] = { .type = BAR_RESERVED, }, @@ -2017,6 +2039,7 @@ tegra_pcie_ep_get_features(struct dw_pcie_ep *ep) } static const struct dw_pcie_ep_ops pcie_ep_ops = { + .init = tegra_pcie_ep_init, .raise_irq = tegra_pcie_ep_raise_irq, .get_features = tegra_pcie_ep_get_features, }; diff --git a/drivers/pci/controller/pci-hyperv.c b/drivers/pci/controller/pci-hyperv.c index d2b7e8ea710b..146b43981b27 100644 --- a/drivers/pci/controller/pci-hyperv.c +++ b/drivers/pci/controller/pci-hyperv.c @@ -1680,7 +1680,6 @@ static void hv_int_desc_free(struct hv_pci_dev *hpdev, /** * hv_msi_free() - Free the MSI. * @domain: The interrupt domain pointer - * @info: Extra MSI-related context * @irq: Identifies the IRQ. * * The Hyper-V parent partition and hypervisor are tracking the @@ -1688,8 +1687,7 @@ static void hv_int_desc_free(struct hv_pci_dev *hpdev, * table up to date. This callback sends a message that frees * the IRT entry and related tracking nonsense. */ -static void hv_msi_free(struct irq_domain *domain, struct msi_domain_info *info, - unsigned int irq) +static void hv_msi_free(struct irq_domain *domain, unsigned int irq) { struct hv_pcibus_device *hbus; struct hv_pci_dev *hpdev; @@ -2181,10 +2179,8 @@ static int hv_pcie_domain_alloc(struct irq_domain *d, unsigned int virq, unsigne static void hv_pcie_domain_free(struct irq_domain *d, unsigned int virq, unsigned int nr_irqs) { - struct msi_domain_info *info = d->host_data; - for (int i = 0; i < nr_irqs; i++) - hv_msi_free(d, info, virq + i); + hv_msi_free(d, virq + i); irq_domain_free_irqs_top(d, virq, nr_irqs); } diff --git a/drivers/pci/controller/pci-tegra.c b/drivers/pci/controller/pci-tegra.c index 467ddc701adc..942ddfca3bf6 100644 --- a/drivers/pci/controller/pci-tegra.c +++ b/drivers/pci/controller/pci-tegra.c @@ -14,6 +14,7 @@ */ #include <linux/clk.h> +#include <linux/cleanup.h> #include <linux/debugfs.h> #include <linux/delay.h> #include <linux/export.h> @@ -270,7 +271,7 @@ struct tegra_msi { DECLARE_BITMAP(used, INT_PCI_MSI_NR); struct irq_domain *domain; struct mutex map_lock; - spinlock_t mask_lock; + raw_spinlock_t mask_lock; void *virt; dma_addr_t phys; int irq; @@ -1344,7 +1345,7 @@ static int tegra_pcie_port_get_phys(struct tegra_pcie_port *port) unsigned int i; int err; - port->phys = devm_kcalloc(dev, sizeof(phy), port->lanes, GFP_KERNEL); + port->phys = devm_kcalloc(dev, port->lanes, sizeof(phy), GFP_KERNEL); if (!port->phys) return -ENOMEM; @@ -1581,14 +1582,13 @@ static void tegra_msi_irq_mask(struct irq_data *d) struct tegra_msi *msi = irq_data_get_irq_chip_data(d); struct tegra_pcie *pcie = msi_to_pcie(msi); unsigned int index = d->hwirq / 32; - unsigned long flags; u32 value; - spin_lock_irqsave(&msi->mask_lock, flags); - value = afi_readl(pcie, AFI_MSI_EN_VEC(index)); - value &= ~BIT(d->hwirq % 32); - afi_writel(pcie, value, AFI_MSI_EN_VEC(index)); - spin_unlock_irqrestore(&msi->mask_lock, flags); + scoped_guard(raw_spinlock_irqsave, &msi->mask_lock) { + value = afi_readl(pcie, AFI_MSI_EN_VEC(index)); + value &= ~BIT(d->hwirq % 32); + afi_writel(pcie, value, AFI_MSI_EN_VEC(index)); + } } static void tegra_msi_irq_unmask(struct irq_data *d) @@ -1596,14 +1596,13 @@ static void tegra_msi_irq_unmask(struct irq_data *d) struct tegra_msi *msi = irq_data_get_irq_chip_data(d); struct tegra_pcie *pcie = msi_to_pcie(msi); unsigned int index = d->hwirq / 32; - unsigned long flags; u32 value; - spin_lock_irqsave(&msi->mask_lock, flags); - value = afi_readl(pcie, AFI_MSI_EN_VEC(index)); - value |= BIT(d->hwirq % 32); - afi_writel(pcie, value, AFI_MSI_EN_VEC(index)); - spin_unlock_irqrestore(&msi->mask_lock, flags); + scoped_guard(raw_spinlock_irqsave, &msi->mask_lock) { + value = afi_readl(pcie, AFI_MSI_EN_VEC(index)); + value |= BIT(d->hwirq % 32); + afi_writel(pcie, value, AFI_MSI_EN_VEC(index)); + } } static void tegra_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) @@ -1711,7 +1710,7 @@ static int tegra_pcie_msi_setup(struct tegra_pcie *pcie) int err; mutex_init(&msi->map_lock); - spin_lock_init(&msi->mask_lock); + raw_spin_lock_init(&msi->mask_lock); if (IS_ENABLED(CONFIG_PCI_MSI)) { err = tegra_allocate_domains(msi); diff --git a/drivers/pci/controller/pci-xgene-msi.c b/drivers/pci/controller/pci-xgene-msi.c index 0a37a3f1809c..654639bccd10 100644 --- a/drivers/pci/controller/pci-xgene-msi.c +++ b/drivers/pci/controller/pci-xgene-msi.c @@ -311,7 +311,7 @@ static int xgene_msi_handler_setup(struct platform_device *pdev) msi_val = xgene_msi_int_read(xgene_msi, i); if (msi_val) { dev_err(&pdev->dev, "Failed to clear spurious IRQ\n"); - return EINVAL; + return -EINVAL; } irq = platform_get_irq(pdev, i); diff --git a/drivers/pci/controller/pcie-mediatek-gen3.c b/drivers/pci/controller/pcie-mediatek-gen3.c index 97147f43e41c..75ddb8bee168 100644 --- a/drivers/pci/controller/pcie-mediatek-gen3.c +++ b/drivers/pci/controller/pcie-mediatek-gen3.c @@ -102,6 +102,9 @@ #define PCIE_MSI_SET_ADDR_HI_BASE 0xc80 #define PCIE_MSI_SET_ADDR_HI_OFFSET 0x04 +#define PCIE_RESOURCE_CTRL_REG 0xd2c +#define PCIE_RSRC_SYS_CLK_RDY_TIME_MASK GENMASK(7, 0) + #define PCIE_ICMD_PM_REG 0x198 #define PCIE_TURN_OFF_LINK BIT(4) @@ -149,6 +152,7 @@ enum mtk_gen3_pcie_flags { * struct mtk_gen3_pcie_pdata - differentiate between host generations * @power_up: pcie power_up callback * @phy_resets: phy reset lines SoC data. + * @sys_clk_rdy_time_us: System clock ready time override (microseconds) * @flags: pcie device flags. */ struct mtk_gen3_pcie_pdata { @@ -157,6 +161,7 @@ struct mtk_gen3_pcie_pdata { const char *id[MAX_NUM_PHY_RESETS]; int num_resets; } phy_resets; + u8 sys_clk_rdy_time_us; u32 flags; }; @@ -435,6 +440,14 @@ static int mtk_pcie_startup_port(struct mtk_gen3_pcie *pcie) writel_relaxed(val, pcie->base + PCIE_CONF_LINK2_CTL_STS); } + /* If parameter is present, adjust SYS_CLK_RDY_TIME to avoid glitching */ + if (pcie->soc->sys_clk_rdy_time_us) { + val = readl_relaxed(pcie->base + PCIE_RESOURCE_CTRL_REG); + FIELD_MODIFY(PCIE_RSRC_SYS_CLK_RDY_TIME_MASK, &val, + pcie->soc->sys_clk_rdy_time_us); + writel_relaxed(val, pcie->base + PCIE_RESOURCE_CTRL_REG); + } + /* Set class code */ val = readl_relaxed(pcie->base + PCIE_PCI_IDS_1); val &= ~GENMASK(31, 8); @@ -1327,6 +1340,15 @@ static const struct mtk_gen3_pcie_pdata mtk_pcie_soc_mt8192 = { }, }; +static const struct mtk_gen3_pcie_pdata mtk_pcie_soc_mt8196 = { + .power_up = mtk_pcie_power_up, + .phy_resets = { + .id[0] = "phy", + .num_resets = 1, + }, + .sys_clk_rdy_time_us = 10, +}; + static const struct mtk_gen3_pcie_pdata mtk_pcie_soc_en7581 = { .power_up = mtk_pcie_en7581_power_up, .phy_resets = { @@ -1341,6 +1363,7 @@ static const struct mtk_gen3_pcie_pdata mtk_pcie_soc_en7581 = { static const struct of_device_id mtk_pcie_of_match[] = { { .compatible = "airoha,en7581-pcie", .data = &mtk_pcie_soc_en7581 }, { .compatible = "mediatek,mt8192-pcie", .data = &mtk_pcie_soc_mt8192 }, + { .compatible = "mediatek,mt8196-pcie", .data = &mtk_pcie_soc_mt8196 }, {}, }; MODULE_DEVICE_TABLE(of, mtk_pcie_of_match); diff --git a/drivers/pci/controller/pcie-rcar-ep.c b/drivers/pci/controller/pcie-rcar-ep.c index a8a966844cf3..657875ef4657 100644 --- a/drivers/pci/controller/pcie-rcar-ep.c +++ b/drivers/pci/controller/pcie-rcar-ep.c @@ -436,9 +436,7 @@ static void rcar_pcie_ep_stop(struct pci_epc *epc) } static const struct pci_epc_features rcar_pcie_epc_features = { - .linkup_notifier = false, .msi_capable = true, - .msix_capable = false, /* use 64-bit BARs so mark BAR[1,3,5] as reserved */ .bar[BAR_0] = { .type = BAR_FIXED, .fixed_size = 128, .only_64bit = true, }, diff --git a/drivers/pci/controller/pcie-rcar-host.c b/drivers/pci/controller/pcie-rcar-host.c index fe288fd770c4..213028052aa5 100644 --- a/drivers/pci/controller/pcie-rcar-host.c +++ b/drivers/pci/controller/pcie-rcar-host.c @@ -12,6 +12,7 @@ */ #include <linux/bitops.h> +#include <linux/cleanup.h> #include <linux/clk.h> #include <linux/clk-provider.h> #include <linux/delay.h> @@ -38,7 +39,7 @@ struct rcar_msi { DECLARE_BITMAP(used, INT_PCI_MSI_NR); struct irq_domain *domain; struct mutex map_lock; - spinlock_t mask_lock; + raw_spinlock_t mask_lock; int irq1; int irq2; }; @@ -52,20 +53,13 @@ struct rcar_pcie_host { int (*phy_init_fn)(struct rcar_pcie_host *host); }; -static DEFINE_SPINLOCK(pmsr_lock); - static int rcar_pcie_wakeup(struct device *pcie_dev, void __iomem *pcie_base) { - unsigned long flags; u32 pmsr, val; int ret = 0; - spin_lock_irqsave(&pmsr_lock, flags); - - if (!pcie_base || pm_runtime_suspended(pcie_dev)) { - ret = -EINVAL; - goto unlock_exit; - } + if (!pcie_base || pm_runtime_suspended(pcie_dev)) + return -EINVAL; pmsr = readl(pcie_base + PMSR); @@ -87,8 +81,6 @@ static int rcar_pcie_wakeup(struct device *pcie_dev, void __iomem *pcie_base) writel(L1FAEG | PMEL1RX, pcie_base + PMSR); } -unlock_exit: - spin_unlock_irqrestore(&pmsr_lock, flags); return ret; } @@ -584,7 +576,7 @@ static irqreturn_t rcar_pcie_msi_irq(int irq, void *data) unsigned int index = find_first_bit(®, 32); int ret; - ret = generic_handle_domain_irq(msi->domain->parent, index); + ret = generic_handle_domain_irq(msi->domain, index); if (ret) { /* Unknown MSI, just clear it */ dev_dbg(dev, "unexpected MSI\n"); @@ -611,28 +603,26 @@ static void rcar_msi_irq_mask(struct irq_data *d) { struct rcar_msi *msi = irq_data_get_irq_chip_data(d); struct rcar_pcie *pcie = &msi_to_host(msi)->pcie; - unsigned long flags; u32 value; - spin_lock_irqsave(&msi->mask_lock, flags); - value = rcar_pci_read_reg(pcie, PCIEMSIIER); - value &= ~BIT(d->hwirq); - rcar_pci_write_reg(pcie, value, PCIEMSIIER); - spin_unlock_irqrestore(&msi->mask_lock, flags); + scoped_guard(raw_spinlock_irqsave, &msi->mask_lock) { + value = rcar_pci_read_reg(pcie, PCIEMSIIER); + value &= ~BIT(d->hwirq); + rcar_pci_write_reg(pcie, value, PCIEMSIIER); + } } static void rcar_msi_irq_unmask(struct irq_data *d) { struct rcar_msi *msi = irq_data_get_irq_chip_data(d); struct rcar_pcie *pcie = &msi_to_host(msi)->pcie; - unsigned long flags; u32 value; - spin_lock_irqsave(&msi->mask_lock, flags); - value = rcar_pci_read_reg(pcie, PCIEMSIIER); - value |= BIT(d->hwirq); - rcar_pci_write_reg(pcie, value, PCIEMSIIER); - spin_unlock_irqrestore(&msi->mask_lock, flags); + scoped_guard(raw_spinlock_irqsave, &msi->mask_lock) { + value = rcar_pci_read_reg(pcie, PCIEMSIIER); + value |= BIT(d->hwirq); + rcar_pci_write_reg(pcie, value, PCIEMSIIER); + } } static void rcar_compose_msi_msg(struct irq_data *data, struct msi_msg *msg) @@ -745,7 +735,7 @@ static int rcar_pcie_enable_msi(struct rcar_pcie_host *host) int err; mutex_init(&msi->map_lock); - spin_lock_init(&msi->mask_lock); + raw_spin_lock_init(&msi->mask_lock); err = of_address_to_resource(dev->of_node, 0, &res); if (err) diff --git a/drivers/pci/controller/pcie-rockchip-ep.c b/drivers/pci/controller/pcie-rockchip-ep.c index 300cd85fa035..799461335762 100644 --- a/drivers/pci/controller/pcie-rockchip-ep.c +++ b/drivers/pci/controller/pcie-rockchip-ep.c @@ -694,7 +694,6 @@ static int rockchip_pcie_ep_setup_irq(struct pci_epc *epc) static const struct pci_epc_features rockchip_pcie_epc_features = { .linkup_notifier = true, .msi_capable = true, - .msix_capable = false, .intx_capable = true, .align = ROCKCHIP_PCIE_AT_SIZE_ALIGN, }; diff --git a/drivers/pci/controller/pcie-xilinx-nwl.c b/drivers/pci/controller/pcie-xilinx-nwl.c index 05b8c205493c..7db2c96c6cec 100644 --- a/drivers/pci/controller/pcie-xilinx-nwl.c +++ b/drivers/pci/controller/pcie-xilinx-nwl.c @@ -718,9 +718,10 @@ static int nwl_pcie_bridge_init(struct nwl_pcie *pcie) nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | E_ECAM_CR_ENABLE, E_ECAM_CONTROL); - nwl_bridge_writel(pcie, nwl_bridge_readl(pcie, E_ECAM_CONTROL) | - (NWL_ECAM_MAX_SIZE << E_ECAM_SIZE_SHIFT), - E_ECAM_CONTROL); + ecam_val = nwl_bridge_readl(pcie, E_ECAM_CONTROL); + ecam_val &= ~E_ECAM_SIZE_LOC; + ecam_val |= NWL_ECAM_MAX_SIZE << E_ECAM_SIZE_SHIFT; + nwl_bridge_writel(pcie, ecam_val, E_ECAM_CONTROL); nwl_bridge_writel(pcie, lower_32_bits(pcie->phys_ecam_base), E_ECAM_BASE_LO); diff --git a/drivers/pci/controller/plda/pcie-plda-host.c b/drivers/pci/controller/plda/pcie-plda-host.c index 8e2db2e5b64b..3c2f68383010 100644 --- a/drivers/pci/controller/plda/pcie-plda-host.c +++ b/drivers/pci/controller/plda/pcie-plda-host.c @@ -599,8 +599,7 @@ int plda_pcie_host_init(struct plda_pcie_rp *port, struct pci_ops *ops, bridge = devm_pci_alloc_host_bridge(dev, 0); if (!bridge) - return dev_err_probe(dev, -ENOMEM, - "failed to alloc bridge\n"); + return -ENOMEM; if (port->host_ops && port->host_ops->host_init) { ret = port->host_ops->host_init(port); diff --git a/drivers/pci/endpoint/functions/pci-epf-test.c b/drivers/pci/endpoint/functions/pci-epf-test.c index e091193bd8a8..31617772ad51 100644 --- a/drivers/pci/endpoint/functions/pci-epf-test.c +++ b/drivers/pci/endpoint/functions/pci-epf-test.c @@ -301,15 +301,20 @@ static void pci_epf_test_clean_dma_chan(struct pci_epf_test *epf_test) if (!epf_test->dma_supported) return; - dma_release_channel(epf_test->dma_chan_tx); - if (epf_test->dma_chan_tx == epf_test->dma_chan_rx) { + if (epf_test->dma_chan_tx) { + dma_release_channel(epf_test->dma_chan_tx); + if (epf_test->dma_chan_tx == epf_test->dma_chan_rx) { + epf_test->dma_chan_tx = NULL; + epf_test->dma_chan_rx = NULL; + return; + } epf_test->dma_chan_tx = NULL; - epf_test->dma_chan_rx = NULL; - return; } - dma_release_channel(epf_test->dma_chan_rx); - epf_test->dma_chan_rx = NULL; + if (epf_test->dma_chan_rx) { + dma_release_channel(epf_test->dma_chan_rx); + epf_test->dma_chan_rx = NULL; + } } static void pci_epf_test_print_rate(struct pci_epf_test *epf_test, @@ -772,12 +777,24 @@ static void pci_epf_test_disable_doorbell(struct pci_epf_test *epf_test, u32 status = le32_to_cpu(reg->status); struct pci_epf *epf = epf_test->epf; struct pci_epc *epc = epf->epc; + int ret; if (bar < BAR_0) goto set_status_err; pci_epf_test_doorbell_cleanup(epf_test); - pci_epc_clear_bar(epc, epf->func_no, epf->vfunc_no, &epf_test->db_bar); + + /* + * The doorbell feature temporarily overrides the inbound translation + * to point to the address stored in epf_test->db_bar.phys_addr, i.e., + * it calls set_bar() twice without ever calling clear_bar(), as + * calling clear_bar() would clear the BAR's PCI address assigned by + * the host. Thus, when disabling the doorbell, restore the inbound + * translation to point to the memory allocated for the BAR. + */ + ret = pci_epc_set_bar(epc, epf->func_no, epf->vfunc_no, &epf->bar[bar]); + if (ret) + goto set_status_err; status |= STATUS_DOORBELL_DISABLE_SUCCESS; reg->status = cpu_to_le32(status); @@ -1050,7 +1067,12 @@ static int pci_epf_test_alloc_space(struct pci_epf *epf) if (bar == test_reg_bar) continue; - base = pci_epf_alloc_space(epf, bar_size[bar], bar, + if (epc_features->bar[bar].type == BAR_FIXED) + test_reg_size = epc_features->bar[bar].fixed_size; + else + test_reg_size = bar_size[bar]; + + base = pci_epf_alloc_space(epf, test_reg_size, bar, epc_features, PRIMARY_INTERFACE); if (!base) dev_err(dev, "Failed to allocate space for BAR%d\n", diff --git a/drivers/pci/endpoint/pci-ep-msi.c b/drivers/pci/endpoint/pci-ep-msi.c index 9ca89cbfec15..1b58357b905f 100644 --- a/drivers/pci/endpoint/pci-ep-msi.c +++ b/drivers/pci/endpoint/pci-ep-msi.c @@ -24,7 +24,7 @@ static void pci_epf_write_msi_msg(struct msi_desc *desc, struct msi_msg *msg) struct pci_epf *epf; epc = pci_epc_get(dev_name(msi_desc_to_dev(desc))); - if (!epc) + if (IS_ERR(epc)) return; epf = list_first_entry_or_null(&epc->pci_epf, struct pci_epf, list); diff --git a/drivers/pci/hotplug/cpqphp_pci.c b/drivers/pci/hotplug/cpqphp_pci.c index ef7534a3ca40..88929360fe77 100644 --- a/drivers/pci/hotplug/cpqphp_pci.c +++ b/drivers/pci/hotplug/cpqphp_pci.c @@ -1302,7 +1302,7 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st dbg("found io_node(base, length) = %x, %x\n", io_node->base, io_node->length); - dbg("populated slot =%d \n", populated_slot); + dbg("populated slot = %d\n", populated_slot); if (!populated_slot) { io_node->next = ctrl->io_head; ctrl->io_head = io_node; @@ -1325,7 +1325,7 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st dbg("found mem_node(base, length) = %x, %x\n", mem_node->base, mem_node->length); - dbg("populated slot =%d \n", populated_slot); + dbg("populated slot = %d\n", populated_slot); if (!populated_slot) { mem_node->next = ctrl->mem_head; ctrl->mem_head = mem_node; @@ -1349,7 +1349,7 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st p_mem_node->length = pre_mem_length << 16; dbg("found p_mem_node(base, length) = %x, %x\n", p_mem_node->base, p_mem_node->length); - dbg("populated slot =%d \n", populated_slot); + dbg("populated slot = %d\n", populated_slot); if (!populated_slot) { p_mem_node->next = ctrl->p_mem_head; @@ -1373,7 +1373,7 @@ int cpqhp_find_available_resources(struct controller *ctrl, void __iomem *rom_st bus_node->length = max_bus - secondary_bus + 1; dbg("found bus_node(base, length) = %x, %x\n", bus_node->base, bus_node->length); - dbg("populated slot =%d \n", populated_slot); + dbg("populated slot = %d\n", populated_slot); if (!populated_slot) { bus_node->next = ctrl->bus_head; ctrl->bus_head = bus_node; diff --git a/drivers/pci/hotplug/ibmphp_hpc.c b/drivers/pci/hotplug/ibmphp_hpc.c index a5720d12e573..2324167656a6 100644 --- a/drivers/pci/hotplug/ibmphp_hpc.c +++ b/drivers/pci/hotplug/ibmphp_hpc.c @@ -124,7 +124,7 @@ static u8 i2c_ctrl_read(struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 i unsigned long ultemp; unsigned long data; // actual data HILO format - debug_polling("%s - Entry WPGBbar[%p] index[%x] \n", __func__, WPGBbar, index); + debug_polling("%s - Entry WPGBbar[%p] index[%x]\n", __func__, WPGBbar, index); //-------------------------------------------------------------------- // READ - step 1 @@ -147,7 +147,7 @@ static u8 i2c_ctrl_read(struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 i ultemp = ultemp << 8; data |= ultemp; } else { - err("this controller type is not supported \n"); + err("this controller type is not supported\n"); return HPC_ERROR; } @@ -258,7 +258,7 @@ static u8 i2c_ctrl_write(struct controller *ctlr_ptr, void __iomem *WPGBbar, u8 ultemp = ultemp << 8; data |= ultemp; } else { - err("this controller type is not supported \n"); + err("this controller type is not supported\n"); return HPC_ERROR; } diff --git a/drivers/pci/iov.c b/drivers/pci/iov.c index ac4375954c94..77dee43b7858 100644 --- a/drivers/pci/iov.c +++ b/drivers/pci/iov.c @@ -629,15 +629,18 @@ static int sriov_add_vfs(struct pci_dev *dev, u16 num_vfs) if (dev->no_vf_scan) return 0; + pci_lock_rescan_remove(); for (i = 0; i < num_vfs; i++) { rc = pci_iov_add_virtfn(dev, i); if (rc) goto failed; } + pci_unlock_rescan_remove(); return 0; failed: while (i--) pci_iov_remove_virtfn(dev, i); + pci_unlock_rescan_remove(); return rc; } @@ -762,8 +765,10 @@ static void sriov_del_vfs(struct pci_dev *dev) struct pci_sriov *iov = dev->sriov; int i; + pci_lock_rescan_remove(); for (i = 0; i < iov->num_VFs; i++) pci_iov_remove_virtfn(dev, i); + pci_unlock_rescan_remove(); } static void sriov_disable(struct pci_dev *dev) diff --git a/drivers/pci/of_property.c b/drivers/pci/of_property.c index 506fcd507113..7aae46f333d9 100644 --- a/drivers/pci/of_property.c +++ b/drivers/pci/of_property.c @@ -279,13 +279,21 @@ static int of_pci_prop_intr_map(struct pci_dev *pdev, struct of_changeset *ocs, mapp++; *mapp = out_irq[i].np->phandle; mapp++; - if (addr_sz[i]) { - ret = of_property_read_u32_array(out_irq[i].np, - "reg", mapp, - addr_sz[i]); - if (ret) - goto failed; - } + + /* + * A device address does not affect the device <-> + * interrupt-controller HW connection for all + * modern interrupt controllers; moreover, the + * kernel (i.e., of_irq_parse_raw()) ignores the + * values in the parent unit address cells while + * parsing the interrupt-map property because they + * are irrelevant for interrupt mapping in modern + * systems. + * + * Leave the parent unit address initialized to 0 -- + * just take into account the #address-cells size + * to build the property properly. + */ mapp += addr_sz[i]; memcpy(mapp, out_irq[i].args, out_irq[i].args_count * sizeof(u32)); diff --git a/drivers/pci/p2pdma.c b/drivers/pci/p2pdma.c index da5657a02007..78e108e47254 100644 --- a/drivers/pci/p2pdma.c +++ b/drivers/pci/p2pdma.c @@ -360,7 +360,7 @@ int pci_p2pdma_add_resource(struct pci_dev *pdev, int bar, size_t size, pages_free: devm_memunmap_pages(&pdev->dev, pgmap); pgmap_free: - devm_kfree(&pdev->dev, pgmap); + devm_kfree(&pdev->dev, p2p_pgmap); return error; } EXPORT_SYMBOL_GPL(pci_p2pdma_add_resource); @@ -738,7 +738,7 @@ EXPORT_SYMBOL_GPL(pci_p2pdma_distance_many); * pci_has_p2pmem - check if a given PCI device has published any p2pmem * @pdev: PCI device to check */ -bool pci_has_p2pmem(struct pci_dev *pdev) +static bool pci_has_p2pmem(struct pci_dev *pdev) { struct pci_p2pdma *p2pdma; bool res; @@ -750,7 +750,6 @@ bool pci_has_p2pmem(struct pci_dev *pdev) return res; } -EXPORT_SYMBOL_GPL(pci_has_p2pmem); /** * pci_p2pmem_find_many - find a peer-to-peer DMA memory device compatible with diff --git a/drivers/pci/pci-acpi.c b/drivers/pci/pci-acpi.c index ddb25960ea47..9369377725fa 100644 --- a/drivers/pci/pci-acpi.c +++ b/drivers/pci/pci-acpi.c @@ -122,6 +122,8 @@ phys_addr_t acpi_pci_root_get_mcfg_addr(acpi_handle handle) bool pci_acpi_preserve_config(struct pci_host_bridge *host_bridge) { + bool ret = false; + if (ACPI_HANDLE(&host_bridge->dev)) { union acpi_object *obj; @@ -135,11 +137,11 @@ bool pci_acpi_preserve_config(struct pci_host_bridge *host_bridge) 1, DSM_PCI_PRESERVE_BOOT_CONFIG, NULL, ACPI_TYPE_INTEGER); if (obj && obj->integer.value == 0) - return true; + ret = true; ACPI_FREE(obj); } - return false; + return ret; } /* _HPX PCI Setting Record (Type 0); same as _HPP */ diff --git a/drivers/pci/pci-driver.c b/drivers/pci/pci-driver.c index 63665240ae87..302d61783f6c 100644 --- a/drivers/pci/pci-driver.c +++ b/drivers/pci/pci-driver.c @@ -1582,7 +1582,7 @@ static int pci_uevent(const struct device *dev, struct kobj_uevent_env *env) return 0; } -#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH) +#if defined(CONFIG_PCIEAER) || defined(CONFIG_EEH) || defined(CONFIG_S390) /** * pci_uevent_ers - emit a uevent during recovery path of PCI device * @pdev: PCI device undergoing error recovery @@ -1596,6 +1596,7 @@ void pci_uevent_ers(struct pci_dev *pdev, enum pci_ers_result err_type) switch (err_type) { case PCI_ERS_RESULT_NONE: case PCI_ERS_RESULT_CAN_RECOVER: + case PCI_ERS_RESULT_NEED_RESET: envp[idx++] = "ERROR_EVENT=BEGIN_RECOVERY"; envp[idx++] = "DEVICE_ONLINE=0"; break; diff --git a/drivers/pci/pci-sysfs.c b/drivers/pci/pci-sysfs.c index 5eea14c1f7f5..af74cf02bb90 100644 --- a/drivers/pci/pci-sysfs.c +++ b/drivers/pci/pci-sysfs.c @@ -30,6 +30,7 @@ #include <linux/msi.h> #include <linux/of.h> #include <linux/aperture.h> +#include <linux/unaligned.h> #include "pci.h" #ifndef ARCH_PCI_DEV_GROUPS @@ -177,6 +178,13 @@ static ssize_t resource_show(struct device *dev, struct device_attribute *attr, for (i = 0; i < max; i++) { struct resource *res = &pci_dev->resource[i]; + struct resource zerores = {}; + + /* For backwards compatibility */ + if (i >= PCI_BRIDGE_RESOURCES && i <= PCI_BRIDGE_RESOURCE_END && + res->flags & (IORESOURCE_UNSET | IORESOURCE_DISABLED)) + res = &zerores; + pci_resource_to_user(pci_dev, i, res, &start, &end); len += sysfs_emit_at(buf, len, "0x%016llx 0x%016llx 0x%016llx\n", (unsigned long long)start, @@ -201,8 +209,14 @@ static ssize_t max_link_width_show(struct device *dev, struct device_attribute *attr, char *buf) { struct pci_dev *pdev = to_pci_dev(dev); + ssize_t ret; - return sysfs_emit(buf, "%u\n", pcie_get_width_cap(pdev)); + /* We read PCI_EXP_LNKCAP, so we need the device to be accessible. */ + pci_config_pm_runtime_get(pdev); + ret = sysfs_emit(buf, "%u\n", pcie_get_width_cap(pdev)); + pci_config_pm_runtime_put(pdev); + + return ret; } static DEVICE_ATTR_RO(max_link_width); @@ -214,7 +228,10 @@ static ssize_t current_link_speed_show(struct device *dev, int err; enum pci_bus_speed speed; + pci_config_pm_runtime_get(pci_dev); err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat); + pci_config_pm_runtime_put(pci_dev); + if (err) return -EINVAL; @@ -231,7 +248,10 @@ static ssize_t current_link_width_show(struct device *dev, u16 linkstat; int err; + pci_config_pm_runtime_get(pci_dev); err = pcie_capability_read_word(pci_dev, PCI_EXP_LNKSTA, &linkstat); + pci_config_pm_runtime_put(pci_dev); + if (err) return -EINVAL; @@ -247,7 +267,10 @@ static ssize_t secondary_bus_number_show(struct device *dev, u8 sec_bus; int err; + pci_config_pm_runtime_get(pci_dev); err = pci_read_config_byte(pci_dev, PCI_SECONDARY_BUS, &sec_bus); + pci_config_pm_runtime_put(pci_dev); + if (err) return -EINVAL; @@ -263,7 +286,10 @@ static ssize_t subordinate_bus_number_show(struct device *dev, u8 sub_bus; int err; + pci_config_pm_runtime_get(pci_dev); err = pci_read_config_byte(pci_dev, PCI_SUBORDINATE_BUS, &sub_bus); + pci_config_pm_runtime_put(pci_dev); + if (err) return -EINVAL; @@ -694,6 +720,22 @@ static ssize_t boot_vga_show(struct device *dev, struct device_attribute *attr, } static DEVICE_ATTR_RO(boot_vga); +static ssize_t serial_number_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + struct pci_dev *pci_dev = to_pci_dev(dev); + u64 dsn; + u8 bytes[8]; + + dsn = pci_get_dsn(pci_dev); + if (!dsn) + return -EIO; + + put_unaligned_be64(dsn, bytes); + return sysfs_emit(buf, "%8phD\n", bytes); +} +static DEVICE_ATTR_ADMIN_RO(serial_number); + static ssize_t pci_read_config(struct file *filp, struct kobject *kobj, const struct bin_attribute *bin_attr, char *buf, loff_t off, size_t count) @@ -1555,13 +1597,19 @@ static ssize_t __resource_resize_store(struct device *dev, int n, const char *buf, size_t count) { struct pci_dev *pdev = to_pci_dev(dev); - unsigned long size, flags; + struct pci_bus *bus = pdev->bus; + struct resource *b_win, *res; + unsigned long size; int ret, i; u16 cmd; if (kstrtoul(buf, 0, &size) < 0) return -EINVAL; + b_win = pbus_select_window(bus, pci_resource_n(pdev, n)); + if (!b_win) + return -EINVAL; + device_lock(dev); if (dev->driver || pci_num_vf(pdev)) { ret = -EBUSY; @@ -1581,19 +1629,19 @@ static ssize_t __resource_resize_store(struct device *dev, int n, pci_write_config_word(pdev, PCI_COMMAND, cmd & ~PCI_COMMAND_MEMORY); - flags = pci_resource_flags(pdev, n); - pci_remove_resource_files(pdev); - for (i = 0; i < PCI_BRIDGE_RESOURCES; i++) { - if (pci_resource_len(pdev, i) && - pci_resource_flags(pdev, i) == flags) + pci_dev_for_each_resource(pdev, res, i) { + if (i >= PCI_BRIDGE_RESOURCES) + break; + + if (b_win == pbus_select_window(bus, res)) pci_release_resource(pdev, i); } ret = pci_resize_resource(pdev, n, size); - pci_assign_unassigned_bus_resources(pdev->bus); + pci_assign_unassigned_bus_resources(bus); if (pci_create_resource_files(pdev)) pci_warn(pdev, "Failed to recreate resource files after BAR resizing\n"); @@ -1698,6 +1746,7 @@ late_initcall(pci_sysfs_init); static struct attribute *pci_dev_dev_attrs[] = { &dev_attr_boot_vga.attr, + &dev_attr_serial_number.attr, NULL, }; @@ -1710,6 +1759,9 @@ static umode_t pci_dev_attrs_are_visible(struct kobject *kobj, if (a == &dev_attr_boot_vga.attr && pci_is_vga(pdev)) return a->mode; + if (a == &dev_attr_serial_number.attr && pci_get_dsn(pdev)) + return a->mode; + return 0; } diff --git a/drivers/pci/pci.c b/drivers/pci/pci.c index 005b92e6585e..b14dd064006c 100644 --- a/drivers/pci/pci.c +++ b/drivers/pci/pci.c @@ -423,36 +423,10 @@ found: return 1; } -static u8 __pci_find_next_cap_ttl(struct pci_bus *bus, unsigned int devfn, - u8 pos, int cap, int *ttl) -{ - u8 id; - u16 ent; - - pci_bus_read_config_byte(bus, devfn, pos, &pos); - - while ((*ttl)--) { - if (pos < 0x40) - break; - pos &= ~3; - pci_bus_read_config_word(bus, devfn, pos, &ent); - - id = ent & 0xff; - if (id == 0xff) - break; - if (id == cap) - return pos; - pos = (ent >> 8); - } - return 0; -} - static u8 __pci_find_next_cap(struct pci_bus *bus, unsigned int devfn, u8 pos, int cap) { - int ttl = PCI_FIND_CAP_TTL; - - return __pci_find_next_cap_ttl(bus, devfn, pos, cap, &ttl); + return PCI_FIND_NEXT_CAP(pci_bus_read_config, pos, cap, bus, devfn); } u8 pci_find_next_capability(struct pci_dev *dev, u8 pos, int cap) @@ -553,42 +527,11 @@ EXPORT_SYMBOL(pci_bus_find_capability); */ u16 pci_find_next_ext_capability(struct pci_dev *dev, u16 start, int cap) { - u32 header; - int ttl; - u16 pos = PCI_CFG_SPACE_SIZE; - - /* minimum 8 bytes per capability */ - ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; - if (dev->cfg_size <= PCI_CFG_SPACE_SIZE) return 0; - if (start) - pos = start; - - if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL) - return 0; - - /* - * If we have no capabilities, this is indicated by cap ID, - * cap version and next pointer all being 0. - */ - if (header == 0) - return 0; - - while (ttl-- > 0) { - if (PCI_EXT_CAP_ID(header) == cap && pos != start) - return pos; - - pos = PCI_EXT_CAP_NEXT(header); - if (pos < PCI_CFG_SPACE_SIZE) - break; - - if (pci_read_config_dword(dev, pos, &header) != PCIBIOS_SUCCESSFUL) - break; - } - - return 0; + return PCI_FIND_NEXT_EXT_CAP(pci_bus_read_config, start, cap, + dev->bus, dev->devfn); } EXPORT_SYMBOL_GPL(pci_find_next_ext_capability); @@ -648,7 +591,7 @@ EXPORT_SYMBOL_GPL(pci_get_dsn); static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap) { - int rc, ttl = PCI_FIND_CAP_TTL; + int rc; u8 cap, mask; if (ht_cap == HT_CAPTYPE_SLAVE || ht_cap == HT_CAPTYPE_HOST) @@ -656,8 +599,8 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap) else mask = HT_5BIT_CAP_MASK; - pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn, pos, - PCI_CAP_ID_HT, &ttl); + pos = PCI_FIND_NEXT_CAP(pci_bus_read_config, pos, + PCI_CAP_ID_HT, dev->bus, dev->devfn); while (pos) { rc = pci_read_config_byte(dev, pos + 3, &cap); if (rc != PCIBIOS_SUCCESSFUL) @@ -666,9 +609,10 @@ static u8 __pci_find_next_ht_cap(struct pci_dev *dev, u8 pos, int ht_cap) if ((cap & mask) == ht_cap) return pos; - pos = __pci_find_next_cap_ttl(dev->bus, dev->devfn, - pos + PCI_CAP_LIST_NEXT, - PCI_CAP_ID_HT, &ttl); + pos = PCI_FIND_NEXT_CAP(pci_bus_read_config, + pos + PCI_CAP_LIST_NEXT, + PCI_CAP_ID_HT, dev->bus, + dev->devfn); } return 0; @@ -1374,6 +1318,11 @@ int pci_power_up(struct pci_dev *dev) return -EIO; } + if (pci_dev_is_disconnected(dev)) { + dev->current_state = PCI_D3cold; + return -EIO; + } + pci_read_config_word(dev, dev->pm_cap + PCI_PM_CTRL, &pmcsr); if (PCI_POSSIBLE_ERROR(pmcsr)) { pci_err(dev, "Unable to change power state from %s to D0, device inaccessible\n", diff --git a/drivers/pci/pci.h b/drivers/pci/pci.h index 34f65d69662e..4492b809094b 100644 --- a/drivers/pci/pci.h +++ b/drivers/pci/pci.h @@ -2,12 +2,15 @@ #ifndef DRIVERS_PCI_H #define DRIVERS_PCI_H +#include <linux/align.h> +#include <linux/bitfield.h> #include <linux/pci.h> struct pcie_tlp_log; /* Number of possible devfns: 0.0 to 1f.7 inclusive */ #define MAX_NR_DEVFNS 256 +#define PCI_MAX_NR_DEVS 32 #define MAX_NR_LANES 16 @@ -81,13 +84,102 @@ struct pcie_tlp_log; #define PCIE_MSG_CODE_DEASSERT_INTC 0x26 #define PCIE_MSG_CODE_DEASSERT_INTD 0x27 +#define PCI_BUS_BRIDGE_IO_WINDOW 0 +#define PCI_BUS_BRIDGE_MEM_WINDOW 1 +#define PCI_BUS_BRIDGE_PREF_MEM_WINDOW 2 + extern const unsigned char pcie_link_speed[]; extern bool pci_early_dump; +extern struct mutex pci_rescan_remove_lock; + bool pcie_cap_has_lnkctl(const struct pci_dev *dev); bool pcie_cap_has_lnkctl2(const struct pci_dev *dev); bool pcie_cap_has_rtctl(const struct pci_dev *dev); +/* Standard Capability finder */ +/** + * PCI_FIND_NEXT_CAP - Find a PCI standard capability + * @read_cfg: Function pointer for reading PCI config space + * @start: Starting position to begin search + * @cap: Capability ID to find + * @args: Arguments to pass to read_cfg function + * + * Search the capability list in PCI config space to find @cap. + * Implements TTL (time-to-live) protection against infinite loops. + * + * Return: Position of the capability if found, 0 otherwise. + */ +#define PCI_FIND_NEXT_CAP(read_cfg, start, cap, args...) \ +({ \ + int __ttl = PCI_FIND_CAP_TTL; \ + u8 __id, __found_pos = 0; \ + u8 __pos = (start); \ + u16 __ent; \ + \ + read_cfg##_byte(args, __pos, &__pos); \ + \ + while (__ttl--) { \ + if (__pos < PCI_STD_HEADER_SIZEOF) \ + break; \ + \ + __pos = ALIGN_DOWN(__pos, 4); \ + read_cfg##_word(args, __pos, &__ent); \ + \ + __id = FIELD_GET(PCI_CAP_ID_MASK, __ent); \ + if (__id == 0xff) \ + break; \ + \ + if (__id == (cap)) { \ + __found_pos = __pos; \ + break; \ + } \ + \ + __pos = FIELD_GET(PCI_CAP_LIST_NEXT_MASK, __ent); \ + } \ + __found_pos; \ +}) + +/* Extended Capability finder */ +/** + * PCI_FIND_NEXT_EXT_CAP - Find a PCI extended capability + * @read_cfg: Function pointer for reading PCI config space + * @start: Starting position to begin search (0 for initial search) + * @cap: Extended capability ID to find + * @args: Arguments to pass to read_cfg function + * + * Search the extended capability list in PCI config space to find @cap. + * Implements TTL protection against infinite loops using a calculated + * maximum search count. + * + * Return: Position of the capability if found, 0 otherwise. + */ +#define PCI_FIND_NEXT_EXT_CAP(read_cfg, start, cap, args...) \ +({ \ + u16 __pos = (start) ?: PCI_CFG_SPACE_SIZE; \ + u16 __found_pos = 0; \ + int __ttl, __ret; \ + u32 __header; \ + \ + __ttl = (PCI_CFG_SPACE_EXP_SIZE - PCI_CFG_SPACE_SIZE) / 8; \ + while (__ttl-- > 0 && __pos >= PCI_CFG_SPACE_SIZE) { \ + __ret = read_cfg##_dword(args, __pos, &__header); \ + if (__ret != PCIBIOS_SUCCESSFUL) \ + break; \ + \ + if (__header == 0) \ + break; \ + \ + if (PCI_EXT_CAP_ID(__header) == (cap) && __pos != start) {\ + __found_pos = __pos; \ + break; \ + } \ + \ + __pos = PCI_EXT_CAP_NEXT(__header); \ + } \ + __found_pos; \ +}) + /* Functions internal to the PCI core code */ #ifdef CONFIG_DMI @@ -330,7 +422,7 @@ struct device *pci_get_host_bridge_device(struct pci_dev *dev); void pci_put_host_bridge_device(struct device *dev); unsigned int pci_rescan_bus_bridge_resize(struct pci_dev *bridge); -int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type); +int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res); int __must_check pci_reassign_resource(struct pci_dev *dev, int i, resource_size_t add_size, resource_size_t align); int pci_configure_extended_tags(struct pci_dev *dev, void *ign); @@ -381,6 +473,8 @@ static inline int pci_resource_num(const struct pci_dev *dev, return resno; } +struct resource *pbus_select_window(struct pci_bus *bus, + const struct resource *res); void pci_reassigndev_resource_alignment(struct pci_dev *dev); void pci_disable_bridge_window(struct pci_dev *dev); struct pci_bus *pci_bus_get(struct pci_bus *bus); diff --git a/drivers/pci/pcie/aer.c b/drivers/pci/pcie/aer.c index e286c197d716..0b5ed4722ac3 100644 --- a/drivers/pci/pcie/aer.c +++ b/drivers/pci/pcie/aer.c @@ -43,7 +43,7 @@ #define AER_ERROR_SOURCES_MAX 128 #define AER_MAX_TYPEOF_COR_ERRS 16 /* as per PCI_ERR_COR_STATUS */ -#define AER_MAX_TYPEOF_UNCOR_ERRS 27 /* as per PCI_ERR_UNCOR_STATUS*/ +#define AER_MAX_TYPEOF_UNCOR_ERRS 32 /* as per PCI_ERR_UNCOR_STATUS*/ struct aer_err_source { u32 status; /* PCI_ERR_ROOT_STATUS */ @@ -96,11 +96,21 @@ struct aer_info { }; #define AER_LOG_TLP_MASKS (PCI_ERR_UNC_POISON_TLP| \ + PCI_ERR_UNC_POISON_BLK | \ PCI_ERR_UNC_ECRC| \ PCI_ERR_UNC_UNSUP| \ PCI_ERR_UNC_COMP_ABORT| \ PCI_ERR_UNC_UNX_COMP| \ - PCI_ERR_UNC_MALF_TLP) + PCI_ERR_UNC_ACSV | \ + PCI_ERR_UNC_MCBTLP | \ + PCI_ERR_UNC_ATOMEG | \ + PCI_ERR_UNC_DMWR_BLK | \ + PCI_ERR_UNC_XLAT_BLK | \ + PCI_ERR_UNC_TLPPRE | \ + PCI_ERR_UNC_MALF_TLP | \ + PCI_ERR_UNC_IDE_CHECK | \ + PCI_ERR_UNC_MISR_IDE | \ + PCI_ERR_UNC_PCRC_CHECK) #define SYSTEM_ERROR_INTR_ON_MESG_MASK (PCI_EXP_RTCTL_SECEE| \ PCI_EXP_RTCTL_SENFEE| \ @@ -383,6 +393,10 @@ void pci_aer_init(struct pci_dev *dev) return; dev->aer_info = kzalloc(sizeof(*dev->aer_info), GFP_KERNEL); + if (!dev->aer_info) { + dev->aer_cap = 0; + return; + } ratelimit_state_init(&dev->aer_info->correctable_ratelimit, DEFAULT_RATELIMIT_INTERVAL, DEFAULT_RATELIMIT_BURST); @@ -525,11 +539,11 @@ static const char *aer_uncorrectable_error_string[] = { "AtomicOpBlocked", /* Bit Position 24 */ "TLPBlockedErr", /* Bit Position 25 */ "PoisonTLPBlocked", /* Bit Position 26 */ - NULL, /* Bit Position 27 */ - NULL, /* Bit Position 28 */ - NULL, /* Bit Position 29 */ - NULL, /* Bit Position 30 */ - NULL, /* Bit Position 31 */ + "DMWrReqBlocked", /* Bit Position 27 */ + "IDECheck", /* Bit Position 28 */ + "MisIDETLP", /* Bit Position 29 */ + "PCRC_CHECK", /* Bit Position 30 */ + "TLPXlatBlocked", /* Bit Position 31 */ }; static const char *aer_agent_string[] = { @@ -786,6 +800,9 @@ static void pci_rootport_aer_stats_incr(struct pci_dev *pdev, static int aer_ratelimit(struct pci_dev *dev, unsigned int severity) { + if (!dev->aer_info) + return 1; + switch (severity) { case AER_NONFATAL: return __ratelimit(&dev->aer_info->nonfatal_ratelimit); @@ -796,6 +813,20 @@ static int aer_ratelimit(struct pci_dev *dev, unsigned int severity) } } +static bool tlp_header_logged(u32 status, u32 capctl) +{ + /* Errors for which a header is always logged (PCIe r7.0 sec 6.2.7) */ + if (status & AER_LOG_TLP_MASKS) + return true; + + /* Completion Timeout header is only logged on capable devices */ + if (status & PCI_ERR_UNC_COMP_TIME && + capctl & PCI_ERR_CAP_COMP_TIME_LOG) + return true; + + return false; +} + static void __aer_print_error(struct pci_dev *dev, struct aer_err_info *info) { const char **strings; @@ -910,7 +941,7 @@ void pci_print_aer(struct pci_dev *dev, int aer_severity, status = aer->uncor_status; mask = aer->uncor_mask; info.level = KERN_ERR; - tlp_header_valid = status & AER_LOG_TLP_MASKS; + tlp_header_valid = tlp_header_logged(status, aer->cap_control); } info.status = status; @@ -1401,7 +1432,7 @@ int aer_get_device_error_info(struct aer_err_info *info, int i) pci_read_config_dword(dev, aer + PCI_ERR_CAP, &aercc); info->first_error = PCI_ERR_CAP_FEP(aercc); - if (info->status & AER_LOG_TLP_MASKS) { + if (tlp_header_logged(info->status, aercc)) { info->tlp_header_valid = 1; pcie_read_tlp_log(dev, aer + PCI_ERR_HEADER_LOG, aer + PCI_ERR_PREFIX_LOG, diff --git a/drivers/pci/pcie/aspm.c b/drivers/pci/pcie/aspm.c index 919a05b97647..7cc8281e7011 100644 --- a/drivers/pci/pcie/aspm.c +++ b/drivers/pci/pcie/aspm.c @@ -15,6 +15,7 @@ #include <linux/math.h> #include <linux/module.h> #include <linux/moduleparam.h> +#include <linux/of.h> #include <linux/pci.h> #include <linux/pci_regs.h> #include <linux/errno.h> @@ -235,13 +236,15 @@ struct pcie_link_state { u32 aspm_support:7; /* Supported ASPM state */ u32 aspm_enabled:7; /* Enabled ASPM state */ u32 aspm_capable:7; /* Capable ASPM state with latency */ - u32 aspm_default:7; /* Default ASPM state by BIOS */ + u32 aspm_default:7; /* Default ASPM state by BIOS or + override */ u32 aspm_disable:7; /* Disabled ASPM state */ /* Clock PM state */ u32 clkpm_capable:1; /* Clock PM capable? */ u32 clkpm_enabled:1; /* Current Clock PM state */ - u32 clkpm_default:1; /* Default Clock PM state by BIOS */ + u32 clkpm_default:1; /* Default Clock PM state by BIOS or + override */ u32 clkpm_disable:1; /* Clock PM disabled */ }; @@ -373,6 +376,18 @@ static void pcie_set_clkpm(struct pcie_link_state *link, int enable) pcie_set_clkpm_nocheck(link, enable); } +static void pcie_clkpm_override_default_link_state(struct pcie_link_state *link, + int enabled) +{ + struct pci_dev *pdev = link->downstream; + + /* For devicetree platforms, enable ClockPM by default */ + if (of_have_populated_dt() && !enabled) { + link->clkpm_default = 1; + pci_info(pdev, "ASPM: DT platform, enabling ClockPM\n"); + } +} + static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) { int capable = 1, enabled = 1; @@ -395,6 +410,7 @@ static void pcie_clkpm_cap_init(struct pcie_link_state *link, int blacklist) } link->clkpm_enabled = enabled; link->clkpm_default = enabled; + pcie_clkpm_override_default_link_state(link, enabled); link->clkpm_capable = capable; link->clkpm_disable = blacklist ? 1 : 0; } @@ -788,6 +804,29 @@ static void aspm_l1ss_init(struct pcie_link_state *link) aspm_calc_l12_info(link, parent_l1ss_cap, child_l1ss_cap); } +#define FLAG(x, y, d) (((x) & (PCIE_LINK_STATE_##y)) ? d : "") + +static void pcie_aspm_override_default_link_state(struct pcie_link_state *link) +{ + struct pci_dev *pdev = link->downstream; + u32 override; + + /* For devicetree platforms, enable all ASPM states by default */ + if (of_have_populated_dt()) { + link->aspm_default = PCIE_LINK_STATE_ASPM_ALL; + override = link->aspm_default & ~link->aspm_enabled; + if (override) + pci_info(pdev, "ASPM: DT platform, enabling%s%s%s%s%s%s%s\n", + FLAG(override, L0S_UP, " L0s-up"), + FLAG(override, L0S_DW, " L0s-dw"), + FLAG(override, L1, " L1"), + FLAG(override, L1_1, " ASPM-L1.1"), + FLAG(override, L1_2, " ASPM-L1.2"), + FLAG(override, L1_1_PCIPM, " PCI-PM-L1.1"), + FLAG(override, L1_2_PCIPM, " PCI-PM-L1.2")); + } +} + static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) { struct pci_dev *child = link->downstream, *parent = link->pdev; @@ -868,6 +907,8 @@ static void pcie_aspm_cap_init(struct pcie_link_state *link, int blacklist) /* Save default state */ link->aspm_default = link->aspm_enabled; + pcie_aspm_override_default_link_state(link); + /* Setup initial capable state. Will be updated later */ link->aspm_capable = link->aspm_support; diff --git a/drivers/pci/pcie/err.c b/drivers/pci/pcie/err.c index de6381c690f5..bebe4bc111d7 100644 --- a/drivers/pci/pcie/err.c +++ b/drivers/pci/pcie/err.c @@ -108,6 +108,24 @@ static int report_normal_detected(struct pci_dev *dev, void *data) return report_error_detected(dev, pci_channel_io_normal, data); } +static int report_perm_failure_detected(struct pci_dev *dev, void *data) +{ + struct pci_driver *pdrv; + const struct pci_error_handlers *err_handler; + + device_lock(&dev->dev); + pdrv = dev->driver; + if (!pdrv || !pdrv->err_handler || !pdrv->err_handler->error_detected) + goto out; + + err_handler = pdrv->err_handler; + err_handler->error_detected(dev, pci_channel_io_perm_failure); +out: + pci_uevent_ers(dev, PCI_ERS_RESULT_DISCONNECT); + device_unlock(&dev->dev); + return 0; +} + static int report_mmio_enabled(struct pci_dev *dev, void *data) { struct pci_driver *pdrv; @@ -135,7 +153,8 @@ static int report_slot_reset(struct pci_dev *dev, void *data) device_lock(&dev->dev); pdrv = dev->driver; - if (!pdrv || !pdrv->err_handler || !pdrv->err_handler->slot_reset) + if (!pci_dev_set_io_state(dev, pci_channel_io_normal) || + !pdrv || !pdrv->err_handler || !pdrv->err_handler->slot_reset) goto out; err_handler = pdrv->err_handler; @@ -217,15 +236,10 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, pci_walk_bridge(bridge, pci_pm_runtime_get_sync, NULL); pci_dbg(bridge, "broadcast error_detected message\n"); - if (state == pci_channel_io_frozen) { + if (state == pci_channel_io_frozen) pci_walk_bridge(bridge, report_frozen_detected, &status); - if (reset_subordinates(bridge) != PCI_ERS_RESULT_RECOVERED) { - pci_warn(bridge, "subordinate device reset failed\n"); - goto failed; - } - } else { + else pci_walk_bridge(bridge, report_normal_detected, &status); - } if (status == PCI_ERS_RESULT_CAN_RECOVER) { status = PCI_ERS_RESULT_RECOVERED; @@ -233,6 +247,14 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, pci_walk_bridge(bridge, report_mmio_enabled, &status); } + if (status == PCI_ERS_RESULT_NEED_RESET || + state == pci_channel_io_frozen) { + if (reset_subordinates(bridge) != PCI_ERS_RESULT_RECOVERED) { + pci_warn(bridge, "subordinate device reset failed\n"); + goto failed; + } + } + if (status == PCI_ERS_RESULT_NEED_RESET) { /* * TODO: Should call platform-specific @@ -269,7 +291,7 @@ pci_ers_result_t pcie_do_recovery(struct pci_dev *dev, failed: pci_walk_bridge(bridge, pci_pm_runtime_put, NULL); - pci_uevent_ers(bridge, PCI_ERS_RESULT_DISCONNECT); + pci_walk_bridge(bridge, report_perm_failure_detected, NULL); pci_info(bridge, "device recovery failed\n"); diff --git a/drivers/pci/probe.c b/drivers/pci/probe.c index f41128f91ca7..c83e75a0ec12 100644 --- a/drivers/pci/probe.c +++ b/drivers/pci/probe.c @@ -3,6 +3,7 @@ * PCI detection and setup code */ +#include <linux/array_size.h> #include <linux/kernel.h> #include <linux/delay.h> #include <linux/init.h> @@ -419,13 +420,17 @@ static void pci_read_bridge_io(struct pci_dev *dev, struct resource *res, limit |= ((unsigned long) io_limit_hi << 16); } + res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; + if (base <= limit) { - res->flags = (io_base_lo & PCI_IO_RANGE_TYPE_MASK) | IORESOURCE_IO; region.start = base; region.end = limit + io_granularity - 1; pcibios_bus_to_resource(dev->bus, res, ®ion); if (log) pci_info(dev, " bridge window %pR\n", res); + } else { + resource_set_range(res, 0, 0); + res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; } } @@ -440,13 +445,18 @@ static void pci_read_bridge_mmio(struct pci_dev *dev, struct resource *res, pci_read_config_word(dev, PCI_MEMORY_LIMIT, &mem_limit_lo); base = ((unsigned long) mem_base_lo & PCI_MEMORY_RANGE_MASK) << 16; limit = ((unsigned long) mem_limit_lo & PCI_MEMORY_RANGE_MASK) << 16; + + res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; + if (base <= limit) { - res->flags = (mem_base_lo & PCI_MEMORY_RANGE_TYPE_MASK) | IORESOURCE_MEM; region.start = base; region.end = limit + 0xfffff; pcibios_bus_to_resource(dev->bus, res, ®ion); if (log) pci_info(dev, " bridge window %pR\n", res); + } else { + resource_set_range(res, 0, 0); + res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; } } @@ -489,16 +499,20 @@ static void pci_read_bridge_mmio_pref(struct pci_dev *dev, struct resource *res, return; } + res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) | IORESOURCE_MEM | + IORESOURCE_PREFETCH; + if (res->flags & PCI_PREF_RANGE_TYPE_64) + res->flags |= IORESOURCE_MEM_64; + if (base <= limit) { - res->flags = (mem_base_lo & PCI_PREF_RANGE_TYPE_MASK) | - IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (res->flags & PCI_PREF_RANGE_TYPE_64) - res->flags |= IORESOURCE_MEM_64; region.start = base; region.end = limit + 0xfffff; pcibios_bus_to_resource(dev->bus, res, ®ion); if (log) pci_info(dev, " bridge window %pR\n", res); + } else { + resource_set_range(res, 0, 0); + res->flags |= IORESOURCE_UNSET | IORESOURCE_DISABLED; } } @@ -524,10 +538,14 @@ static void pci_read_bridge_windows(struct pci_dev *bridge) } if (io) { bridge->io_window = 1; - pci_read_bridge_io(bridge, &res, true); + pci_read_bridge_io(bridge, + pci_resource_n(bridge, PCI_BRIDGE_IO_WINDOW), + true); } - pci_read_bridge_mmio(bridge, &res, true); + pci_read_bridge_mmio(bridge, + pci_resource_n(bridge, PCI_BRIDGE_MEM_WINDOW), + true); /* * DECchip 21050 pass 2 errata: the bridge may miss an address @@ -565,7 +583,10 @@ static void pci_read_bridge_windows(struct pci_dev *bridge) bridge->pref_64_window = 1; } - pci_read_bridge_mmio_pref(bridge, &res, true); + pci_read_bridge_mmio_pref(bridge, + pci_resource_n(bridge, + PCI_BRIDGE_PREF_MEM_WINDOW), + true); } void pci_read_bridge_bases(struct pci_bus *child) @@ -585,9 +606,13 @@ void pci_read_bridge_bases(struct pci_bus *child) for (i = 0; i < PCI_BRIDGE_RESOURCE_NUM; i++) child->resource[i] = &dev->resource[PCI_BRIDGE_RESOURCES+i]; - pci_read_bridge_io(child->self, child->resource[0], false); - pci_read_bridge_mmio(child->self, child->resource[1], false); - pci_read_bridge_mmio_pref(child->self, child->resource[2], false); + pci_read_bridge_io(child->self, + child->resource[PCI_BUS_BRIDGE_IO_WINDOW], false); + pci_read_bridge_mmio(child->self, + child->resource[PCI_BUS_BRIDGE_MEM_WINDOW], false); + pci_read_bridge_mmio_pref(child->self, + child->resource[PCI_BUS_BRIDGE_PREF_MEM_WINDOW], + false); if (!dev->transparent) return; @@ -1912,16 +1937,16 @@ static int pci_intx_mask_broken(struct pci_dev *dev) static void early_dump_pci_device(struct pci_dev *pdev) { - u32 value[256 / 4]; + u32 value[PCI_CFG_SPACE_SIZE / sizeof(u32)]; int i; pci_info(pdev, "config space:\n"); - for (i = 0; i < 256; i += 4) - pci_read_config_dword(pdev, i, &value[i / 4]); + for (i = 0; i < ARRAY_SIZE(value); i++) + pci_read_config_dword(pdev, i * sizeof(u32), &value[i]); print_hex_dump(KERN_INFO, "", DUMP_PREFIX_OFFSET, 16, 1, - value, 256, false); + value, ARRAY_SIZE(value) * sizeof(u32), false); } static const char *pci_type_str(struct pci_dev *dev) @@ -1985,8 +2010,8 @@ int pci_setup_device(struct pci_dev *dev) dev->sysdata = dev->bus->sysdata; dev->dev.parent = dev->bus->bridge; dev->dev.bus = &pci_bus_type; - dev->hdr_type = hdr_type & 0x7f; - dev->multifunction = !!(hdr_type & 0x80); + dev->hdr_type = FIELD_GET(PCI_HEADER_TYPE_MASK, hdr_type); + dev->multifunction = FIELD_GET(PCI_HEADER_TYPE_MFD, hdr_type); dev->error_state = pci_channel_io_normal; set_pcie_port_type(dev); @@ -2516,9 +2541,15 @@ static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, in struct device_node *np; np = of_pci_find_child_device(dev_of_node(&bus->dev), devfn); - if (!np || of_find_device_by_node(np)) + if (!np) return NULL; + pdev = of_find_device_by_node(np); + if (pdev) { + put_device(&pdev->dev); + goto err_put_of_node; + } + /* * First check whether the pwrctrl device really needs to be created or * not. This is decided based on at least one of the power supplies @@ -2526,17 +2557,24 @@ static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, in */ if (!of_pci_supply_present(np)) { pr_debug("PCI/pwrctrl: Skipping OF node: %s\n", np->name); - return NULL; + goto err_put_of_node; } /* Now create the pwrctrl device */ pdev = of_platform_device_create(np, NULL, &host->dev); if (!pdev) { pr_err("PCI/pwrctrl: Failed to create pwrctrl device for node: %s\n", np->name); - return NULL; + goto err_put_of_node; } + of_node_put(np); + return pdev; + +err_put_of_node: + of_node_put(np); + + return NULL; } #else static struct platform_device *pci_pwrctrl_create_device(struct pci_bus *bus, int devfn) @@ -3045,14 +3083,14 @@ static unsigned int pci_scan_child_bus_extend(struct pci_bus *bus, { unsigned int used_buses, normal_bridges = 0, hotplug_bridges = 0; unsigned int start = bus->busn_res.start; - unsigned int devfn, cmax, max = start; + unsigned int devnr, cmax, max = start; struct pci_dev *dev; dev_dbg(&bus->dev, "scanning bus\n"); /* Go find them, Rover! */ - for (devfn = 0; devfn < 256; devfn += 8) - pci_scan_slot(bus, devfn); + for (devnr = 0; devnr < PCI_MAX_NR_DEVS; devnr++) + pci_scan_slot(bus, PCI_DEVFN(devnr, 0)); /* Reserve buses for SR-IOV capability */ used_buses = pci_iov_bus_range(bus); @@ -3469,7 +3507,7 @@ EXPORT_SYMBOL_GPL(pci_rescan_bus); * pci_rescan_bus(), pci_rescan_bus_bridge_resize() and PCI device removal * routines should always be executed under this mutex. */ -static DEFINE_MUTEX(pci_rescan_remove_lock); +DEFINE_MUTEX(pci_rescan_remove_lock); void pci_lock_rescan_remove(void) { diff --git a/drivers/pci/pwrctrl/slot.c b/drivers/pci/pwrctrl/slot.c index 6e138310b45b..3320494b62d8 100644 --- a/drivers/pci/pwrctrl/slot.c +++ b/drivers/pci/pwrctrl/slot.c @@ -49,13 +49,14 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev) ret = regulator_bulk_enable(slot->num_supplies, slot->supplies); if (ret < 0) { dev_err_probe(dev, ret, "Failed to enable slot regulators\n"); - goto err_regulator_free; + regulator_bulk_free(slot->num_supplies, slot->supplies); + return ret; } ret = devm_add_action_or_reset(dev, devm_pci_pwrctrl_slot_power_off, slot); if (ret) - goto err_regulator_disable; + return ret; clk = devm_clk_get_optional_enabled(dev, NULL); if (IS_ERR(clk)) { @@ -70,13 +71,6 @@ static int pci_pwrctrl_slot_probe(struct platform_device *pdev) return dev_err_probe(dev, ret, "Failed to register pwrctrl driver\n"); return 0; - -err_regulator_disable: - regulator_bulk_disable(slot->num_supplies, slot->supplies); -err_regulator_free: - regulator_bulk_free(slot->num_supplies, slot->supplies); - - return ret; } static const struct of_device_id pci_pwrctrl_slot_of_match[] = { diff --git a/drivers/pci/quirks.c b/drivers/pci/quirks.c index 17315a825674..214ed060ca1b 100644 --- a/drivers/pci/quirks.c +++ b/drivers/pci/quirks.c @@ -2717,6 +2717,7 @@ static void quirk_disable_msi(struct pci_dev *dev) DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_8131_BRIDGE, quirk_disable_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_VIA, 0xa238, quirk_disable_msi); DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_ATI, 0x5a3f, quirk_disable_msi); +DECLARE_PCI_FIXUP_FINAL(PCI_VENDOR_ID_RDC, 0x1031, quirk_disable_msi); /* * The APC bridge device in AMD 780 family northbridges has some random diff --git a/drivers/pci/remove.c b/drivers/pci/remove.c index 445afdfa6498..ce5c25adef55 100644 --- a/drivers/pci/remove.c +++ b/drivers/pci/remove.c @@ -31,6 +31,8 @@ static void pci_pwrctrl_unregister(struct device *dev) return; of_device_unregister(pdev); + put_device(&pdev->dev); + of_node_clear_flag(np, OF_POPULATED); } @@ -138,6 +140,7 @@ static void pci_remove_bus_device(struct pci_dev *dev) */ void pci_stop_and_remove_bus_device(struct pci_dev *dev) { + lockdep_assert_held(&pci_rescan_remove_lock); pci_stop_bus_device(dev); pci_remove_bus_device(dev); } diff --git a/drivers/pci/setup-bus.c b/drivers/pci/setup-bus.c index 7853ac6999e2..362ad108794d 100644 --- a/drivers/pci/setup-bus.c +++ b/drivers/pci/setup-bus.c @@ -28,6 +28,10 @@ #include <linux/acpi.h> #include "pci.h" +#define PCI_RES_TYPE_MASK \ + (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\ + IORESOURCE_MEM_64) + unsigned int pci_flags; EXPORT_SYMBOL_GPL(pci_flags); @@ -136,6 +140,139 @@ static void restore_dev_resource(struct pci_dev_resource *dev_res) res->flags = dev_res->flags; } +/* + * Helper function for sizing routines. Assigned resources have non-NULL + * parent resource. + * + * Return first unassigned resource of the correct type. If there is none, + * return first assigned resource of the correct type. If none of the + * above, return NULL. + * + * Returning an assigned resource of the correct type allows the caller to + * distinguish between already assigned and no resource of the correct type. + */ +static struct resource *find_bus_resource_of_type(struct pci_bus *bus, + unsigned long type_mask, + unsigned long type) +{ + struct resource *r, *r_assigned = NULL; + + pci_bus_for_each_resource(bus, r) { + if (!r || r == &ioport_resource || r == &iomem_resource) + continue; + + if ((r->flags & type_mask) != type) + continue; + + if (!r->parent) + return r; + if (!r_assigned) + r_assigned = r; + } + return r_assigned; +} + +/** + * pbus_select_window_for_type - Select bridge window for a resource type + * @bus: PCI bus + * @type: Resource type (resource flags can be passed as is) + * + * Select the bridge window based on a resource @type. + * + * For memory resources, the selection is done as follows: + * + * Any non-prefetchable resource is put into the non-prefetchable window. + * + * If there is no prefetchable MMIO window, put all memory resources into the + * non-prefetchable window. + * + * If there's a 64-bit prefetchable MMIO window, put all 64-bit prefetchable + * resources into it and place 32-bit prefetchable memory into the + * non-prefetchable window. + * + * Otherwise, put all prefetchable resources into the prefetchable window. + * + * Return: the bridge window resource or NULL if no bridge window is found. + */ +static struct resource *pbus_select_window_for_type(struct pci_bus *bus, + unsigned long type) +{ + int iores_type = type & IORESOURCE_TYPE_BITS; /* w/o 64bit & pref */ + struct resource *mmio, *mmio_pref, *win; + + type &= PCI_RES_TYPE_MASK; /* with 64bit & pref */ + + if ((iores_type != IORESOURCE_IO) && (iores_type != IORESOURCE_MEM)) + return NULL; + + if (pci_is_root_bus(bus)) { + win = find_bus_resource_of_type(bus, type, type); + if (win) + return win; + + type &= ~IORESOURCE_MEM_64; + win = find_bus_resource_of_type(bus, type, type); + if (win) + return win; + + type &= ~IORESOURCE_PREFETCH; + return find_bus_resource_of_type(bus, type, type); + } + + switch (iores_type) { + case IORESOURCE_IO: + return pci_bus_resource_n(bus, PCI_BUS_BRIDGE_IO_WINDOW); + + case IORESOURCE_MEM: + mmio = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_MEM_WINDOW); + mmio_pref = pci_bus_resource_n(bus, PCI_BUS_BRIDGE_PREF_MEM_WINDOW); + + if (!(type & IORESOURCE_PREFETCH) || + !(mmio_pref->flags & IORESOURCE_MEM)) + return mmio; + + if ((type & IORESOURCE_MEM_64) || + !(mmio_pref->flags & IORESOURCE_MEM_64)) + return mmio_pref; + + return mmio; + default: + return NULL; + } +} + +/** + * pbus_select_window - Select bridge window for a resource + * @bus: PCI bus + * @res: Resource + * + * Select the bridge window for @res. If the resource is already assigned, + * return the current bridge window. + * + * For memory resources, the selection is done as follows: + * + * Any non-prefetchable resource is put into the non-prefetchable window. + * + * If there is no prefetchable MMIO window, put all memory resources into the + * non-prefetchable window. + * + * If there's a 64-bit prefetchable MMIO window, put all 64-bit prefetchable + * resources into it and place 32-bit prefetchable memory into the + * non-prefetchable window. + * + * Otherwise, put all prefetchable resources into the prefetchable window. + * + * Return: the bridge window resource or NULL if no bridge window is found. + */ +struct resource *pbus_select_window(struct pci_bus *bus, + const struct resource *res) +{ + if (res->parent) + return res->parent; + + return pbus_select_window_for_type(bus, res->flags); +} + static bool pdev_resources_assignable(struct pci_dev *dev) { u16 class = dev->class >> 8, command; @@ -154,6 +291,31 @@ static bool pdev_resources_assignable(struct pci_dev *dev) return true; } +static bool pdev_resource_assignable(struct pci_dev *dev, struct resource *res) +{ + int idx = pci_resource_num(dev, res); + + if (!res->flags) + return false; + + if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END && + res->flags & IORESOURCE_DISABLED) + return false; + + return true; +} + +static bool pdev_resource_should_fit(struct pci_dev *dev, struct resource *res) +{ + if (res->parent) + return false; + + if (res->flags & IORESOURCE_PCI_FIXED) + return false; + + return pdev_resource_assignable(dev, res); +} + /* Sort resources by alignment */ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) { @@ -169,10 +331,7 @@ static void pdev_sort_resources(struct pci_dev *dev, struct list_head *head) resource_size_t r_align; struct list_head *n; - if (r->flags & IORESOURCE_PCI_FIXED) - continue; - - if (!(r->flags) || r->parent) + if (!pdev_resource_should_fit(dev, r)) continue; r_align = pci_resource_alignment(dev, r); @@ -221,8 +380,15 @@ bool pci_resource_is_optional(const struct pci_dev *dev, int resno) return false; } -static inline void reset_resource(struct resource *res) +static inline void reset_resource(struct pci_dev *dev, struct resource *res) { + int idx = pci_resource_num(dev, res); + + if (idx >= PCI_BRIDGE_RESOURCES && idx <= PCI_BRIDGE_RESOURCE_END) { + res->flags |= IORESOURCE_UNSET; + return; + } + res->start = 0; res->end = 0; res->flags = 0; @@ -384,13 +550,19 @@ static bool pci_need_to_release(unsigned long mask, struct resource *res) } /* Return: @true if assignment of a required resource failed. */ -static bool pci_required_resource_failed(struct list_head *fail_head) +static bool pci_required_resource_failed(struct list_head *fail_head, + unsigned long type) { struct pci_dev_resource *fail_res; + type &= PCI_RES_TYPE_MASK; + list_for_each_entry(fail_res, fail_head, list) { int idx = pci_resource_num(fail_res->dev, fail_res->res); + if (type && (fail_res->flags & PCI_RES_TYPE_MASK) != type) + continue; + if (!pci_resource_is_optional(fail_res->dev, idx)) return true; } @@ -431,8 +603,6 @@ static void __assign_resources_sorted(struct list_head *head, struct pci_dev_resource *dev_res, *tmp_res, *dev_res2; struct resource *res; struct pci_dev *dev; - const char *res_name; - int idx; unsigned long fail_type; resource_size_t add_align, align; @@ -504,7 +674,7 @@ assign: } /* Without realloc_head and only optional fails, nothing more to do. */ - if (!pci_required_resource_failed(&local_fail_head) && + if (!pci_required_resource_failed(&local_fail_head, 0) && list_empty(realloc_head)) { list_for_each_entry(save_res, &save_head, list) { struct resource *res = save_res->res; @@ -540,14 +710,7 @@ assign: res = dev_res->res; dev = dev_res->dev; - if (!res->parent) - continue; - - idx = pci_resource_num(dev, res); - res_name = pci_resource_name(dev, idx); - pci_dbg(dev, "%s %pR: releasing\n", res_name, res); - - release_resource(res); + pci_release_resource(dev, pci_resource_num(dev, res)); restore_dev_resource(dev_res); } /* Restore start/end/flags from saved list */ @@ -577,7 +740,7 @@ out: 0 /* don't care */); } - reset_resource(res); + reset_resource(dev, res); } free_list(head); @@ -618,7 +781,7 @@ void pci_setup_cardbus(struct pci_bus *bus) res = bus->resource[0]; pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->flags & IORESOURCE_IO) { + if (res->parent && res->flags & IORESOURCE_IO) { /* * The IO resource is allocated a range twice as large as it * would normally need. This allows us to set both IO regs. @@ -632,7 +795,7 @@ void pci_setup_cardbus(struct pci_bus *bus) res = bus->resource[1]; pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->flags & IORESOURCE_IO) { + if (res->parent && res->flags & IORESOURCE_IO) { pci_info(bridge, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_IO_BASE_1, region.start); @@ -642,7 +805,7 @@ void pci_setup_cardbus(struct pci_bus *bus) res = bus->resource[2]; pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->flags & IORESOURCE_MEM) { + if (res->parent && res->flags & IORESOURCE_MEM) { pci_info(bridge, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_0, region.start); @@ -652,7 +815,7 @@ void pci_setup_cardbus(struct pci_bus *bus) res = bus->resource[3]; pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->flags & IORESOURCE_MEM) { + if (res->parent && res->flags & IORESOURCE_MEM) { pci_info(bridge, " bridge window %pR\n", res); pci_write_config_dword(bridge, PCI_CB_MEMORY_BASE_1, region.start); @@ -693,7 +856,7 @@ static void pci_setup_bridge_io(struct pci_dev *bridge) res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; res_name = pci_resource_name(bridge, PCI_BRIDGE_IO_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->flags & IORESOURCE_IO) { + if (res->parent && res->flags & IORESOURCE_IO) { pci_read_config_word(bridge, PCI_IO_BASE, &l); io_base_lo = (region.start >> 8) & io_mask; io_limit_lo = (region.end >> 8) & io_mask; @@ -725,7 +888,7 @@ static void pci_setup_bridge_mmio(struct pci_dev *bridge) res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; res_name = pci_resource_name(bridge, PCI_BRIDGE_MEM_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->flags & IORESOURCE_MEM) { + if (res->parent && res->flags & IORESOURCE_MEM) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; pci_info(bridge, " %s %pR\n", res_name, res); @@ -754,7 +917,7 @@ static void pci_setup_bridge_mmio_pref(struct pci_dev *bridge) res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; res_name = pci_resource_name(bridge, PCI_BRIDGE_PREF_MEM_WINDOW); pcibios_resource_to_bus(bridge->bus, ®ion, res); - if (res->flags & IORESOURCE_PREFETCH) { + if (res->parent && res->flags & IORESOURCE_PREFETCH) { l = (region.start >> 16) & 0xfff0; l |= region.end & 0xfff00000; if (res->flags & IORESOURCE_MEM_64) { @@ -790,6 +953,23 @@ static void __pci_setup_bridge(struct pci_bus *bus, unsigned long type) pci_write_config_word(bridge, PCI_BRIDGE_CONTROL, bus->bridge_ctl); } +static void pci_setup_one_bridge_window(struct pci_dev *bridge, int resno) +{ + switch (resno) { + case PCI_BRIDGE_IO_WINDOW: + pci_setup_bridge_io(bridge); + break; + case PCI_BRIDGE_MEM_WINDOW: + pci_setup_bridge_mmio(bridge); + break; + case PCI_BRIDGE_PREF_MEM_WINDOW: + pci_setup_bridge_mmio_pref(bridge); + break; + default: + return; + } +} + void __weak pcibios_setup_bridge(struct pci_bus *bus, unsigned long type) { } @@ -806,6 +986,8 @@ static void pci_setup_bridge(struct pci_bus *bus) int pci_claim_bridge_resource(struct pci_dev *bridge, int i) { + int ret = -EINVAL; + if (i < PCI_BRIDGE_RESOURCES || i > PCI_BRIDGE_RESOURCE_END) return 0; @@ -815,27 +997,16 @@ int pci_claim_bridge_resource(struct pci_dev *bridge, int i) if ((bridge->class >> 8) != PCI_CLASS_BRIDGE_PCI) return 0; - if (!pci_bus_clip_resource(bridge, i)) - return -EINVAL; /* Clipping didn't change anything */ - - switch (i) { - case PCI_BRIDGE_IO_WINDOW: - pci_setup_bridge_io(bridge); - break; - case PCI_BRIDGE_MEM_WINDOW: - pci_setup_bridge_mmio(bridge); - break; - case PCI_BRIDGE_PREF_MEM_WINDOW: - pci_setup_bridge_mmio_pref(bridge); - break; - default: + if (i > PCI_BRIDGE_PREF_MEM_WINDOW) return -EINVAL; - } - if (pci_claim_resource(bridge, i) == 0) - return 0; /* Claimed a smaller window */ + /* Try to clip the resource and claim the smaller window */ + if (pci_bus_clip_resource(bridge, i)) + ret = pci_claim_resource(bridge, i); + + pci_setup_one_bridge_window(bridge, i); - return -EINVAL; + return ret; } /* @@ -866,34 +1037,6 @@ static void pci_bridge_check_ranges(struct pci_bus *bus) } } -/* - * Helper function for sizing routines. Assigned resources have non-NULL - * parent resource. - * - * Return first unassigned resource of the correct type. If there is none, - * return first assigned resource of the correct type. If none of the - * above, return NULL. - * - * Returning an assigned resource of the correct type allows the caller to - * distinguish between already assigned and no resource of the correct type. - */ -static struct resource *find_bus_resource_of_type(struct pci_bus *bus, - unsigned long type_mask, - unsigned long type) -{ - struct resource *r, *r_assigned = NULL; - - pci_bus_for_each_resource(bus, r) { - if (r == &ioport_resource || r == &iomem_resource) - continue; - if (r && (r->flags & type_mask) == type && !r->parent) - return r; - if (r && (r->flags & type_mask) == type && !r_assigned) - r_assigned = r; - } - return r_assigned; -} - static resource_size_t calculate_iosize(resource_size_t size, resource_size_t min_size, resource_size_t size1, @@ -984,8 +1127,7 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, struct list_head *realloc_head) { struct pci_dev *dev; - struct resource *b_res = find_bus_resource_of_type(bus, IORESOURCE_IO, - IORESOURCE_IO); + struct resource *b_res = pbus_select_window_for_type(bus, IORESOURCE_IO); resource_size_t size = 0, size0 = 0, size1 = 0; resource_size_t children_add_size = 0; resource_size_t min_align, align; @@ -1006,8 +1148,11 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, if (r->parent || !(r->flags & IORESOURCE_IO)) continue; - r_size = resource_size(r); + if (!pdev_resource_assignable(dev, r)) + continue; + + r_size = resource_size(r); if (r_size < SZ_1K) /* Might be re-aligned for ISA */ size += r_size; @@ -1026,6 +1171,9 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, size0 = calculate_iosize(size, min_size, size1, 0, 0, resource_size(b_res), min_align); + if (size0) + b_res->flags &= ~IORESOURCE_DISABLED; + size1 = size0; if (realloc_head && (add_size > 0 || children_add_size > 0)) { size1 = calculate_iosize(size, min_size, size1, add_size, @@ -1037,13 +1185,14 @@ static void pbus_size_io(struct pci_bus *bus, resource_size_t min_size, if (bus->self && (b_res->start || b_res->end)) pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", b_res, &bus->busn_res); - b_res->flags = 0; + b_res->flags |= IORESOURCE_DISABLED; return; } resource_set_range(b_res, min_align, size0); b_res->flags |= IORESOURCE_STARTALIGN; if (bus->self && size1 > size0 && realloc_head) { + b_res->flags &= ~IORESOURCE_DISABLED; add_to_list(realloc_head, bus->self, b_res, size1-size0, min_align); pci_info(bus->self, "bridge window %pR to %pR add_size %llx\n", @@ -1077,19 +1226,20 @@ static inline resource_size_t calculate_mem_align(resource_size_t *aligns, /** * pbus_upstream_space_available - Check no upstream resource limits allocation * @bus: The bus - * @mask: Mask the resource flag, then compare it with type - * @type: The type of resource from bridge + * @res: The resource to help select the correct bridge window * @size: The size required from the bridge window * @align: Required alignment for the resource * - * Checks that @size can fit inside the upstream bridge resources that are - * already assigned. + * Check that @size can fit inside the upstream bridge resources that are + * already assigned. Select the upstream bridge window based on the type of + * @res. * * Return: %true if enough space is available on all assigned upstream * resources. */ -static bool pbus_upstream_space_available(struct pci_bus *bus, unsigned long mask, - unsigned long type, resource_size_t size, +static bool pbus_upstream_space_available(struct pci_bus *bus, + struct resource *res, + resource_size_t size, resource_size_t align) { struct resource_constraint constraint = { @@ -1097,39 +1247,39 @@ static bool pbus_upstream_space_available(struct pci_bus *bus, unsigned long mas .align = align, }; struct pci_bus *downstream = bus; - struct resource *r; while ((bus = bus->parent)) { if (pci_is_root_bus(bus)) break; - pci_bus_for_each_resource(bus, r) { - if (!r || !r->parent || (r->flags & mask) != type) - continue; - - if (resource_size(r) >= size) { - struct resource gap = {}; + res = pbus_select_window(bus, res); + if (!res) + return false; + if (!res->parent) + continue; - if (find_resource_space(r, &gap, size, &constraint) == 0) { - gap.flags = type; - pci_dbg(bus->self, - "Assigned bridge window %pR to %pR free space at %pR\n", - r, &bus->busn_res, &gap); - return true; - } - } + if (resource_size(res) >= size) { + struct resource gap = {}; - if (bus->self) { - pci_info(bus->self, - "Assigned bridge window %pR to %pR cannot fit 0x%llx required for %s bridging to %pR\n", - r, &bus->busn_res, - (unsigned long long)size, - pci_name(downstream->self), - &downstream->busn_res); + if (find_resource_space(res, &gap, size, &constraint) == 0) { + gap.flags = res->flags; + pci_dbg(bus->self, + "Assigned bridge window %pR to %pR free space at %pR\n", + res, &bus->busn_res, &gap); + return true; } + } - return false; + if (bus->self) { + pci_info(bus->self, + "Assigned bridge window %pR to %pR cannot fit 0x%llx required for %s bridging to %pR\n", + res, &bus->busn_res, + (unsigned long long)size, + pci_name(downstream->self), + &downstream->busn_res); } + + return false; } return true; @@ -1139,24 +1289,22 @@ static bool pbus_upstream_space_available(struct pci_bus *bus, unsigned long mas * pbus_size_mem() - Size the memory window of a given bus * * @bus: The bus - * @mask: Mask the resource flag, then compare it with type - * @type: The type of free resource from bridge - * @type2: Second match type - * @type3: Third match type + * @type: The type of bridge resource * @min_size: The minimum memory window that must be allocated * @add_size: Additional optional memory window * @realloc_head: Track the additional memory window on this list * - * Calculate the size of the bus and minimal alignment which guarantees - * that all child resources fit in this size. + * Calculate the size of the bus resource for @type and minimal alignment + * which guarantees that all child resources fit in this size. + * + * Set the bus resource start/end to indicate the required size if there an + * available unassigned bus resource of the desired @type. * - * Return -ENOSPC if there's no available bus resource of the desired - * type. Otherwise, set the bus resource start/end to indicate the - * required size, add things to realloc_head (if supplied), and return 0. + * Add optional resource requests to the @realloc_head list if it is + * supplied. */ -static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, - unsigned long type, unsigned long type2, - unsigned long type3, resource_size_t min_size, +static void pbus_size_mem(struct pci_bus *bus, unsigned long type, + resource_size_t min_size, resource_size_t add_size, struct list_head *realloc_head) { @@ -1164,18 +1312,19 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, resource_size_t min_align, win_align, align, size, size0, size1 = 0; resource_size_t aligns[28]; /* Alignments from 1MB to 128TB */ int order, max_order; - struct resource *b_res = find_bus_resource_of_type(bus, - mask | IORESOURCE_PREFETCH, type); + struct resource *b_res = pbus_select_window_for_type(bus, type); resource_size_t children_add_size = 0; resource_size_t children_add_align = 0; resource_size_t add_align = 0; + resource_size_t relaxed_align; + resource_size_t old_size; if (!b_res) - return -ENOSPC; + return; /* If resource is already assigned, nothing more to do */ if (b_res->parent) - return 0; + return; memset(aligns, 0, sizeof(aligns)); max_order = 0; @@ -1189,11 +1338,12 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, const char *r_name = pci_resource_name(dev, i); resource_size_t r_size; - if (r->parent || (r->flags & IORESOURCE_PCI_FIXED) || - ((r->flags & mask) != type && - (r->flags & mask) != type2 && - (r->flags & mask) != type3)) + if (!pdev_resources_assignable(dev) || + !pdev_resource_should_fit(dev, r)) continue; + if (b_res != pbus_select_window(bus, r)) + continue; + r_size = resource_size(r); /* Put SRIOV requested res to the optional list */ @@ -1238,17 +1388,24 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, } } + old_size = resource_size(b_res); win_align = window_alignment(bus, b_res->flags); min_align = calculate_mem_align(aligns, max_order); min_align = max(min_align, win_align); - size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), min_align); + size0 = calculate_memsize(size, min_size, 0, 0, old_size, min_align); + + if (size0) { + resource_set_range(b_res, min_align, size0); + b_res->flags &= ~IORESOURCE_DISABLED; + } if (bus->self && size0 && - !pbus_upstream_space_available(bus, mask | IORESOURCE_PREFETCH, type, - size0, min_align)) { - min_align = 1ULL << (max_order + __ffs(SZ_1M)); - min_align = max(min_align, win_align); - size0 = calculate_memsize(size, min_size, 0, 0, resource_size(b_res), win_align); + !pbus_upstream_space_available(bus, b_res, size0, min_align)) { + relaxed_align = 1ULL << (max_order + __ffs(SZ_1M)); + relaxed_align = max(relaxed_align, win_align); + min_align = min(min_align, relaxed_align); + size0 = calculate_memsize(size, min_size, 0, 0, old_size, win_align); + resource_set_range(b_res, min_align, size0); pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n", b_res, &bus->busn_res); } @@ -1256,15 +1413,15 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (realloc_head && (add_size > 0 || children_add_size > 0)) { add_align = max(min_align, add_align); size1 = calculate_memsize(size, min_size, add_size, children_add_size, - resource_size(b_res), add_align); + old_size, add_align); if (bus->self && size1 && - !pbus_upstream_space_available(bus, mask | IORESOURCE_PREFETCH, type, - size1, add_align)) { - min_align = 1ULL << (max_order + __ffs(SZ_1M)); - min_align = max(min_align, win_align); + !pbus_upstream_space_available(bus, b_res, size1, add_align)) { + relaxed_align = 1ULL << (max_order + __ffs(SZ_1M)); + relaxed_align = max(relaxed_align, win_align); + min_align = min(min_align, relaxed_align); size1 = calculate_memsize(size, min_size, add_size, children_add_size, - resource_size(b_res), win_align); + old_size, win_align); pci_info(bus->self, "bridge window %pR to %pR requires relaxed alignment rules\n", b_res, &bus->busn_res); @@ -1275,20 +1432,20 @@ static int pbus_size_mem(struct pci_bus *bus, unsigned long mask, if (bus->self && (b_res->start || b_res->end)) pci_info(bus->self, "disabling bridge window %pR to %pR (unused)\n", b_res, &bus->busn_res); - b_res->flags = 0; - return 0; + b_res->flags |= IORESOURCE_DISABLED; + return; } resource_set_range(b_res, min_align, size0); b_res->flags |= IORESOURCE_STARTALIGN; if (bus->self && size1 > size0 && realloc_head) { + b_res->flags &= ~IORESOURCE_DISABLED; add_to_list(realloc_head, bus->self, b_res, size1-size0, add_align); pci_info(bus->self, "bridge window %pR to %pR add_size %llx add_align %llx\n", b_res, &bus->busn_res, (unsigned long long) (size1 - size0), (unsigned long long) add_align); } - return 0; } unsigned long pci_cardbus_resource_alignment(struct resource *res) @@ -1393,12 +1550,11 @@ handle_done: void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) { struct pci_dev *dev; - unsigned long mask, prefmask, type2 = 0, type3 = 0; resource_size_t additional_io_size = 0, additional_mmio_size = 0, additional_mmio_pref_size = 0; struct resource *pref; struct pci_host_bridge *host; - int hdr_type, ret; + int hdr_type; list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; @@ -1448,71 +1604,15 @@ void __pci_bus_size_bridges(struct pci_bus *bus, struct list_head *realloc_head) pbus_size_io(bus, realloc_head ? 0 : additional_io_size, additional_io_size, realloc_head); - /* - * If there's a 64-bit prefetchable MMIO window, compute - * the size required to put all 64-bit prefetchable - * resources in it. - */ - mask = IORESOURCE_MEM; - prefmask = IORESOURCE_MEM | IORESOURCE_PREFETCH; - if (pref && (pref->flags & IORESOURCE_MEM_64)) { - prefmask |= IORESOURCE_MEM_64; - ret = pbus_size_mem(bus, prefmask, prefmask, - prefmask, prefmask, - realloc_head ? 0 : additional_mmio_pref_size, - additional_mmio_pref_size, realloc_head); - - /* - * If successful, all non-prefetchable resources - * and any 32-bit prefetchable resources will go in - * the non-prefetchable window. - */ - if (ret == 0) { - mask = prefmask; - type2 = prefmask & ~IORESOURCE_MEM_64; - type3 = prefmask & ~IORESOURCE_PREFETCH; - } - } - - /* - * If there is no 64-bit prefetchable window, compute the - * size required to put all prefetchable resources in the - * 32-bit prefetchable window (if there is one). - */ - if (!type2) { - prefmask &= ~IORESOURCE_MEM_64; - ret = pbus_size_mem(bus, prefmask, prefmask, - prefmask, prefmask, - realloc_head ? 0 : additional_mmio_pref_size, - additional_mmio_pref_size, realloc_head); - - /* - * If successful, only non-prefetchable resources - * will go in the non-prefetchable window. - */ - if (ret == 0) - mask = prefmask; - else - additional_mmio_size += additional_mmio_pref_size; - - type2 = type3 = IORESOURCE_MEM; + if (pref) { + pbus_size_mem(bus, + IORESOURCE_MEM | IORESOURCE_PREFETCH | + (pref->flags & IORESOURCE_MEM_64), + realloc_head ? 0 : additional_mmio_pref_size, + additional_mmio_pref_size, realloc_head); } - /* - * Compute the size required to put everything else in the - * non-prefetchable window. This includes: - * - * - all non-prefetchable resources - * - 32-bit prefetchable resources if there's a 64-bit - * prefetchable window or no prefetchable window at all - * - 64-bit prefetchable resources if there's no prefetchable - * window at all - * - * Note that the strategy in __pci_assign_resource() must match - * that used here. Specifically, we cannot put a 32-bit - * prefetchable resource in a 64-bit prefetchable window. - */ - pbus_size_mem(bus, mask, IORESOURCE_MEM, type2, type3, + pbus_size_mem(bus, IORESOURCE_MEM, realloc_head ? 0 : additional_mmio_size, additional_mmio_size, realloc_head); break; @@ -1704,66 +1804,25 @@ static void __pci_bridge_assign_resources(const struct pci_dev *bridge, } } -#define PCI_RES_TYPE_MASK \ - (IORESOURCE_IO | IORESOURCE_MEM | IORESOURCE_PREFETCH |\ - IORESOURCE_MEM_64) - static void pci_bridge_release_resources(struct pci_bus *bus, - unsigned long type) + struct resource *b_win) { struct pci_dev *dev = bus->self; - struct resource *r; - unsigned int old_flags; - struct resource *b_res; - int idx = 1; + int idx, ret; - b_res = &dev->resource[PCI_BRIDGE_RESOURCES]; - - /* - * 1. If IO port assignment fails, release bridge IO port. - * 2. If non pref MMIO assignment fails, release bridge nonpref MMIO. - * 3. If 64bit pref MMIO assignment fails, and bridge pref is 64bit, - * release bridge pref MMIO. - * 4. If pref MMIO assignment fails, and bridge pref is 32bit, - * release bridge pref MMIO. - * 5. If pref MMIO assignment fails, and bridge pref is not - * assigned, release bridge nonpref MMIO. - */ - if (type & IORESOURCE_IO) - idx = 0; - else if (!(type & IORESOURCE_PREFETCH)) - idx = 1; - else if ((type & IORESOURCE_MEM_64) && - (b_res[2].flags & IORESOURCE_MEM_64)) - idx = 2; - else if (!(b_res[2].flags & IORESOURCE_MEM_64) && - (b_res[2].flags & IORESOURCE_PREFETCH)) - idx = 2; - else - idx = 1; - - r = &b_res[idx]; - - if (!r->parent) + if (!b_win->parent) return; + idx = pci_resource_num(dev, b_win); + /* If there are children, release them all */ - release_child_resources(r); - if (!release_resource(r)) { - type = old_flags = r->flags & PCI_RES_TYPE_MASK; - pci_info(dev, "resource %d %pR released\n", - PCI_BRIDGE_RESOURCES + idx, r); - /* Keep the old size */ - resource_set_range(r, 0, resource_size(r)); - r->flags = 0; + release_child_resources(b_win); - /* Avoiding touch the one without PREF */ - if (type & IORESOURCE_PREFETCH) - type = IORESOURCE_PREFETCH; - __pci_setup_bridge(bus, type); - /* For next child res under same bridge */ - r->flags = old_flags; - } + ret = pci_release_resource(dev, idx); + if (ret) + return; + + pci_setup_one_bridge_window(dev, idx); } enum release_type { @@ -1776,7 +1835,7 @@ enum release_type { * a larger window later. */ static void pci_bus_release_bridge_resources(struct pci_bus *bus, - unsigned long type, + struct resource *b_win, enum release_type rel_type) { struct pci_dev *dev; @@ -1784,6 +1843,8 @@ static void pci_bus_release_bridge_resources(struct pci_bus *bus, list_for_each_entry(dev, &bus->devices, bus_list) { struct pci_bus *b = dev->subordinate; + struct resource *res; + if (!b) continue; @@ -1792,9 +1853,15 @@ static void pci_bus_release_bridge_resources(struct pci_bus *bus, if ((dev->class >> 8) != PCI_CLASS_BRIDGE_PCI) continue; - if (rel_type == whole_subtree) - pci_bus_release_bridge_resources(b, type, - whole_subtree); + if (rel_type != whole_subtree) + continue; + + pci_bus_for_each_resource(b, res) { + if (res->parent != b_win) + continue; + + pci_bus_release_bridge_resources(b, res, rel_type); + } } if (pci_is_root_bus(bus)) @@ -1804,7 +1871,7 @@ static void pci_bus_release_bridge_resources(struct pci_bus *bus, return; if ((rel_type == whole_subtree) || is_leaf_bridge) - pci_bridge_release_resources(bus, type); + pci_bridge_release_resources(bus, b_win); } static void pci_bus_dump_res(struct pci_bus *bus) @@ -1979,33 +2046,21 @@ static void remove_dev_resource(struct resource *avail, struct pci_dev *dev, avail->start = min(avail->start + tmp, avail->end + 1); } -static void remove_dev_resources(struct pci_dev *dev, struct resource *io, - struct resource *mmio, - struct resource *mmio_pref) +static void remove_dev_resources(struct pci_dev *dev, + struct resource available[PCI_P2P_BRIDGE_RESOURCE_NUM]) { - struct resource *res; + struct resource *res, *b_win; + int idx; pci_dev_for_each_resource(dev, res) { - if (resource_type(res) == IORESOURCE_IO) { - remove_dev_resource(io, dev, res); - } else if (resource_type(res) == IORESOURCE_MEM) { + b_win = pbus_select_window(dev->bus, res); + if (!b_win) + continue; - /* - * Make sure prefetchable memory is reduced from - * the correct resource. Specifically we put 32-bit - * prefetchable memory in non-prefetchable window - * if there is a 64-bit prefetchable window. - * - * See comments in __pci_bus_size_bridges() for - * more information. - */ - if ((res->flags & IORESOURCE_PREFETCH) && - ((res->flags & IORESOURCE_MEM_64) == - (mmio_pref->flags & IORESOURCE_MEM_64))) - remove_dev_resource(mmio_pref, dev, res); - else - remove_dev_resource(mmio, dev, res); - } + idx = pci_resource_num(dev->bus->self, b_win); + idx -= PCI_BRIDGE_RESOURCES; + + remove_dev_resource(&available[idx], dev, res); } } @@ -2019,45 +2074,39 @@ static void remove_dev_resources(struct pci_dev *dev, struct resource *io, * shared with the bridges. */ static void pci_bus_distribute_available_resources(struct pci_bus *bus, - struct list_head *add_list, - struct resource io, - struct resource mmio, - struct resource mmio_pref) + struct list_head *add_list, + struct resource available_in[PCI_P2P_BRIDGE_RESOURCE_NUM]) { + struct resource available[PCI_P2P_BRIDGE_RESOURCE_NUM]; unsigned int normal_bridges = 0, hotplug_bridges = 0; - struct resource *io_res, *mmio_res, *mmio_pref_res; struct pci_dev *dev, *bridge = bus->self; - resource_size_t io_per_b, mmio_per_b, mmio_pref_per_b, align; - - io_res = &bridge->resource[PCI_BRIDGE_IO_WINDOW]; - mmio_res = &bridge->resource[PCI_BRIDGE_MEM_WINDOW]; - mmio_pref_res = &bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; + resource_size_t per_bridge[PCI_P2P_BRIDGE_RESOURCE_NUM]; + resource_size_t align; + int i; - /* - * The alignment of this bridge is yet to be considered, hence it must - * be done now before extending its bridge window. - */ - align = pci_resource_alignment(bridge, io_res); - if (!io_res->parent && align) - io.start = min(ALIGN(io.start, align), io.end + 1); + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) { + struct resource *res = pci_bus_resource_n(bus, i); - align = pci_resource_alignment(bridge, mmio_res); - if (!mmio_res->parent && align) - mmio.start = min(ALIGN(mmio.start, align), mmio.end + 1); + available[i] = available_in[i]; - align = pci_resource_alignment(bridge, mmio_pref_res); - if (!mmio_pref_res->parent && align) - mmio_pref.start = min(ALIGN(mmio_pref.start, align), - mmio_pref.end + 1); + /* + * The alignment of this bridge is yet to be considered, + * hence it must be done now before extending its bridge + * window. + */ + align = pci_resource_alignment(bridge, res); + if (!res->parent && align) + available[i].start = min(ALIGN(available[i].start, align), + available[i].end + 1); - /* - * Now that we have adjusted for alignment, update the bridge window - * resources to fill as much remaining resource space as possible. - */ - adjust_bridge_window(bridge, io_res, add_list, resource_size(&io)); - adjust_bridge_window(bridge, mmio_res, add_list, resource_size(&mmio)); - adjust_bridge_window(bridge, mmio_pref_res, add_list, - resource_size(&mmio_pref)); + /* + * Now that we have adjusted for alignment, update the + * bridge window resources to fill as much remaining + * resource space as possible. + */ + adjust_bridge_window(bridge, res, add_list, + resource_size(&available[i])); + } /* * Calculate how many hotplug bridges and normal bridges there @@ -2081,7 +2130,7 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, */ list_for_each_entry(dev, &bus->devices, bus_list) { if (!dev->is_virtfn) - remove_dev_resources(dev, &io, &mmio, &mmio_pref); + remove_dev_resources(dev, available); } /* @@ -2093,16 +2142,9 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, * split between non-hotplug bridges. This is to allow possible * hotplug bridges below them to get the extra space as well. */ - if (hotplug_bridges) { - io_per_b = div64_ul(resource_size(&io), hotplug_bridges); - mmio_per_b = div64_ul(resource_size(&mmio), hotplug_bridges); - mmio_pref_per_b = div64_ul(resource_size(&mmio_pref), - hotplug_bridges); - } else { - io_per_b = div64_ul(resource_size(&io), normal_bridges); - mmio_per_b = div64_ul(resource_size(&mmio), normal_bridges); - mmio_pref_per_b = div64_ul(resource_size(&mmio_pref), - normal_bridges); + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) { + per_bridge[i] = div64_ul(resource_size(&available[i]), + hotplug_bridges ?: normal_bridges); } for_each_pci_bridge(dev, bus) { @@ -2115,49 +2157,41 @@ static void pci_bus_distribute_available_resources(struct pci_bus *bus, if (hotplug_bridges && !dev->is_hotplug_bridge) continue; - res = &dev->resource[PCI_BRIDGE_IO_WINDOW]; + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) { + res = pci_bus_resource_n(bus, i); - /* - * Make sure the split resource space is properly aligned - * for bridge windows (align it down to avoid going above - * what is available). - */ - align = pci_resource_alignment(dev, res); - resource_set_size(&io, ALIGN_DOWN_IF_NONZERO(io_per_b, align)); - - /* - * The x_per_b holds the extra resource space that can be - * added for each bridge but there is the minimal already - * reserved as well so adjust x.start down accordingly to - * cover the whole space. - */ - io.start -= resource_size(res); - - res = &dev->resource[PCI_BRIDGE_MEM_WINDOW]; - align = pci_resource_alignment(dev, res); - resource_set_size(&mmio, - ALIGN_DOWN_IF_NONZERO(mmio_per_b,align)); - mmio.start -= resource_size(res); + /* + * Make sure the split resource space is properly + * aligned for bridge windows (align it down to + * avoid going above what is available). + */ + align = pci_resource_alignment(dev, res); + resource_set_size(&available[i], + ALIGN_DOWN_IF_NONZERO(per_bridge[i], + align)); - res = &dev->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; - align = pci_resource_alignment(dev, res); - resource_set_size(&mmio_pref, - ALIGN_DOWN_IF_NONZERO(mmio_pref_per_b, align)); - mmio_pref.start -= resource_size(res); + /* + * The per_bridge holds the extra resource space + * that can be added for each bridge but there is + * the minimal already reserved as well so adjust + * x.start down accordingly to cover the whole + * space. + */ + available[i].start -= resource_size(res); + } - pci_bus_distribute_available_resources(b, add_list, io, mmio, - mmio_pref); + pci_bus_distribute_available_resources(b, add_list, available); - io.start += io.end + 1; - mmio.start += mmio.end + 1; - mmio_pref.start += mmio_pref.end + 1; + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) + available[i].start += available[i].end + 1; } } static void pci_bridge_distribute_available_resources(struct pci_dev *bridge, struct list_head *add_list) { - struct resource available_io, available_mmio, available_mmio_pref; + struct resource *res, available[PCI_P2P_BRIDGE_RESOURCE_NUM]; + unsigned int i; if (!bridge->is_hotplug_bridge) return; @@ -2165,14 +2199,13 @@ static void pci_bridge_distribute_available_resources(struct pci_dev *bridge, pci_dbg(bridge, "distributing available resources\n"); /* Take the initial extra resources from the hotplug port */ - available_io = bridge->resource[PCI_BRIDGE_IO_WINDOW]; - available_mmio = bridge->resource[PCI_BRIDGE_MEM_WINDOW]; - available_mmio_pref = bridge->resource[PCI_BRIDGE_PREF_MEM_WINDOW]; + for (i = 0; i < PCI_P2P_BRIDGE_RESOURCE_NUM; i++) { + res = pci_resource_n(bridge, PCI_BRIDGE_RESOURCES + i); + available[i] = *res; + } pci_bus_distribute_available_resources(bridge->subordinate, - add_list, available_io, - available_mmio, - available_mmio_pref); + add_list, available); } static bool pci_bridge_resources_not_assigned(struct pci_dev *dev) @@ -2235,27 +2268,19 @@ static void pci_prepare_next_assign_round(struct list_head *fail_head, * enough to contain child device resources. */ list_for_each_entry(fail_res, fail_head, list) { - pci_bus_release_bridge_resources(fail_res->dev->bus, - fail_res->flags & PCI_RES_TYPE_MASK, - rel_type); + struct pci_bus *bus = fail_res->dev->bus; + struct resource *b_win; + + b_win = pbus_select_window_for_type(bus, fail_res->flags); + if (!b_win) + continue; + pci_bus_release_bridge_resources(bus, b_win, rel_type); } /* Restore size and flags */ - list_for_each_entry(fail_res, fail_head, list) { - struct resource *res = fail_res->res; - struct pci_dev *dev = fail_res->dev; - int idx = pci_resource_num(dev, res); - + list_for_each_entry(fail_res, fail_head, list) restore_dev_resource(fail_res); - if (!pci_is_bridge(dev)) - continue; - - if (idx >= PCI_BRIDGE_RESOURCES && - idx <= PCI_BRIDGE_RESOURCE_END) - res->flags = 0; - } - free_list(fail_head); } @@ -2389,10 +2414,16 @@ void pci_assign_unassigned_bridge_resources(struct pci_dev *bridge) } EXPORT_SYMBOL_GPL(pci_assign_unassigned_bridge_resources); -int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) +/* + * Walk to the root bus, find the bridge window relevant for @res and + * release it when possible. If the bridge window contains assigned + * resources, it cannot be released. + */ +int pbus_reassign_bridge_resources(struct pci_bus *bus, struct resource *res) { + unsigned long type = res->flags; struct pci_dev_resource *dev_res; - struct pci_dev *next; + struct pci_dev *bridge; LIST_HEAD(saved); LIST_HEAD(added); LIST_HEAD(failed); @@ -2401,39 +2432,31 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) down_read(&pci_bus_sem); - /* Walk to the root hub, releasing bridge BARs when possible */ - next = bridge; - do { - bridge = next; - for (i = PCI_BRIDGE_RESOURCES; i < PCI_BRIDGE_RESOURCE_END; - i++) { - struct resource *res = &bridge->resource[i]; - const char *res_name = pci_resource_name(bridge, i); - - if ((res->flags ^ type) & PCI_RES_TYPE_MASK) - continue; + while (!pci_is_root_bus(bus)) { + bridge = bus->self; + res = pbus_select_window(bus, res); + if (!res) + break; - /* Ignore BARs which are still in use */ - if (res->child) - continue; + i = pci_resource_num(bridge, res); + /* Ignore BARs which are still in use */ + if (!res->child) { ret = add_to_list(&saved, bridge, res, 0, 0); if (ret) goto cleanup; - pci_info(bridge, "%s %pR: releasing\n", res_name, res); + pci_release_resource(bridge, i); + } else { + const char *res_name = pci_resource_name(bridge, i); - if (res->parent) - release_resource(res); - res->start = 0; - res->end = 0; - break; + pci_warn(bridge, + "%s %pR: was not released (still contains assigned resources)\n", + res_name, res); } - if (i == PCI_BRIDGE_RESOURCE_END) - break; - next = bridge->bus ? bridge->bus->self : NULL; - } while (next); + bus = bus->parent; + } if (list_empty(&saved)) { up_read(&pci_bus_sem); @@ -2446,8 +2469,12 @@ int pci_reassign_bridge_resources(struct pci_dev *bridge, unsigned long type) free_list(&added); if (!list_empty(&failed)) { - ret = -ENOSPC; - goto cleanup; + if (pci_required_resource_failed(&failed, type)) { + ret = -ENOSPC; + goto cleanup; + } + /* Only resources with unrelated types failed (again) */ + free_list(&failed); } list_for_each_entry(dev_res, &saved, list) { diff --git a/drivers/pci/setup-res.c b/drivers/pci/setup-res.c index d2b3ed51e880..c3ba4ccecd43 100644 --- a/drivers/pci/setup-res.c +++ b/drivers/pci/setup-res.c @@ -359,6 +359,9 @@ int pci_assign_resource(struct pci_dev *dev, int resno) res->flags &= ~IORESOURCE_UNSET; res->flags &= ~IORESOURCE_STARTALIGN; + if (resno >= PCI_BRIDGE_RESOURCES && resno <= PCI_BRIDGE_RESOURCE_END) + res->flags &= ~IORESOURCE_DISABLED; + pci_info(dev, "%s %pR: assigned\n", res_name, res); if (resno < PCI_BRIDGE_RESOURCES) pci_update_resource(dev, resno); @@ -406,20 +409,25 @@ int pci_reassign_resource(struct pci_dev *dev, int resno, return 0; } -void pci_release_resource(struct pci_dev *dev, int resno) +int pci_release_resource(struct pci_dev *dev, int resno) { struct resource *res = pci_resource_n(dev, resno); const char *res_name = pci_resource_name(dev, resno); + int ret; if (!res->parent) - return; + return 0; pci_info(dev, "%s %pR: releasing\n", res_name, res); - release_resource(res); + ret = release_resource(res); + if (ret) + return ret; res->end = resource_size(res) - 1; res->start = 0; res->flags |= IORESOURCE_UNSET; + + return 0; } EXPORT_SYMBOL(pci_release_resource); @@ -488,7 +496,7 @@ int pci_resize_resource(struct pci_dev *dev, int resno, int size) /* Check if the new config works by trying to assign everything. */ if (dev->bus->self) { - ret = pci_reassign_bridge_resources(dev->bus->self, res->flags); + ret = pbus_reassign_bridge_resources(dev->bus, res); if (ret) goto error_resize; } @@ -522,22 +530,26 @@ int pci_enable_resources(struct pci_dev *dev, int mask) if (pci_resource_is_optional(dev, i)) continue; - if (r->flags & IORESOURCE_UNSET) { - pci_err(dev, "%s %pR: not assigned; can't enable device\n", - r_name, r); - return -EINVAL; + if (i < PCI_BRIDGE_RESOURCES) { + if (r->flags & IORESOURCE_UNSET) { + pci_err(dev, "%s %pR: not assigned; can't enable device\n", + r_name, r); + return -EINVAL; + } + + if (!r->parent) { + pci_err(dev, "%s %pR: not claimed; can't enable device\n", + r_name, r); + return -EINVAL; + } } - if (!r->parent) { - pci_err(dev, "%s %pR: not claimed; can't enable device\n", - r_name, r); - return -EINVAL; + if (r->parent) { + if (r->flags & IORESOURCE_IO) + cmd |= PCI_COMMAND_IO; + if (r->flags & IORESOURCE_MEM) + cmd |= PCI_COMMAND_MEMORY; } - - if (r->flags & IORESOURCE_IO) - cmd |= PCI_COMMAND_IO; - if (r->flags & IORESOURCE_MEM) - cmd |= PCI_COMMAND_MEMORY; } if (cmd != old_cmd) { diff --git a/drivers/pci/switch/switchtec.c b/drivers/pci/switch/switchtec.c index b14dfab04d84..5ff84fb8fb0f 100644 --- a/drivers/pci/switch/switchtec.c +++ b/drivers/pci/switch/switchtec.c @@ -269,10 +269,9 @@ static void mrpc_event_work(struct work_struct *work) dev_dbg(&stdev->dev, "%s\n", __func__); - mutex_lock(&stdev->mrpc_mutex); + guard(mutex)(&stdev->mrpc_mutex); cancel_delayed_work(&stdev->mrpc_timeout); mrpc_complete_cmd(stdev); - mutex_unlock(&stdev->mrpc_mutex); } static void mrpc_error_complete_cmd(struct switchtec_dev *stdev) @@ -1322,18 +1321,18 @@ static void stdev_kill(struct switchtec_dev *stdev) cancel_delayed_work_sync(&stdev->mrpc_timeout); /* Mark the hardware as unavailable and complete all completions */ - mutex_lock(&stdev->mrpc_mutex); - stdev->alive = false; - - /* Wake up and kill any users waiting on an MRPC request */ - list_for_each_entry_safe(stuser, tmpuser, &stdev->mrpc_queue, list) { - stuser->cmd_done = true; - wake_up_interruptible(&stuser->cmd_comp); - list_del_init(&stuser->list); - stuser_put(stuser); - } + scoped_guard (mutex, &stdev->mrpc_mutex) { + stdev->alive = false; + + /* Wake up and kill any users waiting on an MRPC request */ + list_for_each_entry_safe(stuser, tmpuser, &stdev->mrpc_queue, list) { + stuser->cmd_done = true; + wake_up_interruptible(&stuser->cmd_comp); + list_del_init(&stuser->list); + stuser_put(stuser); + } - mutex_unlock(&stdev->mrpc_mutex); + } /* Wake up any users waiting on event_wq */ wake_up_interruptible(&stdev->event_wq); diff --git a/drivers/phy/Kconfig b/drivers/phy/Kconfig index 58c911e1b2d2..678dd0452f0a 100644 --- a/drivers/phy/Kconfig +++ b/drivers/phy/Kconfig @@ -122,6 +122,7 @@ source "drivers/phy/renesas/Kconfig" source "drivers/phy/rockchip/Kconfig" source "drivers/phy/samsung/Kconfig" source "drivers/phy/socionext/Kconfig" +source "drivers/phy/sophgo/Kconfig" source "drivers/phy/st/Kconfig" source "drivers/phy/starfive/Kconfig" source "drivers/phy/sunplus/Kconfig" diff --git a/drivers/phy/Makefile b/drivers/phy/Makefile index c670a8dac468..bfb27fb5a494 100644 --- a/drivers/phy/Makefile +++ b/drivers/phy/Makefile @@ -35,6 +35,7 @@ obj-y += allwinner/ \ rockchip/ \ samsung/ \ socionext/ \ + sophgo/ \ st/ \ starfive/ \ sunplus/ \ diff --git a/drivers/phy/allwinner/phy-sun4i-usb.c b/drivers/phy/allwinner/phy-sun4i-usb.c index 8873aed3a52a..59d38d88efb0 100644 --- a/drivers/phy/allwinner/phy-sun4i-usb.c +++ b/drivers/phy/allwinner/phy-sun4i-usb.c @@ -97,7 +97,6 @@ #define POLL_TIME msecs_to_jiffies(250) struct sun4i_usb_phy_cfg { - int num_phys; int hsic_index; u32 disc_thresh; u32 hci_phy_ctl_clear; @@ -115,6 +114,7 @@ struct sun4i_usb_phy_data { const struct sun4i_usb_phy_cfg *cfg; enum usb_dr_mode dr_mode; spinlock_t reg_lock; /* guard access to phyctl reg */ + int num_phys; struct sun4i_usb_phy { struct phy *phy; void __iomem *pmu; @@ -686,7 +686,7 @@ static struct phy *sun4i_usb_phy_xlate(struct device *dev, { struct sun4i_usb_phy_data *data = dev_get_drvdata(dev); - if (args->args[0] >= data->cfg->num_phys) + if (args->args[0] >= data->num_phys) return ERR_PTR(-ENODEV); if (data->cfg->missing_phys & BIT(args->args[0])) @@ -779,13 +779,22 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) return ret; } - for (i = 0; i < data->cfg->num_phys; i++) { + for (i = 0; i < MAX_PHYS; i++) { struct sun4i_usb_phy *phy = data->phys + i; char name[32]; if (data->cfg->missing_phys & BIT(i)) continue; + snprintf(name, sizeof(name), "usb%d_reset", i); + phy->reset = devm_reset_control_get(dev, name); + if (IS_ERR(phy->reset)) { + if (PTR_ERR(phy->reset) == -ENOENT) + break; + dev_err(dev, "failed to get reset %s\n", name); + return PTR_ERR(phy->reset); + } + snprintf(name, sizeof(name), "usb%d_vbus", i); phy->vbus = devm_regulator_get_optional(dev, name); if (IS_ERR(phy->vbus)) { @@ -828,13 +837,6 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) } } - snprintf(name, sizeof(name), "usb%d_reset", i); - phy->reset = devm_reset_control_get(dev, name); - if (IS_ERR(phy->reset)) { - dev_err(dev, "failed to get reset %s\n", name); - return PTR_ERR(phy->reset); - } - if (i || data->cfg->phy0_dual_route) { /* No pmu for musb */ snprintf(name, sizeof(name), "pmu%d", i); phy->pmu = devm_platform_ioremap_resource_byname(pdev, name); @@ -851,6 +853,7 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) phy->index = i; phy_set_drvdata(phy->phy, &data->phys[i]); } + data->num_phys = i; data->id_det_irq = gpiod_to_irq(data->id_det_gpio); if (data->id_det_irq > 0) { @@ -901,28 +904,24 @@ static int sun4i_usb_phy_probe(struct platform_device *pdev) } static const struct sun4i_usb_phy_cfg suniv_f1c100s_cfg = { - .num_phys = 1, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, }; static const struct sun4i_usb_phy_cfg sun4i_a10_cfg = { - .num_phys = 3, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, }; static const struct sun4i_usb_phy_cfg sun5i_a13_cfg = { - .num_phys = 2, .disc_thresh = 2, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, }; static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { - .num_phys = 3, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, @@ -930,14 +929,12 @@ static const struct sun4i_usb_phy_cfg sun6i_a31_cfg = { }; static const struct sun4i_usb_phy_cfg sun7i_a20_cfg = { - .num_phys = 3, .disc_thresh = 2, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = false, }; static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { - .num_phys = 2, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A10, .dedicated_clocks = true, @@ -945,7 +942,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a23_cfg = { }; static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { - .num_phys = 2, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, @@ -953,7 +949,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a33_cfg = { }; static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { - .num_phys = 3, .hsic_index = 2, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, @@ -961,7 +956,6 @@ static const struct sun4i_usb_phy_cfg sun8i_a83t_cfg = { }; static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { - .num_phys = 4, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, @@ -970,7 +964,6 @@ static const struct sun4i_usb_phy_cfg sun8i_h3_cfg = { }; static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = { - .num_phys = 3, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, @@ -979,7 +972,6 @@ static const struct sun4i_usb_phy_cfg sun8i_r40_cfg = { }; static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { - .num_phys = 1, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, @@ -988,7 +980,6 @@ static const struct sun4i_usb_phy_cfg sun8i_v3s_cfg = { }; static const struct sun4i_usb_phy_cfg sun20i_d1_cfg = { - .num_phys = 2, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, .hci_phy_ctl_clear = PHY_CTL_SIDDQ, @@ -997,7 +988,6 @@ static const struct sun4i_usb_phy_cfg sun20i_d1_cfg = { }; static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { - .num_phys = 2, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, @@ -1006,7 +996,6 @@ static const struct sun4i_usb_phy_cfg sun50i_a64_cfg = { }; static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = { - .num_phys = 4, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, .phy0_dual_route = true, @@ -1015,7 +1004,6 @@ static const struct sun4i_usb_phy_cfg sun50i_h6_cfg = { }; static const struct sun4i_usb_phy_cfg sun50i_h616_cfg = { - .num_phys = 4, .disc_thresh = 3, .phyctl_offset = REG_PHYCTL_A33, .dedicated_clocks = true, diff --git a/drivers/phy/broadcom/phy-brcm-sata.c b/drivers/phy/broadcom/phy-brcm-sata.c index d52dd065e862..fb69e21a0292 100644 --- a/drivers/phy/broadcom/phy-brcm-sata.c +++ b/drivers/phy/broadcom/phy-brcm-sata.c @@ -850,4 +850,3 @@ MODULE_DESCRIPTION("Broadcom SATA PHY driver"); MODULE_LICENSE("GPL"); MODULE_AUTHOR("Marc Carino"); MODULE_AUTHOR("Brian Norris"); -MODULE_ALIAS("platform:phy-brcm-sata"); diff --git a/drivers/phy/broadcom/phy-brcm-usb.c b/drivers/phy/broadcom/phy-brcm-usb.c index 0666864c2f77..59d756a10d6c 100644 --- a/drivers/phy/broadcom/phy-brcm-usb.c +++ b/drivers/phy/broadcom/phy-brcm-usb.c @@ -691,7 +691,6 @@ static struct platform_driver brcm_usb_driver = { module_platform_driver(brcm_usb_driver); -MODULE_ALIAS("platform:brcmstb-usb-phy"); MODULE_AUTHOR("Al Cooper <acooper@broadcom.com>"); MODULE_DESCRIPTION("BRCM USB PHY driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/cadence/cdns-dphy-rx.c b/drivers/phy/cadence/cdns-dphy-rx.c index 7729cf80a9bd..3ac80141189c 100644 --- a/drivers/phy/cadence/cdns-dphy-rx.c +++ b/drivers/phy/cadence/cdns-dphy-rx.c @@ -12,6 +12,7 @@ #include <linux/phy/phy.h> #include <linux/phy/phy-mipi-dphy.h> #include <linux/platform_device.h> +#include <linux/pm_runtime.h> #include <linux/sys_soc.h> #define DPHY_PMA_CMN(reg) (reg) @@ -265,7 +266,7 @@ static int cdns_dphy_rx_probe(struct platform_device *pdev) return PTR_ERR(provider); } - return 0; + return devm_pm_runtime_enable(dev); } static const struct of_device_id cdns_dphy_rx_of_match[] = { diff --git a/drivers/phy/cadence/cdns-dphy.c b/drivers/phy/cadence/cdns-dphy.c index ed87a3970f83..d5b0e516b93c 100644 --- a/drivers/phy/cadence/cdns-dphy.c +++ b/drivers/phy/cadence/cdns-dphy.c @@ -30,6 +30,7 @@ #define DPHY_CMN_SSM DPHY_PMA_CMN(0x20) #define DPHY_CMN_SSM_EN BIT(0) +#define DPHY_CMN_SSM_CAL_WAIT_TIME GENMASK(8, 1) #define DPHY_CMN_TX_MODE_EN BIT(9) #define DPHY_CMN_PWM DPHY_PMA_CMN(0x40) @@ -55,14 +56,6 @@ #define DPHY_PSM_CFG_FROM_REG BIT(0) #define DPHY_PSM_CLK_DIV(x) ((x) << 1) -#define DSI_HBP_FRAME_OVERHEAD 12 -#define DSI_HSA_FRAME_OVERHEAD 14 -#define DSI_HFP_FRAME_OVERHEAD 6 -#define DSI_HSS_VSS_VSE_FRAME_OVERHEAD 4 -#define DSI_BLANKING_FRAME_OVERHEAD 6 -#define DSI_NULL_FRAME_OVERHEAD 6 -#define DSI_EOT_PKT_SIZE 4 - #define DPHY_TX_J721E_WIZ_PLL_CTRL 0xF04 #define DPHY_TX_J721E_WIZ_STATUS 0xF08 #define DPHY_TX_J721E_WIZ_RST_CTRL 0xF0C @@ -79,6 +72,7 @@ struct cdns_dphy_cfg { u8 pll_ipdiv; u8 pll_opdiv; u16 pll_fbdiv; + u32 hs_clk_rate; unsigned int nlanes; }; @@ -99,6 +93,8 @@ struct cdns_dphy_ops { void (*set_pll_cfg)(struct cdns_dphy *dphy, const struct cdns_dphy_cfg *cfg); unsigned long (*get_wakeup_time_ns)(struct cdns_dphy *dphy); + int (*wait_for_pll_lock)(struct cdns_dphy *dphy); + int (*wait_for_cmn_ready)(struct cdns_dphy *dphy); }; struct cdns_dphy { @@ -108,6 +104,8 @@ struct cdns_dphy { struct clk *pll_ref_clk; const struct cdns_dphy_ops *ops; struct phy *phy; + bool is_configured; + bool is_powered; }; /* Order of bands is important since the index is the band number. */ @@ -116,10 +114,9 @@ static const unsigned int tx_bands[] = { 870, 950, 1000, 1200, 1400, 1600, 1800, 2000, 2200, 2500 }; -static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy, - struct cdns_dphy_cfg *cfg, - struct phy_configure_opts_mipi_dphy *opts, - unsigned int *dsi_hfp_ext) +static int cdns_dphy_get_pll_cfg(struct cdns_dphy *dphy, + struct cdns_dphy_cfg *cfg, + struct phy_configure_opts_mipi_dphy *opts) { unsigned long pll_ref_hz = clk_get_rate(dphy->pll_ref_clk); u64 dlane_bps; @@ -139,7 +136,7 @@ static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy, dlane_bps = opts->hs_clk_rate; - if (dlane_bps > 2500000000UL || dlane_bps < 160000000UL) + if (dlane_bps > 2500000000UL || dlane_bps < 80000000UL) return -EINVAL; else if (dlane_bps >= 1250000000) cfg->pll_opdiv = 1; @@ -149,11 +146,16 @@ static int cdns_dsi_get_dphy_pll_cfg(struct cdns_dphy *dphy, cfg->pll_opdiv = 4; else if (dlane_bps >= 160000000) cfg->pll_opdiv = 8; + else if (dlane_bps >= 80000000) + cfg->pll_opdiv = 16; cfg->pll_fbdiv = DIV_ROUND_UP_ULL(dlane_bps * 2 * cfg->pll_opdiv * cfg->pll_ipdiv, pll_ref_hz); + cfg->hs_clk_rate = div_u64((u64)pll_ref_hz * cfg->pll_fbdiv, + 2 * cfg->pll_opdiv * cfg->pll_ipdiv); + return 0; } @@ -191,6 +193,16 @@ static unsigned long cdns_dphy_get_wakeup_time_ns(struct cdns_dphy *dphy) return dphy->ops->get_wakeup_time_ns(dphy); } +static int cdns_dphy_wait_for_pll_lock(struct cdns_dphy *dphy) +{ + return dphy->ops->wait_for_pll_lock ? dphy->ops->wait_for_pll_lock(dphy) : 0; +} + +static int cdns_dphy_wait_for_cmn_ready(struct cdns_dphy *dphy) +{ + return dphy->ops->wait_for_cmn_ready ? dphy->ops->wait_for_cmn_ready(dphy) : 0; +} + static unsigned long cdns_dphy_ref_get_wakeup_time_ns(struct cdns_dphy *dphy) { /* Default wakeup time is 800 ns (in a simulated environment). */ @@ -232,7 +244,6 @@ static unsigned long cdns_dphy_j721e_get_wakeup_time_ns(struct cdns_dphy *dphy) static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy, const struct cdns_dphy_cfg *cfg) { - u32 status; /* * set the PWM and PLL Byteclk divider settings to recommended values @@ -249,13 +260,6 @@ static void cdns_dphy_j721e_set_pll_cfg(struct cdns_dphy *dphy, writel(DPHY_TX_J721E_WIZ_LANE_RSTB, dphy->regs + DPHY_TX_J721E_WIZ_RST_CTRL); - - readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status, - (status & DPHY_TX_WIZ_PLL_LOCK), 0, POLL_TIMEOUT_US); - - readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status, - (status & DPHY_TX_WIZ_O_CMN_READY), 0, - POLL_TIMEOUT_US); } static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div) @@ -263,6 +267,23 @@ static void cdns_dphy_j721e_set_psm_div(struct cdns_dphy *dphy, u8 div) writel(div, dphy->regs + DPHY_TX_J721E_WIZ_PSM_FREQ); } +static int cdns_dphy_j721e_wait_for_pll_lock(struct cdns_dphy *dphy) +{ + u32 status; + + return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_PLL_CTRL, status, + status & DPHY_TX_WIZ_PLL_LOCK, 0, POLL_TIMEOUT_US); +} + +static int cdns_dphy_j721e_wait_for_cmn_ready(struct cdns_dphy *dphy) +{ + u32 status; + + return readl_poll_timeout(dphy->regs + DPHY_TX_J721E_WIZ_STATUS, status, + status & DPHY_TX_WIZ_O_CMN_READY, 0, + POLL_TIMEOUT_US); +} + /* * This is the reference implementation of DPHY hooks. Specific integration of * this IP may have to re-implement some of them depending on how they decided @@ -278,6 +299,8 @@ static const struct cdns_dphy_ops j721e_dphy_ops = { .get_wakeup_time_ns = cdns_dphy_j721e_get_wakeup_time_ns, .set_pll_cfg = cdns_dphy_j721e_set_pll_cfg, .set_psm_div = cdns_dphy_j721e_set_psm_div, + .wait_for_pll_lock = cdns_dphy_j721e_wait_for_pll_lock, + .wait_for_cmn_ready = cdns_dphy_j721e_wait_for_cmn_ready, }; static int cdns_dphy_config_from_opts(struct phy *phy, @@ -285,18 +308,17 @@ static int cdns_dphy_config_from_opts(struct phy *phy, struct cdns_dphy_cfg *cfg) { struct cdns_dphy *dphy = phy_get_drvdata(phy); - unsigned int dsi_hfp_ext = 0; int ret; ret = phy_mipi_dphy_config_validate(opts); if (ret) return ret; - ret = cdns_dsi_get_dphy_pll_cfg(dphy, cfg, - opts, &dsi_hfp_ext); + ret = cdns_dphy_get_pll_cfg(dphy, cfg, opts); if (ret) return ret; + opts->hs_clk_rate = cfg->hs_clk_rate; opts->wakeup = cdns_dphy_get_wakeup_time_ns(dphy) / 1000; return 0; @@ -334,21 +356,36 @@ static int cdns_dphy_validate(struct phy *phy, enum phy_mode mode, int submode, static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) { struct cdns_dphy *dphy = phy_get_drvdata(phy); - struct cdns_dphy_cfg cfg = { 0 }; - int ret, band_ctrl; - unsigned int reg; + int ret; - ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &cfg); - if (ret) - return ret; + ret = cdns_dphy_config_from_opts(phy, &opts->mipi_dphy, &dphy->cfg); + if (!ret) + dphy->is_configured = true; + + return ret; +} + +static int cdns_dphy_power_on(struct phy *phy) +{ + struct cdns_dphy *dphy = phy_get_drvdata(phy); + int ret; + u32 reg; + + if (!dphy->is_configured || dphy->is_powered) + return -EINVAL; + + clk_prepare_enable(dphy->psm_clk); + clk_prepare_enable(dphy->pll_ref_clk); /* * Configure the internal PSM clk divider so that the DPHY has a * 1MHz clk (or something close). */ ret = cdns_dphy_setup_psm(dphy); - if (ret) - return ret; + if (ret) { + dev_err(&dphy->phy->dev, "Failed to setup PSM with error %d\n", ret); + goto err_power_on; + } /* * Configure attach clk lanes to data lanes: the DPHY has 2 clk lanes @@ -363,40 +400,61 @@ static int cdns_dphy_configure(struct phy *phy, union phy_configure_opts *opts) * Configure the DPHY PLL that will be used to generate the TX byte * clk. */ - cdns_dphy_set_pll_cfg(dphy, &cfg); + cdns_dphy_set_pll_cfg(dphy, &dphy->cfg); - band_ctrl = cdns_dphy_tx_get_band_ctrl(opts->mipi_dphy.hs_clk_rate); - if (band_ctrl < 0) - return band_ctrl; + ret = cdns_dphy_tx_get_band_ctrl(dphy->cfg.hs_clk_rate); + if (ret < 0) { + dev_err(&dphy->phy->dev, "Failed to get band control value with error %d\n", ret); + goto err_power_on; + } - reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, band_ctrl) | - FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, band_ctrl); + reg = FIELD_PREP(DPHY_BAND_CFG_LEFT_BAND, ret) | + FIELD_PREP(DPHY_BAND_CFG_RIGHT_BAND, ret); writel(reg, dphy->regs + DPHY_BAND_CFG); - return 0; -} + /* Start TX state machine. */ + reg = readl(dphy->regs + DPHY_CMN_SSM); + writel((reg & DPHY_CMN_SSM_CAL_WAIT_TIME) | DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, + dphy->regs + DPHY_CMN_SSM); -static int cdns_dphy_power_on(struct phy *phy) -{ - struct cdns_dphy *dphy = phy_get_drvdata(phy); + ret = cdns_dphy_wait_for_pll_lock(dphy); + if (ret) { + dev_err(&dphy->phy->dev, "Failed to lock PLL with error %d\n", ret); + goto err_power_on; + } - clk_prepare_enable(dphy->psm_clk); - clk_prepare_enable(dphy->pll_ref_clk); + ret = cdns_dphy_wait_for_cmn_ready(dphy); + if (ret) { + dev_err(&dphy->phy->dev, "O_CMN_READY signal failed to assert with error %d\n", + ret); + goto err_power_on; + } - /* Start TX state machine. */ - writel(DPHY_CMN_SSM_EN | DPHY_CMN_TX_MODE_EN, - dphy->regs + DPHY_CMN_SSM); + dphy->is_powered = true; return 0; + +err_power_on: + clk_disable_unprepare(dphy->pll_ref_clk); + clk_disable_unprepare(dphy->psm_clk); + + return ret; } static int cdns_dphy_power_off(struct phy *phy) { struct cdns_dphy *dphy = phy_get_drvdata(phy); + u32 reg; clk_disable_unprepare(dphy->pll_ref_clk); clk_disable_unprepare(dphy->psm_clk); + /* Stop TX state machine. */ + reg = readl(dphy->regs + DPHY_CMN_SSM); + writel(reg & ~DPHY_CMN_SSM_EN, dphy->regs + DPHY_CMN_SSM); + + dphy->is_powered = false; + return 0; } diff --git a/drivers/phy/cadence/phy-cadence-sierra.c b/drivers/phy/cadence/phy-cadence-sierra.c index 74613382ccb0..92ab1a31646a 100644 --- a/drivers/phy/cadence/phy-cadence-sierra.c +++ b/drivers/phy/cadence/phy-cadence-sierra.c @@ -2919,7 +2919,6 @@ static struct platform_driver cdns_sierra_driver = { }; module_platform_driver(cdns_sierra_driver); -MODULE_ALIAS("platform:cdns_sierra"); MODULE_AUTHOR("Cadence Design Systems"); MODULE_DESCRIPTION("CDNS sierra phy driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/freescale/phy-fsl-lynx-28g.c b/drivers/phy/freescale/phy-fsl-lynx-28g.c index f7994e8983c8..c20d2636c5e9 100644 --- a/drivers/phy/freescale/phy-fsl-lynx-28g.c +++ b/drivers/phy/freescale/phy-fsl-lynx-28g.c @@ -188,6 +188,10 @@ static struct lynx_28g_pll *lynx_28g_pll_get(struct lynx_28g_priv *priv, return pll; } + /* no pll supports requested mode, either caller forgot to check + * lynx_28g_supports_lane_mode, or this is a bug. + */ + dev_WARN_ONCE(priv->dev, 1, "no pll for interface %s\n", phy_modes(intf)); return NULL; } @@ -276,8 +280,12 @@ static void lynx_28g_lane_set_sgmii(struct lynx_28g_lane *lane) lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_SGMII, PROTO_SEL_MSK); lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_10_BIT, IF_WIDTH_MSK); - /* Switch to the PLL that works with this interface type */ + /* Find the PLL that works with this interface type */ pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_SGMII); + if (unlikely(pll == NULL)) + return; + + /* Switch to the PLL that works with this interface type */ lynx_28g_lane_set_pll(lane, pll); /* Choose the portion of clock net to be used on this lane */ @@ -312,8 +320,12 @@ static void lynx_28g_lane_set_10gbaser(struct lynx_28g_lane *lane) lynx_28g_lane_rmw(lane, LNaGCR0, PROTO_SEL_XFI, PROTO_SEL_MSK); lynx_28g_lane_rmw(lane, LNaGCR0, IF_WIDTH_20_BIT, IF_WIDTH_MSK); - /* Switch to the PLL that works with this interface type */ + /* Find the PLL that works with this interface type */ pll = lynx_28g_pll_get(priv, PHY_INTERFACE_MODE_10GBASER); + if (unlikely(pll == NULL)) + return; + + /* Switch to the PLL that works with this interface type */ lynx_28g_lane_set_pll(lane, pll); /* Choose the portion of clock net to be used on this lane */ diff --git a/drivers/phy/hisilicon/phy-hi6220-usb.c b/drivers/phy/hisilicon/phy-hi6220-usb.c index 97bd363dfe87..22d8d8a8dabe 100644 --- a/drivers/phy/hisilicon/phy-hi6220-usb.c +++ b/drivers/phy/hisilicon/phy-hi6220-usb.c @@ -161,5 +161,4 @@ static struct platform_driver hi6220_phy_driver = { module_platform_driver(hi6220_phy_driver); MODULE_DESCRIPTION("HISILICON HI6220 USB PHY driver"); -MODULE_ALIAS("platform:hi6220-usb-phy"); MODULE_LICENSE("GPL"); diff --git a/drivers/phy/hisilicon/phy-histb-combphy.c b/drivers/phy/hisilicon/phy-histb-combphy.c index 7436dcae3981..9dd0bd00b4e4 100644 --- a/drivers/phy/hisilicon/phy-histb-combphy.c +++ b/drivers/phy/hisilicon/phy-histb-combphy.c @@ -73,7 +73,7 @@ static void nano_register_write(struct histb_combphy_priv *priv, static int is_mode_fixed(struct histb_combphy_mode *mode) { - return (mode->fixed != PHY_NONE) ? true : false; + return mode->fixed != PHY_NONE; } static int histb_combphy_set_mode(struct histb_combphy_priv *priv) diff --git a/drivers/phy/ingenic/phy-ingenic-usb.c b/drivers/phy/ingenic/phy-ingenic-usb.c index eb2721f72a4c..7e62d46850fd 100644 --- a/drivers/phy/ingenic/phy-ingenic-usb.c +++ b/drivers/phy/ingenic/phy-ingenic-usb.c @@ -339,17 +339,13 @@ static int ingenic_usb_phy_probe(struct platform_device *pdev) priv->clk = devm_clk_get(dev, NULL); if (IS_ERR(priv->clk)) { err = PTR_ERR(priv->clk); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get clock\n"); - return err; + return dev_err_probe(dev, err, "Failed to get clock\n"); } priv->vcc_supply = devm_regulator_get(dev, "vcc"); if (IS_ERR(priv->vcc_supply)) { err = PTR_ERR(priv->vcc_supply); - if (err != -EPROBE_DEFER) - dev_err(dev, "Failed to get regulator\n"); - return err; + return dev_err_probe(dev, err, "Failed to get regulator\n"); } priv->phy = devm_phy_create(dev, NULL, &ingenic_usb_phy_ops); diff --git a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c index 8fcbc312fd61..651a12b59bc8 100644 --- a/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c +++ b/drivers/phy/qualcomm/phy-qcom-eusb2-repeater.c @@ -82,6 +82,14 @@ static const struct eusb2_repeater_cfg pm8550b_eusb2_cfg = { .num_vregs = ARRAY_SIZE(pm8550b_vreg_l), }; +static const struct eusb2_repeater_cfg pmiv0104_eusb2_cfg = { + /* No PMIC-specific init sequence, only board level tuning via DT */ + .init_tbl = (struct eusb2_repeater_init_tbl_reg[]) {}, + .init_tbl_num = 0, + .vreg_list = pm8550b_vreg_l, + .num_vregs = ARRAY_SIZE(pm8550b_vreg_l), +}; + static const struct eusb2_repeater_cfg smb2360_eusb2_cfg = { .init_tbl = smb2360_init_tbl, .init_tbl_num = ARRAY_SIZE(smb2360_init_tbl), @@ -136,6 +144,9 @@ static int eusb2_repeater_init(struct phy *phy) if (!of_property_read_u8(np, "qcom,tune-usb2-amplitude", &val)) regmap_write(regmap, base + EUSB2_TUNE_IUSB2, val); + if (!of_property_read_u8(np, "qcom,tune-res-fsdif", &val)) + regmap_write(regmap, base + EUSB2_TUNE_RES_FSDIF, val); + /* Wait for status OK */ ret = regmap_read_poll_timeout(regmap, base + EUSB2_RPTR_STATUS, poll_val, poll_val & RPTR_OK, 10, 5); @@ -260,6 +271,10 @@ static const struct of_device_id eusb2_repeater_of_match_table[] = { .data = &pm8550b_eusb2_cfg, }, { + .compatible = "qcom,pmiv0104-eusb2-repeater", + .data = &pmiv0104_eusb2_cfg, + }, + { .compatible = "qcom,smb2360-eusb2-repeater", .data = &smb2360_eusb2_cfg, }, diff --git a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c index 06392ed7c91b..f22c0000479f 100644 --- a/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-ipq806x-usb.c @@ -559,7 +559,6 @@ static struct platform_driver qcom_ipq806x_usb_phy_driver = { module_platform_driver(qcom_ipq806x_usb_phy_driver); -MODULE_ALIAS("platform:phy-qcom-ipq806x-usb"); MODULE_LICENSE("GPL v2"); MODULE_AUTHOR("Andy Gross <agross@codeaurora.org>"); MODULE_AUTHOR("Ivan T. Ivanov <iivanov@mm-sol.com>"); diff --git a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c index bf32572566c4..0a0d2d9fc846 100644 --- a/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-m31-eusb2.c @@ -196,7 +196,7 @@ static int m31eusb2_phy_init(struct phy *uphy) ret = clk_prepare_enable(phy->clk); if (ret) { - dev_err(&uphy->dev, "failed to enable cfg ahb clock, %d\n", ret); + dev_err(&uphy->dev, "failed to enable ref clock, %d\n", ret); goto disable_repeater; } diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index f07d097b129f..7b5af30f1d02 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -19,6 +19,7 @@ #include <linux/reset.h> #include <linux/slab.h> #include <linux/usb/typec.h> +#include <linux/usb/typec_dp.h> #include <linux/usb/typec_mux.h> #include <drm/bridge/aux-bridge.h> @@ -62,6 +63,12 @@ #define PHY_INIT_COMPLETE_TIMEOUT 10000 +enum qmpphy_mode { + QMPPHY_MODE_USB3DP = 0, + QMPPHY_MODE_DP_ONLY, + QMPPHY_MODE_USB3_ONLY, +}; + /* set of registers with offsets different per-PHY */ enum qphy_reg_layout { /* PCS registers */ @@ -1844,15 +1851,17 @@ struct qmp_combo { struct mutex phy_mutex; int init_count; + enum qmpphy_mode qmpphy_mode; struct phy *usb_phy; - enum phy_mode mode; + enum phy_mode phy_mode; unsigned int usb_init_count; struct phy *dp_phy; unsigned int dp_aux_cfg; struct phy_configure_opts_dp dp_opts; unsigned int dp_init_count; + bool dp_powered_on; struct clk_fixed_rate pipe_clk_fixed; struct clk_hw dp_link_hw; @@ -1860,6 +1869,8 @@ struct qmp_combo { struct typec_switch_dev *sw; enum typec_orientation orientation; + + struct typec_mux_dev *mux; }; static void qmp_v3_dp_aux_init(struct qmp_combo *qmp); @@ -3036,12 +3047,33 @@ static int qmp_combo_com_init(struct qmp_combo *qmp, bool force) if (qmp->orientation == TYPEC_ORIENTATION_REVERSE) val |= SW_PORTSELECT_VAL; writel(val, com + QPHY_V3_DP_COM_TYPEC_CTRL); - writel(USB3_MODE | DP_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL); - /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ - qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, - SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | - SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + switch (qmp->qmpphy_mode) { + case QMPPHY_MODE_USB3DP: + writel(USB3_MODE | DP_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL); + + /* bring both QMP USB and QMP DP PHYs PCS block out of reset */ + qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + break; + + case QMPPHY_MODE_DP_ONLY: + writel(DP_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL); + + /* bring QMP DP PHY PCS block out of reset */ + qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_DPPHY_RESET_MUX | SW_DPPHY_RESET); + break; + + case QMPPHY_MODE_USB3_ONLY: + writel(USB3_MODE, com + QPHY_V3_DP_COM_PHY_MODE_CTRL); + + /* bring QMP USB PHY PCS block out of reset */ + qphy_clrbits(com, QPHY_V3_DP_COM_RESET_OVRD_CTRL, + SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); + break; + } qphy_clrbits(com, QPHY_V3_DP_COM_SWI_CTRL, 0x03); qphy_clrbits(com, QPHY_V3_DP_COM_SW_RESET, SW_RESET); @@ -3133,6 +3165,8 @@ static int qmp_combo_dp_power_on(struct phy *phy) /* Configure link rate, swing, etc. */ cfg->configure_dp_phy(qmp); + qmp->dp_powered_on = true; + mutex_unlock(&qmp->phy_mutex); return 0; @@ -3147,6 +3181,8 @@ static int qmp_combo_dp_power_off(struct phy *phy) /* Assert DP PHY power down */ writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); + qmp->dp_powered_on = false; + mutex_unlock(&qmp->phy_mutex); return 0; @@ -3282,7 +3318,7 @@ static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submo { struct qmp_combo *qmp = phy_get_drvdata(phy); - qmp->mode = mode; + qmp->phy_mode = mode; return 0; } @@ -3311,8 +3347,8 @@ static void qmp_combo_enable_autonomous_mode(struct qmp_combo *qmp) void __iomem *pcs_misc = qmp->pcs_misc; u32 intr_mask; - if (qmp->mode == PHY_MODE_USB_HOST_SS || - qmp->mode == PHY_MODE_USB_DEVICE_SS) + if (qmp->phy_mode == PHY_MODE_USB_HOST_SS || + qmp->phy_mode == PHY_MODE_USB_DEVICE_SS) intr_mask = ARCVR_DTCT_EN | ALFPS_DTCT_EN; else intr_mask = ARCVR_DTCT_EN | ARCVR_DTCT_EVENT_SEL; @@ -3355,7 +3391,7 @@ static int __maybe_unused qmp_combo_runtime_suspend(struct device *dev) { struct qmp_combo *qmp = dev_get_drvdata(dev); - dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->mode); + dev_vdbg(dev, "Suspending QMP phy, mode:%d\n", qmp->phy_mode); if (!qmp->init_count) { dev_vdbg(dev, "PHY not initialized, bailing out\n"); @@ -3375,7 +3411,7 @@ static int __maybe_unused qmp_combo_runtime_resume(struct device *dev) struct qmp_combo *qmp = dev_get_drvdata(dev); int ret = 0; - dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->mode); + dev_vdbg(dev, "Resuming QMP phy, mode:%d\n", qmp->phy_mode); if (!qmp->init_count) { dev_vdbg(dev, "PHY not initialized, bailing out\n"); @@ -3769,17 +3805,109 @@ static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw, return 0; } -static void qmp_combo_typec_unregister(void *data) +static int qmp_combo_typec_mux_set(struct typec_mux_dev *mux, struct typec_mux_state *state) +{ + struct qmp_combo *qmp = typec_mux_get_drvdata(mux); + const struct qmp_phy_cfg *cfg = qmp->cfg; + enum qmpphy_mode new_mode; + unsigned int svid; + + guard(mutex)(&qmp->phy_mutex); + + if (state->alt) + svid = state->alt->svid; + else + svid = 0; + + if (svid == USB_TYPEC_DP_SID) { + switch (state->mode) { + /* DP Only */ + case TYPEC_DP_STATE_C: + case TYPEC_DP_STATE_E: + new_mode = QMPPHY_MODE_DP_ONLY; + break; + + /* DP + USB */ + case TYPEC_DP_STATE_D: + case TYPEC_DP_STATE_F: + + /* Safe fallback...*/ + default: + new_mode = QMPPHY_MODE_USB3DP; + break; + } + } else { + /* No DP SVID => don't care, assume it's just USB3 */ + new_mode = QMPPHY_MODE_USB3_ONLY; + } + + if (new_mode == qmp->qmpphy_mode) { + dev_dbg(qmp->dev, "typec_mux_set: same qmpphy mode, bail out\n"); + return 0; + } + + if (qmp->qmpphy_mode != QMPPHY_MODE_USB3_ONLY && qmp->dp_powered_on) { + dev_dbg(qmp->dev, "typec_mux_set: DP PHY is still in use, delaying switch\n"); + return 0; + } + + dev_dbg(qmp->dev, "typec_mux_set: switching from qmpphy mode %d to %d\n", + qmp->qmpphy_mode, new_mode); + + qmp->qmpphy_mode = new_mode; + + if (qmp->init_count) { + if (qmp->usb_init_count) + qmp_combo_usb_power_off(qmp->usb_phy); + + if (qmp->dp_init_count) + writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); + + qmp_combo_com_exit(qmp, true); + + /* Now everything's powered down, power up the right PHYs */ + qmp_combo_com_init(qmp, true); + + if (new_mode == QMPPHY_MODE_DP_ONLY) { + if (qmp->usb_init_count) + qmp->usb_init_count--; + } + + if (new_mode == QMPPHY_MODE_USB3DP || new_mode == QMPPHY_MODE_USB3_ONLY) { + qmp_combo_usb_power_on(qmp->usb_phy); + if (!qmp->usb_init_count) + qmp->usb_init_count++; + } + + if (new_mode == QMPPHY_MODE_DP_ONLY || new_mode == QMPPHY_MODE_USB3DP) { + if (qmp->dp_init_count) + cfg->dp_aux_init(qmp); + } + } + + return 0; +} + +static void qmp_combo_typec_switch_unregister(void *data) { struct qmp_combo *qmp = data; typec_switch_unregister(qmp->sw); } -static int qmp_combo_typec_switch_register(struct qmp_combo *qmp) +static void qmp_combo_typec_mux_unregister(void *data) +{ + struct qmp_combo *qmp = data; + + typec_mux_unregister(qmp->mux); +} + +static int qmp_combo_typec_register(struct qmp_combo *qmp) { struct typec_switch_desc sw_desc = {}; + struct typec_mux_desc mux_desc = { }; struct device *dev = qmp->dev; + int ret; sw_desc.drvdata = qmp; sw_desc.fwnode = dev->fwnode; @@ -3790,10 +3918,23 @@ static int qmp_combo_typec_switch_register(struct qmp_combo *qmp) return PTR_ERR(qmp->sw); } - return devm_add_action_or_reset(dev, qmp_combo_typec_unregister, qmp); + ret = devm_add_action_or_reset(dev, qmp_combo_typec_switch_unregister, qmp); + if (ret) + return ret; + + mux_desc.drvdata = qmp; + mux_desc.fwnode = dev->fwnode; + mux_desc.set = qmp_combo_typec_mux_set; + qmp->mux = typec_mux_register(dev, &mux_desc); + if (IS_ERR(qmp->mux)) { + dev_err(dev, "Unable to register typec mux: %pe\n", qmp->mux); + return PTR_ERR(qmp->mux); + } + + return devm_add_action_or_reset(dev, qmp_combo_typec_mux_unregister, qmp); } #else -static int qmp_combo_typec_switch_register(struct qmp_combo *qmp) +static int qmp_combo_typec_register(struct qmp_combo *qmp) { return 0; } @@ -4026,7 +4167,7 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) goto err_node_put; - ret = qmp_combo_typec_switch_register(qmp); + ret = qmp_combo_typec_register(qmp); if (ret) goto err_node_put; @@ -4048,6 +4189,12 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) goto err_node_put; + /* + * The hw default is USB3_ONLY, but USB3+DP mode lets us more easily + * check both sub-blocks' init tables for blunders at probe time. + */ + qmp->qmpphy_mode = QMPPHY_MODE_USB3DP; + qmp->usb_phy = devm_phy_create(dev, usb_np, &qmp_combo_usb_phy_ops); if (IS_ERR(qmp->usb_phy)) { ret = PTR_ERR(qmp->usb_phy); diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c index 0fa63b734b67..62b1c845b627 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcie.c @@ -93,6 +93,13 @@ static const unsigned int pciephy_v6_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V6_PCS_POWER_DOWN_CONTROL, }; +static const unsigned int pciephy_v7_regs_layout[QPHY_LAYOUT_SIZE] = { + [QPHY_SW_RESET] = QPHY_V7_PCS_SW_RESET, + [QPHY_START_CTRL] = QPHY_V7_PCS_START_CONTROL, + [QPHY_PCS_STATUS] = QPHY_V7_PCS_PCS_STATUS1, + [QPHY_PCS_POWER_DOWN_CONTROL] = QPHY_V7_PCS_POWER_DOWN_CONTROL, +}; + static const struct qmp_phy_init_tbl msm8998_pcie_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V3_COM_BIAS_EN_CLKBUFLR_EN, 0x14), QMP_PHY_INIT_CFG(QSERDES_V3_COM_CLK_SELECT, 0x30), @@ -2590,6 +2597,108 @@ static const struct qmp_phy_init_tbl sm8650_qmp_gen4x2_pcie_rx_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V6_20_RX_MODE_RATE3_B6, 0xff), }; +static const struct qmp_phy_init_tbl sm8750_qmp_gen3x2_pcie_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_EN_CENTER, 0x1), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_PER1, 0x62), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_PER2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_STEP_SIZE1_MODE0, 0xf8), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_STEP_SIZE2_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_STEP_SIZE1_MODE1, 0x93), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SSC_STEP_SIZE2_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CLK_ENABLE1, 0x90), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SYS_CLK_CTRL, 0x82), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_PLL_IVCO, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CP_CTRL_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CP_CTRL_MODE1, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_PLL_RCTRL_MODE1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_PLL_CCTRL_MODE0, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_PLL_CCTRL_MODE1, 0x36), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_SYSCLK_EN_SEL, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_BG_TIMER, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP_EN, 0x42), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP1_MODE0, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP2_MODE0, 0x0d), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP1_MODE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_LOCK_CMP2_MODE1, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DEC_START_MODE0, 0x41), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DEC_START_MODE1, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START3_MODE0, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START1_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START2_MODE1, 0x55), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_DIV_FRAC_START3_MODE1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_VCO_TUNE_MAP, 0x14), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CLK_SELECT, 0x34), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_HSCLK_SEL_1, 0x01), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CORECLK_DIV_MODE1, 0x04), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CMN_CONFIG_1, 0x16), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_ADDITIONAL_MISC_3, 0x0F), + QMP_PHY_INIT_CFG(QSERDES_V7_COM_CORE_CLK_EN, 0xA0), +}; + +static const struct qmp_phy_init_tbl sm8750_qmp_gen3x2_pcie_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V7_RX_DFE_CTLE_POST_CAL_OFFSET, 0x38), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_GM_CAL, 0x11), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_HIGH, 0xBF), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_HIGH2, 0xBF), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_HIGH3, 0xB7), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_HIGH4, 0xEA), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_00_LOW, 0x3F), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_HIGH, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_HIGH2, 0x49), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_HIGH3, 0x1B), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_HIGH4, 0x9C), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_01_LOW, 0xD1), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_10_HIGH, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_10_HIGH2, 0x49), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_10_HIGH3, 0x1B), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_10_HIGH4, 0x9C), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_MODE_10_LOW, 0xD1), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_TX_ADAPT_PRE_THRESH1, 0x3E), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_TX_ADAPT_PRE_THRESH2, 0x1E), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_TX_ADAPT_POST_THRESH, 0xD2), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_FO_GAIN, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_SO_GAIN, 0x05), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_SB2_THRESH1, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_UCDR_SB2_THRESH2, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_VGA_CAL_CNTRL2, 0x09), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_SIGDET_ENABLES, 0x1C), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_SIGDET_CNTRL, 0x60), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_RX_IDAC_TSETTLE_LOW, 0x07), + QMP_PHY_INIT_CFG(QSERDES_V7_RX_SIGDET_CAL_TRIM, 0x08), +}; + +static const struct qmp_phy_init_tbl sm8750_qmp_gen3x2_pcie_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_V7_TX_LANE_MODE_1, 0x35), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_LANE_MODE_3, 0x10), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_LANE_MODE_4, 0x31), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_LANE_MODE_5, 0x7F), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_PI_QEC_CTRL, 0x02), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_RES_CODE_LANE_OFFSET_RX, 0x08), + QMP_PHY_INIT_CFG(QSERDES_V7_TX_RES_CODE_LANE_OFFSET_TX, 0x14), +}; + +static const struct qmp_phy_init_tbl sm8750_qmp_gen3x2_pcie_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V7_PCS_REFGEN_REQ_CONFIG1, 0x05), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_RX_SIGDET_LVL, 0x77), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_RATE_SLEW_CNTRL1, 0x0B), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_EQ_CONFIG2, 0x0F), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_PCS_TX_RX_CONFIG, 0x8C), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_G12S1_TXDEEMPH_M6DB, 0x17), + QMP_PHY_INIT_CFG(QPHY_V7_PCS_G3S2_PRE_GAIN, 0x2E), +}; + +static const struct qmp_phy_init_tbl sm8750_qmp_gen3x2_pcie_pcs_misc_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_EQ_CONFIG1, 0x1E), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_RXEQEVAL_TIME, 0x27), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_POWER_STATE_CONFIG2, 0x1D), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_POWER_STATE_CONFIG4, 0x07), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_ENDPOINT_REFCLK_DRIVE, 0xC1), + QMP_PHY_INIT_CFG(QPHY_PCIE_V6_PCS_PCIE_OSC_DTCT_ACTIONS, 0x00), +}; + static const struct qmp_phy_init_tbl sa8775p_qmp_gen4x2_pcie_serdes_alt_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_V5_COM_BIAS_EN_CLKBUFLR_EN, 0x14), QMP_PHY_INIT_CFG(QSERDES_V5_COM_PLL_IVCO, 0x0f), @@ -3215,6 +3324,16 @@ static const struct qmp_pcie_offsets qmp_pcie_offsets_v5_30 = { .rx2 = 0x3a00, }; +static const struct qmp_pcie_offsets qmp_pcie_offsets_v7 = { + .serdes = 0x0, + .pcs = 0x400, + .pcs_misc = 0x800, + .tx = 0x1000, + .rx = 0x1200, + .tx2 = 0x1800, + .rx2 = 0x1a00, +}; + static const struct qmp_pcie_offsets qmp_pcie_offsets_v6_20 = { .serdes = 0x1000, .pcs = 0x1200, @@ -4004,6 +4123,33 @@ static const struct qmp_phy_cfg sm8550_qmp_gen3x2_pciephy_cfg = { .phy_status = PHYSTATUS, }; +static const struct qmp_phy_cfg sm8750_qmp_gen3x2_pciephy_cfg = { + .lanes = 2, + + .offsets = &qmp_pcie_offsets_v7, + + .tbls = { + .serdes = sm8750_qmp_gen3x2_pcie_serdes_tbl, + .serdes_num = ARRAY_SIZE(sm8750_qmp_gen3x2_pcie_serdes_tbl), + .tx = sm8750_qmp_gen3x2_pcie_tx_tbl, + .tx_num = ARRAY_SIZE(sm8750_qmp_gen3x2_pcie_tx_tbl), + .rx = sm8750_qmp_gen3x2_pcie_rx_tbl, + .rx_num = ARRAY_SIZE(sm8750_qmp_gen3x2_pcie_rx_tbl), + .pcs = sm8750_qmp_gen3x2_pcie_pcs_tbl, + .pcs_num = ARRAY_SIZE(sm8750_qmp_gen3x2_pcie_pcs_tbl), + .pcs_misc = sm8750_qmp_gen3x2_pcie_pcs_misc_tbl, + .pcs_misc_num = ARRAY_SIZE(sm8750_qmp_gen3x2_pcie_pcs_misc_tbl), + }, + .reset_list = sdm845_pciephy_reset_l, + .num_resets = ARRAY_SIZE(sdm845_pciephy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = pciephy_v7_regs_layout, + + .pwrdn_ctrl = SW_PWRDN | REFCLK_DRV_DSBL, + .phy_status = PHYSTATUS, +}; + static const struct qmp_phy_cfg sm8550_qmp_gen4x2_pciephy_cfg = { .lanes = 2, @@ -5113,6 +5259,9 @@ static const struct of_device_id qmp_pcie_of_match_table[] = { .compatible = "qcom,sm8650-qmp-gen4x2-pcie-phy", .data = &sm8650_qmp_gen4x2_pciephy_cfg, }, { + .compatible = "qcom,sm8750-qmp-gen3x2-pcie-phy", + .data = &sm8750_qmp_gen3x2_pciephy_cfg, + }, { .compatible = "qcom,x1e80100-qmp-gen3x2-pcie-phy", .data = &sm8550_qmp_gen3x2_pciephy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v7.h index c7759892ed2e..4b7fcaa6a374 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v7.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-pcs-v7.h @@ -17,6 +17,8 @@ #define QPHY_V7_PCS_LOCK_DETECT_CONFIG3 0x0cc #define QPHY_V7_PCS_LOCK_DETECT_CONFIG6 0x0d8 #define QPHY_V7_PCS_REFGEN_REQ_CONFIG1 0x0dc +#define QPHY_V7_PCS_G12S1_TXDEEMPH_M6DB 0x168 +#define QPHY_V7_PCS_G3S2_PRE_GAIN 0x170 #define QPHY_V7_PCS_RX_SIGDET_LVL 0x188 #define QPHY_V7_PCS_RCVR_DTCT_DLY_P1U2_L 0x190 #define QPHY_V7_PCS_RCVR_DTCT_DLY_P1U2_H 0x194 diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v7.h b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v7.h index 91f865b11347..6ab943ff57ff 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v7.h +++ b/drivers/phy/qualcomm/phy-qcom-qmp-qserdes-txrx-v7.h @@ -40,6 +40,8 @@ #define QSERDES_V7_RX_UCDR_SB2_GAIN1 0x54 #define QSERDES_V7_RX_UCDR_SB2_GAIN2 0x58 #define QSERDES_V7_RX_AUX_DATA_TCOARSE_TFINE 0x60 +#define QSERDES_V7_RX_TX_ADAPT_PRE_THRESH1 0xc4 +#define QSERDES_V7_RX_TX_ADAPT_PRE_THRESH2 0xc8 #define QSERDES_V7_RX_TX_ADAPT_POST_THRESH 0xcc #define QSERDES_V7_RX_VGA_CAL_CNTRL1 0xd4 #define QSERDES_V7_RX_VGA_CAL_CNTRL2 0xd8 @@ -50,7 +52,7 @@ #define QSERDES_V7_RX_RX_IDAC_TSETTLE_LOW 0xf8 #define QSERDES_V7_RX_RX_IDAC_TSETTLE_HIGH 0xfc #define QSERDES_V7_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 0x110 -#define QSERDES_V7_RX_SIDGET_ENABLES 0x118 +#define QSERDES_V7_RX_SIGDET_ENABLES 0x118 #define QSERDES_V7_RX_SIGDET_CNTRL 0x11c #define QSERDES_V7_RX_SIGDET_DEGLITCH_CNTRL 0x124 #define QSERDES_V7_RX_RX_MODE_00_LOW 0x15c diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c index 9c69c77d10c8..8a280433a42b 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-ufs.c @@ -1107,7 +1107,7 @@ struct qmp_phy_cfg { const struct qmp_phy_cfg_tbls tbls_hs_overlay[NUM_OVERLAY]; /* regulators to be requested */ - const char * const *vreg_list; + const struct regulator_bulk_data *vreg_list; int num_vregs; /* array of registers with different offsets */ @@ -1164,9 +1164,80 @@ static inline void qphy_clrbits(void __iomem *base, u32 offset, u32 val) readl(base + offset); } -/* list of regulators */ -static const char * const qmp_phy_vreg_l[] = { - "vdda-phy", "vdda-pll", +/* Regulator bulk data with load values for specific configurations */ +static const struct regulator_bulk_data msm8996_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 51400 }, + { .supply = "vdda-pll", .init_load_uA = 14600 }, +}; + +static const struct regulator_bulk_data sa8775p_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 137000 }, + { .supply = "vdda-pll", .init_load_uA = 18300 }, +}; + +static const struct regulator_bulk_data sc7280_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 97500 }, + { .supply = "vdda-pll", .init_load_uA = 18400 }, +}; + +static const struct regulator_bulk_data sc8280xp_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 85700 }, + { .supply = "vdda-pll", .init_load_uA = 18300 }, +}; + +static const struct regulator_bulk_data sdm845_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 51400 }, + { .supply = "vdda-pll", .init_load_uA = 14600 }, +}; + +static const struct regulator_bulk_data sm6115_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 51400 }, + { .supply = "vdda-pll", .init_load_uA = 14200 }, +}; + +static const struct regulator_bulk_data sm7150_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 62900 }, + { .supply = "vdda-pll", .init_load_uA = 18300 }, +}; + +static const struct regulator_bulk_data sm8150_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 90200 }, + { .supply = "vdda-pll", .init_load_uA = 19000 }, +}; + +static const struct regulator_bulk_data sm8250_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 89900 }, + { .supply = "vdda-pll", .init_load_uA = 18800 }, +}; + +static const struct regulator_bulk_data sm8350_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 91600 }, + { .supply = "vdda-pll", .init_load_uA = 19000 }, +}; + +static const struct regulator_bulk_data sm8450_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 173000 }, + { .supply = "vdda-pll", .init_load_uA = 24900 }, +}; + +static const struct regulator_bulk_data sm8475_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 213030 }, + { .supply = "vdda-pll", .init_load_uA = 18340 }, +}; + +static const struct regulator_bulk_data sm8550_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 188000 }, + { .supply = "vdda-pll", .init_load_uA = 18300 }, +}; + +static const struct regulator_bulk_data sm8650_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 205000 }, + { .supply = "vdda-pll", .init_load_uA = 17500 }, +}; + +static const struct regulator_bulk_data sm8750_ufsphy_vreg_l[] = { + { .supply = "vdda-phy", .init_load_uA = 213000 }, + { .supply = "vdda-pll", .init_load_uA = 18300 }, }; static const struct qmp_ufs_offsets qmp_ufs_offsets = { @@ -1202,8 +1273,8 @@ static const struct qmp_phy_cfg msm8996_ufsphy_cfg = { .rx_num = ARRAY_SIZE(msm8996_ufsphy_rx), }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = msm8996_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(msm8996_ufsphy_vreg_l), .regs = ufsphy_v2_regs_layout, @@ -1239,8 +1310,8 @@ static const struct qmp_phy_cfg sa8775p_ufsphy_cfg = { .pcs_num = ARRAY_SIZE(sm8350_ufsphy_g4_pcs), .max_gear = UFS_HS_G4, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sa8775p_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sa8775p_ufsphy_vreg_l), .regs = ufsphy_v5_regs_layout, }; @@ -1273,8 +1344,8 @@ static const struct qmp_phy_cfg sc7280_ufsphy_cfg = { .pcs_num = ARRAY_SIZE(sm8150_ufsphy_hs_g4_pcs), .max_gear = UFS_HS_G4, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sc7280_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sc7280_ufsphy_vreg_l), .regs = ufsphy_v4_regs_layout, }; @@ -1307,8 +1378,8 @@ static const struct qmp_phy_cfg sc8280xp_ufsphy_cfg = { .pcs_num = ARRAY_SIZE(sm8350_ufsphy_g4_pcs), .max_gear = UFS_HS_G4, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sc8280xp_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sc8280xp_ufsphy_vreg_l), .regs = ufsphy_v5_regs_layout, }; @@ -1332,8 +1403,8 @@ static const struct qmp_phy_cfg sdm845_ufsphy_cfg = { .serdes = sdm845_ufsphy_hs_b_serdes, .serdes_num = ARRAY_SIZE(sdm845_ufsphy_hs_b_serdes), }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sdm845_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sdm845_ufsphy_vreg_l), .regs = ufsphy_v3_regs_layout, .no_pcs_sw_reset = true, @@ -1359,8 +1430,8 @@ static const struct qmp_phy_cfg sm6115_ufsphy_cfg = { .serdes = sm6115_ufsphy_hs_b_serdes, .serdes_num = ARRAY_SIZE(sm6115_ufsphy_hs_b_serdes), }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm6115_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm6115_ufsphy_vreg_l), .regs = ufsphy_v2_regs_layout, .no_pcs_sw_reset = true, @@ -1386,8 +1457,8 @@ static const struct qmp_phy_cfg sm7150_ufsphy_cfg = { .serdes = sdm845_ufsphy_hs_b_serdes, .serdes_num = ARRAY_SIZE(sdm845_ufsphy_hs_b_serdes), }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm7150_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm7150_ufsphy_vreg_l), .regs = ufsphy_v3_regs_layout, .no_pcs_sw_reset = true, @@ -1422,8 +1493,8 @@ static const struct qmp_phy_cfg sm8150_ufsphy_cfg = { .pcs_num = ARRAY_SIZE(sm8150_ufsphy_hs_g4_pcs), .max_gear = UFS_HS_G4, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm8150_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8150_ufsphy_vreg_l), .regs = ufsphy_v4_regs_layout, }; @@ -1456,8 +1527,8 @@ static const struct qmp_phy_cfg sm8250_ufsphy_cfg = { .pcs_num = ARRAY_SIZE(sm8150_ufsphy_hs_g4_pcs), .max_gear = UFS_HS_G4, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm8250_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8250_ufsphy_vreg_l), .regs = ufsphy_v4_regs_layout, }; @@ -1490,8 +1561,8 @@ static const struct qmp_phy_cfg sm8350_ufsphy_cfg = { .pcs_num = ARRAY_SIZE(sm8350_ufsphy_g4_pcs), .max_gear = UFS_HS_G4, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm8350_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8350_ufsphy_vreg_l), .regs = ufsphy_v5_regs_layout, }; @@ -1524,8 +1595,8 @@ static const struct qmp_phy_cfg sm8450_ufsphy_cfg = { .pcs_num = ARRAY_SIZE(sm8350_ufsphy_g4_pcs), .max_gear = UFS_HS_G4, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm8450_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8450_ufsphy_vreg_l), .regs = ufsphy_v5_regs_layout, }; @@ -1560,8 +1631,8 @@ static const struct qmp_phy_cfg sm8475_ufsphy_cfg = { .pcs_num = ARRAY_SIZE(sm8475_ufsphy_g4_pcs), .max_gear = UFS_HS_G4, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm8475_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8475_ufsphy_vreg_l), .regs = ufsphy_v6_regs_layout, }; @@ -1605,8 +1676,8 @@ static const struct qmp_phy_cfg sm8550_ufsphy_cfg = { .pcs_num = ARRAY_SIZE(sm8550_ufsphy_g5_pcs), .max_gear = UFS_HS_G5, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm8550_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8550_ufsphy_vreg_l), .regs = ufsphy_v6_regs_layout, }; @@ -1637,8 +1708,8 @@ static const struct qmp_phy_cfg sm8650_ufsphy_cfg = { .max_gear = UFS_HS_G5, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm8650_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8650_ufsphy_vreg_l), .regs = ufsphy_v6_regs_layout, }; @@ -1675,8 +1746,8 @@ static const struct qmp_phy_cfg sm8750_ufsphy_cfg = { .max_gear = UFS_HS_G5, }, - .vreg_list = qmp_phy_vreg_l, - .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .vreg_list = sm8750_ufsphy_vreg_l, + .num_vregs = ARRAY_SIZE(sm8750_ufsphy_vreg_l), .regs = ufsphy_v6_regs_layout, }; @@ -1890,22 +1961,6 @@ static const struct phy_ops qcom_qmp_ufs_phy_ops = { .owner = THIS_MODULE, }; -static int qmp_ufs_vreg_init(struct qmp_ufs *qmp) -{ - const struct qmp_phy_cfg *cfg = qmp->cfg; - struct device *dev = qmp->dev; - int num = cfg->num_vregs; - int i; - - qmp->vregs = devm_kcalloc(dev, num, sizeof(*qmp->vregs), GFP_KERNEL); - if (!qmp->vregs) - return -ENOMEM; - - for (i = 0; i < num; i++) - qmp->vregs[i].supply = cfg->vreg_list[i]; - - return devm_regulator_bulk_get(dev, num, qmp->vregs); -} static int qmp_ufs_clk_init(struct qmp_ufs *qmp) { @@ -2068,7 +2123,9 @@ static int qmp_ufs_probe(struct platform_device *pdev) if (ret) return ret; - ret = qmp_ufs_vreg_init(qmp); + ret = devm_regulator_bulk_get_const(dev, qmp->cfg->num_vregs, + qmp->cfg->vreg_list, + &qmp->vregs); if (ret) return ret; diff --git a/drivers/phy/renesas/phy-rcar-gen3-usb2.c b/drivers/phy/renesas/phy-rcar-gen3-usb2.c index 47beb94cd424..3f6b480e1092 100644 --- a/drivers/phy/renesas/phy-rcar-gen3-usb2.c +++ b/drivers/phy/renesas/phy-rcar-gen3-usb2.c @@ -9,6 +9,8 @@ * Copyright (C) 2014 Cogent Embedded, Inc. */ +#include <linux/bitfield.h> +#include <linux/bits.h> #include <linux/cleanup.h> #include <linux/extcon-provider.h> #include <linux/interrupt.h> @@ -69,14 +71,20 @@ #define USB2_COMMCTRL_OTG_PERI BIT(31) /* 1 = Peripheral mode */ /* OBINTSTA and OBINTEN */ +#define USB2_OBINTSTA_CLEAR GENMASK(31, 0) #define USB2_OBINT_SESSVLDCHG BIT(12) #define USB2_OBINT_IDDIGCHG BIT(11) -#define USB2_OBINT_BITS (USB2_OBINT_SESSVLDCHG | \ - USB2_OBINT_IDDIGCHG) +#define USB2_OBINT_VBSTAINT BIT(3) +#define USB2_OBINT_IDCHG_EN BIT(0) /* RZ/G2L specific */ /* VBCTRL */ +#define USB2_VBCTRL_VBSTA_MASK GENMASK(31, 28) +#define USB2_VBCTRL_VBSTA_DEFAULT 2 +#define USB2_VBCTRL_VBLVL_MASK GENMASK(23, 20) +#define USB2_VBCTRL_VBLVL(m) FIELD_PREP_CONST(USB2_VBCTRL_VBLVL_MASK, (m)) #define USB2_VBCTRL_OCCLREN BIT(16) #define USB2_VBCTRL_DRVVBUSSEL BIT(8) +#define USB2_VBCTRL_SIDDQREL BIT(2) #define USB2_VBCTRL_VBOUT BIT(0) /* LINECTRL1 */ @@ -89,11 +97,11 @@ /* ADPCTRL */ #define USB2_ADPCTRL_OTGSESSVLD BIT(20) #define USB2_ADPCTRL_IDDIG BIT(19) +#define USB2_ADPCTRL_VBUSVALID BIT(18) #define USB2_ADPCTRL_IDPULLUP BIT(5) /* 1 = ID sampling is enabled */ #define USB2_ADPCTRL_DRVVBUS BIT(4) /* RZ/G2L specific */ -#define USB2_OBINT_IDCHG_EN BIT(0) #define USB2_LINECTRL1_USB2_IDMON BIT(0) #define NUM_OF_PHYS 4 @@ -122,6 +130,7 @@ struct rcar_gen3_phy { struct rcar_gen3_chan { void __iomem *base; struct device *dev; /* platform_device's device */ + const struct rcar_gen3_phy_drv_data *phy_data; struct extcon_dev *extcon; struct rcar_gen3_phy rphys[NUM_OF_PHYS]; struct regulator *vbus; @@ -129,12 +138,9 @@ struct rcar_gen3_chan { struct work_struct work; spinlock_t lock; /* protects access to hardware and driver data structure. */ enum usb_dr_mode dr_mode; - u32 obint_enable_bits; bool extcon_host; bool is_otg_channel; bool uses_otg_pins; - bool soc_no_adp_ctrl; - bool utmi_ctrl; }; struct rcar_gen3_phy_drv_data { @@ -142,6 +148,8 @@ struct rcar_gen3_phy_drv_data { bool no_adp_ctrl; bool init_bus; bool utmi_ctrl; + bool vblvl_ctrl; + u32 obint_enable_bits; }; /* @@ -203,8 +211,7 @@ static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) u32 vbus_ctrl_val = USB2_ADPCTRL_DRVVBUS; u32 val; - dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, vbus); - if (ch->soc_no_adp_ctrl) { + if (ch->phy_data->no_adp_ctrl || ch->phy_data->vblvl_ctrl) { if (ch->vbus) regulator_hardware_enable(ch->vbus, vbus); @@ -217,6 +224,7 @@ static void rcar_gen3_enable_vbus_ctrl(struct rcar_gen3_chan *ch, int vbus) val |= vbus_ctrl_val; else val &= ~vbus_ctrl_val; + dev_vdbg(ch->dev, "%s: %08x, %d\n", __func__, val, vbus); writel(val, usb2_base + vbus_ctrl_reg); } @@ -226,9 +234,9 @@ static void rcar_gen3_control_otg_irq(struct rcar_gen3_chan *ch, int enable) u32 val = readl(usb2_base + USB2_OBINTEN); if (ch->uses_otg_pins && enable) - val |= ch->obint_enable_bits; + val |= ch->phy_data->obint_enable_bits; else - val &= ~ch->obint_enable_bits; + val &= ~ch->phy_data->obint_enable_bits; writel(val, usb2_base + USB2_OBINTEN); } @@ -287,10 +295,20 @@ static void rcar_gen3_init_from_a_peri_to_a_host(struct rcar_gen3_chan *ch) static bool rcar_gen3_check_id(struct rcar_gen3_chan *ch) { + if (ch->phy_data->vblvl_ctrl) { + bool vbus_valid; + bool device; + + device = !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); + vbus_valid = !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_VBUSVALID); + + return vbus_valid ? device : !device; + } + if (!ch->uses_otg_pins) - return (ch->dr_mode == USB_DR_MODE_HOST) ? false : true; + return ch->dr_mode != USB_DR_MODE_HOST; - if (ch->soc_no_adp_ctrl) + if (ch->phy_data->no_adp_ctrl) return !!(readl(ch->base + USB2_LINECTRL1) & USB2_LINECTRL1_USB2_IDMON); return !!(readl(ch->base + USB2_ADPCTRL) & USB2_ADPCTRL_IDDIG); @@ -421,21 +439,47 @@ static void rcar_gen3_init_otg(struct rcar_gen3_chan *ch) USB2_LINECTRL1_DMRPD_EN | USB2_LINECTRL1_DM_RPD; writel(val, usb2_base + USB2_LINECTRL1); - if (!ch->soc_no_adp_ctrl) { - val = readl(usb2_base + USB2_VBCTRL); - val &= ~USB2_VBCTRL_OCCLREN; - writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); - val = readl(usb2_base + USB2_ADPCTRL); - writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); + if (!ch->phy_data->no_adp_ctrl) { + if (ch->phy_data->vblvl_ctrl) { + val = readl(usb2_base + USB2_VBCTRL); + val = (val & ~USB2_VBCTRL_VBLVL_MASK) | USB2_VBCTRL_VBLVL(2); + writel(val, usb2_base + USB2_VBCTRL); + val = readl(usb2_base + USB2_ADPCTRL); + writel(val | USB2_ADPCTRL_IDPULLUP | USB2_ADPCTRL_DRVVBUS, + usb2_base + USB2_ADPCTRL); + } else { + val = readl(usb2_base + USB2_VBCTRL); + val &= ~USB2_VBCTRL_OCCLREN; + writel(val | USB2_VBCTRL_DRVVBUSSEL, usb2_base + USB2_VBCTRL); + val = readl(usb2_base + USB2_ADPCTRL); + writel(val | USB2_ADPCTRL_IDPULLUP, usb2_base + USB2_ADPCTRL); + } } mdelay(20); writel(0xffffffff, usb2_base + USB2_OBINTSTA); - writel(ch->obint_enable_bits, usb2_base + USB2_OBINTEN); + writel(ch->phy_data->obint_enable_bits, usb2_base + USB2_OBINTEN); rcar_gen3_device_recognition(ch); } +static void rcar_gen3_configure_vblvl_ctrl(struct rcar_gen3_chan *ch) +{ + void __iomem *usb2_base = ch->base; + u32 val; + + if (!ch->phy_data->vblvl_ctrl) + return; + + val = readl(usb2_base + USB2_VBCTRL); + if ((val & USB2_VBCTRL_VBSTA_MASK) == + FIELD_PREP_CONST(USB2_VBCTRL_VBSTA_MASK, USB2_VBCTRL_VBSTA_DEFAULT)) + val &= ~USB2_VBCTRL_VBLVL_MASK; + else + val |= USB2_VBCTRL_VBLVL(USB2_VBCTRL_VBSTA_DEFAULT); + writel(val, usb2_base + USB2_VBCTRL); +} + static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) { struct rcar_gen3_chan *ch = _ch; @@ -451,10 +495,14 @@ static irqreturn_t rcar_gen3_phy_usb2_irq(int irq, void *_ch) scoped_guard(spinlock, &ch->lock) { status = readl(usb2_base + USB2_OBINTSTA); - if (status & ch->obint_enable_bits) { + if (status & ch->phy_data->obint_enable_bits) { dev_vdbg(dev, "%s: %08x\n", __func__, status); - writel(ch->obint_enable_bits, usb2_base + USB2_OBINTSTA); + if (ch->phy_data->vblvl_ctrl) + writel(USB2_OBINTSTA_CLEAR, usb2_base + USB2_OBINTSTA); + else + writel(ch->phy_data->obint_enable_bits, usb2_base + USB2_OBINTSTA); rcar_gen3_device_recognition(ch); + rcar_gen3_configure_vblvl_ctrl(ch); ret = IRQ_HANDLED; } } @@ -487,7 +535,14 @@ static int rcar_gen3_phy_usb2_init(struct phy *p) if (rphy->int_enable_bits) rcar_gen3_init_otg(channel); - if (channel->utmi_ctrl) { + if (channel->phy_data->vblvl_ctrl) { + /* SIDDQ mode release */ + writel(readl(usb2_base + USB2_VBCTRL) | USB2_VBCTRL_SIDDQREL, + usb2_base + USB2_VBCTRL); + udelay(250); + } + + if (channel->phy_data->utmi_ctrl) { val = readl(usb2_base + USB2_REGEN_CG_CTRL) | USB2_REGEN_CG_CTRL_UPHY_WEN; writel(val, usb2_base + USB2_REGEN_CG_CTRL); @@ -592,28 +647,41 @@ static const struct phy_ops rz_g1c_phy_usb2_ops = { static const struct rcar_gen3_phy_drv_data rcar_gen3_phy_usb2_data = { .phy_usb2_ops = &rcar_gen3_phy_usb2_ops, .no_adp_ctrl = false, + .obint_enable_bits = USB2_OBINT_SESSVLDCHG | + USB2_OBINT_IDDIGCHG, }; static const struct rcar_gen3_phy_drv_data rz_g1c_phy_usb2_data = { .phy_usb2_ops = &rz_g1c_phy_usb2_ops, .no_adp_ctrl = false, + .obint_enable_bits = USB2_OBINT_SESSVLDCHG | + USB2_OBINT_IDDIGCHG, }; static const struct rcar_gen3_phy_drv_data rz_g2l_phy_usb2_data = { .phy_usb2_ops = &rcar_gen3_phy_usb2_ops, .no_adp_ctrl = true, + .obint_enable_bits = USB2_OBINT_IDCHG_EN, }; static const struct rcar_gen3_phy_drv_data rz_g3s_phy_usb2_data = { .phy_usb2_ops = &rcar_gen3_phy_usb2_ops, .no_adp_ctrl = true, .init_bus = true, + .obint_enable_bits = USB2_OBINT_IDCHG_EN, +}; + +static const struct rcar_gen3_phy_drv_data rz_t2h_phy_usb2_data = { + .phy_usb2_ops = &rcar_gen3_phy_usb2_ops, + .vblvl_ctrl = true, + .obint_enable_bits = USB2_OBINT_IDCHG_EN | USB2_OBINT_VBSTAINT, }; static const struct rcar_gen3_phy_drv_data rz_v2h_phy_usb2_data = { .phy_usb2_ops = &rcar_gen3_phy_usb2_ops, .no_adp_ctrl = true, .utmi_ctrl = true, + .obint_enable_bits = USB2_OBINT_IDCHG_EN, }; static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { @@ -642,6 +710,10 @@ static const struct of_device_id rcar_gen3_phy_usb2_match_table[] = { .data = &rz_v2h_phy_usb2_data, }, { + .compatible = "renesas,usb2-phy-r9a09g077", + .data = &rz_t2h_phy_usb2_data, + }, + { .compatible = "renesas,rzg2l-usb2-phy", .data = &rz_g2l_phy_usb2_data, }, @@ -730,7 +802,6 @@ rpm_put: static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) { - const struct rcar_gen3_phy_drv_data *phy_data; struct device *dev = &pdev->dev; struct rcar_gen3_chan *channel; struct phy_provider *provider; @@ -749,7 +820,6 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) if (IS_ERR(channel->base)) return PTR_ERR(channel->base); - channel->obint_enable_bits = USB2_OBINT_BITS; channel->dr_mode = rcar_gen3_get_dr_mode(dev->of_node); if (channel->dr_mode != USB_DR_MODE_UNKNOWN) { channel->is_otg_channel = true; @@ -773,8 +843,8 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) */ pm_runtime_enable(dev); - phy_data = of_device_get_match_data(dev); - if (!phy_data) { + channel->phy_data = of_device_get_match_data(dev); + if (!channel->phy_data) { ret = -EINVAL; goto error; } @@ -782,22 +852,16 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) platform_set_drvdata(pdev, channel); channel->dev = dev; - if (phy_data->init_bus) { + if (channel->phy_data->init_bus) { ret = rcar_gen3_phy_usb2_init_bus(channel); if (ret) goto error; } - channel->soc_no_adp_ctrl = phy_data->no_adp_ctrl; - if (phy_data->no_adp_ctrl) - channel->obint_enable_bits = USB2_OBINT_IDCHG_EN; - - channel->utmi_ctrl = phy_data->utmi_ctrl; - spin_lock_init(&channel->lock); for (i = 0; i < NUM_OF_PHYS; i++) { channel->rphys[i].phy = devm_phy_create(dev, NULL, - phy_data->phy_usb2_ops); + channel->phy_data->phy_usb2_ops); if (IS_ERR(channel->rphys[i].phy)) { dev_err(dev, "Failed to create USB2 PHY\n"); ret = PTR_ERR(channel->rphys[i].phy); @@ -808,7 +872,7 @@ static int rcar_gen3_phy_usb2_probe(struct platform_device *pdev) phy_set_drvdata(channel->rphys[i].phy, &channel->rphys[i]); } - if (channel->soc_no_adp_ctrl && channel->is_otg_channel) + if (channel->phy_data->no_adp_ctrl && channel->is_otg_channel) channel->vbus = devm_regulator_get_exclusive(dev, "vbus"); else channel->vbus = devm_regulator_get_optional(dev, "vbus"); diff --git a/drivers/phy/renesas/r8a779f0-ether-serdes.c b/drivers/phy/renesas/r8a779f0-ether-serdes.c index 3b2d8cef75e5..8a6b6f366fe3 100644 --- a/drivers/phy/renesas/r8a779f0-ether-serdes.c +++ b/drivers/phy/renesas/r8a779f0-ether-serdes.c @@ -1,7 +1,7 @@ // SPDX-License-Identifier: GPL-2.0 /* Renesas Ethernet SERDES device driver * - * Copyright (C) 2022 Renesas Electronics Corporation + * Copyright (C) 2022-2025 Renesas Electronics Corporation */ #include <linux/delay.h> @@ -49,6 +49,13 @@ static void r8a779f0_eth_serdes_write32(void __iomem *addr, u32 offs, u32 bank, iowrite32(data, addr + offs); } +static u32 r8a779f0_eth_serdes_read32(void __iomem *addr, u32 offs, u32 bank) +{ + iowrite32(bank, addr + R8A779F0_ETH_SERDES_BANK_SELECT); + + return ioread32(addr + offs); +} + static int r8a779f0_eth_serdes_reg_wait(struct r8a779f0_eth_serdes_channel *channel, u32 offs, u32 bank, u32 mask, u32 expected) @@ -92,17 +99,18 @@ r8a779f0_eth_serdes_common_setting(struct r8a779f0_eth_serdes_channel *channel) { struct r8a779f0_eth_serdes_drv_data *dd = channel->dd; - switch (channel->phy_interface) { - case PHY_INTERFACE_MODE_SGMII: - r8a779f0_eth_serdes_write32(dd->addr, 0x0244, 0x180, 0x0097); - r8a779f0_eth_serdes_write32(dd->addr, 0x01d0, 0x180, 0x0060); - r8a779f0_eth_serdes_write32(dd->addr, 0x01d8, 0x180, 0x2200); - r8a779f0_eth_serdes_write32(dd->addr, 0x01d4, 0x180, 0x0000); - r8a779f0_eth_serdes_write32(dd->addr, 0x01e0, 0x180, 0x003d); - return 0; - default: - return -EOPNOTSUPP; - } + /* Set combination mode */ + r8a779f0_eth_serdes_write32(dd->addr, 0x0244, 0x180, 0x00d7); + r8a779f0_eth_serdes_write32(dd->addr, 0x01cc, 0x180, 0xc200); + r8a779f0_eth_serdes_write32(dd->addr, 0x01c4, 0x180, 0x0042); + r8a779f0_eth_serdes_write32(dd->addr, 0x01c8, 0x180, 0x0000); + r8a779f0_eth_serdes_write32(dd->addr, 0x01dc, 0x180, 0x002f); + r8a779f0_eth_serdes_write32(dd->addr, 0x01d0, 0x180, 0x0060); + r8a779f0_eth_serdes_write32(dd->addr, 0x01d8, 0x180, 0x2200); + r8a779f0_eth_serdes_write32(dd->addr, 0x01d4, 0x180, 0x0000); + r8a779f0_eth_serdes_write32(dd->addr, 0x01e0, 0x180, 0x003d); + + return 0; } static int @@ -155,6 +163,42 @@ r8a779f0_eth_serdes_chan_setting(struct r8a779f0_eth_serdes_channel *channel) r8a779f0_eth_serdes_write32(channel->addr, 0x0028, 0x1f80, 0x07a1); r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x1f80, 0x0208); break; + + case PHY_INTERFACE_MODE_USXGMII: + r8a779f0_eth_serdes_write32(channel->addr, 0x001c, 0x300, 0x0000); + r8a779f0_eth_serdes_write32(channel->addr, 0x0014, 0x380, 0x0050); + r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x380, 0x2200); + r8a779f0_eth_serdes_write32(channel->addr, 0x001c, 0x380, 0x0400); + r8a779f0_eth_serdes_write32(channel->addr, 0x01c0, 0x180, 0x0001); + r8a779f0_eth_serdes_write32(channel->addr, 0x0248, 0x180, 0x056a); + r8a779f0_eth_serdes_write32(channel->addr, 0x0258, 0x180, 0x0015); + r8a779f0_eth_serdes_write32(channel->addr, 0x0144, 0x180, 0x1100); + r8a779f0_eth_serdes_write32(channel->addr, 0x01a0, 0x180, 0x0001); + r8a779f0_eth_serdes_write32(channel->addr, 0x00d0, 0x180, 0x0001); + r8a779f0_eth_serdes_write32(channel->addr, 0x0150, 0x180, 0x0001); + r8a779f0_eth_serdes_write32(channel->addr, 0x00c8, 0x180, 0x0300); + r8a779f0_eth_serdes_write32(channel->addr, 0x0148, 0x180, 0x0300); + r8a779f0_eth_serdes_write32(channel->addr, 0x0174, 0x180, 0x0000); + r8a779f0_eth_serdes_write32(channel->addr, 0x0160, 0x180, 0x0004); + r8a779f0_eth_serdes_write32(channel->addr, 0x01ac, 0x180, 0x0000); + r8a779f0_eth_serdes_write32(channel->addr, 0x00c4, 0x180, 0x0310); + r8a779f0_eth_serdes_write32(channel->addr, 0x00c8, 0x180, 0x0301); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x00c8, 0x180, BIT(0), 0); + if (ret) + return ret; + r8a779f0_eth_serdes_write32(channel->addr, 0x0148, 0x180, 0x0301); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0148, 0x180, BIT(0), 0); + if (ret) + return ret; + r8a779f0_eth_serdes_write32(channel->addr, 0x00c4, 0x180, 0x1310); + r8a779f0_eth_serdes_write32(channel->addr, 0x00d8, 0x180, 0x1800); + r8a779f0_eth_serdes_write32(channel->addr, 0x00dc, 0x180, 0x0000); + r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x380, 0x2300); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0000, 0x380, BIT(8), 0); + if (ret) + return ret; + break; + default: return -EOPNOTSUPP; } @@ -179,6 +223,14 @@ r8a779f0_eth_serdes_chan_speed(struct r8a779f0_eth_serdes_channel *channel) return ret; r8a779f0_eth_serdes_write32(channel->addr, 0x0008, 0x1f80, 0x0000); break; + case PHY_INTERFACE_MODE_USXGMII: + r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x1f00, 0x0120); + usleep_range(10, 20); + r8a779f0_eth_serdes_write32(channel->addr, 0x0000, 0x380, 0x2600); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0000, 0x380, BIT(10), 0); + if (ret) + return ret; + break; default: return -EOPNOTSUPP; } @@ -274,6 +326,7 @@ static int r8a779f0_eth_serdes_hw_init_late(struct r8a779f0_eth_serdes_channel *channel) { int ret; + u32 val; ret = r8a779f0_eth_serdes_chan_setting(channel); if (ret) @@ -287,6 +340,26 @@ static int r8a779f0_eth_serdes_hw_init_late(struct r8a779f0_eth_serdes_channel r8a779f0_eth_serdes_write32(channel->addr, 0x03d0, 0x380, 0x0000); + val = r8a779f0_eth_serdes_read32(channel->addr, 0x00c0, 0x180); + r8a779f0_eth_serdes_write32(channel->addr, 0x00c0, 0x180, val | BIT(8)); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0100, 0x180, BIT(0), 1); + if (ret) + return ret; + r8a779f0_eth_serdes_write32(channel->addr, 0x00c0, 0x180, val & ~BIT(8)); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0100, 0x180, BIT(0), 0); + if (ret) + return ret; + + val = r8a779f0_eth_serdes_read32(channel->addr, 0x0144, 0x180); + r8a779f0_eth_serdes_write32(channel->addr, 0x0144, 0x180, val | BIT(4)); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0180, 0x180, BIT(0), 1); + if (ret) + return ret; + r8a779f0_eth_serdes_write32(channel->addr, 0x0144, 0x180, val & ~BIT(4)); + ret = r8a779f0_eth_serdes_reg_wait(channel, 0x0180, 0x180, BIT(0), 0); + if (ret) + return ret; + return r8a779f0_eth_serdes_monitor_linkup(channel); } diff --git a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c index 2ab99e1d47eb..c79fb53d8ee5 100644 --- a/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c +++ b/drivers/phy/rockchip/phy-rockchip-inno-csidphy.c @@ -30,6 +30,8 @@ #define RK3568_GRF_VI_CON0 0x0340 #define RK3568_GRF_VI_CON1 0x0344 +#define RK3588_CSIDPHY_GRF_CON0 0x0000 + /* PHY */ #define CSIDPHY_CTRL_LANE_ENABLE 0x00 #define CSIDPHY_CTRL_LANE_ENABLE_CK BIT(6) @@ -67,6 +69,8 @@ #define RK1808_CSIDPHY_CLK_CALIB_EN 0x168 #define RK3568_CSIDPHY_CLK_CALIB_EN 0x168 +#define RESETS_MAX 2 + /* * The higher 16-bit of this register is used for write protection * only if BIT(x + 16) set to 1 the BIT(x) can be written. @@ -87,10 +91,11 @@ struct dphy_reg { u32 offset; u32 mask; u32 shift; + u8 valid; }; #define PHY_REG(_offset, _width, _shift) \ - { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, } + { .offset = _offset, .mask = BIT(_width) - 1, .shift = _shift, .valid = 1, } static const struct dphy_reg rk1808_grf_dphy_regs[] = { [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK1808_GRF_PD_VI_CON_OFFSET, 4, 0), @@ -114,6 +119,12 @@ static const struct dphy_reg rk3568_grf_dphy_regs[] = { [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3568_GRF_VI_CON0, 1, 8), }; +static const struct dphy_reg rk3588_grf_dphy_regs[] = { + [GRF_DPHY_CSIPHY_FORCERXMODE] = PHY_REG(RK3588_CSIDPHY_GRF_CON0, 4, 0), + [GRF_DPHY_CSIPHY_DATALANE_EN] = PHY_REG(RK3588_CSIDPHY_GRF_CON0, 4, 4), + [GRF_DPHY_CSIPHY_CLKLANE_EN] = PHY_REG(RK3588_CSIDPHY_GRF_CON0, 1, 8), +}; + struct hsfreq_range { u32 range_h; u8 cfg_bit; @@ -126,6 +137,8 @@ struct dphy_drv_data { const struct hsfreq_range *hsfreq_ranges; int num_hsfreq_ranges; const struct dphy_reg *grf_regs; + const char *const *resets; + unsigned int resets_num; }; struct rockchip_inno_csidphy { @@ -133,7 +146,8 @@ struct rockchip_inno_csidphy { void __iomem *phy_base; struct clk *pclk; struct regmap *grf; - struct reset_control *rst; + struct reset_control_bulk_data resets[RESETS_MAX]; + unsigned int resets_num; const struct dphy_drv_data *drv_data; struct phy_configure_opts_mipi_dphy config; u8 hsfreq; @@ -145,7 +159,7 @@ static inline void write_grf_reg(struct rockchip_inno_csidphy *priv, const struct dphy_drv_data *drv_data = priv->drv_data; const struct dphy_reg *reg = &drv_data->grf_regs[index]; - if (reg->offset) + if (reg->valid) regmap_write(priv->grf, reg->offset, HIWORD_UPDATE(value, reg->mask, reg->shift)); } @@ -173,6 +187,15 @@ static const struct hsfreq_range rk3368_mipidphy_hsfreq_ranges[] = { {1249, 0x0c}, {1349, 0x0d}, {1500, 0x0e} }; +static const char *const rk3368_reset_names[] = { + "apb" +}; + +static const char *const rk3588_reset_names[] = { + "apb", + "phy" +}; + static void rockchip_inno_csidphy_ths_settle(struct rockchip_inno_csidphy *priv, int hsfreq, int offset) { @@ -343,6 +366,8 @@ static const struct dphy_drv_data rk1808_mipidphy_drv_data = { .hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges, .num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges), .grf_regs = rk1808_grf_dphy_regs, + .resets = rk3368_reset_names, + .resets_num = ARRAY_SIZE(rk3368_reset_names), }; static const struct dphy_drv_data rk3326_mipidphy_drv_data = { @@ -352,6 +377,8 @@ static const struct dphy_drv_data rk3326_mipidphy_drv_data = { .hsfreq_ranges = rk3326_mipidphy_hsfreq_ranges, .num_hsfreq_ranges = ARRAY_SIZE(rk3326_mipidphy_hsfreq_ranges), .grf_regs = rk3326_grf_dphy_regs, + .resets = rk3368_reset_names, + .resets_num = ARRAY_SIZE(rk3368_reset_names), }; static const struct dphy_drv_data rk3368_mipidphy_drv_data = { @@ -361,6 +388,8 @@ static const struct dphy_drv_data rk3368_mipidphy_drv_data = { .hsfreq_ranges = rk3368_mipidphy_hsfreq_ranges, .num_hsfreq_ranges = ARRAY_SIZE(rk3368_mipidphy_hsfreq_ranges), .grf_regs = rk3368_grf_dphy_regs, + .resets = rk3368_reset_names, + .resets_num = ARRAY_SIZE(rk3368_reset_names), }; static const struct dphy_drv_data rk3568_mipidphy_drv_data = { @@ -370,6 +399,19 @@ static const struct dphy_drv_data rk3568_mipidphy_drv_data = { .hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges, .num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges), .grf_regs = rk3568_grf_dphy_regs, + .resets = rk3368_reset_names, + .resets_num = ARRAY_SIZE(rk3368_reset_names), +}; + +static const struct dphy_drv_data rk3588_mipidphy_drv_data = { + .pwrctl_offset = -1, + .ths_settle_offset = RK3568_CSIDPHY_CLK_WR_THS_SETTLE, + .calib_offset = RK3568_CSIDPHY_CLK_CALIB_EN, + .hsfreq_ranges = rk1808_mipidphy_hsfreq_ranges, + .num_hsfreq_ranges = ARRAY_SIZE(rk1808_mipidphy_hsfreq_ranges), + .grf_regs = rk3588_grf_dphy_regs, + .resets = rk3588_reset_names, + .resets_num = ARRAY_SIZE(rk3588_reset_names), }; static const struct of_device_id rockchip_inno_csidphy_match_id[] = { @@ -393,6 +435,10 @@ static const struct of_device_id rockchip_inno_csidphy_match_id[] = { .compatible = "rockchip,rk3568-csi-dphy", .data = &rk3568_mipidphy_drv_data, }, + { + .compatible = "rockchip,rk3588-csi-dphy", + .data = &rk3588_mipidphy_drv_data, + }, {} }; MODULE_DEVICE_TABLE(of, rockchip_inno_csidphy_match_id); @@ -403,6 +449,7 @@ static int rockchip_inno_csidphy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct phy_provider *phy_provider; struct phy *phy; + int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) @@ -434,10 +481,18 @@ static int rockchip_inno_csidphy_probe(struct platform_device *pdev) return PTR_ERR(priv->pclk); } - priv->rst = devm_reset_control_get(dev, "apb"); - if (IS_ERR(priv->rst)) { + if (priv->drv_data->resets_num > RESETS_MAX) { + dev_err(dev, "invalid number of resets\n"); + return -EINVAL; + } + priv->resets_num = priv->drv_data->resets_num; + for (unsigned int i = 0; i < priv->resets_num; i++) + priv->resets[i].id = priv->drv_data->resets[i]; + ret = devm_reset_control_bulk_get_exclusive(dev, priv->resets_num, + priv->resets); + if (ret) { dev_err(dev, "failed to get system reset control\n"); - return PTR_ERR(priv->rst); + return ret; } phy = devm_phy_create(dev, NULL, &rockchip_inno_csidphy_ops); diff --git a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c index ce91fb1d5167..a3ef19807b9e 100644 --- a/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c +++ b/drivers/phy/rockchip/phy-rockchip-naneng-combphy.c @@ -20,79 +20,120 @@ #define REF_CLOCK_25MHz (25 * HZ_PER_MHZ) #define REF_CLOCK_100MHz (100 * HZ_PER_MHZ) -/* COMBO PHY REG */ -#define PHYREG6 0x14 -#define PHYREG6_PLL_DIV_MASK GENMASK(7, 6) -#define PHYREG6_PLL_DIV_SHIFT 6 -#define PHYREG6_PLL_DIV_2 1 - -#define PHYREG7 0x18 -#define PHYREG7_TX_RTERM_MASK GENMASK(7, 4) -#define PHYREG7_TX_RTERM_SHIFT 4 -#define PHYREG7_TX_RTERM_50OHM 8 -#define PHYREG7_RX_RTERM_MASK GENMASK(3, 0) -#define PHYREG7_RX_RTERM_SHIFT 0 -#define PHYREG7_RX_RTERM_44OHM 15 - -#define PHYREG8 0x1C -#define PHYREG8_SSC_EN BIT(4) - -#define PHYREG10 0x24 -#define PHYREG10_SSC_PCM_MASK GENMASK(3, 0) -#define PHYREG10_SSC_PCM_3500PPM 7 - -#define PHYREG11 0x28 -#define PHYREG11_SU_TRIM_0_7 0xF0 - -#define PHYREG12 0x2C -#define PHYREG12_PLL_LPF_ADJ_VALUE 4 - -#define PHYREG13 0x30 -#define PHYREG13_RESISTER_MASK GENMASK(5, 4) -#define PHYREG13_RESISTER_SHIFT 0x4 -#define PHYREG13_RESISTER_HIGH_Z 3 -#define PHYREG13_CKRCV_AMP0 BIT(7) - -#define PHYREG14 0x34 -#define PHYREG14_CKRCV_AMP1 BIT(0) - -#define PHYREG15 0x38 -#define PHYREG15_CTLE_EN BIT(0) -#define PHYREG15_SSC_CNT_MASK GENMASK(7, 6) -#define PHYREG15_SSC_CNT_SHIFT 6 -#define PHYREG15_SSC_CNT_VALUE 1 - -#define PHYREG16 0x3C -#define PHYREG16_SSC_CNT_VALUE 0x5f - -#define PHYREG17 0x40 - -#define PHYREG18 0x44 -#define PHYREG18_PLL_LOOP 0x32 - -#define PHYREG21 0x50 -#define PHYREG21_RX_SQUELCH_VAL 0x0D - -#define PHYREG27 0x6C -#define PHYREG27_RX_TRIM_RK3588 0x4C - -#define PHYREG30 0x74 - -#define PHYREG32 0x7C -#define PHYREG32_SSC_MASK GENMASK(7, 4) -#define PHYREG32_SSC_DIR_MASK GENMASK(5, 4) -#define PHYREG32_SSC_DIR_SHIFT 4 -#define PHYREG32_SSC_UPWARD 0 -#define PHYREG32_SSC_DOWNWARD 1 -#define PHYREG32_SSC_OFFSET_MASK GENMASK(7, 6) -#define PHYREG32_SSC_OFFSET_SHIFT 6 -#define PHYREG32_SSC_OFFSET_500PPM 1 - -#define PHYREG33 0x80 -#define PHYREG33_PLL_KVCO_MASK GENMASK(4, 2) -#define PHYREG33_PLL_KVCO_SHIFT 2 -#define PHYREG33_PLL_KVCO_VALUE 2 -#define PHYREG33_PLL_KVCO_VALUE_RK3576 4 +/* RK3528 COMBO PHY REG */ +#define RK3528_PHYREG6 0x18 +#define RK3528_PHYREG6_PLL_KVCO GENMASK(12, 10) +#define RK3528_PHYREG6_PLL_KVCO_VALUE 0x2 +#define RK3528_PHYREG6_SSC_DIR GENMASK(5, 4) +#define RK3528_PHYREG6_SSC_UPWARD 0 +#define RK3528_PHYREG6_SSC_DOWNWARD 1 + +#define RK3528_PHYREG40 0x100 +#define RK3528_PHYREG40_SSC_EN BIT(20) +#define RK3528_PHYREG40_SSC_CNT GENMASK(10, 0) +#define RK3528_PHYREG40_SSC_CNT_VALUE 0x17d + +#define RK3528_PHYREG42 0x108 +#define RK3528_PHYREG42_CKDRV_CLK_SEL BIT(29) +#define RK3528_PHYREG42_CKDRV_CLK_PLL 0 +#define RK3528_PHYREG42_CKDRV_CLK_CKRCV 1 +#define RK3528_PHYREG42_PLL_LPF_R1_ADJ GENMASK(10, 7) +#define RK3528_PHYREG42_PLL_LPF_R1_ADJ_VALUE 0x9 +#define RK3528_PHYREG42_PLL_CHGPUMP_CUR_ADJ GENMASK(6, 4) +#define RK3528_PHYREG42_PLL_CHGPUMP_CUR_ADJ_VALUE 0x7 +#define RK3528_PHYREG42_PLL_KVCO_ADJ GENMASK(2, 0) +#define RK3528_PHYREG42_PLL_KVCO_ADJ_VALUE 0x0 + +#define RK3528_PHYREG80 0x200 +#define RK3528_PHYREG80_CTLE_EN BIT(17) + +#define RK3528_PHYREG81 0x204 +#define RK3528_PHYREG81_CDR_PHASE_PATH_GAIN_2X BIT(5) +#define RK3528_PHYREG81_SLEW_RATE_CTRL GENMASK(2, 0) +#define RK3528_PHYREG81_SLEW_RATE_CTRL_SLOW 0x7 + +#define RK3528_PHYREG83 0x20c +#define RK3528_PHYREG83_RX_SQUELCH GENMASK(2, 0) +#define RK3528_PHYREG83_RX_SQUELCH_VALUE 0x6 + +#define RK3528_PHYREG86 0x218 +#define RK3528_PHYREG86_RTERM_DET_CLK_EN BIT(14) + +/* RK3568 COMBO PHY REG */ +#define RK3568_PHYREG6 0x14 +#define RK3568_PHYREG6_PLL_DIV_MASK GENMASK(7, 6) +#define RK3568_PHYREG6_PLL_DIV_SHIFT 6 +#define RK3568_PHYREG6_PLL_DIV_2 1 + +#define RK3568_PHYREG7 0x18 +#define RK3568_PHYREG7_TX_RTERM_MASK GENMASK(7, 4) +#define RK3568_PHYREG7_TX_RTERM_SHIFT 4 +#define RK3568_PHYREG7_TX_RTERM_50OHM 8 +#define RK3568_PHYREG7_RX_RTERM_MASK GENMASK(3, 0) +#define RK3568_PHYREG7_RX_RTERM_SHIFT 0 +#define RK3568_PHYREG7_RX_RTERM_44OHM 15 + +#define RK3568_PHYREG8 0x1C +#define RK3568_PHYREG8_SSC_EN BIT(4) + +#define RK3568_PHYREG11 0x28 +#define RK3568_PHYREG11_SU_TRIM_0_7 0xF0 + +#define RK3568_PHYREG12 0x2C +#define RK3568_PHYREG12_PLL_LPF_ADJ_VALUE 4 + +#define RK3568_PHYREG13 0x30 +#define RK3568_PHYREG13_RESISTER_MASK GENMASK(5, 4) +#define RK3568_PHYREG13_RESISTER_SHIFT 0x4 +#define RK3568_PHYREG13_RESISTER_HIGH_Z 3 +#define RK3568_PHYREG13_CKRCV_AMP0 BIT(7) + +#define RK3568_PHYREG14 0x34 +#define RK3568_PHYREG14_CKRCV_AMP1 BIT(0) + +#define RK3568_PHYREG15 0x38 +#define RK3568_PHYREG15_CTLE_EN BIT(0) +#define RK3568_PHYREG15_SSC_CNT_MASK GENMASK(7, 6) +#define RK3568_PHYREG15_SSC_CNT_SHIFT 6 +#define RK3568_PHYREG15_SSC_CNT_VALUE 1 + +#define RK3568_PHYREG16 0x3C +#define RK3568_PHYREG16_SSC_CNT_VALUE 0x5f + +#define RK3568_PHYREG18 0x44 +#define RK3568_PHYREG18_PLL_LOOP 0x32 + +#define RK3568_PHYREG32 0x7C +#define RK3568_PHYREG32_SSC_MASK GENMASK(7, 4) +#define RK3568_PHYREG32_SSC_DIR_MASK GENMASK(5, 4) +#define RK3568_PHYREG32_SSC_DIR_SHIFT 4 +#define RK3568_PHYREG32_SSC_UPWARD 0 +#define RK3568_PHYREG32_SSC_DOWNWARD 1 +#define RK3568_PHYREG32_SSC_OFFSET_MASK GENMASK(7, 6) +#define RK3568_PHYREG32_SSC_OFFSET_SHIFT 6 +#define RK3568_PHYREG32_SSC_OFFSET_500PPM 1 + +#define RK3568_PHYREG33 0x80 +#define RK3568_PHYREG33_PLL_KVCO_MASK GENMASK(4, 2) +#define RK3568_PHYREG33_PLL_KVCO_SHIFT 2 +#define RK3568_PHYREG33_PLL_KVCO_VALUE 2 +#define RK3576_PHYREG33_PLL_KVCO_VALUE 4 + +/* RK3588 COMBO PHY registers */ +#define RK3588_PHYREG27 0x6C +#define RK3588_PHYREG27_RX_TRIM 0x4C + +/* RK3576 COMBO PHY registers */ +#define RK3576_PHYREG10 0x24 +#define RK3576_PHYREG10_SSC_PCM_MASK GENMASK(3, 0) +#define RK3576_PHYREG10_SSC_PCM_3500PPM 7 + +#define RK3576_PHYREG17 0x40 + +#define RK3576_PHYREG21 0x50 +#define RK3576_PHYREG21_RX_SQUELCH_VAL 0x0D + +#define RK3576_PHYREG30 0x74 struct rockchip_combphy_priv; @@ -137,6 +178,8 @@ struct rockchip_combphy_grfcfg { struct combphy_reg pipe_xpcs_phy_ready; struct combphy_reg pipe_pcie1l0_sel; struct combphy_reg pipe_pcie1l1_sel; + struct combphy_reg u3otg0_port_en; + struct combphy_reg u3otg1_port_en; }; struct rockchip_combphy_cfg { @@ -396,6 +439,150 @@ static int rockchip_combphy_probe(struct platform_device *pdev) return PTR_ERR_OR_ZERO(phy_provider); } +static int rk3528_combphy_cfg(struct rockchip_combphy_priv *priv) +{ + const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; + unsigned long rate; + u32 val; + + /* Set SSC downward spread spectrum */ + val = FIELD_PREP(RK3528_PHYREG6_SSC_DIR, RK3528_PHYREG6_SSC_DOWNWARD); + rockchip_combphy_updatel(priv, RK3528_PHYREG6_SSC_DIR, val, RK3528_PHYREG6); + + switch (priv->type) { + case PHY_TYPE_PCIE: + rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con2_for_pcie, true); + rockchip_combphy_param_write(priv->phy_grf, &cfg->con3_for_pcie, true); + break; + case PHY_TYPE_USB3: + /* Enable adaptive CTLE for USB3.0 Rx */ + rockchip_combphy_updatel(priv, RK3528_PHYREG80_CTLE_EN, RK3528_PHYREG80_CTLE_EN, + RK3528_PHYREG80); + + /* Set slow slew rate control for PI */ + val = FIELD_PREP(RK3528_PHYREG81_SLEW_RATE_CTRL, + RK3528_PHYREG81_SLEW_RATE_CTRL_SLOW); + rockchip_combphy_updatel(priv, RK3528_PHYREG81_SLEW_RATE_CTRL, val, + RK3528_PHYREG81); + + /* Set CDR phase path with 2x gain */ + rockchip_combphy_updatel(priv, RK3528_PHYREG81_CDR_PHASE_PATH_GAIN_2X, + RK3528_PHYREG81_CDR_PHASE_PATH_GAIN_2X, RK3528_PHYREG81); + + /* Set Rx squelch input filler bandwidth */ + val = FIELD_PREP(RK3528_PHYREG83_RX_SQUELCH, RK3528_PHYREG83_RX_SQUELCH_VALUE); + rockchip_combphy_updatel(priv, RK3528_PHYREG83_RX_SQUELCH, val, RK3528_PHYREG83); + + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); + rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true); + rockchip_combphy_param_write(priv->pipe_grf, &cfg->u3otg0_port_en, true); + break; + default: + dev_err(priv->dev, "incompatible PHY type\n"); + return -EINVAL; + } + + rate = clk_get_rate(priv->refclk); + + switch (rate) { + case REF_CLOCK_24MHz: + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_24m, true); + if (priv->type == PHY_TYPE_USB3) { + /* Set ssc_cnt[10:0]=00101111101 & 31.5KHz */ + val = FIELD_PREP(RK3528_PHYREG40_SSC_CNT, RK3528_PHYREG40_SSC_CNT_VALUE); + rockchip_combphy_updatel(priv, RK3528_PHYREG40_SSC_CNT, val, + RK3528_PHYREG40); + } else if (priv->type == PHY_TYPE_PCIE) { + /* tx_trim[14]=1, Enable the counting clock of the rterm detect */ + rockchip_combphy_updatel(priv, RK3528_PHYREG86_RTERM_DET_CLK_EN, + RK3528_PHYREG86_RTERM_DET_CLK_EN, RK3528_PHYREG86); + } + break; + case REF_CLOCK_100MHz: + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); + if (priv->type == PHY_TYPE_PCIE) { + /* PLL KVCO tuning fine */ + val = FIELD_PREP(RK3528_PHYREG6_PLL_KVCO, RK3528_PHYREG6_PLL_KVCO_VALUE); + rockchip_combphy_updatel(priv, RK3528_PHYREG6_PLL_KVCO, val, + RK3528_PHYREG6); + + /* su_trim[6:4]=111, [10:7]=1001, [2:0]=000, swing 650mv */ + writel(0x570804f0, priv->mmio + RK3528_PHYREG42); + } + break; + default: + dev_err(priv->dev, "Unsupported rate: %lu\n", rate); + return -EINVAL; + } + + if (device_property_read_bool(priv->dev, "rockchip,ext-refclk")) { + rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true); + + if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) { + val = FIELD_PREP(RK3528_PHYREG42_CKDRV_CLK_SEL, + RK3528_PHYREG42_CKDRV_CLK_CKRCV); + val |= FIELD_PREP(RK3528_PHYREG42_PLL_LPF_R1_ADJ, + RK3528_PHYREG42_PLL_LPF_R1_ADJ_VALUE); + val |= FIELD_PREP(RK3528_PHYREG42_PLL_CHGPUMP_CUR_ADJ, + RK3528_PHYREG42_PLL_CHGPUMP_CUR_ADJ_VALUE); + val |= FIELD_PREP(RK3528_PHYREG42_PLL_KVCO_ADJ, + RK3528_PHYREG42_PLL_KVCO_ADJ_VALUE); + rockchip_combphy_updatel(priv, + RK3528_PHYREG42_CKDRV_CLK_SEL | + RK3528_PHYREG42_PLL_LPF_R1_ADJ | + RK3528_PHYREG42_PLL_CHGPUMP_CUR_ADJ | + RK3528_PHYREG42_PLL_KVCO_ADJ, + val, RK3528_PHYREG42); + + val = FIELD_PREP(RK3528_PHYREG6_PLL_KVCO, RK3528_PHYREG6_PLL_KVCO_VALUE); + rockchip_combphy_updatel(priv, RK3528_PHYREG6_PLL_KVCO, val, + RK3528_PHYREG6); + } + } + + if (priv->type == PHY_TYPE_PCIE) { + if (device_property_read_bool(priv->dev, "rockchip,enable-ssc")) + rockchip_combphy_updatel(priv, RK3528_PHYREG40_SSC_EN, + RK3528_PHYREG40_SSC_EN, RK3528_PHYREG40); + } + + return 0; +} + +static const struct rockchip_combphy_grfcfg rk3528_combphy_grfcfgs = { + /* pipe-phy-grf */ + .pcie_mode_set = { 0x0000, 5, 0, 0x00, 0x11 }, + .usb_mode_set = { 0x0000, 5, 0, 0x00, 0x04 }, + .pipe_rxterm_set = { 0x0000, 12, 12, 0x00, 0x01 }, + .pipe_txelec_set = { 0x0004, 1, 1, 0x00, 0x01 }, + .pipe_txcomp_set = { 0x0004, 4, 4, 0x00, 0x01 }, + .pipe_clk_24m = { 0x0004, 14, 13, 0x00, 0x00 }, + .pipe_clk_100m = { 0x0004, 14, 13, 0x00, 0x02 }, + .pipe_rxterm_sel = { 0x0008, 8, 8, 0x00, 0x01 }, + .pipe_txelec_sel = { 0x0008, 12, 12, 0x00, 0x01 }, + .pipe_txcomp_sel = { 0x0008, 15, 15, 0x00, 0x01 }, + .pipe_clk_ext = { 0x000c, 9, 8, 0x02, 0x01 }, + .pipe_phy_status = { 0x0034, 6, 6, 0x01, 0x00 }, + .con0_for_pcie = { 0x0000, 15, 0, 0x00, 0x110 }, + .con1_for_pcie = { 0x0004, 15, 0, 0x00, 0x00 }, + .con2_for_pcie = { 0x0008, 15, 0, 0x00, 0x101 }, + .con3_for_pcie = { 0x000c, 15, 0, 0x00, 0x0200 }, + /* pipe-grf */ + .u3otg0_port_en = { 0x0044, 15, 0, 0x0181, 0x1100 }, +}; + +static const struct rockchip_combphy_cfg rk3528_combphy_cfgs = { + .num_phys = 1, + .phy_ids = { + 0xffdc0000, + }, + .grfcfg = &rk3528_combphy_grfcfgs, + .combphy_cfg = rk3528_combphy_cfg, +}; + static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv) { const struct rockchip_combphy_grfcfg *cfg = priv->cfg->grfcfg; @@ -405,9 +592,8 @@ static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv) switch (priv->type) { case PHY_TYPE_PCIE: /* Set SSC downward spread spectrum */ - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, - PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, - PHYREG32); + val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); @@ -416,29 +602,30 @@ static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv) break; case PHY_TYPE_USB3: /* Set SSC downward spread spectrum */ - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, - PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, - PHYREG32); + val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, + RK3568_PHYREG32); /* Enable adaptive CTLE for USB3.0 Rx */ - rockchip_combphy_updatel(priv, PHYREG15_CTLE_EN, - PHYREG15_CTLE_EN, PHYREG15); + rockchip_combphy_updatel(priv, RK3568_PHYREG15_CTLE_EN, + RK3568_PHYREG15_CTLE_EN, RK3568_PHYREG15); /* Set PLL KVCO fine tuning signals */ - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, BIT(3), PHYREG33); + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, + BIT(3), RK3568_PHYREG33); /* Set PLL LPF R1 to su_trim[10:7]=1001 */ - writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + writel(RK3568_PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + RK3568_PHYREG12); /* Set PLL input clock divider 1/2 */ - val = FIELD_PREP(PHYREG6_PLL_DIV_MASK, PHYREG6_PLL_DIV_2); - rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, val, PHYREG6); + val = FIELD_PREP(RK3568_PHYREG6_PLL_DIV_MASK, RK3568_PHYREG6_PLL_DIV_2); + rockchip_combphy_updatel(priv, RK3568_PHYREG6_PLL_DIV_MASK, val, RK3568_PHYREG6); /* Set PLL loop divider */ - writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); + writel(RK3568_PHYREG18_PLL_LOOP, priv->mmio + RK3568_PHYREG18); /* Set PLL KVCO to min and set PLL charge pump current to max */ - writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + writel(RK3568_PHYREG11_SU_TRIM_0_7, priv->mmio + RK3568_PHYREG11); rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_usb, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); @@ -456,11 +643,12 @@ static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv) case REF_CLOCK_24MHz: if (priv->type == PHY_TYPE_USB3) { /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz */ - val = FIELD_PREP(PHYREG15_SSC_CNT_MASK, PHYREG15_SSC_CNT_VALUE); - rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK, - val, PHYREG15); + val = FIELD_PREP(RK3568_PHYREG15_SSC_CNT_MASK, + RK3568_PHYREG15_SSC_CNT_VALUE); + rockchip_combphy_updatel(priv, RK3568_PHYREG15_SSC_CNT_MASK, + val, RK3568_PHYREG15); - writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16); + writel(RK3568_PHYREG16_SSC_CNT_VALUE, priv->mmio + RK3568_PHYREG16); } break; case REF_CLOCK_25MHz: @@ -470,19 +658,20 @@ static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv) rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); if (priv->type == PHY_TYPE_PCIE) { /* PLL KVCO tuning fine */ - val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE); - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, - val, PHYREG33); + val = FIELD_PREP(RK3568_PHYREG33_PLL_KVCO_MASK, + RK3568_PHYREG33_PLL_KVCO_VALUE); + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, + val, RK3568_PHYREG33); /* Enable controlling random jitter, aka RMJ */ - writel(0x4, priv->mmio + PHYREG12); + writel(0x4, priv->mmio + RK3568_PHYREG12); - val = PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT; - rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, - val, PHYREG6); + val = RK3568_PHYREG6_PLL_DIV_2 << RK3568_PHYREG6_PLL_DIV_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG6_PLL_DIV_MASK, + val, RK3568_PHYREG6); - writel(0x32, priv->mmio + PHYREG18); - writel(0xf0, priv->mmio + PHYREG11); + writel(0x32, priv->mmio + RK3568_PHYREG18); + writel(0xf0, priv->mmio + RK3568_PHYREG11); } break; default: @@ -493,20 +682,21 @@ static int rk3562_combphy_cfg(struct rockchip_combphy_priv *priv) if (priv->ext_refclk) { rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true); if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) { - val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT; - val |= PHYREG13_CKRCV_AMP0; - rockchip_combphy_updatel(priv, PHYREG13_RESISTER_MASK, val, PHYREG13); - - val = readl(priv->mmio + PHYREG14); - val |= PHYREG14_CKRCV_AMP1; - writel(val, priv->mmio + PHYREG14); + val = RK3568_PHYREG13_RESISTER_HIGH_Z << RK3568_PHYREG13_RESISTER_SHIFT; + val |= RK3568_PHYREG13_CKRCV_AMP0; + rockchip_combphy_updatel(priv, RK3568_PHYREG13_RESISTER_MASK, val, + RK3568_PHYREG13); + + val = readl(priv->mmio + RK3568_PHYREG14); + val |= RK3568_PHYREG14_CKRCV_AMP1; + writel(val, priv->mmio + RK3568_PHYREG14); } } if (priv->enable_ssc) { - val = readl(priv->mmio + PHYREG8); - val |= PHYREG8_SSC_EN; - writel(val, priv->mmio + PHYREG8); + val = readl(priv->mmio + RK3568_PHYREG8); + val |= RK3568_PHYREG8_SSC_EN; + writel(val, priv->mmio + RK3568_PHYREG8); } return 0; @@ -553,9 +743,9 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) switch (priv->type) { case PHY_TYPE_PCIE: /* Set SSC downward spread spectrum. */ - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, - PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, - PHYREG32); + val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; + + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); @@ -565,49 +755,55 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) case PHY_TYPE_USB3: /* Set SSC downward spread spectrum. */ - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, - PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, - PHYREG32); + val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT, + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); /* Enable adaptive CTLE for USB3.0 Rx. */ - val = readl(priv->mmio + PHYREG15); - val |= PHYREG15_CTLE_EN; - writel(val, priv->mmio + PHYREG15); + val = readl(priv->mmio + RK3568_PHYREG15); + val |= RK3568_PHYREG15_CTLE_EN; + writel(val, priv->mmio + RK3568_PHYREG15); /* Set PLL KVCO fine tuning signals. */ - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, - PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT, - PHYREG33); + val = RK3568_PHYREG33_PLL_KVCO_VALUE << RK3568_PHYREG33_PLL_KVCO_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, val, RK3568_PHYREG33); /* Enable controlling random jitter. */ - writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + writel(RK3568_PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + RK3568_PHYREG12); /* Set PLL input clock divider 1/2. */ - rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, - PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT, - PHYREG6); + rockchip_combphy_updatel(priv, RK3568_PHYREG6_PLL_DIV_MASK, + RK3568_PHYREG6_PLL_DIV_2 << RK3568_PHYREG6_PLL_DIV_SHIFT, + RK3568_PHYREG6); - writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); - writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + writel(RK3568_PHYREG18_PLL_LOOP, priv->mmio + RK3568_PHYREG18); + writel(RK3568_PHYREG11_SU_TRIM_0_7, priv->mmio + RK3568_PHYREG11); rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_sel_usb, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); rockchip_combphy_param_write(priv->phy_grf, &cfg->usb_mode_set, true); + switch (priv->id) { + case 0: + rockchip_combphy_param_write(priv->pipe_grf, &cfg->u3otg0_port_en, true); + break; + case 1: + rockchip_combphy_param_write(priv->pipe_grf, &cfg->u3otg1_port_en, true); + break; + } break; case PHY_TYPE_SATA: /* Enable adaptive CTLE for SATA Rx. */ - val = readl(priv->mmio + PHYREG15); - val |= PHYREG15_CTLE_EN; - writel(val, priv->mmio + PHYREG15); + val = readl(priv->mmio + RK3568_PHYREG15); + val |= RK3568_PHYREG15_CTLE_EN; + writel(val, priv->mmio + RK3568_PHYREG15); /* * Set tx_rterm=50ohm and rx_rterm=44ohm for SATA. * 0: 60ohm, 8: 50ohm 15: 44ohm (by step abort 1ohm) */ - val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT; - val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT; - writel(val, priv->mmio + PHYREG7); + val = RK3568_PHYREG7_TX_RTERM_50OHM << RK3568_PHYREG7_TX_RTERM_SHIFT; + val |= RK3568_PHYREG7_RX_RTERM_44OHM << RK3568_PHYREG7_RX_RTERM_SHIFT; + writel(val, priv->mmio + RK3568_PHYREG7); rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_sata, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_sata, true); @@ -642,11 +838,11 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) case REF_CLOCK_24MHz: if (priv->type == PHY_TYPE_USB3 || priv->type == PHY_TYPE_SATA) { /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz. */ - val = PHYREG15_SSC_CNT_VALUE << PHYREG15_SSC_CNT_SHIFT; - rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK, - val, PHYREG15); + val = RK3568_PHYREG15_SSC_CNT_VALUE << RK3568_PHYREG15_SSC_CNT_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG15_SSC_CNT_MASK, + val, RK3568_PHYREG15); - writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16); + writel(RK3568_PHYREG16_SSC_CNT_VALUE, priv->mmio + RK3568_PHYREG16); } break; @@ -658,24 +854,26 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); if (priv->type == PHY_TYPE_PCIE) { /* PLL KVCO fine tuning. */ - val = PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT; - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, - val, PHYREG33); + val = RK3568_PHYREG33_PLL_KVCO_VALUE << RK3568_PHYREG33_PLL_KVCO_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, + val, RK3568_PHYREG33); /* Enable controlling random jitter. */ - writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + writel(RK3568_PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + RK3568_PHYREG12); - val = PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT; - rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, - val, PHYREG6); + val = RK3568_PHYREG6_PLL_DIV_2 << RK3568_PHYREG6_PLL_DIV_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG6_PLL_DIV_MASK, + val, RK3568_PHYREG6); - writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); - writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + writel(RK3568_PHYREG18_PLL_LOOP, priv->mmio + RK3568_PHYREG18); + writel(RK3568_PHYREG11_SU_TRIM_0_7, priv->mmio + RK3568_PHYREG11); } else if (priv->type == PHY_TYPE_SATA) { /* downward spread spectrum +500ppm */ - val = PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT; - val |= PHYREG32_SSC_OFFSET_500PPM << PHYREG32_SSC_OFFSET_SHIFT; - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32); + val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; + val |= RK3568_PHYREG32_SSC_OFFSET_500PPM << + RK3568_PHYREG32_SSC_OFFSET_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, + RK3568_PHYREG32); } break; @@ -687,20 +885,21 @@ static int rk3568_combphy_cfg(struct rockchip_combphy_priv *priv) if (priv->ext_refclk) { rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true); if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) { - val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT; - val |= PHYREG13_CKRCV_AMP0; - rockchip_combphy_updatel(priv, PHYREG13_RESISTER_MASK, val, PHYREG13); - - val = readl(priv->mmio + PHYREG14); - val |= PHYREG14_CKRCV_AMP1; - writel(val, priv->mmio + PHYREG14); + val = RK3568_PHYREG13_RESISTER_HIGH_Z << RK3568_PHYREG13_RESISTER_SHIFT; + val |= RK3568_PHYREG13_CKRCV_AMP0; + rockchip_combphy_updatel(priv, RK3568_PHYREG13_RESISTER_MASK, val, + RK3568_PHYREG13); + + val = readl(priv->mmio + RK3568_PHYREG14); + val |= RK3568_PHYREG14_CKRCV_AMP1; + writel(val, priv->mmio + RK3568_PHYREG14); } } if (priv->enable_ssc) { - val = readl(priv->mmio + PHYREG8); - val |= PHYREG8_SSC_EN; - writel(val, priv->mmio + PHYREG8); + val = readl(priv->mmio + RK3568_PHYREG8); + val |= RK3568_PHYREG8_SSC_EN; + writel(val, priv->mmio + RK3568_PHYREG8); } return 0; @@ -737,6 +936,8 @@ static const struct rockchip_combphy_grfcfg rk3568_combphy_grfcfgs = { /* pipe-grf */ .pipe_con0_for_sata = { 0x0000, 15, 0, 0x00, 0x2220 }, .pipe_xpcs_phy_ready = { 0x0040, 2, 2, 0x00, 0x01 }, + .u3otg0_port_en = { 0x0104, 15, 0, 0x0181, 0x1100 }, + .u3otg1_port_en = { 0x0144, 15, 0, 0x0181, 0x1100 }, }; static const struct rockchip_combphy_cfg rk3568_combphy_cfgs = { @@ -759,8 +960,8 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) switch (priv->type) { case PHY_TYPE_PCIE: /* Set SSC downward spread spectrum */ - val = FIELD_PREP(PHYREG32_SSC_MASK, PHYREG32_SSC_DOWNWARD); - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32); + val = FIELD_PREP(RK3568_PHYREG32_SSC_MASK, RK3568_PHYREG32_SSC_DOWNWARD); + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_pcie, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_pcie, true); @@ -770,32 +971,33 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) case PHY_TYPE_USB3: /* Set SSC downward spread spectrum */ - val = FIELD_PREP(PHYREG32_SSC_MASK, PHYREG32_SSC_DOWNWARD); - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32); + val = FIELD_PREP(RK3568_PHYREG32_SSC_MASK, RK3568_PHYREG32_SSC_DOWNWARD); + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); /* Enable adaptive CTLE for USB3.0 Rx */ - val = readl(priv->mmio + PHYREG15); - val |= PHYREG15_CTLE_EN; - writel(val, priv->mmio + PHYREG15); + val = readl(priv->mmio + RK3568_PHYREG15); + val |= RK3568_PHYREG15_CTLE_EN; + writel(val, priv->mmio + RK3568_PHYREG15); /* Set PLL KVCO fine tuning signals */ - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, BIT(3), PHYREG33); + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, BIT(3), + RK3568_PHYREG33); /* Set PLL LPF R1 to su_trim[10:7]=1001 */ - writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + writel(RK3568_PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + RK3568_PHYREG12); /* Set PLL input clock divider 1/2 */ - val = FIELD_PREP(PHYREG6_PLL_DIV_MASK, PHYREG6_PLL_DIV_2); - rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, val, PHYREG6); + val = FIELD_PREP(RK3568_PHYREG6_PLL_DIV_MASK, RK3568_PHYREG6_PLL_DIV_2); + rockchip_combphy_updatel(priv, RK3568_PHYREG6_PLL_DIV_MASK, val, RK3568_PHYREG6); /* Set PLL loop divider */ - writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); + writel(RK3568_PHYREG18_PLL_LOOP, priv->mmio + RK3568_PHYREG18); /* Set PLL KVCO to min and set PLL charge pump current to max */ - writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + writel(RK3568_PHYREG11_SU_TRIM_0_7, priv->mmio + RK3568_PHYREG11); /* Set Rx squelch input filler bandwidth */ - writel(PHYREG21_RX_SQUELCH_VAL, priv->mmio + PHYREG21); + writel(RK3576_PHYREG21_RX_SQUELCH_VAL, priv->mmio + RK3576_PHYREG21); rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); @@ -804,14 +1006,14 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) case PHY_TYPE_SATA: /* Enable adaptive CTLE for SATA Rx */ - val = readl(priv->mmio + PHYREG15); - val |= PHYREG15_CTLE_EN; - writel(val, priv->mmio + PHYREG15); + val = readl(priv->mmio + RK3568_PHYREG15); + val |= RK3568_PHYREG15_CTLE_EN; + writel(val, priv->mmio + RK3568_PHYREG15); /* Set tx_rterm = 50 ohm and rx_rterm = 43.5 ohm */ - val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT; - val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT; - writel(val, priv->mmio + PHYREG7); + val = RK3568_PHYREG7_TX_RTERM_50OHM << RK3568_PHYREG7_TX_RTERM_SHIFT; + val |= RK3568_PHYREG7_RX_RTERM_44OHM << RK3568_PHYREG7_RX_RTERM_SHIFT; + writel(val, priv->mmio + RK3568_PHYREG7); rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_sata, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_sata, true); @@ -833,19 +1035,21 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_24m, true); if (priv->type == PHY_TYPE_USB3 || priv->type == PHY_TYPE_SATA) { /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz */ - val = FIELD_PREP(PHYREG15_SSC_CNT_MASK, PHYREG15_SSC_CNT_VALUE); - rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK, - val, PHYREG15); + val = FIELD_PREP(RK3568_PHYREG15_SSC_CNT_MASK, + RK3568_PHYREG15_SSC_CNT_VALUE); + rockchip_combphy_updatel(priv, RK3568_PHYREG15_SSC_CNT_MASK, + val, RK3568_PHYREG15); - writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16); + writel(RK3568_PHYREG16_SSC_CNT_VALUE, priv->mmio + RK3568_PHYREG16); } else if (priv->type == PHY_TYPE_PCIE) { /* PLL KVCO tuning fine */ - val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE_RK3576); - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, - val, PHYREG33); + val = FIELD_PREP(RK3568_PHYREG33_PLL_KVCO_MASK, + RK3576_PHYREG33_PLL_KVCO_VALUE); + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, + val, RK3568_PHYREG33); /* Set up rx_pck invert and rx msb to disable */ - writel(0x00, priv->mmio + PHYREG27); + writel(0x00, priv->mmio + RK3588_PHYREG27); /* * Set up SU adjust signal: @@ -853,11 +1057,11 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) * su_trim[15:8], PLL LPF R1 adujst bits[9:7]=3'b011 * su_trim[31:24], CKDRV adjust */ - writel(0x90, priv->mmio + PHYREG11); - writel(0x02, priv->mmio + PHYREG12); - writel(0x57, priv->mmio + PHYREG14); + writel(0x90, priv->mmio + RK3568_PHYREG11); + writel(0x02, priv->mmio + RK3568_PHYREG12); + writel(0x57, priv->mmio + RK3568_PHYREG14); - writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16); + writel(RK3568_PHYREG16_SSC_CNT_VALUE, priv->mmio + RK3568_PHYREG16); } break; @@ -869,15 +1073,16 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); if (priv->type == PHY_TYPE_PCIE) { /* gate_tx_pck_sel length select work for L1SS */ - writel(0xc0, priv->mmio + PHYREG30); + writel(0xc0, priv->mmio + RK3576_PHYREG30); /* PLL KVCO tuning fine */ - val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE_RK3576); - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, - val, PHYREG33); + val = FIELD_PREP(RK3568_PHYREG33_PLL_KVCO_MASK, + RK3576_PHYREG33_PLL_KVCO_VALUE); + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, + val, RK3568_PHYREG33); /* Set up rx_trim: PLL LPF C1 85pf R1 1.25kohm */ - writel(0x4c, priv->mmio + PHYREG27); + writel(0x4c, priv->mmio + RK3588_PHYREG27); /* * Set up SU adjust signal: @@ -887,20 +1092,23 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) * su_trim[23:16], CKRCV adjust * su_trim[31:24], CKDRV adjust */ - writel(0x90, priv->mmio + PHYREG11); - writel(0x43, priv->mmio + PHYREG12); - writel(0x88, priv->mmio + PHYREG13); - writel(0x56, priv->mmio + PHYREG14); + writel(0x90, priv->mmio + RK3568_PHYREG11); + writel(0x43, priv->mmio + RK3568_PHYREG12); + writel(0x88, priv->mmio + RK3568_PHYREG13); + writel(0x56, priv->mmio + RK3568_PHYREG14); } else if (priv->type == PHY_TYPE_SATA) { /* downward spread spectrum +500ppm */ - val = FIELD_PREP(PHYREG32_SSC_DIR_MASK, PHYREG32_SSC_DOWNWARD); - val |= FIELD_PREP(PHYREG32_SSC_OFFSET_MASK, PHYREG32_SSC_OFFSET_500PPM); - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32); + val = FIELD_PREP(RK3568_PHYREG32_SSC_DIR_MASK, + RK3568_PHYREG32_SSC_DOWNWARD); + val |= FIELD_PREP(RK3568_PHYREG32_SSC_OFFSET_MASK, + RK3568_PHYREG32_SSC_OFFSET_500PPM); + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, + RK3568_PHYREG32); /* ssc ppm adjust to 3500ppm */ - rockchip_combphy_updatel(priv, PHYREG10_SSC_PCM_MASK, - PHYREG10_SSC_PCM_3500PPM, - PHYREG10); + rockchip_combphy_updatel(priv, RK3576_PHYREG10_SSC_PCM_MASK, + RK3576_PHYREG10_SSC_PCM_3500PPM, + RK3576_PHYREG10); } break; @@ -912,12 +1120,13 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) if (priv->ext_refclk) { rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true); if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) { - val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE_RK3576); - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, - val, PHYREG33); + val = FIELD_PREP(RK3568_PHYREG33_PLL_KVCO_MASK, + RK3576_PHYREG33_PLL_KVCO_VALUE); + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, + val, RK3568_PHYREG33); /* Set up rx_trim: PLL LPF C1 85pf R1 2.5kohm */ - writel(0x0c, priv->mmio + PHYREG27); + writel(0x0c, priv->mmio + RK3588_PHYREG27); /* * Set up SU adjust signal: @@ -927,25 +1136,25 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) * su_trim[23:16], CKRCV adjust * su_trim[31:24], CKDRV adjust */ - writel(0x90, priv->mmio + PHYREG11); - writel(0x43, priv->mmio + PHYREG12); - writel(0x88, priv->mmio + PHYREG13); - writel(0x56, priv->mmio + PHYREG14); + writel(0x90, priv->mmio + RK3568_PHYREG11); + writel(0x43, priv->mmio + RK3568_PHYREG12); + writel(0x88, priv->mmio + RK3568_PHYREG13); + writel(0x56, priv->mmio + RK3568_PHYREG14); } } if (priv->enable_ssc) { - val = readl(priv->mmio + PHYREG8); - val |= PHYREG8_SSC_EN; - writel(val, priv->mmio + PHYREG8); + val = readl(priv->mmio + RK3568_PHYREG8); + val |= RK3568_PHYREG8_SSC_EN; + writel(val, priv->mmio + RK3568_PHYREG8); if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_24MHz) { /* Set PLL loop divider */ - writel(0x00, priv->mmio + PHYREG17); - writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); + writel(0x00, priv->mmio + RK3576_PHYREG17); + writel(RK3568_PHYREG18_PLL_LOOP, priv->mmio + RK3568_PHYREG18); /* Set up rx_pck invert and rx msb to disable */ - writel(0x00, priv->mmio + PHYREG27); + writel(0x00, priv->mmio + RK3588_PHYREG27); /* * Set up SU adjust signal: @@ -954,16 +1163,17 @@ static int rk3576_combphy_cfg(struct rockchip_combphy_priv *priv) * su_trim[23:16], CKRCV adjust * su_trim[31:24], CKDRV adjust */ - writel(0x90, priv->mmio + PHYREG11); - writel(0x02, priv->mmio + PHYREG12); - writel(0x08, priv->mmio + PHYREG13); - writel(0x57, priv->mmio + PHYREG14); - writel(0x40, priv->mmio + PHYREG15); + writel(0x90, priv->mmio + RK3568_PHYREG11); + writel(0x02, priv->mmio + RK3568_PHYREG12); + writel(0x08, priv->mmio + RK3568_PHYREG13); + writel(0x57, priv->mmio + RK3568_PHYREG14); + writel(0x40, priv->mmio + RK3568_PHYREG15); - writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16); + writel(RK3568_PHYREG16_SSC_CNT_VALUE, priv->mmio + RK3568_PHYREG16); - val = FIELD_PREP(PHYREG33_PLL_KVCO_MASK, PHYREG33_PLL_KVCO_VALUE_RK3576); - writel(val, priv->mmio + PHYREG33); + val = FIELD_PREP(RK3568_PHYREG33_PLL_KVCO_MASK, + RK3576_PHYREG33_PLL_KVCO_VALUE); + writel(val, priv->mmio + RK3568_PHYREG33); } } @@ -1033,30 +1243,28 @@ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) break; case PHY_TYPE_USB3: /* Set SSC downward spread spectrum */ - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, - PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT, - PHYREG32); + val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, RK3568_PHYREG32); /* Enable adaptive CTLE for USB3.0 Rx. */ - val = readl(priv->mmio + PHYREG15); - val |= PHYREG15_CTLE_EN; - writel(val, priv->mmio + PHYREG15); + val = readl(priv->mmio + RK3568_PHYREG15); + val |= RK3568_PHYREG15_CTLE_EN; + writel(val, priv->mmio + RK3568_PHYREG15); /* Set PLL KVCO fine tuning signals. */ - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, - PHYREG33_PLL_KVCO_VALUE << PHYREG33_PLL_KVCO_SHIFT, - PHYREG33); + val = RK3568_PHYREG33_PLL_KVCO_VALUE << RK3568_PHYREG33_PLL_KVCO_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, val, RK3568_PHYREG33); /* Enable controlling random jitter. */ - writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + writel(RK3568_PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + RK3568_PHYREG12); /* Set PLL input clock divider 1/2. */ - rockchip_combphy_updatel(priv, PHYREG6_PLL_DIV_MASK, - PHYREG6_PLL_DIV_2 << PHYREG6_PLL_DIV_SHIFT, - PHYREG6); + rockchip_combphy_updatel(priv, RK3568_PHYREG6_PLL_DIV_MASK, + RK3568_PHYREG6_PLL_DIV_2 << RK3568_PHYREG6_PLL_DIV_SHIFT, + RK3568_PHYREG6); - writel(PHYREG18_PLL_LOOP, priv->mmio + PHYREG18); - writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + writel(RK3568_PHYREG18_PLL_LOOP, priv->mmio + RK3568_PHYREG18); + writel(RK3568_PHYREG11_SU_TRIM_0_7, priv->mmio + RK3568_PHYREG11); rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txcomp_sel, false); rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_txelec_sel, false); @@ -1064,16 +1272,16 @@ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) break; case PHY_TYPE_SATA: /* Enable adaptive CTLE for SATA Rx. */ - val = readl(priv->mmio + PHYREG15); - val |= PHYREG15_CTLE_EN; - writel(val, priv->mmio + PHYREG15); + val = readl(priv->mmio + RK3568_PHYREG15); + val |= RK3568_PHYREG15_CTLE_EN; + writel(val, priv->mmio + RK3568_PHYREG15); /* * Set tx_rterm=50ohm and rx_rterm=44ohm for SATA. * 0: 60ohm, 8: 50ohm 15: 44ohm (by step abort 1ohm) */ - val = PHYREG7_TX_RTERM_50OHM << PHYREG7_TX_RTERM_SHIFT; - val |= PHYREG7_RX_RTERM_44OHM << PHYREG7_RX_RTERM_SHIFT; - writel(val, priv->mmio + PHYREG7); + val = RK3568_PHYREG7_TX_RTERM_50OHM << RK3568_PHYREG7_TX_RTERM_SHIFT; + val |= RK3568_PHYREG7_RX_RTERM_44OHM << RK3568_PHYREG7_RX_RTERM_SHIFT; + writel(val, priv->mmio + RK3568_PHYREG7); rockchip_combphy_param_write(priv->phy_grf, &cfg->con0_for_sata, true); rockchip_combphy_param_write(priv->phy_grf, &cfg->con1_for_sata, true); @@ -1095,11 +1303,11 @@ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) case REF_CLOCK_24MHz: if (priv->type == PHY_TYPE_USB3 || priv->type == PHY_TYPE_SATA) { /* Set ssc_cnt[9:0]=0101111101 & 31.5KHz. */ - val = PHYREG15_SSC_CNT_VALUE << PHYREG15_SSC_CNT_SHIFT; - rockchip_combphy_updatel(priv, PHYREG15_SSC_CNT_MASK, - val, PHYREG15); + val = RK3568_PHYREG15_SSC_CNT_VALUE << RK3568_PHYREG15_SSC_CNT_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG15_SSC_CNT_MASK, + val, RK3568_PHYREG15); - writel(PHYREG16_SSC_CNT_VALUE, priv->mmio + PHYREG16); + writel(RK3568_PHYREG16_SSC_CNT_VALUE, priv->mmio + RK3568_PHYREG16); } break; @@ -1110,23 +1318,25 @@ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_100m, true); if (priv->type == PHY_TYPE_PCIE) { /* PLL KVCO fine tuning. */ - val = 4 << PHYREG33_PLL_KVCO_SHIFT; - rockchip_combphy_updatel(priv, PHYREG33_PLL_KVCO_MASK, - val, PHYREG33); + val = 4 << RK3568_PHYREG33_PLL_KVCO_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG33_PLL_KVCO_MASK, + val, RK3568_PHYREG33); /* Enable controlling random jitter. */ - writel(PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + PHYREG12); + writel(RK3568_PHYREG12_PLL_LPF_ADJ_VALUE, priv->mmio + RK3568_PHYREG12); /* Set up rx_trim: PLL LPF C1 85pf R1 1.25kohm */ - writel(PHYREG27_RX_TRIM_RK3588, priv->mmio + PHYREG27); + writel(RK3588_PHYREG27_RX_TRIM, priv->mmio + RK3588_PHYREG27); /* Set up su_trim: */ - writel(PHYREG11_SU_TRIM_0_7, priv->mmio + PHYREG11); + writel(RK3568_PHYREG11_SU_TRIM_0_7, priv->mmio + RK3568_PHYREG11); } else if (priv->type == PHY_TYPE_SATA) { /* downward spread spectrum +500ppm */ - val = PHYREG32_SSC_DOWNWARD << PHYREG32_SSC_DIR_SHIFT; - val |= PHYREG32_SSC_OFFSET_500PPM << PHYREG32_SSC_OFFSET_SHIFT; - rockchip_combphy_updatel(priv, PHYREG32_SSC_MASK, val, PHYREG32); + val = RK3568_PHYREG32_SSC_DOWNWARD << RK3568_PHYREG32_SSC_DIR_SHIFT; + val |= RK3568_PHYREG32_SSC_OFFSET_500PPM << + RK3568_PHYREG32_SSC_OFFSET_SHIFT; + rockchip_combphy_updatel(priv, RK3568_PHYREG32_SSC_MASK, val, + RK3568_PHYREG32); } break; default: @@ -1137,20 +1347,21 @@ static int rk3588_combphy_cfg(struct rockchip_combphy_priv *priv) if (priv->ext_refclk) { rockchip_combphy_param_write(priv->phy_grf, &cfg->pipe_clk_ext, true); if (priv->type == PHY_TYPE_PCIE && rate == REF_CLOCK_100MHz) { - val = PHYREG13_RESISTER_HIGH_Z << PHYREG13_RESISTER_SHIFT; - val |= PHYREG13_CKRCV_AMP0; - rockchip_combphy_updatel(priv, PHYREG13_RESISTER_MASK, val, PHYREG13); - - val = readl(priv->mmio + PHYREG14); - val |= PHYREG14_CKRCV_AMP1; - writel(val, priv->mmio + PHYREG14); + val = RK3568_PHYREG13_RESISTER_HIGH_Z << RK3568_PHYREG13_RESISTER_SHIFT; + val |= RK3568_PHYREG13_CKRCV_AMP0; + rockchip_combphy_updatel(priv, RK3568_PHYREG13_RESISTER_MASK, val, + RK3568_PHYREG13); + + val = readl(priv->mmio + RK3568_PHYREG14); + val |= RK3568_PHYREG14_CKRCV_AMP1; + writel(val, priv->mmio + RK3568_PHYREG14); } } if (priv->enable_ssc) { - val = readl(priv->mmio + PHYREG8); - val |= PHYREG8_SSC_EN; - writel(val, priv->mmio + PHYREG8); + val = readl(priv->mmio + RK3568_PHYREG8); + val |= RK3568_PHYREG8_SSC_EN; + writel(val, priv->mmio + RK3568_PHYREG8); } return 0; @@ -1198,6 +1409,10 @@ static const struct rockchip_combphy_cfg rk3588_combphy_cfgs = { static const struct of_device_id rockchip_combphy_of_match[] = { { + .compatible = "rockchip,rk3528-naneng-combphy", + .data = &rk3528_combphy_cfgs, + }, + { .compatible = "rockchip,rk3562-naneng-combphy", .data = &rk3562_combphy_cfgs, }, diff --git a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c index 79db57ee90d1..01bbf668e05e 100644 --- a/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c +++ b/drivers/phy/rockchip/phy-rockchip-samsung-hdptx.c @@ -795,7 +795,6 @@ static const struct regmap_config rk_hdptx_phy_regmap_config = { .val_bits = 32, .writeable_reg = rk_hdptx_phy_is_rw_reg, .readable_reg = rk_hdptx_phy_is_rw_reg, - .fast_io = true, .max_register = 0x18b4, }; diff --git a/drivers/phy/rockchip/phy-rockchip-usbdp.c b/drivers/phy/rockchip/phy-rockchip-usbdp.c index c066cc0a7b4f..fba35510d88c 100644 --- a/drivers/phy/rockchip/phy-rockchip-usbdp.c +++ b/drivers/phy/rockchip/phy-rockchip-usbdp.c @@ -666,7 +666,7 @@ static int rk_udphy_orien_sw_set(struct typec_switch_dev *sw, goto unlock_ret; } - udphy->flip = (orien == TYPEC_ORIENTATION_REVERSE) ? true : false; + udphy->flip = orien == TYPEC_ORIENTATION_REVERSE; rk_udphy_set_typec_default_mapping(udphy); rk_udphy_usb_bvalid_enable(udphy, true); @@ -1430,7 +1430,6 @@ static const struct regmap_config rk_udphy_pma_regmap_cfg = { .reg_bits = 32, .reg_stride = 4, .val_bits = 32, - .fast_io = true, .max_register = 0x20dc, }; diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index dd660ebe8045..a88ba95bdc8f 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -2417,4 +2417,3 @@ module_platform_driver(exynos5_usb3drd_phy); MODULE_DESCRIPTION("Samsung Exynos5 SoCs USB 3.0 DRD controller PHY driver"); MODULE_AUTHOR("Vivek Gautam <gautam.vivek@samsung.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:exynos5_usb3drd_phy"); diff --git a/drivers/phy/samsung/phy-samsung-usb2.c b/drivers/phy/samsung/phy-samsung-usb2.c index 9de744cd6f39..d2749b67cf8f 100644 --- a/drivers/phy/samsung/phy-samsung-usb2.c +++ b/drivers/phy/samsung/phy-samsung-usb2.c @@ -258,4 +258,3 @@ module_platform_driver(samsung_usb2_phy_driver); MODULE_DESCRIPTION("Samsung S5P/Exynos SoC USB PHY driver"); MODULE_AUTHOR("Kamil Debski <k.debski@samsung.com>"); MODULE_LICENSE("GPL v2"); -MODULE_ALIAS("platform:samsung-usb2-phy"); diff --git a/drivers/phy/sophgo/Kconfig b/drivers/phy/sophgo/Kconfig new file mode 100644 index 000000000000..2c943bbe1f81 --- /dev/null +++ b/drivers/phy/sophgo/Kconfig @@ -0,0 +1,19 @@ +# SPDX-License-Identifier: GPL-2.0-only +# +# Phy drivers for Sophgo platforms +# + +if ARCH_SOPHGO || COMPILE_TEST + +config PHY_SOPHGO_CV1800_USB2 + tristate "Sophgo CV18XX/SG200X USB 2.0 PHY support" + depends on MFD_SYSCON + depends on USB_SUPPORT + select GENERIC_PHY + help + Enable this to support the USB 2.0 PHY used with + the DWC2 USB controller in Sophgo CV18XX/SG200X + series SoC. + If unsure, say N. + +endif # ARCH_SOPHGO || COMPILE_TEST diff --git a/drivers/phy/sophgo/Makefile b/drivers/phy/sophgo/Makefile new file mode 100644 index 000000000000..318060661759 --- /dev/null +++ b/drivers/phy/sophgo/Makefile @@ -0,0 +1,2 @@ +# SPDX-License-Identifier: GPL-2.0 +obj-$(CONFIG_PHY_SOPHGO_CV1800_USB2) += phy-cv1800-usb2.o diff --git a/drivers/phy/sophgo/phy-cv1800-usb2.c b/drivers/phy/sophgo/phy-cv1800-usb2.c new file mode 100644 index 000000000000..64f8e37b4b52 --- /dev/null +++ b/drivers/phy/sophgo/phy-cv1800-usb2.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (C) 2025 Inochi Amaoto <inochiama@outlook.com> + */ + +#include <linux/clk.h> +#include <linux/bitfield.h> +#include <linux/debugfs.h> +#include <linux/kernel.h> +#include <linux/mfd/syscon.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/of_address.h> +#include <linux/of_gpio.h> +#include <linux/platform_device.h> +#include <linux/phy/phy.h> +#include <linux/regmap.h> +#include <linux/spinlock.h> + +#define REG_USB_PHY_CTRL 0x048 + +#define PHY_VBUS_POWER_EN BIT(0) +#define PHY_VBUS_POWER BIT(1) +#define PHY_ID_OVERWRITE_EN BIT(6) +#define PHY_ID_OVERWRITE_MODE BIT(7) +#define PHY_ID_OVERWRITE_MODE_HOST FIELD_PREP(BIT(7), 0) +#define PHY_ID_OVERWRITE_MODE_DEVICE FIELD_PREP(BIT(7), 1) + +#define PHY_APP_CLK_RATE 125000000 +#define PHY_LPM_CLK_RATE 12000000 +#define PHY_STB_CLK_RATE 333334 + +struct cv1800_usb_phy { + struct phy *phy; + struct regmap *syscon; + spinlock_t lock; + struct clk *usb_app_clk; + struct clk *usb_lpm_clk; + struct clk *usb_stb_clk; + bool support_otg; +}; + +static int cv1800_usb_phy_set_mode(struct phy *_phy, + enum phy_mode mode, int submode) +{ + struct cv1800_usb_phy *phy = phy_get_drvdata(_phy); + unsigned int regval = 0; + int ret; + + dev_info(&phy->phy->dev, "set mode %d", (int)mode); + + switch (mode) { + case PHY_MODE_USB_DEVICE: + regval = PHY_ID_OVERWRITE_EN | PHY_ID_OVERWRITE_MODE_DEVICE; + regmap_clear_bits(phy->syscon, REG_USB_PHY_CTRL, PHY_VBUS_POWER); + break; + case PHY_MODE_USB_HOST: + regval = PHY_ID_OVERWRITE_EN | PHY_ID_OVERWRITE_MODE_HOST; + regmap_set_bits(phy->syscon, REG_USB_PHY_CTRL, PHY_VBUS_POWER); + break; + case PHY_MODE_USB_OTG: + if (!phy->support_otg) + return 0; + + ret = regmap_read(phy->syscon, REG_USB_PHY_CTRL, ®val); + if (ret) + return ret; + + regval = FIELD_GET(PHY_ID_OVERWRITE_MODE, regval); + break; + default: + return -EINVAL; + } + + return regmap_update_bits(phy->syscon, REG_USB_PHY_CTRL, + PHY_ID_OVERWRITE_EN | PHY_ID_OVERWRITE_MODE, + regval); +} + +static int cv1800_usb_phy_set_clock(struct cv1800_usb_phy *phy) +{ + int ret; + + ret = clk_set_rate(phy->usb_app_clk, PHY_APP_CLK_RATE); + if (ret) + return ret; + + ret = clk_set_rate(phy->usb_lpm_clk, PHY_LPM_CLK_RATE); + if (ret) + return ret; + + return clk_set_rate(phy->usb_stb_clk, PHY_STB_CLK_RATE); +} + +static const struct phy_ops cv1800_usb_phy_ops = { + .set_mode = cv1800_usb_phy_set_mode, + .owner = THIS_MODULE, +}; + +static int cv1800_usb_phy_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct device *parent = dev->parent; + struct cv1800_usb_phy *phy; + struct phy_provider *phy_provider; + int ret; + + if (!parent) + return -ENODEV; + + phy = devm_kmalloc(dev, sizeof(*phy), GFP_KERNEL); + if (!phy) + return -ENOMEM; + + phy->syscon = syscon_node_to_regmap(parent->of_node); + if (IS_ERR_OR_NULL(phy->syscon)) + return -ENODEV; + + phy->support_otg = false; + + spin_lock_init(&phy->lock); + + phy->usb_app_clk = devm_clk_get_enabled(dev, "app"); + if (IS_ERR(phy->usb_app_clk)) + return dev_err_probe(dev, PTR_ERR(phy->usb_app_clk), + "Failed to get app clock\n"); + + phy->usb_lpm_clk = devm_clk_get_enabled(dev, "lpm"); + if (IS_ERR(phy->usb_lpm_clk)) + return dev_err_probe(dev, PTR_ERR(phy->usb_lpm_clk), + "Failed to get lpm clock\n"); + + phy->usb_stb_clk = devm_clk_get_enabled(dev, "stb"); + if (IS_ERR(phy->usb_stb_clk)) + return dev_err_probe(dev, PTR_ERR(phy->usb_stb_clk), + "Failed to get stb clock\n"); + + phy->phy = devm_phy_create(dev, NULL, &cv1800_usb_phy_ops); + if (IS_ERR(phy->phy)) + return dev_err_probe(dev, PTR_ERR(phy->phy), + "Failed to create phy\n"); + + ret = cv1800_usb_phy_set_clock(phy); + if (ret) + return ret; + + phy_set_drvdata(phy->phy, phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + + return PTR_ERR_OR_ZERO(phy_provider); +} + +static const struct of_device_id cv1800_usb_phy_ids[] = { + { .compatible = "sophgo,cv1800b-usb2-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, cv1800_usb_phy_ids); + +static struct platform_driver cv1800_usb_phy_driver = { + .probe = cv1800_usb_phy_probe, + .driver = { + .name = "cv1800-usb2-phy", + .of_match_table = cv1800_usb_phy_ids, + }, +}; +module_platform_driver(cv1800_usb_phy_driver); + +MODULE_AUTHOR("Inochi Amaoto <inochiama@outlook.com>"); +MODULE_DESCRIPTION("CV1800/SG2000 SoC USB 2.0 PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/ti/Kconfig b/drivers/phy/ti/Kconfig index b905902d5750..b40f28019131 100644 --- a/drivers/phy/ti/Kconfig +++ b/drivers/phy/ti/Kconfig @@ -62,7 +62,7 @@ config OMAP_CONTROL_PHY config OMAP_USB2 tristate "OMAP USB2 PHY Driver" - depends on ARCH_OMAP2PLUS || ARCH_K3 + depends on ARCH_OMAP2PLUS || ARCH_K3 || COMPILE_TEST depends on USB_SUPPORT select GENERIC_PHY select USB_PHY diff --git a/drivers/phy/ti/phy-am654-serdes.c b/drivers/phy/ti/phy-am654-serdes.c index 431b223996e0..5b6c27aa7e8b 100644 --- a/drivers/phy/ti/phy-am654-serdes.c +++ b/drivers/phy/ti/phy-am654-serdes.c @@ -99,7 +99,6 @@ static const struct regmap_config serdes_am654_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .fast_io = true, .max_register = 0x1ffc, }; diff --git a/drivers/phy/ti/phy-dm816x-usb.c b/drivers/phy/ti/phy-dm816x-usb.c index e8f842d4e841..d274831b731c 100644 --- a/drivers/phy/ti/phy-dm816x-usb.c +++ b/drivers/phy/ti/phy-dm816x-usb.c @@ -269,7 +269,6 @@ static struct platform_driver dm816x_usb_phy_driver = { module_platform_driver(dm816x_usb_phy_driver); -MODULE_ALIAS("platform:dm816x_usb"); MODULE_AUTHOR("Tony Lindgren <tony@atomide.com>"); MODULE_DESCRIPTION("dm816x usb phy driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/phy-j721e-wiz.c b/drivers/phy/ti/phy-j721e-wiz.c index ab2a4f2c0a5b..a8b440c6c46b 100644 --- a/drivers/phy/ti/phy-j721e-wiz.c +++ b/drivers/phy/ti/phy-j721e-wiz.c @@ -1319,7 +1319,6 @@ static const struct regmap_config wiz_regmap_config = { .reg_bits = 32, .val_bits = 32, .reg_stride = 4, - .fast_io = true, }; static struct wiz_data j721e_16g_data = { diff --git a/drivers/phy/ti/phy-omap-control.c b/drivers/phy/ti/phy-omap-control.c index 2fdb8f4241c7..4968434312f8 100644 --- a/drivers/phy/ti/phy-omap-control.c +++ b/drivers/phy/ti/phy-omap-control.c @@ -334,7 +334,6 @@ static void __exit omap_control_phy_exit(void) } module_exit(omap_control_phy_exit); -MODULE_ALIAS("platform:omap_control_phy"); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("OMAP Control Module PHY Driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/phy-omap-usb2.c b/drivers/phy/ti/phy-omap-usb2.c index c444bb2530ca..1eb252604441 100644 --- a/drivers/phy/ti/phy-omap-usb2.c +++ b/drivers/phy/ti/phy-omap-usb2.c @@ -533,7 +533,6 @@ static struct platform_driver omap_usb2_driver = { module_platform_driver(omap_usb2_driver); -MODULE_ALIAS("platform:omap_usb2"); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("OMAP USB2 phy driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/phy/ti/phy-ti-pipe3.c b/drivers/phy/ti/phy-ti-pipe3.c index ae764d6524c9..b5543b5c674c 100644 --- a/drivers/phy/ti/phy-ti-pipe3.c +++ b/drivers/phy/ti/phy-ti-pipe3.c @@ -942,7 +942,6 @@ static struct platform_driver ti_pipe3_driver = { module_platform_driver(ti_pipe3_driver); -MODULE_ALIAS("platform:ti_pipe3"); MODULE_AUTHOR("Texas Instruments Inc."); MODULE_DESCRIPTION("TI PIPE3 phy driver"); MODULE_LICENSE("GPL v2"); diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 73b78d6eac67..c5dbf4e9db84 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -1656,6 +1656,19 @@ int pinctrl_pm_select_default_state(struct device *dev) EXPORT_SYMBOL_GPL(pinctrl_pm_select_default_state); /** + * pinctrl_pm_select_init_state() - select init pinctrl state for PM + * @dev: device to select init state for + */ +int pinctrl_pm_select_init_state(struct device *dev) +{ + if (!dev->pins) + return 0; + + return pinctrl_select_bound_state(dev, dev->pins->init_state); +} +EXPORT_SYMBOL_GPL(pinctrl_pm_select_init_state); + +/** * pinctrl_pm_select_sleep_state() - select sleep pinctrl state for PM * @dev: device to select sleep state for */ diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index 766557547f83..eb5ff49f6fe7 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -354,7 +354,7 @@ static int vfio_ap_validate_nib(struct kvm_vcpu *vcpu, dma_addr_t *nib) if (!*nib) return -EINVAL; - if (kvm_is_error_hva(gfn_to_hva(vcpu->kvm, *nib >> PAGE_SHIFT))) + if (!kvm_s390_is_gpa_in_memslot(vcpu->kvm, *nib)) return -EINVAL; return 0; diff --git a/drivers/scsi/lpfc/lpfc_init.c b/drivers/scsi/lpfc/lpfc_init.c index 0ca7429d86b8..f206267d9ecd 100644 --- a/drivers/scsi/lpfc/lpfc_init.c +++ b/drivers/scsi/lpfc/lpfc_init.c @@ -14367,7 +14367,7 @@ lpfc_sli_prep_dev_for_perm_failure(struct lpfc_hba *phba) * as desired. * * Return codes - * PCI_ERS_RESULT_CAN_RECOVER - can be recovered with reset_link + * PCI_ERS_RESULT_CAN_RECOVER - can be recovered without reset * PCI_ERS_RESULT_NEED_RESET - need to reset before recovery * PCI_ERS_RESULT_DISCONNECT - device could not be recovered **/ diff --git a/drivers/scsi/qla2xxx/qla_os.c b/drivers/scsi/qla2xxx/qla_os.c index 98a5c105fdfd..cb56d2af6cfa 100644 --- a/drivers/scsi/qla2xxx/qla_os.c +++ b/drivers/scsi/qla2xxx/qla_os.c @@ -7884,11 +7884,6 @@ qla2xxx_pci_slot_reset(struct pci_dev *pdev) "Slot Reset.\n"); ha->pci_error_state = QLA_PCI_SLOT_RESET; - /* Workaround: qla2xxx driver which access hardware earlier - * needs error state to be pci_channel_io_online. - * Otherwise mailbox command timesout. - */ - pdev->error_state = pci_channel_io_normal; pci_restore_state(pdev); diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index bc1e653080d9..91e70cb46fb5 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -114,7 +114,6 @@ static int sdw_drv_probe(struct device *dev) ret = drv->probe(slave, id); if (ret) { - dev_pm_domain_detach(dev, false); ida_free(&slave->bus->slave_ida, slave->index); return ret; } @@ -180,8 +179,6 @@ static int sdw_drv_remove(struct device *dev) if (drv->remove) ret = drv->remove(slave); - dev_pm_domain_detach(dev, false); - ida_free(&slave->bus->slave_ida, slave->index); return ret; diff --git a/drivers/soundwire/debugfs.c b/drivers/soundwire/debugfs.c index 230a51489486..1e0f9318b616 100644 --- a/drivers/soundwire/debugfs.c +++ b/drivers/soundwire/debugfs.c @@ -91,6 +91,8 @@ static int sdw_slave_reg_show(struct seq_file *s_file, void *data) ret += sdw_sprintf(slave, buf, ret, i); for (i = SDW_SCP_DEVID_0; i <= SDW_SCP_DEVID_5; i++) ret += sdw_sprintf(slave, buf, ret, i); + for (i = SDW_SCP_SDCA_INT1; i <= SDW_SCP_SDCA_INTMASK4; i++) + ret += sdw_sprintf(slave, buf, ret, i); for (i = SDW_SCP_FRAMECTRL_B0; i <= SDW_SCP_BUSCLOCK_SCALE_B0; i++) ret += sdw_sprintf(slave, buf, ret, i); for (i = SDW_SCP_FRAMECTRL_B1; i <= SDW_SCP_BUSCLOCK_SCALE_B1; i++) diff --git a/drivers/soundwire/qcom.c b/drivers/soundwire/qcom.c index bd2b293b44f2..5b3078220189 100644 --- a/drivers/soundwire/qcom.c +++ b/drivers/soundwire/qcom.c @@ -924,10 +924,7 @@ static enum sdw_command_response qcom_swrm_xfer_msg(struct sdw_bus *bus, if (msg->flags == SDW_MSG_FLAG_READ) { for (i = 0; i < msg->len;) { - if ((msg->len - i) < QCOM_SWRM_MAX_RD_LEN) - len = msg->len - i; - else - len = QCOM_SWRM_MAX_RD_LEN; + len = min(msg->len - i, QCOM_SWRM_MAX_RD_LEN); ret = qcom_swrm_cmd_fifo_rd_cmd(ctrl, msg->dev_num, msg->addr + i, len, diff --git a/drivers/watchdog/intel_oc_wdt.c b/drivers/watchdog/intel_oc_wdt.c index 7c0551106981..a39892c10770 100644 --- a/drivers/watchdog/intel_oc_wdt.c +++ b/drivers/watchdog/intel_oc_wdt.c @@ -41,6 +41,7 @@ struct intel_oc_wdt { struct watchdog_device wdd; struct resource *ctrl_res; + struct watchdog_info info; bool locked; }; @@ -115,7 +116,6 @@ static const struct watchdog_ops intel_oc_wdt_ops = { static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt) { - struct watchdog_info *info; unsigned long val; val = inl(INTEL_OC_WDT_CTRL_REG(oc_wdt)); @@ -134,7 +134,6 @@ static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt) set_bit(WDOG_HW_RUNNING, &oc_wdt->wdd.status); if (oc_wdt->locked) { - info = (struct watchdog_info *)&intel_oc_wdt_info; /* * Set nowayout unconditionally as we cannot stop * the watchdog. @@ -145,7 +144,7 @@ static int intel_oc_wdt_setup(struct intel_oc_wdt *oc_wdt) * and inform the core we can't change it. */ oc_wdt->wdd.timeout = (val & INTEL_OC_WDT_TOV) + 1; - info->options &= ~WDIOF_SETTIMEOUT; + oc_wdt->info.options &= ~WDIOF_SETTIMEOUT; dev_info(oc_wdt->wdd.parent, "Register access locked, heartbeat fixed at: %u s\n", @@ -193,7 +192,8 @@ static int intel_oc_wdt_probe(struct platform_device *pdev) wdd->min_timeout = INTEL_OC_WDT_MIN_TOV; wdd->max_timeout = INTEL_OC_WDT_MAX_TOV; wdd->timeout = INTEL_OC_WDT_DEF_TOV; - wdd->info = &intel_oc_wdt_info; + oc_wdt->info = intel_oc_wdt_info; + wdd->info = &oc_wdt->info; wdd->ops = &intel_oc_wdt_ops; wdd->parent = dev; diff --git a/drivers/watchdog/mpc8xxx_wdt.c b/drivers/watchdog/mpc8xxx_wdt.c index 867f9f311379..a4b497ecfa20 100644 --- a/drivers/watchdog/mpc8xxx_wdt.c +++ b/drivers/watchdog/mpc8xxx_wdt.c @@ -100,6 +100,8 @@ static int mpc8xxx_wdt_start(struct watchdog_device *w) ddata->swtc = tmp >> 16; set_bit(WDOG_HW_RUNNING, &ddata->wdd.status); + mpc8xxx_wdt_keepalive(ddata); + return 0; } diff --git a/drivers/watchdog/rzg2l_wdt.c b/drivers/watchdog/rzg2l_wdt.c index 11bbe48160ec..1c9aa366d0a0 100644 --- a/drivers/watchdog/rzg2l_wdt.c +++ b/drivers/watchdog/rzg2l_wdt.c @@ -310,9 +310,7 @@ static int rzg2l_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&priv->wdev, nowayout); watchdog_stop_on_unregister(&priv->wdev); - ret = watchdog_init_timeout(&priv->wdev, 0, dev); - if (ret) - dev_warn(dev, "Specified timeout invalid, using default"); + watchdog_init_timeout(&priv->wdev, 0, dev); return devm_watchdog_register_device(&pdev->dev, &priv->wdev); } diff --git a/drivers/watchdog/rzv2h_wdt.c b/drivers/watchdog/rzv2h_wdt.c index 8defd0241213..a694786837e1 100644 --- a/drivers/watchdog/rzv2h_wdt.c +++ b/drivers/watchdog/rzv2h_wdt.c @@ -21,11 +21,17 @@ #define WDTSR 0x04 /* WDT Status Register RW, 16 */ #define WDTRCR 0x06 /* WDT Reset Control Register RW, 8 */ +/* This register is only available on RZ/T2H and RZ/N2H SoCs */ +#define WDTDCR 0x00 /* WDT Debug Control Register RW, 32 */ + #define WDTCR_TOPS_1024 0x00 +#define WDTCR_TOPS_4096 0x01 #define WDTCR_TOPS_16384 0x03 #define WDTCR_CKS_CLK_1 0x00 +#define WDTCR_CKS_CLK_4 0x10 #define WDTCR_CKS_CLK_256 0x50 +#define WDTCR_CKS_CLK_8192 0x80 #define WDTCR_RPES_0 0x300 #define WDTCR_RPES_75 0x000 @@ -35,8 +41,7 @@ #define WDTRCR_RSTIRQS BIT(7) -#define MAX_TIMEOUT_CYCLES 16384 -#define CLOCK_DIV_BY_256 256 +#define WDTDCR_WDTSTOPCTRL BIT(0) #define WDT_DEFAULT_TIMEOUT 60U @@ -45,12 +50,29 @@ module_param(nowayout, bool, 0); MODULE_PARM_DESC(nowayout, "Watchdog cannot be stopped once started (default=" __MODULE_STRING(WATCHDOG_NOWAYOUT) ")"); +enum rzv2h_wdt_count_source { + COUNT_SOURCE_LOCO, + COUNT_SOURCE_PCLK, +}; + +struct rzv2h_of_data { + u8 cks_min; + u8 cks_max; + u16 cks_div; + u8 tops; + u16 timeout_cycles; + enum rzv2h_wdt_count_source count_source; + bool wdtdcr; +}; + struct rzv2h_wdt_priv { void __iomem *base; + void __iomem *wdtdcr; struct clk *pclk; struct clk *oscclk; struct reset_control *rstc; struct watchdog_device wdev; + const struct rzv2h_of_data *of_data; }; static int rzv2h_wdt_ping(struct watchdog_device *wdev) @@ -67,6 +89,20 @@ static int rzv2h_wdt_ping(struct watchdog_device *wdev) return 0; } +static void rzt2h_wdt_wdtdcr_count_stop(struct rzv2h_wdt_priv *priv) +{ + u32 reg = readl(priv->wdtdcr + WDTDCR); + + writel(reg | WDTDCR_WDTSTOPCTRL, priv->wdtdcr + WDTDCR); +} + +static void rzt2h_wdt_wdtdcr_count_start(struct rzv2h_wdt_priv *priv) +{ + u32 reg = readl(priv->wdtdcr + WDTDCR); + + writel(reg & ~WDTDCR_WDTSTOPCTRL, priv->wdtdcr + WDTDCR); +} + static void rzv2h_wdt_setup(struct watchdog_device *wdev, u16 wdtcr) { struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); @@ -84,6 +120,7 @@ static void rzv2h_wdt_setup(struct watchdog_device *wdev, u16 wdtcr) static int rzv2h_wdt_start(struct watchdog_device *wdev) { struct rzv2h_wdt_priv *priv = watchdog_get_drvdata(wdev); + const struct rzv2h_of_data *of_data = priv->of_data; int ret; ret = pm_runtime_resume_and_get(wdev->parent); @@ -101,13 +138,20 @@ static int rzv2h_wdt_start(struct watchdog_device *wdev) /* * WDTCR - * - CKS[7:4] - Clock Division Ratio Select - 0101b: oscclk/256 + * - CKS[7:4] - Clock Division Ratio Select + * - 0101b: oscclk/256 for RZ/V2H(P) + * - 1000b: pclkl/8192 for RZ/T2H * - RPSS[13:12] - Window Start Position Select - 11b: 100% * - RPES[9:8] - Window End Position Select - 11b: 0% - * - TOPS[1:0] - Timeout Period Select - 11b: 16384 cycles (3FFFh) + * - TOPS[1:0] - Timeout Period Select + * - 11b: 16384 cycles (3FFFh) for RZ/V2H(P) + * - 01b: 4096 cycles (0FFFh) for RZ/T2H */ - rzv2h_wdt_setup(wdev, WDTCR_CKS_CLK_256 | WDTCR_RPSS_100 | - WDTCR_RPES_0 | WDTCR_TOPS_16384); + rzv2h_wdt_setup(wdev, of_data->cks_max | WDTCR_RPSS_100 | + WDTCR_RPES_0 | of_data->tops); + + if (priv->of_data->wdtdcr) + rzt2h_wdt_wdtdcr_count_start(priv); /* * Down counting starts after writing the sequence 00h -> FFh to the @@ -127,6 +171,9 @@ static int rzv2h_wdt_stop(struct watchdog_device *wdev) if (ret) return ret; + if (priv->of_data->wdtdcr) + rzt2h_wdt_wdtdcr_count_stop(priv); + ret = pm_runtime_put(wdev->parent); if (ret < 0) return ret; @@ -179,14 +226,19 @@ static int rzv2h_wdt_restart(struct watchdog_device *wdev, /* * WDTCR - * - CKS[7:4] - Clock Division Ratio Select - 0000b: oscclk/1 + * - CKS[7:4] - Clock Division Ratio Select + * - 0000b: oscclk/1 for RZ/V2H(P) + * - 0100b: pclkl/4 for RZ/T2H * - RPSS[13:12] - Window Start Position Select - 00b: 25% * - RPES[9:8] - Window End Position Select - 00b: 75% * - TOPS[1:0] - Timeout Period Select - 00b: 1024 cycles (03FFh) */ - rzv2h_wdt_setup(wdev, WDTCR_CKS_CLK_1 | WDTCR_RPSS_25 | + rzv2h_wdt_setup(wdev, priv->of_data->cks_min | WDTCR_RPSS_25 | WDTCR_RPES_75 | WDTCR_TOPS_1024); + if (priv->of_data->wdtdcr) + rzt2h_wdt_wdtdcr_count_start(priv); + rzv2h_wdt_ping(wdev); /* wait for underflow to trigger... */ @@ -203,41 +255,83 @@ static const struct watchdog_ops rzv2h_wdt_ops = { .restart = rzv2h_wdt_restart, }; +static int rzt2h_wdt_wdtdcr_init(struct platform_device *pdev, + struct rzv2h_wdt_priv *priv) +{ + int ret; + + priv->wdtdcr = devm_platform_ioremap_resource(pdev, 1); + if (IS_ERR(priv->wdtdcr)) + return PTR_ERR(priv->wdtdcr); + + ret = pm_runtime_resume_and_get(&pdev->dev); + if (ret) + return ret; + + rzt2h_wdt_wdtdcr_count_stop(priv); + + ret = pm_runtime_put(&pdev->dev); + if (ret < 0) + return ret; + + return 0; +} + static int rzv2h_wdt_probe(struct platform_device *pdev) { struct device *dev = &pdev->dev; struct rzv2h_wdt_priv *priv; + struct clk *count_clk; int ret; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); if (!priv) return -ENOMEM; + priv->of_data = of_device_get_match_data(dev); + priv->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(priv->base)) return PTR_ERR(priv->base); priv->pclk = devm_clk_get_prepared(dev, "pclk"); if (IS_ERR(priv->pclk)) - return dev_err_probe(dev, PTR_ERR(priv->pclk), "no pclk"); + return dev_err_probe(dev, PTR_ERR(priv->pclk), "Failed to get pclk\n"); - priv->oscclk = devm_clk_get_prepared(dev, "oscclk"); + priv->oscclk = devm_clk_get_optional_prepared(dev, "oscclk"); if (IS_ERR(priv->oscclk)) - return dev_err_probe(dev, PTR_ERR(priv->oscclk), "no oscclk"); + return dev_err_probe(dev, PTR_ERR(priv->oscclk), "Failed to get oscclk\n"); - priv->rstc = devm_reset_control_get_exclusive(dev, NULL); + priv->rstc = devm_reset_control_get_optional_exclusive(dev, NULL); if (IS_ERR(priv->rstc)) return dev_err_probe(dev, PTR_ERR(priv->rstc), - "failed to get cpg reset"); + "Failed to get cpg reset\n"); + + switch (priv->of_data->count_source) { + case COUNT_SOURCE_LOCO: + count_clk = priv->oscclk; + break; + case COUNT_SOURCE_PCLK: + count_clk = priv->pclk; + break; + default: + return dev_err_probe(dev, -EINVAL, "Invalid count source\n"); + } - priv->wdev.max_hw_heartbeat_ms = (MILLI * MAX_TIMEOUT_CYCLES * CLOCK_DIV_BY_256) / - clk_get_rate(priv->oscclk); + priv->wdev.max_hw_heartbeat_ms = (MILLI * priv->of_data->timeout_cycles * + priv->of_data->cks_div) / clk_get_rate(count_clk); dev_dbg(dev, "max hw timeout of %dms\n", priv->wdev.max_hw_heartbeat_ms); ret = devm_pm_runtime_enable(dev); if (ret) return ret; + if (priv->of_data->wdtdcr) { + ret = rzt2h_wdt_wdtdcr_init(pdev, priv); + if (ret) + return dev_err_probe(dev, ret, "WDTDCR init failed\n"); + } + priv->wdev.min_timeout = 1; priv->wdev.timeout = WDT_DEFAULT_TIMEOUT; priv->wdev.info = &rzv2h_wdt_ident; @@ -247,15 +341,33 @@ static int rzv2h_wdt_probe(struct platform_device *pdev) watchdog_set_nowayout(&priv->wdev, nowayout); watchdog_stop_on_unregister(&priv->wdev); - ret = watchdog_init_timeout(&priv->wdev, 0, dev); - if (ret) - dev_warn(dev, "Specified timeout invalid, using default"); + watchdog_init_timeout(&priv->wdev, 0, dev); return devm_watchdog_register_device(dev, &priv->wdev); } +static const struct rzv2h_of_data rzt2h_wdt_of_data = { + .cks_min = WDTCR_CKS_CLK_4, + .cks_max = WDTCR_CKS_CLK_8192, + .cks_div = 8192, + .tops = WDTCR_TOPS_4096, + .timeout_cycles = 4096, + .count_source = COUNT_SOURCE_PCLK, + .wdtdcr = true, +}; + +static const struct rzv2h_of_data rzv2h_wdt_of_data = { + .cks_min = WDTCR_CKS_CLK_1, + .cks_max = WDTCR_CKS_CLK_256, + .cks_div = 256, + .tops = WDTCR_TOPS_16384, + .timeout_cycles = 16384, + .count_source = COUNT_SOURCE_LOCO, +}; + static const struct of_device_id rzv2h_wdt_ids[] = { - { .compatible = "renesas,r9a09g057-wdt", }, + { .compatible = "renesas,r9a09g057-wdt", .data = &rzv2h_wdt_of_data }, + { .compatible = "renesas,r9a09g077-wdt", .data = &rzt2h_wdt_of_data }, { /* sentinel */ } }; MODULE_DEVICE_TABLE(of, rzv2h_wdt_ids); diff --git a/drivers/watchdog/s3c2410_wdt.c b/drivers/watchdog/s3c2410_wdt.c index 40901bdac426..b774477190b6 100644 --- a/drivers/watchdog/s3c2410_wdt.c +++ b/drivers/watchdog/s3c2410_wdt.c @@ -27,13 +27,15 @@ #include <linux/mfd/syscon.h> #include <linux/regmap.h> #include <linux/delay.h> +#include <linux/math64.h> #define S3C2410_WTCON 0x00 #define S3C2410_WTDAT 0x04 #define S3C2410_WTCNT 0x08 #define S3C2410_WTCLRINT 0x0c -#define S3C2410_WTCNT_MAXCNT 0xffff +#define S3C2410_WTCNT_MAXCNT_16 0xffff +#define S3C2410_WTCNT_MAXCNT_32 0xffffffff #define S3C2410_WTCON_RSTEN BIT(0) #define S3C2410_WTCON_INTEN BIT(2) @@ -123,6 +125,10 @@ * %QUIRK_HAS_DBGACK_BIT: WTCON register has DBGACK_MASK bit. Setting the * DBGACK_MASK bit disables the watchdog outputs when the SoC is in debug mode. * Debug mode is determined by the DBGACK CPU signal. + * + * %QUIRK_HAS_32BIT_CNT: WTDAT and WTCNT are 32-bit registers. With these + * 32-bit registers, larger values will be set, which means that larger timeouts + * value can be set. */ #define QUIRK_HAS_WTCLRINT_REG BIT(0) #define QUIRK_HAS_PMU_MASK_RESET BIT(1) @@ -130,6 +136,7 @@ #define QUIRK_HAS_PMU_AUTO_DISABLE BIT(3) #define QUIRK_HAS_PMU_CNT_EN BIT(4) #define QUIRK_HAS_DBGACK_BIT BIT(5) +#define QUIRK_HAS_32BIT_CNT BIT(6) /* These quirks require that we have a PMU register map */ #define QUIRKS_HAVE_PMUREG \ @@ -198,6 +205,7 @@ struct s3c2410_wdt { struct notifier_block freq_transition; const struct s3c2410_wdt_variant *drv_data; struct regmap *pmureg; + u32 max_cnt; }; static const struct s3c2410_wdt_variant drv_data_s3c2410 = { @@ -298,7 +306,8 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl0 = { .cnt_en_reg = EXYNOS850_CLUSTER0_NONCPU_OUT, .cnt_en_bit = 7, .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | - QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN, + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT, }; static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = { @@ -310,7 +319,8 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov9_cl1 = { .cnt_en_reg = EXYNOSAUTOV9_CLUSTER1_NONCPU_OUT, .cnt_en_bit = 7, .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | - QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN, + QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | + QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT, }; static const struct s3c2410_wdt_variant drv_data_gs101_cl0 = { @@ -349,7 +359,7 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl0 = { .cnt_en_bit = 8, .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | - QUIRK_HAS_DBGACK_BIT, + QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT, }; static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = { @@ -362,7 +372,7 @@ static const struct s3c2410_wdt_variant drv_data_exynosautov920_cl1 = { .cnt_en_bit = 8, .quirks = QUIRK_HAS_WTCLRINT_REG | QUIRK_HAS_PMU_MASK_RESET | QUIRK_HAS_PMU_RST_STAT | QUIRK_HAS_PMU_CNT_EN | - QUIRK_HAS_DBGACK_BIT, + QUIRK_HAS_DBGACK_BIT | QUIRK_HAS_32BIT_CNT, }; static const struct of_device_id s3c2410_wdt_match[] = { @@ -410,9 +420,14 @@ static inline unsigned long s3c2410wdt_get_freq(struct s3c2410_wdt *wdt) static inline unsigned int s3c2410wdt_max_timeout(struct s3c2410_wdt *wdt) { const unsigned long freq = s3c2410wdt_get_freq(wdt); + const u64 n_max = (u64)(S3C2410_WTCON_PRESCALE_MAX + 1) * + S3C2410_WTCON_MAXDIV * wdt->max_cnt; + u64 t_max = div64_ul(n_max, freq); - return S3C2410_WTCNT_MAXCNT / (freq / (S3C2410_WTCON_PRESCALE_MAX + 1) - / S3C2410_WTCON_MAXDIV); + if (t_max > UINT_MAX) + t_max = UINT_MAX; + + return t_max; } static int s3c2410wdt_disable_wdt_reset(struct s3c2410_wdt *wdt, bool mask) @@ -566,7 +581,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, { struct s3c2410_wdt *wdt = watchdog_get_drvdata(wdd); unsigned long freq = s3c2410wdt_get_freq(wdt); - unsigned int count; + unsigned long count; unsigned int divisor = 1; unsigned long wtcon; @@ -576,7 +591,7 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, freq = DIV_ROUND_UP(freq, 128); count = timeout * freq; - dev_dbg(wdt->dev, "Heartbeat: count=%d, timeout=%d, freq=%lu\n", + dev_dbg(wdt->dev, "Heartbeat: count=%lu, timeout=%d, freq=%lu\n", count, timeout, freq); /* if the count is bigger than the watchdog register, @@ -584,16 +599,16 @@ static int s3c2410wdt_set_heartbeat(struct watchdog_device *wdd, actually make this value */ - if (count >= 0x10000) { - divisor = DIV_ROUND_UP(count, 0xffff); + if (count > wdt->max_cnt) { + divisor = DIV_ROUND_UP(count, wdt->max_cnt); - if (divisor > 0x100) { + if (divisor > S3C2410_WTCON_PRESCALE_MAX + 1) { dev_err(wdt->dev, "timeout %d too big\n", timeout); return -EINVAL; } } - dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%d (%08x)\n", + dev_dbg(wdt->dev, "Heartbeat: timeout=%d, divisor=%d, count=%lu (%08lx)\n", timeout, divisor, count, DIV_ROUND_UP(count, divisor)); count = DIV_ROUND_UP(count, divisor); @@ -801,6 +816,11 @@ static int s3c2410wdt_probe(struct platform_device *pdev) if (IS_ERR(wdt->src_clk)) return dev_err_probe(dev, PTR_ERR(wdt->src_clk), "failed to get source clock\n"); + if (wdt->drv_data->quirks & QUIRK_HAS_32BIT_CNT) + wdt->max_cnt = S3C2410_WTCNT_MAXCNT_32; + else + wdt->max_cnt = S3C2410_WTCNT_MAXCNT_16; + wdt->wdt_device.min_timeout = 1; wdt->wdt_device.max_timeout = s3c2410wdt_max_timeout(wdt); diff --git a/drivers/watchdog/visconti_wdt.c b/drivers/watchdog/visconti_wdt.c index cef0794708e7..7795e7fbf67e 100644 --- a/drivers/watchdog/visconti_wdt.c +++ b/drivers/watchdog/visconti_wdt.c @@ -118,7 +118,6 @@ static int visconti_wdt_probe(struct platform_device *pdev) struct visconti_wdt_priv *priv; struct device *dev = &pdev->dev; struct clk *clk; - int ret; unsigned long clk_freq; priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -153,9 +152,7 @@ static int visconti_wdt_probe(struct platform_device *pdev) watchdog_stop_on_unregister(wdev); /* This overrides the default timeout only if DT configuration was found */ - ret = watchdog_init_timeout(wdev, 0, dev); - if (ret) - dev_warn(dev, "Specified timeout value invalid, using default\n"); + watchdog_init_timeout(wdev, 0, dev); return devm_watchdog_register_device(dev, wdev); } |