diff options
author | Jakub Kicinski <kuba@kernel.org> | 2023-07-13 17:33:19 -0700 |
---|---|---|
committer | Jakub Kicinski <kuba@kernel.org> | 2023-07-13 17:33:28 -0700 |
commit | eb1b24a9bb6982bb44b135e3490d3159ba0be4f2 (patch) | |
tree | 1ef056beee29642a6c12db299c680ce104c2e9d7 /drivers | |
parent | ba2274dcfda859b8a27193e68ad37bfe4da28ddc (diff) | |
parent | b1983d427a53911ea71ba621d4bf994ae22b1536 (diff) |
Merge git://git.kernel.org/pub/scm/linux/kernel/git/netdev/net
Cross-merge networking fixes after downstream PR.
No conflicts or adjacent changes.
Signed-off-by: Jakub Kicinski <kuba@kernel.org>
Diffstat (limited to 'drivers')
316 files changed, 6303 insertions, 2024 deletions
diff --git a/drivers/acpi/acpi_platform.c b/drivers/acpi/acpi_platform.c index fe00a5783f53..48d15dd785f6 100644 --- a/drivers/acpi/acpi_platform.c +++ b/drivers/acpi/acpi_platform.c @@ -9,6 +9,7 @@ */ #include <linux/acpi.h> +#include <linux/bits.h> #include <linux/device.h> #include <linux/err.h> #include <linux/kernel.h> @@ -19,13 +20,16 @@ #include "internal.h" +/* Exclude devices that have no _CRS resources provided */ +#define ACPI_ALLOW_WO_RESOURCES BIT(0) + static const struct acpi_device_id forbidden_id_list[] = { {"ACPI0009", 0}, /* IOxAPIC */ {"ACPI000A", 0}, /* IOAPIC */ {"PNP0000", 0}, /* PIC */ {"PNP0100", 0}, /* Timer */ {"PNP0200", 0}, /* AT DMA Controller */ - {"SMB0001", 0}, /* ACPI SMBUS virtual device */ + {ACPI_SMBUS_MS_HID, ACPI_ALLOW_WO_RESOURCES}, /* ACPI SMBUS virtual device */ { } }; @@ -83,6 +87,15 @@ static void acpi_platform_fill_resource(struct acpi_device *adev, dest->parent = pci_find_resource(to_pci_dev(parent), dest); } +static unsigned int acpi_platform_resource_count(struct acpi_resource *ares, void *data) +{ + bool *has_resources = data; + + *has_resources = true; + + return AE_CTRL_TERMINATE; +} + /** * acpi_create_platform_device - Create platform device for ACPI device node * @adev: ACPI device node to create a platform device for. @@ -100,6 +113,7 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, struct acpi_device *parent = acpi_dev_parent(adev); struct platform_device *pdev = NULL; struct platform_device_info pdevinfo; + const struct acpi_device_id *match; struct resource_entry *rentry; struct list_head resource_list; struct resource *resources = NULL; @@ -109,8 +123,19 @@ struct platform_device *acpi_create_platform_device(struct acpi_device *adev, if (adev->physical_node_count) return NULL; - if (!acpi_match_device_ids(adev, forbidden_id_list)) - return ERR_PTR(-EINVAL); + match = acpi_match_acpi_device(forbidden_id_list, adev); + if (match) { + if (match->driver_data & ACPI_ALLOW_WO_RESOURCES) { + bool has_resources = false; + + acpi_walk_resources(adev->handle, METHOD_NAME__CRS, + acpi_platform_resource_count, &has_resources); + if (has_resources) + return ERR_PTR(-EINVAL); + } else { + return ERR_PTR(-EINVAL); + } + } INIT_LIST_HEAD(&resource_list); count = acpi_dev_get_resources(adev, &resource_list, NULL, NULL); diff --git a/drivers/acpi/bus.c b/drivers/acpi/bus.c index e3e0bd0c5a50..2fc2b43a4ed3 100644 --- a/drivers/acpi/bus.c +++ b/drivers/acpi/bus.c @@ -682,7 +682,7 @@ bool acpi_device_is_first_physical_node(struct acpi_device *adev, * resources available from it but they will be matched normally using functions * provided by their bus types (and analogously for their modalias). */ -struct acpi_device *acpi_companion_match(const struct device *dev) +const struct acpi_device *acpi_companion_match(const struct device *dev) { struct acpi_device *adev; @@ -706,7 +706,7 @@ struct acpi_device *acpi_companion_match(const struct device *dev) * identifiers and a _DSD object with the "compatible" property, use that * property to match against the given list of identifiers. */ -static bool acpi_of_match_device(struct acpi_device *adev, +static bool acpi_of_match_device(const struct acpi_device *adev, const struct of_device_id *of_match_table, const struct of_device_id **of_id) { @@ -808,7 +808,7 @@ static bool __acpi_match_device_cls(const struct acpi_device_id *id, return true; } -static bool __acpi_match_device(struct acpi_device *device, +static bool __acpi_match_device(const struct acpi_device *device, const struct acpi_device_id *acpi_ids, const struct of_device_id *of_ids, const struct acpi_device_id **acpi_id, @@ -851,6 +851,26 @@ out_acpi_match: } /** + * acpi_match_acpi_device - Match an ACPI device against a given list of ACPI IDs + * @ids: Array of struct acpi_device_id objects to match against. + * @adev: The ACPI device pointer to match. + * + * Match the ACPI device @adev against a given list of ACPI IDs @ids. + * + * Return: + * a pointer to the first matching ACPI ID on success or %NULL on failure. + */ +const struct acpi_device_id *acpi_match_acpi_device(const struct acpi_device_id *ids, + const struct acpi_device *adev) +{ + const struct acpi_device_id *id = NULL; + + __acpi_match_device(adev, ids, NULL, &id, NULL); + return id; +} +EXPORT_SYMBOL_GPL(acpi_match_acpi_device); + +/** * acpi_match_device - Match a struct device against a given list of ACPI IDs * @ids: Array of struct acpi_device_id object to match against. * @dev: The device structure to match. @@ -864,10 +884,7 @@ out_acpi_match: const struct acpi_device_id *acpi_match_device(const struct acpi_device_id *ids, const struct device *dev) { - const struct acpi_device_id *id = NULL; - - __acpi_match_device(acpi_companion_match(dev), ids, NULL, &id, NULL); - return id; + return acpi_match_acpi_device(ids, acpi_companion_match(dev)); } EXPORT_SYMBOL_GPL(acpi_match_device); diff --git a/drivers/acpi/device_sysfs.c b/drivers/acpi/device_sysfs.c index 0fbfbaa8d8e3..b9bbf0746199 100644 --- a/drivers/acpi/device_sysfs.c +++ b/drivers/acpi/device_sysfs.c @@ -283,7 +283,7 @@ int acpi_device_uevent_modalias(const struct device *dev, struct kobj_uevent_env } EXPORT_SYMBOL_GPL(acpi_device_uevent_modalias); -static int __acpi_device_modalias(struct acpi_device *adev, char *buf, int size) +static int __acpi_device_modalias(const struct acpi_device *adev, char *buf, int size) { int len, count; diff --git a/drivers/acpi/internal.h b/drivers/acpi/internal.h index 06ad497067ac..f4148dc50b9c 100644 --- a/drivers/acpi/internal.h +++ b/drivers/acpi/internal.h @@ -11,6 +11,8 @@ #include <linux/idr.h> +extern struct acpi_device *acpi_root; + int early_acpi_osi_init(void); int acpi_osi_init(void); acpi_status acpi_os_initialize1(void); @@ -119,7 +121,7 @@ int acpi_bus_register_early_device(int type); /* -------------------------------------------------------------------------- Device Matching and Notification -------------------------------------------------------------------------- */ -struct acpi_device *acpi_companion_match(const struct device *dev); +const struct acpi_device *acpi_companion_match(const struct device *dev); int __acpi_device_uevent_modalias(const struct acpi_device *adev, struct kobj_uevent_env *env); diff --git a/drivers/acpi/scan.c b/drivers/acpi/scan.c index 1c3e1e2bb0b5..5b145f1aaa1b 100644 --- a/drivers/acpi/scan.c +++ b/drivers/acpi/scan.c @@ -23,8 +23,7 @@ #include <linux/dma-direct.h> #include "internal.h" - -extern struct acpi_device *acpi_root; +#include "sleep.h" #define ACPI_BUS_CLASS "system_bus" #define ACPI_BUS_HID "LNXSYBUS" @@ -930,26 +929,29 @@ static int acpi_bus_extract_wakeup_device_power_package(struct acpi_device *dev) return err; } +/* Do not use a button for S5 wakeup */ +#define ACPI_AVOID_WAKE_FROM_S5 BIT(0) + static bool acpi_wakeup_gpe_init(struct acpi_device *device) { static const struct acpi_device_id button_device_ids[] = { - {"PNP0C0C", 0}, /* Power button */ - {"PNP0C0D", 0}, /* Lid */ - {"PNP0C0E", 0}, /* Sleep button */ + {"PNP0C0C", 0}, /* Power button */ + {"PNP0C0D", ACPI_AVOID_WAKE_FROM_S5}, /* Lid */ + {"PNP0C0E", ACPI_AVOID_WAKE_FROM_S5}, /* Sleep button */ {"", 0}, }; struct acpi_device_wakeup *wakeup = &device->wakeup; + const struct acpi_device_id *match; acpi_status status; wakeup->flags.notifier_present = 0; /* Power button, Lid switch always enable wakeup */ - if (!acpi_match_device_ids(device, button_device_ids)) { - if (!acpi_match_device_ids(device, &button_device_ids[1])) { - /* Do not use Lid/sleep button for S5 wakeup */ - if (wakeup->sleep_state == ACPI_STATE_S5) - wakeup->sleep_state = ACPI_STATE_S4; - } + match = acpi_match_acpi_device(button_device_ids, device); + if (match) { + if ((match->driver_data & ACPI_AVOID_WAKE_FROM_S5) && + wakeup->sleep_state == ACPI_STATE_S5) + wakeup->sleep_state = ACPI_STATE_S4; acpi_mark_gpe_for_wake(wakeup->gpe_device, wakeup->gpe_number); device_set_wakeup_capable(&device->dev, true); return true; diff --git a/drivers/bus/intel-ixp4xx-eb.c b/drivers/bus/intel-ixp4xx-eb.c index f5ba6bee6fd8..320cf307db05 100644 --- a/drivers/bus/intel-ixp4xx-eb.c +++ b/drivers/bus/intel-ixp4xx-eb.c @@ -33,7 +33,7 @@ #define IXP4XX_EXP_TIMING_STRIDE 0x04 #define IXP4XX_EXP_CS_EN BIT(31) #define IXP456_EXP_PAR_EN BIT(30) /* Only on IXP45x and IXP46x */ -#define IXP4XX_EXP_T1_MASK GENMASK(28, 27) +#define IXP4XX_EXP_T1_MASK GENMASK(29, 28) #define IXP4XX_EXP_T1_SHIFT 28 #define IXP4XX_EXP_T2_MASK GENMASK(27, 26) #define IXP4XX_EXP_T2_SHIFT 26 diff --git a/drivers/crypto/Kconfig b/drivers/crypto/Kconfig index 9f5b2d28bff5..44e44b8d9ce6 100644 --- a/drivers/crypto/Kconfig +++ b/drivers/crypto/Kconfig @@ -92,17 +92,6 @@ config ZCRYPT_DEBUG If unsure, say N. -config ZCRYPT_MULTIDEVNODES - bool "Support for multiple zcrypt device nodes" - default y - depends on S390 - depends on ZCRYPT - help - With this option enabled the zcrypt device driver can - provide multiple devices nodes in /dev. Each device - node can get customized to limit access and narrow - down the use of the available crypto hardware. - config PKEY tristate "Kernel API for protected key handling" depends on S390 diff --git a/drivers/dma/Kconfig b/drivers/dma/Kconfig index f5f422f9b850..644c188d6a11 100644 --- a/drivers/dma/Kconfig +++ b/drivers/dma/Kconfig @@ -553,6 +553,7 @@ config STE_DMA40 bool "ST-Ericsson DMA40 support" depends on ARCH_U8500 select DMA_ENGINE + select SRAM help Support for ST-Ericsson DMA40 controller diff --git a/drivers/dma/dma-axi-dmac.c b/drivers/dma/dma-axi-dmac.c index a812b9b00e6b..fc7cdad37161 100644 --- a/drivers/dma/dma-axi-dmac.c +++ b/drivers/dma/dma-axi-dmac.c @@ -963,7 +963,6 @@ static int axi_dmac_probe(struct platform_device *pdev) dma_dev->device_terminate_all = axi_dmac_terminate_all; dma_dev->device_synchronize = axi_dmac_synchronize; dma_dev->dev = &pdev->dev; - dma_dev->chancnt = 1; dma_dev->src_addr_widths = BIT(dmac->chan.src_width); dma_dev->dst_addr_widths = BIT(dmac->chan.dest_width); dma_dev->directions = BIT(dmac->chan.direction); diff --git a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c index 6937cc0c0b65..796b6caf0bab 100644 --- a/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c +++ b/drivers/dma/dw-axi-dmac/dw-axi-dmac-platform.c @@ -1466,7 +1466,6 @@ static int dw_probe(struct platform_device *pdev) dma_cap_set(DMA_CYCLIC, dw->dma.cap_mask); /* DMA capabilities */ - dw->dma.chancnt = hdata->nr_channels; dw->dma.max_burst = hdata->axi_rw_burst_len; dw->dma.src_addr_widths = AXI_DMA_BUSWIDTHS; dw->dma.dst_addr_widths = AXI_DMA_BUSWIDTHS; diff --git a/drivers/dma/dw-edma/Makefile b/drivers/dma/dw-edma/Makefile index 8d45c0d5689d..83ab58f87760 100644 --- a/drivers/dma/dw-edma/Makefile +++ b/drivers/dma/dw-edma/Makefile @@ -1,7 +1,9 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_DW_EDMA) += dw-edma.o -dw-edma-$(CONFIG_DEBUG_FS) := dw-edma-v0-debugfs.o -dw-edma-objs := dw-edma-core.o \ - dw-edma-v0-core.o $(dw-edma-y) +dw-edma-$(CONFIG_DEBUG_FS) := dw-edma-v0-debugfs.o \ + dw-hdma-v0-debugfs.o +dw-edma-objs := dw-edma-core.o \ + dw-edma-v0-core.o \ + dw-hdma-v0-core.o $(dw-edma-y) obj-$(CONFIG_DW_EDMA_PCIE) += dw-edma-pcie.o diff --git a/drivers/dma/dw-edma/dw-edma-core.c b/drivers/dma/dw-edma/dw-edma-core.c index 7d2b73ef0872..68236247059d 100644 --- a/drivers/dma/dw-edma/dw-edma-core.c +++ b/drivers/dma/dw-edma/dw-edma-core.c @@ -18,6 +18,7 @@ #include "dw-edma-core.h" #include "dw-edma-v0-core.h" +#include "dw-hdma-v0-core.h" #include "../dmaengine.h" #include "../virt-dma.h" @@ -183,6 +184,7 @@ static void vchan_free_desc(struct virt_dma_desc *vdesc) static int dw_edma_start_transfer(struct dw_edma_chan *chan) { + struct dw_edma *dw = chan->dw; struct dw_edma_chunk *child; struct dw_edma_desc *desc; struct virt_dma_desc *vd; @@ -200,7 +202,7 @@ static int dw_edma_start_transfer(struct dw_edma_chan *chan) if (!child) return 0; - dw_edma_v0_core_start(child, !desc->xfer_sz); + dw_edma_core_start(dw, child, !desc->xfer_sz); desc->xfer_sz += child->ll_region.sz; dw_edma_free_burst(child); list_del(&child->list); @@ -287,7 +289,7 @@ static int dw_edma_device_terminate_all(struct dma_chan *dchan) chan->configured = false; } else if (chan->status == EDMA_ST_IDLE) { chan->configured = false; - } else if (dw_edma_v0_core_ch_status(chan) == DMA_COMPLETE) { + } else if (dw_edma_core_ch_status(chan) == DMA_COMPLETE) { /* * The channel is in a false BUSY state, probably didn't * receive or lost an interrupt @@ -599,8 +601,6 @@ static void dw_edma_done_interrupt(struct dw_edma_chan *chan) struct virt_dma_desc *vd; unsigned long flags; - dw_edma_v0_core_clear_done_int(chan); - spin_lock_irqsave(&chan->vc.lock, flags); vd = vchan_next_desc(&chan->vc); if (vd) { @@ -641,8 +641,6 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) struct virt_dma_desc *vd; unsigned long flags; - dw_edma_v0_core_clear_abort_int(chan); - spin_lock_irqsave(&chan->vc.lock, flags); vd = vchan_next_desc(&chan->vc); if (vd) { @@ -654,63 +652,32 @@ static void dw_edma_abort_interrupt(struct dw_edma_chan *chan) chan->status = EDMA_ST_IDLE; } -static irqreturn_t dw_edma_interrupt(int irq, void *data, bool write) +static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) { struct dw_edma_irq *dw_irq = data; - struct dw_edma *dw = dw_irq->dw; - unsigned long total, pos, val; - unsigned long off; - u32 mask; - - if (write) { - total = dw->wr_ch_cnt; - off = 0; - mask = dw_irq->wr_mask; - } else { - total = dw->rd_ch_cnt; - off = dw->wr_ch_cnt; - mask = dw_irq->rd_mask; - } - - val = dw_edma_v0_core_status_done_int(dw, write ? - EDMA_DIR_WRITE : - EDMA_DIR_READ); - val &= mask; - for_each_set_bit(pos, &val, total) { - struct dw_edma_chan *chan = &dw->chan[pos + off]; - - dw_edma_done_interrupt(chan); - } - - val = dw_edma_v0_core_status_abort_int(dw, write ? - EDMA_DIR_WRITE : - EDMA_DIR_READ); - val &= mask; - for_each_set_bit(pos, &val, total) { - struct dw_edma_chan *chan = &dw->chan[pos + off]; - - dw_edma_abort_interrupt(chan); - } - return IRQ_HANDLED; -} - -static inline irqreturn_t dw_edma_interrupt_write(int irq, void *data) -{ - return dw_edma_interrupt(irq, data, true); + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_WRITE, + dw_edma_done_interrupt, + dw_edma_abort_interrupt); } static inline irqreturn_t dw_edma_interrupt_read(int irq, void *data) { - return dw_edma_interrupt(irq, data, false); + struct dw_edma_irq *dw_irq = data; + + return dw_edma_core_handle_int(dw_irq, EDMA_DIR_READ, + dw_edma_done_interrupt, + dw_edma_abort_interrupt); } static irqreturn_t dw_edma_interrupt_common(int irq, void *data) { - dw_edma_interrupt(irq, data, true); - dw_edma_interrupt(irq, data, false); + irqreturn_t ret = IRQ_NONE; + + ret |= dw_edma_interrupt_write(irq, data); + ret |= dw_edma_interrupt_read(irq, data); - return IRQ_HANDLED; + return ret; } static int dw_edma_alloc_chan_resources(struct dma_chan *dchan) @@ -811,7 +778,7 @@ static int dw_edma_channel_setup(struct dw_edma *dw, u32 wr_alloc, u32 rd_alloc) vchan_init(&chan->vc, dma); - dw_edma_v0_core_device_config(chan); + dw_edma_core_ch_config(chan); } /* Set DMA channel capabilities */ @@ -956,14 +923,19 @@ int dw_edma_probe(struct dw_edma_chip *chip) dw->chip = chip; + if (dw->chip->mf == EDMA_MF_HDMA_NATIVE) + dw_hdma_v0_core_register(dw); + else + dw_edma_v0_core_register(dw); + raw_spin_lock_init(&dw->lock); dw->wr_ch_cnt = min_t(u16, chip->ll_wr_cnt, - dw_edma_v0_core_ch_count(dw, EDMA_DIR_WRITE)); + dw_edma_core_ch_count(dw, EDMA_DIR_WRITE)); dw->wr_ch_cnt = min_t(u16, dw->wr_ch_cnt, EDMA_MAX_WR_CH); dw->rd_ch_cnt = min_t(u16, chip->ll_rd_cnt, - dw_edma_v0_core_ch_count(dw, EDMA_DIR_READ)); + dw_edma_core_ch_count(dw, EDMA_DIR_READ)); dw->rd_ch_cnt = min_t(u16, dw->rd_ch_cnt, EDMA_MAX_RD_CH); if (!dw->wr_ch_cnt && !dw->rd_ch_cnt) @@ -982,7 +954,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) dev_name(chip->dev)); /* Disable eDMA, only to establish the ideal initial conditions */ - dw_edma_v0_core_off(dw); + dw_edma_core_off(dw); /* Request IRQs */ err = dw_edma_irq_request(dw, &wr_alloc, &rd_alloc); @@ -995,7 +967,7 @@ int dw_edma_probe(struct dw_edma_chip *chip) goto err_irq_free; /* Turn debugfs on */ - dw_edma_v0_core_debugfs_on(dw); + dw_edma_core_debugfs_on(dw); chip->dw = dw; @@ -1021,7 +993,7 @@ int dw_edma_remove(struct dw_edma_chip *chip) return -ENODEV; /* Disable eDMA */ - dw_edma_v0_core_off(dw); + dw_edma_core_off(dw); /* Free irqs */ for (i = (dw->nr_irqs - 1); i >= 0; i--) diff --git a/drivers/dma/dw-edma/dw-edma-core.h b/drivers/dma/dw-edma/dw-edma-core.h index 0ab2b6dba880..71894b9e0b15 100644 --- a/drivers/dma/dw-edma/dw-edma-core.h +++ b/drivers/dma/dw-edma/dw-edma-core.h @@ -111,6 +111,21 @@ struct dw_edma { raw_spinlock_t lock; /* Only for legacy */ struct dw_edma_chip *chip; + + const struct dw_edma_core_ops *core; +}; + +typedef void (*dw_edma_handler_t)(struct dw_edma_chan *); + +struct dw_edma_core_ops { + void (*off)(struct dw_edma *dw); + u16 (*ch_count)(struct dw_edma *dw, enum dw_edma_dir dir); + enum dma_status (*ch_status)(struct dw_edma_chan *chan); + irqreturn_t (*handle_int)(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, + dw_edma_handler_t done, dw_edma_handler_t abort); + void (*start)(struct dw_edma_chunk *chunk, bool first); + void (*ch_config)(struct dw_edma_chan *chan); + void (*debugfs_on)(struct dw_edma *dw); }; struct dw_edma_sg { @@ -148,4 +163,47 @@ struct dw_edma_chan *dchan2dw_edma_chan(struct dma_chan *dchan) return vc2dw_edma_chan(to_virt_chan(dchan)); } +static inline +void dw_edma_core_off(struct dw_edma *dw) +{ + dw->core->off(dw); +} + +static inline +u16 dw_edma_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) +{ + return dw->core->ch_count(dw, dir); +} + +static inline +enum dma_status dw_edma_core_ch_status(struct dw_edma_chan *chan) +{ + return chan->dw->core->ch_status(chan); +} + +static inline irqreturn_t +dw_edma_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, + dw_edma_handler_t done, dw_edma_handler_t abort) +{ + return dw_irq->dw->core->handle_int(dw_irq, dir, done, abort); +} + +static inline +void dw_edma_core_start(struct dw_edma *dw, struct dw_edma_chunk *chunk, bool first) +{ + dw->core->start(chunk, first); +} + +static inline +void dw_edma_core_ch_config(struct dw_edma_chan *chan) +{ + chan->dw->core->ch_config(chan); +} + +static inline +void dw_edma_core_debugfs_on(struct dw_edma *dw) +{ + dw->core->debugfs_on(dw); +} + #endif /* _DW_EDMA_CORE_H */ diff --git a/drivers/dma/dw-edma/dw-edma-pcie.c b/drivers/dma/dw-edma/dw-edma-pcie.c index 2b40f2b44f5e..1c6043751dc9 100644 --- a/drivers/dma/dw-edma/dw-edma-pcie.c +++ b/drivers/dma/dw-edma/dw-edma-pcie.c @@ -109,7 +109,7 @@ static u64 dw_edma_pcie_address(struct device *dev, phys_addr_t cpu_addr) return region.start; } -static const struct dw_edma_core_ops dw_edma_pcie_core_ops = { +static const struct dw_edma_plat_ops dw_edma_pcie_plat_ops = { .irq_vector = dw_edma_pcie_irq_vector, .pci_address = dw_edma_pcie_address, }; @@ -225,7 +225,7 @@ static int dw_edma_pcie_probe(struct pci_dev *pdev, chip->mf = vsec_data.mf; chip->nr_irqs = nr_irqs; - chip->ops = &dw_edma_pcie_core_ops; + chip->ops = &dw_edma_pcie_plat_ops; chip->ll_wr_cnt = vsec_data.wr_ch_cnt; chip->ll_rd_cnt = vsec_data.rd_ch_cnt; diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.c b/drivers/dma/dw-edma/dw-edma-v0-core.c index 32f834a3848a..b38786f0ad79 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.c +++ b/drivers/dma/dw-edma/dw-edma-v0-core.c @@ -7,7 +7,7 @@ */ #include <linux/bitfield.h> - +#include <linux/irqreturn.h> #include <linux/io-64-nonatomic-lo-hi.h> #include "dw-edma-core.h" @@ -160,7 +160,7 @@ static inline u32 readl_ch(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch, readl_ch(dw, dir, ch, &(__dw_ch_regs(dw, dir, ch)->name)) /* eDMA management callbacks */ -void dw_edma_v0_core_off(struct dw_edma *dw) +static void dw_edma_v0_core_off(struct dw_edma *dw) { SET_BOTH_32(dw, int_mask, EDMA_V0_DONE_INT_MASK | EDMA_V0_ABORT_INT_MASK); @@ -169,7 +169,7 @@ void dw_edma_v0_core_off(struct dw_edma *dw) SET_BOTH_32(dw, engine_en, 0); } -u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) +static u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) { u32 num_ch; @@ -186,7 +186,7 @@ u16 dw_edma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) return (u16)num_ch; } -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) +static enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; u32 tmp; @@ -202,7 +202,7 @@ enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan) return DMA_ERROR; } -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) +static void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; @@ -210,7 +210,7 @@ void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan) FIELD_PREP(EDMA_V0_DONE_INT_MASK, BIT(chan->id))); } -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) +static void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; @@ -218,18 +218,64 @@ void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan) FIELD_PREP(EDMA_V0_ABORT_INT_MASK, BIT(chan->id))); } -u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) +static u32 dw_edma_v0_core_status_done_int(struct dw_edma *dw, enum dw_edma_dir dir) { return FIELD_GET(EDMA_V0_DONE_INT_MASK, GET_RW_32(dw, dir, int_status)); } -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) +static u32 dw_edma_v0_core_status_abort_int(struct dw_edma *dw, enum dw_edma_dir dir) { return FIELD_GET(EDMA_V0_ABORT_INT_MASK, GET_RW_32(dw, dir, int_status)); } +static irqreturn_t +dw_edma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, + dw_edma_handler_t done, dw_edma_handler_t abort) +{ + struct dw_edma *dw = dw_irq->dw; + unsigned long total, pos, val; + irqreturn_t ret = IRQ_NONE; + struct dw_edma_chan *chan; + unsigned long off; + u32 mask; + + if (dir == EDMA_DIR_WRITE) { + total = dw->wr_ch_cnt; + off = 0; + mask = dw_irq->wr_mask; + } else { + total = dw->rd_ch_cnt; + off = dw->wr_ch_cnt; + mask = dw_irq->rd_mask; + } + + val = dw_edma_v0_core_status_done_int(dw, dir); + val &= mask; + for_each_set_bit(pos, &val, total) { + chan = &dw->chan[pos + off]; + + dw_edma_v0_core_clear_done_int(chan); + done(chan); + + ret = IRQ_HANDLED; + } + + val = dw_edma_v0_core_status_abort_int(dw, dir); + val &= mask; + for_each_set_bit(pos, &val, total) { + chan = &dw->chan[pos + off]; + + dw_edma_v0_core_clear_abort_int(chan); + abort(chan); + + ret = IRQ_HANDLED; + } + + return ret; +} + static void dw_edma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i, u32 control, u32 size, u64 sar, u64 dar) { @@ -300,7 +346,7 @@ static void dw_edma_v0_core_write_chunk(struct dw_edma_chunk *chunk) dw_edma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr); } -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) +static void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) { struct dw_edma_chan *chan = chunk->chan; struct dw_edma *dw = chan->dw; @@ -371,7 +417,7 @@ void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first) FIELD_PREP(EDMA_V0_DOORBELL_CH_MASK, chan->id)); } -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) +static void dw_edma_v0_core_ch_config(struct dw_edma_chan *chan) { struct dw_edma *dw = chan->dw; u32 tmp = 0; @@ -438,12 +484,25 @@ int dw_edma_v0_core_device_config(struct dw_edma_chan *chan) SET_RW_32(dw, chan->dir, ch67_imwr_data, tmp); break; } - - return 0; } /* eDMA debugfs callbacks */ -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) +static void dw_edma_v0_core_debugfs_on(struct dw_edma *dw) { dw_edma_v0_debugfs_on(dw); } + +static const struct dw_edma_core_ops dw_edma_v0_core = { + .off = dw_edma_v0_core_off, + .ch_count = dw_edma_v0_core_ch_count, + .ch_status = dw_edma_v0_core_ch_status, + .handle_int = dw_edma_v0_core_handle_int, + .start = dw_edma_v0_core_start, + .ch_config = dw_edma_v0_core_ch_config, + .debugfs_on = dw_edma_v0_core_debugfs_on, +}; + +void dw_edma_v0_core_register(struct dw_edma *dw) +{ + dw->core = &dw_edma_v0_core; +} diff --git a/drivers/dma/dw-edma/dw-edma-v0-core.h b/drivers/dma/dw-edma/dw-edma-v0-core.h index ab96a1f48080..04a882222f99 100644 --- a/drivers/dma/dw-edma/dw-edma-v0-core.h +++ b/drivers/dma/dw-edma/dw-edma-v0-core.h @@ -11,17 +11,7 @@ #include <linux/dma/edma.h> -/* eDMA management callbacks */ -void dw_edma_v0_core_off(struct dw_edma *chan); -u16 dw_edma_v0_core_ch_count(struct dw_edma *chan, enum dw_edma_dir dir); -enum dma_status dw_edma_v0_core_ch_status(struct dw_edma_chan *chan); -void dw_edma_v0_core_clear_done_int(struct dw_edma_chan *chan); -void dw_edma_v0_core_clear_abort_int(struct dw_edma_chan *chan); -u32 dw_edma_v0_core_status_done_int(struct dw_edma *chan, enum dw_edma_dir dir); -u32 dw_edma_v0_core_status_abort_int(struct dw_edma *chan, enum dw_edma_dir dir); -void dw_edma_v0_core_start(struct dw_edma_chunk *chunk, bool first); -int dw_edma_v0_core_device_config(struct dw_edma_chan *chan); -/* eDMA debug fs callbacks */ -void dw_edma_v0_core_debugfs_on(struct dw_edma *dw); +/* eDMA core register */ +void dw_edma_v0_core_register(struct dw_edma *dw); #endif /* _DW_EDMA_V0_CORE_H */ diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.c b/drivers/dma/dw-edma/dw-hdma-v0-core.c new file mode 100644 index 000000000000..00b735a0202a --- /dev/null +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.c @@ -0,0 +1,296 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Cai Huoqing + * Synopsys DesignWare HDMA v0 core + */ + +#include <linux/bitfield.h> +#include <linux/irqreturn.h> +#include <linux/io-64-nonatomic-lo-hi.h> + +#include "dw-edma-core.h" +#include "dw-hdma-v0-core.h" +#include "dw-hdma-v0-regs.h" +#include "dw-hdma-v0-debugfs.h" + +enum dw_hdma_control { + DW_HDMA_V0_CB = BIT(0), + DW_HDMA_V0_TCB = BIT(1), + DW_HDMA_V0_LLP = BIT(2), + DW_HDMA_V0_LIE = BIT(3), + DW_HDMA_V0_RIE = BIT(4), + DW_HDMA_V0_CCS = BIT(8), + DW_HDMA_V0_LLE = BIT(9), +}; + +static inline struct dw_hdma_v0_regs __iomem *__dw_regs(struct dw_edma *dw) +{ + return dw->chip->reg_base; +} + +static inline struct dw_hdma_v0_ch_regs __iomem * +__dw_ch_regs(struct dw_edma *dw, enum dw_edma_dir dir, u16 ch) +{ + if (dir == EDMA_DIR_WRITE) + return &(__dw_regs(dw)->ch[ch].wr); + else + return &(__dw_regs(dw)->ch[ch].rd); +} + +#define SET_CH_32(dw, dir, ch, name, value) \ + writel(value, &(__dw_ch_regs(dw, dir, ch)->name)) + +#define GET_CH_32(dw, dir, ch, name) \ + readl(&(__dw_ch_regs(dw, dir, ch)->name)) + +#define SET_BOTH_CH_32(dw, ch, name, value) \ + do { \ + writel(value, &(__dw_ch_regs(dw, EDMA_DIR_WRITE, ch)->name)); \ + writel(value, &(__dw_ch_regs(dw, EDMA_DIR_READ, ch)->name)); \ + } while (0) + +/* HDMA management callbacks */ +static void dw_hdma_v0_core_off(struct dw_edma *dw) +{ + int id; + + for (id = 0; id < HDMA_V0_MAX_NR_CH; id++) { + SET_BOTH_CH_32(dw, id, int_setup, + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + SET_BOTH_CH_32(dw, id, int_clear, + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK); + SET_BOTH_CH_32(dw, id, ch_en, 0); + } +} + +static u16 dw_hdma_v0_core_ch_count(struct dw_edma *dw, enum dw_edma_dir dir) +{ + u32 num_ch = 0; + int id; + + for (id = 0; id < HDMA_V0_MAX_NR_CH; id++) { + if (GET_CH_32(dw, id, dir, ch_en) & BIT(0)) + num_ch++; + } + + if (num_ch > HDMA_V0_MAX_NR_CH) + num_ch = HDMA_V0_MAX_NR_CH; + + return (u16)num_ch; +} + +static enum dma_status dw_hdma_v0_core_ch_status(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + u32 tmp; + + tmp = FIELD_GET(HDMA_V0_CH_STATUS_MASK, + GET_CH_32(dw, chan->id, chan->dir, ch_stat)); + + if (tmp == 1) + return DMA_IN_PROGRESS; + else if (tmp == 3) + return DMA_COMPLETE; + else + return DMA_ERROR; +} + +static void dw_hdma_v0_core_clear_done_int(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + SET_CH_32(dw, chan->dir, chan->id, int_clear, HDMA_V0_STOP_INT_MASK); +} + +static void dw_hdma_v0_core_clear_abort_int(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + SET_CH_32(dw, chan->dir, chan->id, int_clear, HDMA_V0_ABORT_INT_MASK); +} + +static u32 dw_hdma_v0_core_status_int(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + return GET_CH_32(dw, chan->dir, chan->id, int_stat); +} + +static irqreturn_t +dw_hdma_v0_core_handle_int(struct dw_edma_irq *dw_irq, enum dw_edma_dir dir, + dw_edma_handler_t done, dw_edma_handler_t abort) +{ + struct dw_edma *dw = dw_irq->dw; + unsigned long total, pos, val; + irqreturn_t ret = IRQ_NONE; + struct dw_edma_chan *chan; + unsigned long off, mask; + + if (dir == EDMA_DIR_WRITE) { + total = dw->wr_ch_cnt; + off = 0; + mask = dw_irq->wr_mask; + } else { + total = dw->rd_ch_cnt; + off = dw->wr_ch_cnt; + mask = dw_irq->rd_mask; + } + + for_each_set_bit(pos, &mask, total) { + chan = &dw->chan[pos + off]; + + val = dw_hdma_v0_core_status_int(chan); + if (FIELD_GET(HDMA_V0_STOP_INT_MASK, val)) { + dw_hdma_v0_core_clear_done_int(chan); + done(chan); + + ret = IRQ_HANDLED; + } + + if (FIELD_GET(HDMA_V0_ABORT_INT_MASK, val)) { + dw_hdma_v0_core_clear_abort_int(chan); + abort(chan); + + ret = IRQ_HANDLED; + } + } + + return ret; +} + +static void dw_hdma_v0_write_ll_data(struct dw_edma_chunk *chunk, int i, + u32 control, u32 size, u64 sar, u64 dar) +{ + ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli); + + if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { + struct dw_hdma_v0_lli *lli = chunk->ll_region.vaddr.mem + ofs; + + lli->control = control; + lli->transfer_size = size; + lli->sar.reg = sar; + lli->dar.reg = dar; + } else { + struct dw_hdma_v0_lli __iomem *lli = chunk->ll_region.vaddr.io + ofs; + + writel(control, &lli->control); + writel(size, &lli->transfer_size); + writeq(sar, &lli->sar.reg); + writeq(dar, &lli->dar.reg); + } +} + +static void dw_hdma_v0_write_ll_link(struct dw_edma_chunk *chunk, + int i, u32 control, u64 pointer) +{ + ptrdiff_t ofs = i * sizeof(struct dw_hdma_v0_lli); + + if (chunk->chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL) { + struct dw_hdma_v0_llp *llp = chunk->ll_region.vaddr.mem + ofs; + + llp->control = control; + llp->llp.reg = pointer; + } else { + struct dw_hdma_v0_llp __iomem *llp = chunk->ll_region.vaddr.io + ofs; + + writel(control, &llp->control); + writeq(pointer, &llp->llp.reg); + } +} + +static void dw_hdma_v0_core_write_chunk(struct dw_edma_chunk *chunk) +{ + struct dw_edma_burst *child; + struct dw_edma_chan *chan = chunk->chan; + u32 control = 0, i = 0; + int j; + + if (chunk->cb) + control = DW_HDMA_V0_CB; + + j = chunk->bursts_alloc; + list_for_each_entry(child, &chunk->burst->list, list) { + j--; + if (!j) { + control |= DW_HDMA_V0_LIE; + if (!(chan->dw->chip->flags & DW_EDMA_CHIP_LOCAL)) + control |= DW_HDMA_V0_RIE; + } + + dw_hdma_v0_write_ll_data(chunk, i++, control, child->sz, + child->sar, child->dar); + } + + control = DW_HDMA_V0_LLP | DW_HDMA_V0_TCB; + if (!chunk->cb) + control |= DW_HDMA_V0_CB; + + dw_hdma_v0_write_ll_link(chunk, i, control, chunk->ll_region.paddr); +} + +static void dw_hdma_v0_core_start(struct dw_edma_chunk *chunk, bool first) +{ + struct dw_edma_chan *chan = chunk->chan; + struct dw_edma *dw = chan->dw; + u32 tmp; + + dw_hdma_v0_core_write_chunk(chunk); + + if (first) { + /* Enable engine */ + SET_CH_32(dw, chan->dir, chan->id, ch_en, BIT(0)); + /* Interrupt enable&unmask - done, abort */ + tmp = GET_CH_32(dw, chan->dir, chan->id, int_setup) | + HDMA_V0_STOP_INT_MASK | HDMA_V0_ABORT_INT_MASK | + HDMA_V0_LOCAL_STOP_INT_EN | HDMA_V0_LOCAL_STOP_INT_EN; + SET_CH_32(dw, chan->dir, chan->id, int_setup, tmp); + /* Channel control */ + SET_CH_32(dw, chan->dir, chan->id, control1, HDMA_V0_LINKLIST_EN); + /* Linked list */ + /* llp is not aligned on 64bit -> keep 32bit accesses */ + SET_CH_32(dw, chan->dir, chan->id, llp.lsb, + lower_32_bits(chunk->ll_region.paddr)); + SET_CH_32(dw, chan->dir, chan->id, llp.msb, + upper_32_bits(chunk->ll_region.paddr)); + } + /* Set consumer cycle */ + SET_CH_32(dw, chan->dir, chan->id, cycle_sync, + HDMA_V0_CONSUMER_CYCLE_STAT | HDMA_V0_CONSUMER_CYCLE_BIT); + /* Doorbell */ + SET_CH_32(dw, chan->dir, chan->id, doorbell, HDMA_V0_DOORBELL_START); +} + +static void dw_hdma_v0_core_ch_config(struct dw_edma_chan *chan) +{ + struct dw_edma *dw = chan->dw; + + /* MSI done addr - low, high */ + SET_CH_32(dw, chan->dir, chan->id, msi_stop.lsb, chan->msi.address_lo); + SET_CH_32(dw, chan->dir, chan->id, msi_stop.msb, chan->msi.address_hi); + /* MSI abort addr - low, high */ + SET_CH_32(dw, chan->dir, chan->id, msi_abort.lsb, chan->msi.address_lo); + SET_CH_32(dw, chan->dir, chan->id, msi_abort.msb, chan->msi.address_hi); + /* config MSI data */ + SET_CH_32(dw, chan->dir, chan->id, msi_msgdata, chan->msi.data); +} + +/* HDMA debugfs callbacks */ +static void dw_hdma_v0_core_debugfs_on(struct dw_edma *dw) +{ + dw_hdma_v0_debugfs_on(dw); +} + +static const struct dw_edma_core_ops dw_hdma_v0_core = { + .off = dw_hdma_v0_core_off, + .ch_count = dw_hdma_v0_core_ch_count, + .ch_status = dw_hdma_v0_core_ch_status, + .handle_int = dw_hdma_v0_core_handle_int, + .start = dw_hdma_v0_core_start, + .ch_config = dw_hdma_v0_core_ch_config, + .debugfs_on = dw_hdma_v0_core_debugfs_on, +}; + +void dw_hdma_v0_core_register(struct dw_edma *dw) +{ + dw->core = &dw_hdma_v0_core; +} diff --git a/drivers/dma/dw-edma/dw-hdma-v0-core.h b/drivers/dma/dw-edma/dw-hdma-v0-core.h new file mode 100644 index 000000000000..c373b4f0bd8a --- /dev/null +++ b/drivers/dma/dw-edma/dw-hdma-v0-core.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Cai Huoqing + * Synopsys DesignWare HDMA v0 core + * + * Author: Cai Huoqing <cai.huoqing@linux.dev> + */ + +#ifndef _DW_HDMA_V0_CORE_H +#define _DW_HDMA_V0_CORE_H + +#include <linux/dma/edma.h> + +/* HDMA core register */ +void dw_hdma_v0_core_register(struct dw_edma *dw); + +#endif /* _DW_HDMA_V0_CORE_H */ diff --git a/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c new file mode 100644 index 000000000000..520c81978b08 --- /dev/null +++ b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2023 Cai Huoqing + * Synopsys DesignWare HDMA v0 debugfs + * + * Author: Cai Huoqing <cai.huoqing@linux.dev> + */ + +#include <linux/debugfs.h> +#include <linux/bitfield.h> + +#include "dw-hdma-v0-debugfs.h" +#include "dw-hdma-v0-regs.h" +#include "dw-edma-core.h" + +#define REGS_ADDR(dw, name) \ + ({ \ + struct dw_hdma_v0_regs __iomem *__regs = (dw)->chip->reg_base; \ + \ + (void __iomem *)&__regs->name; \ + }) + +#define REGS_CH_ADDR(dw, name, _dir, _ch) \ + ({ \ + struct dw_hdma_v0_ch_regs __iomem *__ch_regs; \ + \ + if (_dir == EDMA_DIR_READ) \ + __ch_regs = REGS_ADDR(dw, ch[_ch].rd); \ + else \ + __ch_regs = REGS_ADDR(dw, ch[_ch].wr); \ + \ + (void __iomem *)&__ch_regs->name; \ + }) + +#define CTX_REGISTER(dw, name, dir, ch) \ + {#name, REGS_CH_ADDR(dw, name, dir, ch)} + +#define WRITE_STR "write" +#define READ_STR "read" +#define CHANNEL_STR "channel" +#define REGISTERS_STR "registers" + +struct dw_hdma_debugfs_entry { + const char *name; + void __iomem *reg; +}; + +static int dw_hdma_debugfs_u32_get(void *data, u64 *val) +{ + struct dw_hdma_debugfs_entry *entry = data; + void __iomem *reg = entry->reg; + + *val = readl(reg); + + return 0; +} +DEFINE_DEBUGFS_ATTRIBUTE(fops_x32, dw_hdma_debugfs_u32_get, NULL, "0x%08llx\n"); + +static void dw_hdma_debugfs_create_x32(struct dw_edma *dw, + const struct dw_hdma_debugfs_entry ini[], + int nr_entries, struct dentry *dent) +{ + struct dw_hdma_debugfs_entry *entries; + int i; + + entries = devm_kcalloc(dw->chip->dev, nr_entries, sizeof(*entries), + GFP_KERNEL); + if (!entries) + return; + + for (i = 0; i < nr_entries; i++) { + entries[i] = ini[i]; + + debugfs_create_file_unsafe(entries[i].name, 0444, dent, + &entries[i], &fops_x32); + } +} + +static void dw_hdma_debugfs_regs_ch(struct dw_edma *dw, enum dw_edma_dir dir, + u16 ch, struct dentry *dent) +{ + const struct dw_hdma_debugfs_entry debugfs_regs[] = { + CTX_REGISTER(dw, ch_en, dir, ch), + CTX_REGISTER(dw, doorbell, dir, ch), + CTX_REGISTER(dw, prefetch, dir, ch), + CTX_REGISTER(dw, handshake, dir, ch), + CTX_REGISTER(dw, llp.lsb, dir, ch), + CTX_REGISTER(dw, llp.msb, dir, ch), + CTX_REGISTER(dw, cycle_sync, dir, ch), + CTX_REGISTER(dw, transfer_size, dir, ch), + CTX_REGISTER(dw, sar.lsb, dir, ch), + CTX_REGISTER(dw, sar.msb, dir, ch), + CTX_REGISTER(dw, dar.lsb, dir, ch), + CTX_REGISTER(dw, dar.msb, dir, ch), + CTX_REGISTER(dw, watermark_en, dir, ch), + CTX_REGISTER(dw, control1, dir, ch), + CTX_REGISTER(dw, func_num, dir, ch), + CTX_REGISTER(dw, qos, dir, ch), + CTX_REGISTER(dw, ch_stat, dir, ch), + CTX_REGISTER(dw, int_stat, dir, ch), + CTX_REGISTER(dw, int_setup, dir, ch), + CTX_REGISTER(dw, int_clear, dir, ch), + CTX_REGISTER(dw, msi_stop.lsb, dir, ch), + CTX_REGISTER(dw, msi_stop.msb, dir, ch), + CTX_REGISTER(dw, msi_watermark.lsb, dir, ch), + CTX_REGISTER(dw, msi_watermark.msb, dir, ch), + CTX_REGISTER(dw, msi_abort.lsb, dir, ch), + CTX_REGISTER(dw, msi_abort.msb, dir, ch), + CTX_REGISTER(dw, msi_msgdata, dir, ch), + }; + int nr_entries = ARRAY_SIZE(debugfs_regs); + + dw_hdma_debugfs_create_x32(dw, debugfs_regs, nr_entries, dent); +} + +static void dw_hdma_debugfs_regs_wr(struct dw_edma *dw, struct dentry *dent) +{ + struct dentry *regs_dent, *ch_dent; + char name[16]; + int i; + + regs_dent = debugfs_create_dir(WRITE_STR, dent); + + for (i = 0; i < dw->wr_ch_cnt; i++) { + snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i); + + ch_dent = debugfs_create_dir(name, regs_dent); + + dw_hdma_debugfs_regs_ch(dw, EDMA_DIR_WRITE, i, ch_dent); + } +} + +static void dw_hdma_debugfs_regs_rd(struct dw_edma *dw, struct dentry *dent) +{ + struct dentry *regs_dent, *ch_dent; + char name[16]; + int i; + + regs_dent = debugfs_create_dir(READ_STR, dent); + + for (i = 0; i < dw->rd_ch_cnt; i++) { + snprintf(name, sizeof(name), "%s:%d", CHANNEL_STR, i); + + ch_dent = debugfs_create_dir(name, regs_dent); + + dw_hdma_debugfs_regs_ch(dw, EDMA_DIR_READ, i, ch_dent); + } +} + +static void dw_hdma_debugfs_regs(struct dw_edma *dw) +{ + struct dentry *regs_dent; + + regs_dent = debugfs_create_dir(REGISTERS_STR, dw->dma.dbg_dev_root); + + dw_hdma_debugfs_regs_wr(dw, regs_dent); + dw_hdma_debugfs_regs_rd(dw, regs_dent); +} + +void dw_hdma_v0_debugfs_on(struct dw_edma *dw) +{ + if (!debugfs_initialized()) + return; + + debugfs_create_u32("mf", 0444, dw->dma.dbg_dev_root, &dw->chip->mf); + debugfs_create_u16("wr_ch_cnt", 0444, dw->dma.dbg_dev_root, &dw->wr_ch_cnt); + debugfs_create_u16("rd_ch_cnt", 0444, dw->dma.dbg_dev_root, &dw->rd_ch_cnt); + + dw_hdma_debugfs_regs(dw); +} diff --git a/drivers/dma/dw-edma/dw-hdma-v0-debugfs.h b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.h new file mode 100644 index 000000000000..e6842c83777d --- /dev/null +++ b/drivers/dma/dw-edma/dw-hdma-v0-debugfs.h @@ -0,0 +1,22 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Cai Huoqing + * Synopsys DesignWare HDMA v0 debugfs + * + * Author: Cai Huoqing <cai.huoqing@linux.dev> + */ + +#ifndef _DW_HDMA_V0_DEBUG_FS_H +#define _DW_HDMA_V0_DEBUG_FS_H + +#include <linux/dma/edma.h> + +#ifdef CONFIG_DEBUG_FS +void dw_hdma_v0_debugfs_on(struct dw_edma *dw); +#else +static inline void dw_hdma_v0_debugfs_on(struct dw_edma *dw) +{ +} +#endif /* CONFIG_DEBUG_FS */ + +#endif /* _DW_HDMA_V0_DEBUG_FS_H */ diff --git a/drivers/dma/dw-edma/dw-hdma-v0-regs.h b/drivers/dma/dw-edma/dw-hdma-v0-regs.h new file mode 100644 index 000000000000..a974abdf8aaf --- /dev/null +++ b/drivers/dma/dw-edma/dw-hdma-v0-regs.h @@ -0,0 +1,129 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2023 Cai Huoqing + * Synopsys DesignWare HDMA v0 reg + * + * Author: Cai Huoqing <cai.huoqing@linux.dev> + */ + +#ifndef _DW_HDMA_V0_REGS_H +#define _DW_HDMA_V0_REGS_H + +#include <linux/dmaengine.h> + +#define HDMA_V0_MAX_NR_CH 8 +#define HDMA_V0_LOCAL_ABORT_INT_EN BIT(6) +#define HDMA_V0_REMOTE_ABORT_INT_EN BIT(5) +#define HDMA_V0_LOCAL_STOP_INT_EN BIT(4) +#define HDMA_V0_REMOTEL_STOP_INT_EN BIT(3) +#define HDMA_V0_ABORT_INT_MASK BIT(2) +#define HDMA_V0_STOP_INT_MASK BIT(0) +#define HDMA_V0_LINKLIST_EN BIT(0) +#define HDMA_V0_CONSUMER_CYCLE_STAT BIT(1) +#define HDMA_V0_CONSUMER_CYCLE_BIT BIT(0) +#define HDMA_V0_DOORBELL_START BIT(0) +#define HDMA_V0_CH_STATUS_MASK GENMASK(1, 0) + +struct dw_hdma_v0_ch_regs { + u32 ch_en; /* 0x0000 */ + u32 doorbell; /* 0x0004 */ + u32 prefetch; /* 0x0008 */ + u32 handshake; /* 0x000c */ + union { + u64 reg; /* 0x0010..0x0014 */ + struct { + u32 lsb; /* 0x0010 */ + u32 msb; /* 0x0014 */ + }; + } llp; + u32 cycle_sync; /* 0x0018 */ + u32 transfer_size; /* 0x001c */ + union { + u64 reg; /* 0x0020..0x0024 */ + struct { + u32 lsb; /* 0x0020 */ + u32 msb; /* 0x0024 */ + }; + } sar; + union { + u64 reg; /* 0x0028..0x002c */ + struct { + u32 lsb; /* 0x0028 */ + u32 msb; /* 0x002c */ + }; + } dar; + u32 watermark_en; /* 0x0030 */ + u32 control1; /* 0x0034 */ + u32 func_num; /* 0x0038 */ + u32 qos; /* 0x003c */ + u32 padding_1[16]; /* 0x0040..0x007c */ + u32 ch_stat; /* 0x0080 */ + u32 int_stat; /* 0x0084 */ + u32 int_setup; /* 0x0088 */ + u32 int_clear; /* 0x008c */ + union { + u64 reg; /* 0x0090..0x0094 */ + struct { + u32 lsb; /* 0x0090 */ + u32 msb; /* 0x0094 */ + }; + } msi_stop; + union { + u64 reg; /* 0x0098..0x009c */ + struct { + u32 lsb; /* 0x0098 */ + u32 msb; /* 0x009c */ + }; + } msi_watermark; + union { + u64 reg; /* 0x00a0..0x00a4 */ + struct { + u32 lsb; /* 0x00a0 */ + u32 msb; /* 0x00a4 */ + }; + } msi_abort; + u32 msi_msgdata; /* 0x00a8 */ + u32 padding_2[21]; /* 0x00ac..0x00fc */ +} __packed; + +struct dw_hdma_v0_ch { + struct dw_hdma_v0_ch_regs wr; /* 0x0000 */ + struct dw_hdma_v0_ch_regs rd; /* 0x0100 */ +} __packed; + +struct dw_hdma_v0_regs { + struct dw_hdma_v0_ch ch[HDMA_V0_MAX_NR_CH]; /* 0x0000..0x0fa8 */ +} __packed; + +struct dw_hdma_v0_lli { + u32 control; + u32 transfer_size; + union { + u64 reg; + struct { + u32 lsb; + u32 msb; + }; + } sar; + union { + u64 reg; + struct { + u32 lsb; + u32 msb; + }; + } dar; +} __packed; + +struct dw_hdma_v0_llp { + u32 control; + u32 reserved; + union { + u64 reg; + struct { + u32 lsb; + u32 msb; + }; + } llp; +} __packed; + +#endif /* _DW_HDMA_V0_REGS_H */ diff --git a/drivers/dma/plx_dma.c b/drivers/dma/plx_dma.c index 12725fa1655f..34b6416c3287 100644 --- a/drivers/dma/plx_dma.c +++ b/drivers/dma/plx_dma.c @@ -517,7 +517,6 @@ static int plx_dma_create(struct pci_dev *pdev) plxdev->bar = pcim_iomap_table(pdev)[0]; dma = &plxdev->dma_dev; - dma->chancnt = 1; INIT_LIST_HEAD(&dma->channels); dma_cap_set(DMA_MEMCPY, dma->cap_mask); dma->copy_align = DMAENGINE_ALIGN_1_BYTE; diff --git a/drivers/dma/qcom/Kconfig b/drivers/dma/qcom/Kconfig index 3f926a653bd8..ace75d7b835a 100644 --- a/drivers/dma/qcom/Kconfig +++ b/drivers/dma/qcom/Kconfig @@ -45,6 +45,7 @@ config QCOM_HIDMA_MGMT config QCOM_HIDMA tristate "Qualcomm Technologies HIDMA Channel support" + depends on HAS_IOMEM select DMA_ENGINE help Enable support for the Qualcomm Technologies HIDMA controller. diff --git a/drivers/dma/qcom/bam_dma.c b/drivers/dma/qcom/bam_dma.c index 1e47d27e1f81..4c3eb972039d 100644 --- a/drivers/dma/qcom/bam_dma.c +++ b/drivers/dma/qcom/bam_dma.c @@ -1272,7 +1272,15 @@ static int bam_dma_probe(struct platform_device *pdev) bdev->powered_remotely = of_property_read_bool(pdev->dev.of_node, "qcom,powered-remotely"); - if (bdev->controlled_remotely || bdev->powered_remotely) { + if (bdev->controlled_remotely || bdev->powered_remotely) + bdev->bamclk = devm_clk_get_optional(bdev->dev, "bam_clk"); + else + bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk"); + + if (IS_ERR(bdev->bamclk)) + return PTR_ERR(bdev->bamclk); + + if (!bdev->bamclk) { ret = of_property_read_u32(pdev->dev.of_node, "num-channels", &bdev->num_channels); if (ret) @@ -1284,14 +1292,6 @@ static int bam_dma_probe(struct platform_device *pdev) dev_err(bdev->dev, "num-ees unspecified in dt\n"); } - if (bdev->controlled_remotely || bdev->powered_remotely) - bdev->bamclk = devm_clk_get_optional(bdev->dev, "bam_clk"); - else - bdev->bamclk = devm_clk_get(bdev->dev, "bam_clk"); - - if (IS_ERR(bdev->bamclk)) - return PTR_ERR(bdev->bamclk); - ret = clk_prepare_enable(bdev->bamclk); if (ret) { dev_err(bdev->dev, "failed to prepare/enable clock\n"); diff --git a/drivers/dma/qcom/hidma.c b/drivers/dma/qcom/hidma.c index 04d1c33afc12..344525c3a32f 100644 --- a/drivers/dma/qcom/hidma.c +++ b/drivers/dma/qcom/hidma.c @@ -214,7 +214,6 @@ static int hidma_chan_init(struct hidma_dev *dmadev, u32 dma_sig) spin_lock_init(&mchan->lock); list_add_tail(&mchan->chan.device_node, &ddev->channels); - dmadev->ddev.chancnt++; return 0; } diff --git a/drivers/dma/sprd-dma.c b/drivers/dma/sprd-dma.c index 474d3ba8ec9f..2b639adb48ba 100644 --- a/drivers/dma/sprd-dma.c +++ b/drivers/dma/sprd-dma.c @@ -1169,7 +1169,6 @@ static int sprd_dma_probe(struct platform_device *pdev) dma_cap_set(DMA_MEMCPY, sdev->dma_dev.cap_mask); sdev->total_chns = chn_count; - sdev->dma_dev.chancnt = chn_count; INIT_LIST_HEAD(&sdev->dma_dev.channels); INIT_LIST_HEAD(&sdev->dma_dev.global_node); sdev->dma_dev.dev = &pdev->dev; diff --git a/drivers/dma/ste_dma40.c b/drivers/dma/ste_dma40.c index f093e08c23b1..825001bde42c 100644 --- a/drivers/dma/ste_dma40.c +++ b/drivers/dma/ste_dma40.c @@ -19,14 +19,43 @@ #include <linux/pm_runtime.h> #include <linux/err.h> #include <linux/of.h> +#include <linux/of_address.h> #include <linux/of_dma.h> #include <linux/amba/bus.h> #include <linux/regulator/consumer.h> -#include <linux/platform_data/dma-ste-dma40.h> #include "dmaengine.h" +#include "ste_dma40.h" #include "ste_dma40_ll.h" +/** + * struct stedma40_platform_data - Configuration struct for the dma device. + * + * @dev_tx: mapping between destination event line and io address + * @dev_rx: mapping between source event line and io address + * @disabled_channels: A vector, ending with -1, that marks physical channels + * that are for different reasons not available for the driver. + * @soft_lli_chans: A vector, that marks physical channels will use LLI by SW + * which avoids HW bug that exists in some versions of the controller. + * SoftLLI introduces relink overhead that could impact performace for + * certain use cases. + * @num_of_soft_lli_chans: The number of channels that needs to be configured + * to use SoftLLI. + * @use_esram_lcla: flag for mapping the lcla into esram region + * @num_of_memcpy_chans: The number of channels reserved for memcpy. + * @num_of_phy_chans: The number of physical channels implemented in HW. + * 0 means reading the number of channels from DMA HW but this is only valid + * for 'multiple of 4' channels, like 8. + */ +struct stedma40_platform_data { + int disabled_channels[STEDMA40_MAX_PHYS]; + int *soft_lli_chans; + int num_of_soft_lli_chans; + bool use_esram_lcla; + int num_of_memcpy_chans; + int num_of_phy_chans; +}; + #define D40_NAME "dma40" #define D40_PHY_CHAN -1 @@ -107,7 +136,7 @@ static const struct stedma40_chan_cfg dma40_memcpy_conf_log = { }; /** - * enum 40_command - The different commands and/or statuses. + * enum d40_command - The different commands and/or statuses. * * @D40_DMA_STOP: DMA channel command STOP or status STOPPED, * @D40_DMA_RUN: The DMA channel is RUNNING of the command RUN. @@ -525,8 +554,6 @@ struct d40_gen_dmac { * @virtbase: The virtual base address of the DMA's register. * @rev: silicon revision detected. * @clk: Pointer to the DMA clock structure. - * @phy_start: Physical memory start of the DMA registers. - * @phy_size: Size of the DMA register map. * @irq: The IRQ number. * @num_memcpy_chans: The number of channels used for memcpy (mem-to-mem * transfers). @@ -570,8 +597,6 @@ struct d40_base { void __iomem *virtbase; u8 rev:4; struct clk *clk; - phys_addr_t phy_start; - resource_size_t phy_size; int irq; int num_memcpy_chans; int num_phy_chans; @@ -2268,7 +2293,7 @@ d40_prep_sg(struct dma_chan *dchan, struct scatterlist *sg_src, return NULL; } -bool stedma40_filter(struct dma_chan *chan, void *data) +static bool stedma40_filter(struct dma_chan *chan, void *data) { struct stedma40_chan_cfg *info = data; struct d40_chan *d40c = @@ -2287,7 +2312,6 @@ bool stedma40_filter(struct dma_chan *chan, void *data) return err == 0; } -EXPORT_SYMBOL(stedma40_filter); static void __d40_set_prio_rt(struct d40_chan *d40c, int dev_type, bool src) { @@ -3100,64 +3124,57 @@ static int __init d40_phy_res_init(struct d40_base *base) return num_phy_chans_avail; } -static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) +/* Called from the registered devm action */ +static void d40_drop_kmem_cache_action(void *d) +{ + struct kmem_cache *desc_slab = d; + + kmem_cache_destroy(desc_slab); +} + +static int __init d40_hw_detect_init(struct platform_device *pdev, + struct d40_base **retbase) { struct stedma40_platform_data *plat_data = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; struct clk *clk; void __iomem *virtbase; - struct resource *res; struct d40_base *base; int num_log_chans; int num_phy_chans; int num_memcpy_chans; - int clk_ret = -EINVAL; int i; u32 pid; u32 cid; u8 rev; + int ret; - clk = clk_get(&pdev->dev, NULL); - if (IS_ERR(clk)) { - d40_err(&pdev->dev, "No matching clock found\n"); - goto check_prepare_enabled; - } - - clk_ret = clk_prepare_enable(clk); - if (clk_ret) { - d40_err(&pdev->dev, "Failed to prepare/enable clock\n"); - goto disable_unprepare; - } + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); /* Get IO for DMAC base address */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "base"); - if (!res) - goto disable_unprepare; - - if (request_mem_region(res->start, resource_size(res), - D40_NAME " I/O base") == NULL) - goto release_region; - - virtbase = ioremap(res->start, resource_size(res)); - if (!virtbase) - goto release_region; + virtbase = devm_platform_ioremap_resource_byname(pdev, "base"); + if (IS_ERR(virtbase)) + return PTR_ERR(virtbase); /* This is just a regular AMBA PrimeCell ID actually */ for (pid = 0, i = 0; i < 4; i++) - pid |= (readl(virtbase + resource_size(res) - 0x20 + 4 * i) + pid |= (readl(virtbase + SZ_4K - 0x20 + 4 * i) & 255) << (i * 8); for (cid = 0, i = 0; i < 4; i++) - cid |= (readl(virtbase + resource_size(res) - 0x10 + 4 * i) + cid |= (readl(virtbase + SZ_4K - 0x10 + 4 * i) & 255) << (i * 8); if (cid != AMBA_CID) { - d40_err(&pdev->dev, "Unknown hardware! No PrimeCell ID\n"); - goto unmap_io; + d40_err(dev, "Unknown hardware! No PrimeCell ID\n"); + return -EINVAL; } if (AMBA_MANF_BITS(pid) != AMBA_VENDOR_ST) { - d40_err(&pdev->dev, "Unknown designer! Got %x wanted %x\n", + d40_err(dev, "Unknown designer! Got %x wanted %x\n", AMBA_MANF_BITS(pid), AMBA_VENDOR_ST); - goto unmap_io; + return -EINVAL; } /* * HW revision: @@ -3170,8 +3187,8 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) */ rev = AMBA_REV_BITS(pid); if (rev < 2) { - d40_err(&pdev->dev, "hardware revision: %d is not supported", rev); - goto unmap_io; + d40_err(dev, "hardware revision: %d is not supported", rev); + return -EINVAL; } /* The number of physical channels on this HW */ @@ -3188,27 +3205,26 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) num_log_chans = num_phy_chans * D40_MAX_LOG_CHAN_PER_PHY; - dev_info(&pdev->dev, - "hardware rev: %d @ %pa with %d physical and %d logical channels\n", - rev, &res->start, num_phy_chans, num_log_chans); + dev_info(dev, + "hardware rev: %d with %d physical and %d logical channels\n", + rev, num_phy_chans, num_log_chans); - base = kzalloc(ALIGN(sizeof(struct d40_base), 4) + - (num_phy_chans + num_log_chans + num_memcpy_chans) * - sizeof(struct d40_chan), GFP_KERNEL); + base = devm_kzalloc(dev, + ALIGN(sizeof(struct d40_base), 4) + + (num_phy_chans + num_log_chans + num_memcpy_chans) * + sizeof(struct d40_chan), GFP_KERNEL); - if (base == NULL) - goto unmap_io; + if (!base) + return -ENOMEM; base->rev = rev; base->clk = clk; base->num_memcpy_chans = num_memcpy_chans; base->num_phy_chans = num_phy_chans; base->num_log_chans = num_log_chans; - base->phy_start = res->start; - base->phy_size = resource_size(res); base->virtbase = virtbase; base->plat_data = plat_data; - base->dev = &pdev->dev; + base->dev = dev; base->phy_chans = ((void *)base) + ALIGN(sizeof(struct d40_base), 4); base->log_chans = &base->phy_chans[num_phy_chans]; @@ -3242,76 +3258,57 @@ static struct d40_base * __init d40_hw_detect_init(struct platform_device *pdev) base->gen_dmac.init_reg_size = ARRAY_SIZE(dma_init_reg_v4a); } - base->phy_res = kcalloc(num_phy_chans, - sizeof(*base->phy_res), - GFP_KERNEL); + base->phy_res = devm_kcalloc(dev, num_phy_chans, + sizeof(*base->phy_res), + GFP_KERNEL); if (!base->phy_res) - goto free_base; + return -ENOMEM; - base->lookup_phy_chans = kcalloc(num_phy_chans, - sizeof(*base->lookup_phy_chans), - GFP_KERNEL); + base->lookup_phy_chans = devm_kcalloc(dev, num_phy_chans, + sizeof(*base->lookup_phy_chans), + GFP_KERNEL); if (!base->lookup_phy_chans) - goto free_phy_res; + return -ENOMEM; - base->lookup_log_chans = kcalloc(num_log_chans, - sizeof(*base->lookup_log_chans), - GFP_KERNEL); + base->lookup_log_chans = devm_kcalloc(dev, num_log_chans, + sizeof(*base->lookup_log_chans), + GFP_KERNEL); if (!base->lookup_log_chans) - goto free_phy_chans; + return -ENOMEM; - base->reg_val_backup_chan = kmalloc_array(base->num_phy_chans, + base->reg_val_backup_chan = devm_kmalloc_array(dev, base->num_phy_chans, sizeof(d40_backup_regs_chan), GFP_KERNEL); if (!base->reg_val_backup_chan) - goto free_log_chans; + return -ENOMEM; - base->lcla_pool.alloc_map = kcalloc(num_phy_chans + base->lcla_pool.alloc_map = devm_kcalloc(dev, num_phy_chans * D40_LCLA_LINK_PER_EVENT_GRP, sizeof(*base->lcla_pool.alloc_map), GFP_KERNEL); if (!base->lcla_pool.alloc_map) - goto free_backup_chan; + return -ENOMEM; - base->regs_interrupt = kmalloc_array(base->gen_dmac.il_size, + base->regs_interrupt = devm_kmalloc_array(dev, base->gen_dmac.il_size, sizeof(*base->regs_interrupt), GFP_KERNEL); if (!base->regs_interrupt) - goto free_map; + return -ENOMEM; base->desc_slab = kmem_cache_create(D40_NAME, sizeof(struct d40_desc), 0, SLAB_HWCACHE_ALIGN, NULL); - if (base->desc_slab == NULL) - goto free_regs; - - - return base; - free_regs: - kfree(base->regs_interrupt); - free_map: - kfree(base->lcla_pool.alloc_map); - free_backup_chan: - kfree(base->reg_val_backup_chan); - free_log_chans: - kfree(base->lookup_log_chans); - free_phy_chans: - kfree(base->lookup_phy_chans); - free_phy_res: - kfree(base->phy_res); - free_base: - kfree(base); - unmap_io: - iounmap(virtbase); - release_region: - release_mem_region(res->start, resource_size(res)); - check_prepare_enabled: - if (!clk_ret) - disable_unprepare: - clk_disable_unprepare(clk); - if (!IS_ERR(clk)) - clk_put(clk); - return NULL; + if (!base->desc_slab) + return -ENOMEM; + + ret = devm_add_action_or_reset(dev, d40_drop_kmem_cache_action, + base->desc_slab); + if (ret) + return ret; + + *retbase = base; + + return 0; } static void __init d40_hw_init(struct d40_base *base) @@ -3451,14 +3448,14 @@ static int __init d40_lcla_allocate(struct d40_base *base) return ret; } -static int __init d40_of_probe(struct platform_device *pdev, +static int __init d40_of_probe(struct device *dev, struct device_node *np) { struct stedma40_platform_data *pdata; int num_phy = 0, num_memcpy = 0, num_disabled = 0; const __be32 *list; - pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL); + pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL); if (!pdata) return -ENOMEM; @@ -3471,7 +3468,7 @@ static int __init d40_of_probe(struct platform_device *pdev, num_memcpy /= sizeof(*list); if (num_memcpy > D40_MEMCPY_MAX_CHANS || num_memcpy <= 0) { - d40_err(&pdev->dev, + d40_err(dev, "Invalid number of memcpy channels specified (%d)\n", num_memcpy); return -EINVAL; @@ -3486,7 +3483,7 @@ static int __init d40_of_probe(struct platform_device *pdev, num_disabled /= sizeof(*list); if (num_disabled >= STEDMA40_MAX_PHYS || num_disabled < 0) { - d40_err(&pdev->dev, + d40_err(dev, "Invalid number of disabled channels specified (%d)\n", num_disabled); return -EINVAL; @@ -3497,35 +3494,30 @@ static int __init d40_of_probe(struct platform_device *pdev, num_disabled); pdata->disabled_channels[num_disabled] = -1; - pdev->dev.platform_data = pdata; + dev->platform_data = pdata; return 0; } static int __init d40_probe(struct platform_device *pdev) { - struct stedma40_platform_data *plat_data = dev_get_platdata(&pdev->dev); + struct device *dev = &pdev->dev; struct device_node *np = pdev->dev.of_node; - int ret = -ENOENT; + struct device_node *np_lcpa; struct d40_base *base; struct resource *res; + struct resource res_lcpa; int num_reserved_chans; u32 val; + int ret; - if (!plat_data) { - if (np) { - if (d40_of_probe(pdev, np)) { - ret = -ENOMEM; - goto report_failure; - } - } else { - d40_err(&pdev->dev, "No pdata or Device Tree provided\n"); - goto report_failure; - } + if (d40_of_probe(dev, np)) { + ret = -ENOMEM; + goto report_failure; } - base = d40_hw_detect_init(pdev); - if (!base) + ret = d40_hw_detect_init(pdev, &base); + if (ret) goto report_failure; num_reserved_chans = d40_phy_res_init(base); @@ -3535,37 +3527,38 @@ static int __init d40_probe(struct platform_device *pdev) spin_lock_init(&base->interrupt_lock); spin_lock_init(&base->execmd_lock); - /* Get IO for logical channel parameter address */ - res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "lcpa"); - if (!res) { - ret = -ENOENT; - d40_err(&pdev->dev, "No \"lcpa\" memory resource\n"); - goto destroy_cache; + /* Get IO for logical channel parameter address (LCPA) */ + np_lcpa = of_parse_phandle(np, "sram", 0); + if (!np_lcpa) { + dev_err(dev, "no LCPA SRAM node\n"); + ret = -EINVAL; + goto report_failure; } - base->lcpa_size = resource_size(res); - base->phy_lcpa = res->start; - - if (request_mem_region(res->start, resource_size(res), - D40_NAME " I/O lcpa") == NULL) { - ret = -EBUSY; - d40_err(&pdev->dev, "Failed to request LCPA region %pR\n", res); - goto destroy_cache; + /* This is no device so read the address directly from the node */ + ret = of_address_to_resource(np_lcpa, 0, &res_lcpa); + if (ret) { + dev_err(dev, "no LCPA SRAM resource\n"); + goto report_failure; } + base->lcpa_size = resource_size(&res_lcpa); + base->phy_lcpa = res_lcpa.start; + dev_info(dev, "found LCPA SRAM at %pad, size %pa\n", + &base->phy_lcpa, &base->lcpa_size); /* We make use of ESRAM memory for this. */ val = readl(base->virtbase + D40_DREG_LCPA); - if (res->start != val && val != 0) { - dev_warn(&pdev->dev, - "[%s] Mismatch LCPA dma 0x%x, def %pa\n", - __func__, val, &res->start); + if (base->phy_lcpa != val && val != 0) { + dev_warn(dev, + "[%s] Mismatch LCPA dma 0x%x, def %08x\n", + __func__, val, (u32)base->phy_lcpa); } else - writel(res->start, base->virtbase + D40_DREG_LCPA); + writel(base->phy_lcpa, base->virtbase + D40_DREG_LCPA); - base->lcpa_base = ioremap(res->start, resource_size(res)); + base->lcpa_base = devm_ioremap(dev, base->phy_lcpa, base->lcpa_size); if (!base->lcpa_base) { ret = -ENOMEM; - d40_err(&pdev->dev, "Failed to ioremap LCPA region\n"); - goto destroy_cache; + d40_err(dev, "Failed to ioremap LCPA region\n"); + goto report_failure; } /* If lcla has to be located in ESRAM we don't need to allocate */ if (base->plat_data->use_esram_lcla) { @@ -3573,23 +3566,23 @@ static int __init d40_probe(struct platform_device *pdev) "lcla_esram"); if (!res) { ret = -ENOENT; - d40_err(&pdev->dev, + d40_err(dev, "No \"lcla_esram\" memory resource\n"); - goto destroy_cache; + goto report_failure; } - base->lcla_pool.base = ioremap(res->start, - resource_size(res)); + base->lcla_pool.base = devm_ioremap(dev, res->start, + resource_size(res)); if (!base->lcla_pool.base) { ret = -ENOMEM; - d40_err(&pdev->dev, "Failed to ioremap LCLA region\n"); - goto destroy_cache; + d40_err(dev, "Failed to ioremap LCLA region\n"); + goto report_failure; } writel(res->start, base->virtbase + D40_DREG_LCLA); } else { ret = d40_lcla_allocate(base); if (ret) { - d40_err(&pdev->dev, "Failed to allocate LCLA area\n"); + d40_err(dev, "Failed to allocate LCLA area\n"); goto destroy_cache; } } @@ -3600,7 +3593,7 @@ static int __init d40_probe(struct platform_device *pdev) ret = request_irq(base->irq, d40_handle_interrupt, 0, D40_NAME, base); if (ret) { - d40_err(&pdev->dev, "No IRQ defined\n"); + d40_err(dev, "No IRQ defined\n"); goto destroy_cache; } @@ -3608,7 +3601,7 @@ static int __init d40_probe(struct platform_device *pdev) base->lcpa_regulator = regulator_get(base->dev, "lcla_esram"); if (IS_ERR(base->lcpa_regulator)) { - d40_err(&pdev->dev, "Failed to get lcpa_regulator\n"); + d40_err(dev, "Failed to get lcpa_regulator\n"); ret = PTR_ERR(base->lcpa_regulator); base->lcpa_regulator = NULL; goto destroy_cache; @@ -3616,7 +3609,7 @@ static int __init d40_probe(struct platform_device *pdev) ret = regulator_enable(base->lcpa_regulator); if (ret) { - d40_err(&pdev->dev, + d40_err(dev, "Failed to enable lcpa_regulator\n"); regulator_put(base->lcpa_regulator); base->lcpa_regulator = NULL; @@ -3639,31 +3632,23 @@ static int __init d40_probe(struct platform_device *pdev) ret = dma_set_max_seg_size(base->dev, STEDMA40_MAX_SEG_SIZE); if (ret) { - d40_err(&pdev->dev, "Failed to set dma max seg size\n"); + d40_err(dev, "Failed to set dma max seg size\n"); goto destroy_cache; } d40_hw_init(base); - if (np) { - ret = of_dma_controller_register(np, d40_xlate, NULL); - if (ret) - dev_err(&pdev->dev, - "could not register of_dma_controller\n"); + ret = of_dma_controller_register(np, d40_xlate, NULL); + if (ret) { + dev_err(dev, + "could not register of_dma_controller\n"); + goto destroy_cache; } dev_info(base->dev, "initialized\n"); return 0; - destroy_cache: - kmem_cache_destroy(base->desc_slab); - if (base->virtbase) - iounmap(base->virtbase); - - if (base->lcla_pool.base && base->plat_data->use_esram_lcla) { - iounmap(base->lcla_pool.base); - base->lcla_pool.base = NULL; - } + destroy_cache: if (base->lcla_pool.dma_addr) dma_unmap_single(base->dev, base->lcla_pool.dma_addr, SZ_1K * base->num_phy_chans, @@ -3675,32 +3660,13 @@ static int __init d40_probe(struct platform_device *pdev) kfree(base->lcla_pool.base_unaligned); - if (base->lcpa_base) - iounmap(base->lcpa_base); - - if (base->phy_lcpa) - release_mem_region(base->phy_lcpa, - base->lcpa_size); - if (base->phy_start) - release_mem_region(base->phy_start, - base->phy_size); - if (base->clk) { - clk_disable_unprepare(base->clk); - clk_put(base->clk); - } - if (base->lcpa_regulator) { regulator_disable(base->lcpa_regulator); regulator_put(base->lcpa_regulator); } - kfree(base->lcla_pool.alloc_map); - kfree(base->lookup_log_chans); - kfree(base->lookup_phy_chans); - kfree(base->phy_res); - kfree(base); report_failure: - d40_err(&pdev->dev, "probe failed\n"); + d40_err(dev, "probe failed\n"); return ret; } diff --git a/drivers/dma/ste_dma40.h b/drivers/dma/ste_dma40.h new file mode 100644 index 000000000000..c697bfe16a01 --- /dev/null +++ b/drivers/dma/ste_dma40.h @@ -0,0 +1,110 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ + +#ifndef STE_DMA40_H +#define STE_DMA40_H + +/* + * Maxium size for a single dma descriptor + * Size is limited to 16 bits. + * Size is in the units of addr-widths (1,2,4,8 bytes) + * Larger transfers will be split up to multiple linked desc + */ +#define STEDMA40_MAX_SEG_SIZE 0xFFFF + +/* dev types for memcpy */ +#define STEDMA40_DEV_DST_MEMORY (-1) +#define STEDMA40_DEV_SRC_MEMORY (-1) + +enum stedma40_mode { + STEDMA40_MODE_LOGICAL = 0, + STEDMA40_MODE_PHYSICAL, + STEDMA40_MODE_OPERATION, +}; + +enum stedma40_mode_opt { + STEDMA40_PCHAN_BASIC_MODE = 0, + STEDMA40_LCHAN_SRC_LOG_DST_LOG = 0, + STEDMA40_PCHAN_MODULO_MODE, + STEDMA40_PCHAN_DOUBLE_DST_MODE, + STEDMA40_LCHAN_SRC_PHY_DST_LOG, + STEDMA40_LCHAN_SRC_LOG_DST_PHY, +}; + +#define STEDMA40_ESIZE_8_BIT 0x0 +#define STEDMA40_ESIZE_16_BIT 0x1 +#define STEDMA40_ESIZE_32_BIT 0x2 +#define STEDMA40_ESIZE_64_BIT 0x3 + +/* The value 4 indicates that PEN-reg shall be set to 0 */ +#define STEDMA40_PSIZE_PHY_1 0x4 +#define STEDMA40_PSIZE_PHY_2 0x0 +#define STEDMA40_PSIZE_PHY_4 0x1 +#define STEDMA40_PSIZE_PHY_8 0x2 +#define STEDMA40_PSIZE_PHY_16 0x3 + +/* + * The number of elements differ in logical and + * physical mode + */ +#define STEDMA40_PSIZE_LOG_1 STEDMA40_PSIZE_PHY_2 +#define STEDMA40_PSIZE_LOG_4 STEDMA40_PSIZE_PHY_4 +#define STEDMA40_PSIZE_LOG_8 STEDMA40_PSIZE_PHY_8 +#define STEDMA40_PSIZE_LOG_16 STEDMA40_PSIZE_PHY_16 + +/* Maximum number of possible physical channels */ +#define STEDMA40_MAX_PHYS 32 + +enum stedma40_flow_ctrl { + STEDMA40_NO_FLOW_CTRL, + STEDMA40_FLOW_CTRL, +}; + +/** + * struct stedma40_half_channel_info - dst/src channel configuration + * + * @big_endian: true if the src/dst should be read as big endian + * @data_width: Data width of the src/dst hardware + * @p_size: Burst size + * @flow_ctrl: Flow control on/off. + */ +struct stedma40_half_channel_info { + bool big_endian; + enum dma_slave_buswidth data_width; + int psize; + enum stedma40_flow_ctrl flow_ctrl; +}; + +/** + * struct stedma40_chan_cfg - Structure to be filled by client drivers. + * + * @dir: MEM 2 MEM, PERIPH 2 MEM , MEM 2 PERIPH, PERIPH 2 PERIPH + * @high_priority: true if high-priority + * @realtime: true if realtime mode is to be enabled. Only available on DMA40 + * version 3+, i.e DB8500v2+ + * @mode: channel mode: physical, logical, or operation + * @mode_opt: options for the chosen channel mode + * @dev_type: src/dst device type (driver uses dir to figure out which) + * @src_info: Parameters for dst half channel + * @dst_info: Parameters for dst half channel + * @use_fixed_channel: if true, use physical channel specified by phy_channel + * @phy_channel: physical channel to use, only if use_fixed_channel is true + * + * This structure has to be filled by the client drivers. + * It is recommended to do all dma configurations for clients in the machine. + * + */ +struct stedma40_chan_cfg { + enum dma_transfer_direction dir; + bool high_priority; + bool realtime; + enum stedma40_mode mode; + enum stedma40_mode_opt mode_opt; + int dev_type; + struct stedma40_half_channel_info src_info; + struct stedma40_half_channel_info dst_info; + + bool use_fixed_channel; + int phy_channel; +}; + +#endif /* STE_DMA40_H */ diff --git a/drivers/dma/ste_dma40_ll.c b/drivers/dma/ste_dma40_ll.c index b5287c661eb7..4c489b126cb2 100644 --- a/drivers/dma/ste_dma40_ll.c +++ b/drivers/dma/ste_dma40_ll.c @@ -6,8 +6,9 @@ */ #include <linux/kernel.h> -#include <linux/platform_data/dma-ste-dma40.h> +#include <linux/dmaengine.h> +#include "ste_dma40.h" #include "ste_dma40_ll.h" static u8 d40_width_to_bits(enum dma_slave_buswidth width) diff --git a/drivers/dma/ti/k3-psil-j721s2.c b/drivers/dma/ti/k3-psil-j721s2.c index a488c2250623..1d5430fc5724 100644 --- a/drivers/dma/ti/k3-psil-j721s2.c +++ b/drivers/dma/ti/k3-psil-j721s2.c @@ -99,6 +99,8 @@ static struct psil_ep j721s2_src_ep_map[] = { PSIL_PDMA_XY_PKT(0x461d), PSIL_PDMA_XY_PKT(0x461e), PSIL_PDMA_XY_PKT(0x461f), + /* MAIN_CPSW2G */ + PSIL_ETHERNET(0x4640), /* PDMA_USART_G0 - UART0-1 */ PSIL_PDMA_XY_PKT(0x4700), PSIL_PDMA_XY_PKT(0x4701), @@ -161,6 +163,15 @@ static struct psil_ep j721s2_dst_ep_map[] = { PSIL_ETHERNET(0xf005), PSIL_ETHERNET(0xf006), PSIL_ETHERNET(0xf007), + /* MAIN_CPSW2G */ + PSIL_ETHERNET(0xc640), + PSIL_ETHERNET(0xc641), + PSIL_ETHERNET(0xc642), + PSIL_ETHERNET(0xc643), + PSIL_ETHERNET(0xc644), + PSIL_ETHERNET(0xc645), + PSIL_ETHERNET(0xc646), + PSIL_ETHERNET(0xc647), /* SA2UL */ PSIL_SA2UL(0xf500, 1), PSIL_SA2UL(0xf501, 1), diff --git a/drivers/dma/ti/k3-udma.c b/drivers/dma/ti/k3-udma.c index b8329a23728d..eb4dc5fffe64 100644 --- a/drivers/dma/ti/k3-udma.c +++ b/drivers/dma/ti/k3-udma.c @@ -4308,6 +4308,15 @@ static struct udma_soc_data am62a_dmss_csi_soc_data = { }, }; +static struct udma_soc_data j721s2_bcdma_csi_soc_data = { + .oes = { + .bcdma_tchan_data = 0x800, + .bcdma_tchan_ring = 0xa00, + .bcdma_rchan_data = 0xe00, + .bcdma_rchan_ring = 0x1000, + }, +}; + static struct udma_match_data am62a_bcdma_csirx_data = { .type = DMA_TYPE_BCDMA, .psil_base = 0x3100, @@ -4346,6 +4355,18 @@ static struct udma_match_data am64_pktdma_data = { }, }; +static struct udma_match_data j721s2_bcdma_csi_data = { + .type = DMA_TYPE_BCDMA, + .psil_base = 0x2000, + .enable_memcpy_support = false, + .burst_size = { + TI_SCI_RM_UDMAP_CHAN_BURST_SIZE_64_BYTES, /* Normal Channels */ + 0, /* No H Channels */ + 0, /* No UH Channels */ + }, + .soc_data = &j721s2_bcdma_csi_soc_data, +}; + static const struct of_device_id udma_of_match[] = { { .compatible = "ti,am654-navss-main-udmap", @@ -4373,6 +4394,10 @@ static const struct of_device_id udma_of_match[] = { .compatible = "ti,am62a-dmss-bcdma-csirx", .data = &am62a_bcdma_csirx_data, }, + { + .compatible = "ti,j721s2-dmss-bcdma-csi", + .data = &j721s2_bcdma_csi_data, + }, { /* Sentinel */ }, }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu.h b/drivers/gpu/drm/amd/amdgpu/amdgpu.h index a84bd4a0c421..2f9c14aca73c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu.h @@ -286,6 +286,9 @@ extern int amdgpu_user_partt_mode; #define AMDGPU_SMARTSHIFT_MAX_BIAS (100) #define AMDGPU_SMARTSHIFT_MIN_BIAS (-100) +/* Extra time delay(in ms) to eliminate the influence of temperature momentary fluctuation */ +#define AMDGPU_SWCTF_EXTRA_DELAY 50 + struct amdgpu_xcp_mgr; struct amdgpu_device; struct amdgpu_irq_src; @@ -1277,9 +1280,10 @@ int emu_soc_asic_init(struct amdgpu_device *adev); #define amdgpu_inc_vram_lost(adev) atomic_inc(&((adev)->vram_lost_counter)); -#define for_each_inst(i, inst_mask) \ - for (i = ffs(inst_mask) - 1; inst_mask; \ - inst_mask &= ~(1U << i), i = ffs(inst_mask) - 1) +#define BIT_MASK_UPPER(i) ((i) >= BITS_PER_LONG ? 0 : ~0UL << (i)) +#define for_each_inst(i, inst_mask) \ + for (i = ffs(inst_mask); i-- != 0; \ + i = ffs(inst_mask & BIT_MASK_UPPER(i + 1))) #define MIN(X, Y) ((X) < (Y) ? (X) : (Y)) diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c index 9ba4817a9148..f4e3c133a16c 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.c @@ -1791,6 +1791,15 @@ const struct attribute_group amdgpu_vbios_version_attr_group = { .attrs = amdgpu_vbios_version_attrs }; +int amdgpu_atombios_sysfs_init(struct amdgpu_device *adev) +{ + if (adev->mode_info.atom_context) + return devm_device_add_group(adev->dev, + &amdgpu_vbios_version_attr_group); + + return 0; +} + /** * amdgpu_atombios_fini - free the driver info and callbacks for atombios * diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h index 4153d520e2a3..b639a80ee3fc 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atombios.h @@ -217,5 +217,6 @@ int amdgpu_atombios_get_data_table(struct amdgpu_device *adev, void amdgpu_atombios_fini(struct amdgpu_device *adev); int amdgpu_atombios_init(struct amdgpu_device *adev); +int amdgpu_atombios_sysfs_init(struct amdgpu_device *adev); #endif diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c index ef4b9a41f20a..0b7f4c4d58e5 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_atomfirmware.c @@ -327,10 +327,13 @@ amdgpu_atomfirmware_get_vram_info(struct amdgpu_device *adev, mem_channel_number = igp_info->v11.umachannelnumber; if (!mem_channel_number) mem_channel_number = 1; - /* channel width is 64 */ - if (vram_width) - *vram_width = mem_channel_number * 64; mem_type = igp_info->v11.memorytype; + if (mem_type == LpDdr5MemType) + mem_channel_width = 32; + else + mem_channel_width = 64; + if (vram_width) + *vram_width = mem_channel_number * mem_channel_width; if (vram_type) *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); break; @@ -345,10 +348,13 @@ amdgpu_atomfirmware_get_vram_info(struct amdgpu_device *adev, mem_channel_number = igp_info->v21.umachannelnumber; if (!mem_channel_number) mem_channel_number = 1; - /* channel width is 64 */ - if (vram_width) - *vram_width = mem_channel_number * 64; mem_type = igp_info->v21.memorytype; + if (mem_type == LpDdr5MemType) + mem_channel_width = 32; + else + mem_channel_width = 64; + if (vram_width) + *vram_width = mem_channel_number * mem_channel_width; if (vram_type) *vram_type = convert_atom_mem_type_to_vram_type(adev, mem_type); break; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c index d9503882ea97..040f4cb6ab2d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_cs.c @@ -136,9 +136,6 @@ static int amdgpu_cs_p1_user_fence(struct amdgpu_cs_parser *p, bo = amdgpu_bo_ref(gem_to_amdgpu_bo(gobj)); p->uf_entry.priority = 0; p->uf_entry.tv.bo = &bo->tbo; - /* One for TTM and two for the CS job */ - p->uf_entry.tv.num_shared = 3; - drm_gem_object_put(gobj); size = amdgpu_bo_size(bo); @@ -912,15 +909,19 @@ static int amdgpu_cs_parser_bos(struct amdgpu_cs_parser *p, mutex_lock(&p->bo_list->bo_list_mutex); - /* One for TTM and one for the CS job */ + /* One for TTM and one for each CS job */ amdgpu_bo_list_for_each_entry(e, p->bo_list) - e->tv.num_shared = 2; + e->tv.num_shared = 1 + p->gang_size; + p->uf_entry.tv.num_shared = 1 + p->gang_size; amdgpu_bo_list_get_list(p->bo_list, &p->validated); INIT_LIST_HEAD(&duplicates); amdgpu_vm_get_pd_bo(&fpriv->vm, &p->validated, &p->vm_pd); + /* Two for VM updates, one for TTM and one for each CS job */ + p->vm_pd.tv.num_shared = 3 + p->gang_size; + if (p->uf_entry.tv.bo && !ttm_to_amdgpu_bo(p->uf_entry.tv.bo)->parent) list_add(&p->uf_entry.tv.head, &p->validated); @@ -1653,15 +1654,15 @@ static int amdgpu_cs_wait_all_fences(struct amdgpu_device *adev, continue; r = dma_fence_wait_timeout(fence, true, timeout); + if (r > 0 && fence->error) + r = fence->error; + dma_fence_put(fence); if (r < 0) return r; if (r == 0) break; - - if (fence->error) - return fence->error; } memset(wait, 0, sizeof(*wait)); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c index e25f085ee886..a92c6189b4b6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_device.c @@ -2552,7 +2552,7 @@ static int amdgpu_device_ip_init(struct amdgpu_device *adev) adev->ip_blocks[i].status.hw = true; /* right after GMC hw init, we create CSA */ - if (amdgpu_mcbp) { + if (adev->gfx.mcbp) { r = amdgpu_allocate_static_csa(adev, &adev->virt.csa_obj, AMDGPU_GEM_DOMAIN_VRAM | AMDGPU_GEM_DOMAIN_GTT, @@ -3673,6 +3673,23 @@ static const struct attribute *amdgpu_dev_attributes[] = { NULL }; +static void amdgpu_device_set_mcbp(struct amdgpu_device *adev) +{ + if (amdgpu_mcbp == 1) + adev->gfx.mcbp = true; + + if ((adev->ip_versions[GC_HWIP][0] >= IP_VERSION(9, 0, 0)) && + (adev->ip_versions[GC_HWIP][0] < IP_VERSION(10, 0, 0)) && + adev->gfx.num_gfx_rings) + adev->gfx.mcbp = true; + + if (amdgpu_sriov_vf(adev)) + adev->gfx.mcbp = true; + + if (adev->gfx.mcbp) + DRM_INFO("MCBP is enabled\n"); +} + /** * amdgpu_device_init - initialize the driver * @@ -3824,9 +3841,6 @@ int amdgpu_device_init(struct amdgpu_device *adev, DRM_INFO("register mmio base: 0x%08X\n", (uint32_t)adev->rmmio_base); DRM_INFO("register mmio size: %u\n", (unsigned)adev->rmmio_size); - if (amdgpu_mcbp) - DRM_INFO("MCBP is enabled\n"); - /* * Reset domain needs to be present early, before XGMI hive discovered * (if any) and intitialized to use reset sem and in_gpu reset flag @@ -3852,6 +3866,8 @@ int amdgpu_device_init(struct amdgpu_device *adev, if (r) return r; + amdgpu_device_set_mcbp(adev); + /* Get rid of things like offb */ r = drm_aperture_remove_conflicting_pci_framebuffers(adev->pdev, &amdgpu_kms_driver); if (r) @@ -4018,6 +4034,11 @@ fence_driver_init: /* Get a log2 for easy divisions. */ adev->mm_stats.log2_max_MBps = ilog2(max(1u, max_MBps)); + r = amdgpu_atombios_sysfs_init(adev); + if (r) + drm_err(&adev->ddev, + "registering atombios sysfs failed (%d).\n", r); + r = amdgpu_pm_sysfs_init(adev); if (r) DRM_ERROR("registering pm sysfs failed (%d).\n", r); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c index 3b711babd4e2..0593ef8fe0a6 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_drv.c @@ -180,7 +180,7 @@ uint amdgpu_dc_feature_mask = 2; uint amdgpu_dc_debug_mask; uint amdgpu_dc_visual_confirm; int amdgpu_async_gfx_ring = 1; -int amdgpu_mcbp; +int amdgpu_mcbp = -1; int amdgpu_discovery = -1; int amdgpu_mes; int amdgpu_mes_kiq; @@ -634,10 +634,10 @@ module_param_named(async_gfx_ring, amdgpu_async_gfx_ring, int, 0444); /** * DOC: mcbp (int) - * It is used to enable mid command buffer preemption. (0 = disabled (default), 1 = enabled) + * It is used to enable mid command buffer preemption. (0 = disabled, 1 = enabled, -1 auto (default)) */ MODULE_PARM_DESC(mcbp, - "Enable Mid-command buffer preemption (0 = disabled (default), 1 = enabled)"); + "Enable Mid-command buffer preemption (0 = disabled, 1 = enabled), -1 = auto (default)"); module_param_named(mcbp, amdgpu_mcbp, int, 0444); /** @@ -2899,12 +2899,10 @@ static struct pci_error_handlers amdgpu_pci_err_handler = { extern const struct attribute_group amdgpu_vram_mgr_attr_group; extern const struct attribute_group amdgpu_gtt_mgr_attr_group; -extern const struct attribute_group amdgpu_vbios_version_attr_group; static const struct attribute_group *amdgpu_sysfs_groups[] = { &amdgpu_vram_mgr_attr_group, &amdgpu_gtt_mgr_attr_group, - &amdgpu_vbios_version_attr_group, NULL, }; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h index ce0f7a8ad4b8..a4ff515ce896 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_gfx.h @@ -434,6 +434,7 @@ struct amdgpu_gfx { uint16_t xcc_mask; uint32_t num_xcc_per_xcp; struct mutex partition_mutex; + bool mcbp; /* mid command buffer preemption */ }; struct amdgpu_gfx_ras_reg_entry { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c index 3add4b4f0667..2ff2897fd1db 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_jpeg.c @@ -255,7 +255,8 @@ int amdgpu_jpeg_ras_late_init(struct amdgpu_device *adev, struct ras_common_if * if (amdgpu_ras_is_supported(adev, ras_block->block)) { for (i = 0; i < adev->jpeg.num_jpeg_inst; ++i) { - if (adev->jpeg.harvest_config & (1 << i)) + if (adev->jpeg.harvest_config & (1 << i) || + !adev->jpeg.inst[i].ras_poison_irq.funcs) continue; r = amdgpu_irq_get(adev, &adev->jpeg.inst[i].ras_poison_irq, 0); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c index e3531aa3c8bd..cca5a495611f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_kms.c @@ -805,7 +805,7 @@ int amdgpu_info_ioctl(struct drm_device *dev, void *data, struct drm_file *filp) dev_info->ids_flags = 0; if (adev->flags & AMD_IS_APU) dev_info->ids_flags |= AMDGPU_IDS_FLAGS_FUSION; - if (amdgpu_mcbp) + if (adev->gfx.mcbp) dev_info->ids_flags |= AMDGPU_IDS_FLAGS_PREEMPTION; if (amdgpu_is_tmz(adev)) dev_info->ids_flags |= AMDGPU_IDS_FLAGS_TMZ; @@ -1247,7 +1247,7 @@ int amdgpu_driver_open_kms(struct drm_device *dev, struct drm_file *file_priv) goto error_vm; } - if (amdgpu_mcbp) { + if (adev->gfx.mcbp) { uint64_t csa_addr = amdgpu_csa_vaddr(adev) & AMDGPU_GMC_HOLE_MASK; r = amdgpu_map_static_csa(adev, &fpriv->vm, adev->virt.csa_obj, diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c index e15c27e05564..6d676bdd1505 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_psp.c @@ -839,6 +839,7 @@ static bool psp_skip_tmr(struct psp_context *psp) case IP_VERSION(11, 0, 9): case IP_VERSION(11, 0, 7): case IP_VERSION(13, 0, 2): + case IP_VERSION(13, 0, 6): case IP_VERSION(13, 0, 10): return true; default: @@ -2039,6 +2040,8 @@ static int psp_securedisplay_initialize(struct psp_context *psp) psp_securedisplay_parse_resp_status(psp, securedisplay_cmd->status); dev_err(psp->adev->dev, "SECUREDISPLAY: query securedisplay TA failed. ret 0x%x\n", securedisplay_cmd->securedisplay_out_message.query_ta.query_cmd_ret); + /* don't try again */ + psp->securedisplay_context.context.bin_desc.size_bytes = 0; } return 0; @@ -3703,7 +3706,6 @@ static DEVICE_ATTR(psp_vbflash_status, 0440, amdgpu_psp_vbflash_status, NULL); int amdgpu_psp_sysfs_init(struct amdgpu_device *adev) { int ret = 0; - struct psp_context *psp = &adev->psp; if (amdgpu_sriov_vf(adev)) return -EINVAL; @@ -3712,10 +3714,6 @@ int amdgpu_psp_sysfs_init(struct amdgpu_device *adev) case IP_VERSION(13, 0, 0): case IP_VERSION(13, 0, 7): case IP_VERSION(13, 0, 10): - if (!psp->adev) { - psp->adev = adev; - psp_v13_0_set_psp_funcs(psp); - } ret = sysfs_create_bin_file(&adev->dev->kobj, &psp_vbflash_bin_attr); if (ret) dev_err(adev->dev, "Failed to create device file psp_vbflash"); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c index 12010c988c8b..123bcf5c2bb1 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_rap.c @@ -116,7 +116,6 @@ static const struct file_operations amdgpu_rap_debugfs_ops = { void amdgpu_rap_debugfs_init(struct amdgpu_device *adev) { -#if defined(CONFIG_DEBUG_FS) struct drm_minor *minor = adev_to_drm(adev)->primary; if (!adev->psp.rap_context.context.initialized) @@ -124,5 +123,4 @@ void amdgpu_rap_debugfs_init(struct amdgpu_device *adev) debugfs_create_file("rap_test", S_IWUSR, minor->debugfs_root, adev, &amdgpu_rap_debugfs_ops); -#endif } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c index 4769a18304d7..8aaa427f8c0f 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.c @@ -2065,6 +2065,14 @@ static void amdgpu_ras_do_recovery(struct work_struct *work) ras->gpu_reset_flags &= ~AMDGPU_RAS_GPU_RESET_MODE2_RESET; reset_context.method = AMD_RESET_METHOD_MODE2; } + + /* Fatal error occurs in poison mode, mode1 reset is used to + * recover gpu. + */ + if (ras->gpu_reset_flags & AMDGPU_RAS_GPU_RESET_MODE1_RESET) { + ras->gpu_reset_flags &= ~AMDGPU_RAS_GPU_RESET_MODE1_RESET; + set_bit(AMDGPU_NEED_FULL_RESET, &reset_context.flags); + } } amdgpu_device_gpu_recover(ras->adev, NULL, &reset_context); @@ -2955,9 +2963,12 @@ void amdgpu_ras_global_ras_isr(struct amdgpu_device *adev) return; if (atomic_cmpxchg(&amdgpu_ras_in_intr, 0, 1) == 0) { + struct amdgpu_ras *ras = amdgpu_ras_get_context(adev); + dev_info(adev->dev, "uncorrectable hardware error" "(ERREVENT_ATHUB_INTERRUPT) detected!\n"); + ras->gpu_reset_flags |= AMDGPU_RAS_GPU_RESET_MODE1_RESET; amdgpu_ras_reset_gpu(adev); } } diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h index 46bf1889a9d7..ffb49b2d533a 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ras.h @@ -340,6 +340,7 @@ enum amdgpu_ras_ret { #define AMDGPU_RAS_ERR_ADDRESS_VALID (1 << 2) #define AMDGPU_RAS_GPU_RESET_MODE2_RESET (0x1 << 0) +#define AMDGPU_RAS_GPU_RESET_MODE1_RESET (0x1 << 1) struct amdgpu_ras_err_status_reg_entry { uint32_t hwip; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring_mux.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring_mux.c index 73516abef662..b779ee4bbaa7 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_ring_mux.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_ring_mux.c @@ -423,6 +423,9 @@ void amdgpu_sw_ring_ib_mark_offset(struct amdgpu_ring *ring, enum amdgpu_ring_mu struct amdgpu_ring_mux *mux = &adev->gfx.muxer; unsigned offset; + if (ring->hw_prio > AMDGPU_RING_PRIO_DEFAULT) + return; + offset = ring->wptr & ring->buf_mask; amdgpu_ring_mux_ib_mark_offset(mux, ring, offset, type); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c index 78ec3420ef85..dacf281d2b21 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_sdma.c @@ -72,7 +72,7 @@ uint64_t amdgpu_sdma_get_csa_mc_addr(struct amdgpu_ring *ring, int r; /* don't enable OS preemption on SDMA under SRIOV */ - if (amdgpu_sriov_vf(adev) || vmid == 0 || !amdgpu_mcbp) + if (amdgpu_sriov_vf(adev) || vmid == 0 || !adev->gfx.mcbp) return 0; if (ring->is_mes_queue) { diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c index acbef1a24b9c..ae455aab5d29 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vcn.c @@ -1198,7 +1198,8 @@ int amdgpu_vcn_ras_late_init(struct amdgpu_device *adev, struct ras_common_if *r if (amdgpu_ras_is_supported(adev, ras_block->block)) { for (i = 0; i < adev->vcn.num_vcn_inst; i++) { - if (adev->vcn.harvest_config & (1 << i)) + if (adev->vcn.harvest_config & (1 << i) || + !adev->vcn.inst[i].ras_poison_irq.funcs) continue; r = amdgpu_irq_get(adev, &adev->vcn.inst[i].ras_poison_irq, 0); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c index 25b4d7f0bd35..41aa853a07d2 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_virt.c @@ -66,9 +66,6 @@ void amdgpu_virt_init_setting(struct amdgpu_device *adev) adev->cg_flags = 0; adev->pg_flags = 0; - /* enable mcbp for sriov */ - amdgpu_mcbp = 1; - /* Reduce kcq number to 2 to reduce latency */ if (amdgpu_num_kcq == -1) amdgpu_num_kcq = 2; diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c index 143d11afe0e5..291977b93b1d 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_vm.c @@ -1771,18 +1771,30 @@ int amdgpu_vm_bo_clear_mappings(struct amdgpu_device *adev, /* Insert partial mapping before the range */ if (!list_empty(&before->list)) { + struct amdgpu_bo *bo = before->bo_va->base.bo; + amdgpu_vm_it_insert(before, &vm->va); if (before->flags & AMDGPU_PTE_PRT) amdgpu_vm_prt_get(adev); + + if (bo && bo->tbo.base.resv == vm->root.bo->tbo.base.resv && + !before->bo_va->base.moved) + amdgpu_vm_bo_moved(&before->bo_va->base); } else { kfree(before); } /* Insert partial mapping after the range */ if (!list_empty(&after->list)) { + struct amdgpu_bo *bo = after->bo_va->base.bo; + amdgpu_vm_it_insert(after, &vm->va); if (after->flags & AMDGPU_PTE_PRT) amdgpu_vm_prt_get(adev); + + if (bo && bo->tbo.base.resv == vm->root.bo->tbo.base.resv && + !after->bo_va->base.moved) + amdgpu_vm_bo_moved(&after->bo_va->base); } else { kfree(after); } @@ -2233,16 +2245,16 @@ int amdgpu_vm_make_compute(struct amdgpu_device *adev, struct amdgpu_vm *vm) if (r) return r; - /* Sanity checks */ - if (!amdgpu_vm_pt_is_root_clean(adev, vm)) { - r = -EINVAL; - goto unreserve_bo; - } - /* Check if PD needs to be reinitialized and do it before * changing any other state, in case it fails. */ if (pte_support_ats != vm->pte_support_ats) { + /* Sanity checks */ + if (!amdgpu_vm_pt_is_root_clean(adev, vm)) { + r = -EINVAL; + goto unreserve_bo; + } + vm->pte_support_ats = pte_support_ats; r = amdgpu_vm_pt_clear(adev, vm, to_amdgpu_bo_vm(vm->root.bo), false); diff --git a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c index d733fa6e7477..d175e862f222 100644 --- a/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c +++ b/drivers/gpu/drm/amd/amdgpu/amdgpu_xcp.c @@ -132,6 +132,9 @@ int amdgpu_xcp_init(struct amdgpu_xcp_mgr *xcp_mgr, int num_xcps, int mode) for (i = 0; i < MAX_XCP; ++i) xcp_mgr->xcp[i].valid = false; + /* This is needed for figuring out memory id of xcp */ + xcp_mgr->num_xcp_per_mem_partition = num_xcps / xcp_mgr->adev->gmc.num_mem_partitions; + for (i = 0; i < num_xcps; ++i) { for (j = AMDGPU_XCP_GFXHUB; j < AMDGPU_XCP_MAX_BLOCKS; ++j) { ret = xcp_mgr->funcs->get_ip_details(xcp_mgr, i, j, @@ -157,7 +160,6 @@ int amdgpu_xcp_init(struct amdgpu_xcp_mgr *xcp_mgr, int num_xcps, int mode) xcp_mgr->num_xcps = num_xcps; amdgpu_xcp_update_partition_sched_list(adev); - xcp_mgr->num_xcp_per_mem_partition = num_xcps / xcp_mgr->adev->gmc.num_mem_partitions; return 0; } @@ -232,7 +234,10 @@ static int amdgpu_xcp_dev_alloc(struct amdgpu_device *adev) ddev = adev_to_drm(adev); - for (i = 0; i < MAX_XCP; i++) { + /* xcp #0 shares drm device setting with adev */ + adev->xcp_mgr->xcp->ddev = ddev; + + for (i = 1; i < MAX_XCP; i++) { ret = amdgpu_xcp_drm_dev_alloc(&p_ddev); if (ret) return ret; @@ -322,7 +327,7 @@ int amdgpu_xcp_dev_register(struct amdgpu_device *adev, if (!adev->xcp_mgr) return 0; - for (i = 0; i < MAX_XCP; i++) { + for (i = 1; i < MAX_XCP; i++) { ret = drm_dev_register(adev->xcp_mgr->xcp[i].ddev, ent->driver_data); if (ret) return ret; @@ -339,7 +344,7 @@ void amdgpu_xcp_dev_unplug(struct amdgpu_device *adev) if (!adev->xcp_mgr) return; - for (i = 0; i < MAX_XCP; i++) { + for (i = 1; i < MAX_XCP; i++) { p_ddev = adev->xcp_mgr->xcp[i].ddev; drm_dev_unplug(p_ddev); p_ddev->render->dev = adev->xcp_mgr->xcp[i].rdev; diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c index be984f8c71c7..44af8022b89f 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v10_0.c @@ -8307,7 +8307,7 @@ static void gfx_v10_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, control |= ib->length_dw | (vmid << 24); - if (amdgpu_mcbp && (ib->flags & AMDGPU_IB_FLAG_PREEMPT)) { + if (ring->adev->gfx.mcbp && (ib->flags & AMDGPU_IB_FLAG_PREEMPT)) { control |= INDIRECT_BUFFER_PRE_ENB(1); if (flags & AMDGPU_IB_PREEMPTED) @@ -8482,7 +8482,7 @@ static void gfx_v10_0_ring_emit_cntxcntl(struct amdgpu_ring *ring, { uint32_t dw2 = 0; - if (amdgpu_mcbp) + if (ring->adev->gfx.mcbp) gfx_v10_0_ring_emit_ce_meta(ring, (!amdgpu_sriov_vf(ring->adev) && flags & AMDGPU_IB_PREEMPTED) ? true : false); diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c index 690e121d9dda..3a7af59e83ca 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v11_0.c @@ -5311,7 +5311,7 @@ static void gfx_v11_0_ring_emit_ib_gfx(struct amdgpu_ring *ring, control |= ib->length_dw | (vmid << 24); - if (amdgpu_mcbp && (ib->flags & AMDGPU_IB_FLAG_PREEMPT)) { + if (ring->adev->gfx.mcbp && (ib->flags & AMDGPU_IB_FLAG_PREEMPT)) { control |= INDIRECT_BUFFER_PRE_ENB(1); if (flags & AMDGPU_IB_PREEMPTED) diff --git a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c index c1ee54d4c3d3..9e3b835bdbb2 100644 --- a/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c +++ b/drivers/gpu/drm/amd/amdgpu/gfx_v9_4_3.c @@ -623,12 +623,28 @@ static void gfx_v9_4_3_select_me_pipe_q(struct amdgpu_device *adev, static int gfx_v9_4_3_switch_compute_partition(struct amdgpu_device *adev, int num_xccs_per_xcp) { - int ret; - - ret = psp_spatial_partition(&adev->psp, NUM_XCC(adev->gfx.xcc_mask) / - num_xccs_per_xcp); - if (ret) - return ret; + int ret, i, num_xcc; + u32 tmp = 0; + + if (adev->psp.funcs) { + ret = psp_spatial_partition(&adev->psp, + NUM_XCC(adev->gfx.xcc_mask) / + num_xccs_per_xcp); + if (ret) + return ret; + } else { + num_xcc = NUM_XCC(adev->gfx.xcc_mask); + + for (i = 0; i < num_xcc; i++) { + tmp = REG_SET_FIELD(tmp, CP_HYP_XCP_CTL, NUM_XCC_IN_XCP, + num_xccs_per_xcp); + tmp = REG_SET_FIELD(tmp, CP_HYP_XCP_CTL, VIRTUAL_XCC_ID, + i % num_xccs_per_xcp); + WREG32_SOC15(GC, GET_INST(GC, i), regCP_HYP_XCP_CTL, + tmp); + } + ret = 0; + } adev->gfx.num_xcc_per_xcp = num_xccs_per_xcp; @@ -1762,6 +1778,8 @@ static int gfx_v9_4_3_xcc_kiq_init_queue(struct amdgpu_ring *ring, int xcc_id) ((struct v9_mqd_allocation *)mqd)->dynamic_cu_mask = 0xFFFFFFFF; ((struct v9_mqd_allocation *)mqd)->dynamic_rb_mask = 0xFFFFFFFF; mutex_lock(&adev->srbm_mutex); + if (amdgpu_sriov_vf(adev) && adev->in_suspend) + amdgpu_ring_clear_ring(ring); soc15_grbm_select(adev, ring->me, ring->pipe, ring->queue, 0, GET_INST(GC, xcc_id)); gfx_v9_4_3_xcc_mqd_init(ring, xcc_id); gfx_v9_4_3_xcc_kiq_init_register(ring, xcc_id); @@ -1960,6 +1978,16 @@ static void gfx_v9_4_3_xcc_fini(struct amdgpu_device *adev, int xcc_id) if (amdgpu_gfx_disable_kcq(adev, xcc_id)) DRM_ERROR("XCD %d KCQ disable failed\n", xcc_id); + if (amdgpu_sriov_vf(adev)) { + /* must disable polling for SRIOV when hw finished, otherwise + * CPC engine may still keep fetching WB address which is already + * invalid after sw finished and trigger DMAR reading error in + * hypervisor side. + */ + WREG32_FIELD15_PREREG(GC, GET_INST(GC, xcc_id), CP_PQ_WPTR_POLL_CNTL, EN, 0); + return; + } + /* Use deinitialize sequence from CAIL when unbinding device * from driver, otherwise KIQ is hanging when binding back */ @@ -1984,7 +2012,8 @@ static int gfx_v9_4_3_hw_init(void *handle) int r; struct amdgpu_device *adev = (struct amdgpu_device *)handle; - gfx_v9_4_3_init_golden_registers(adev); + if (!amdgpu_sriov_vf(adev)) + gfx_v9_4_3_init_golden_registers(adev); gfx_v9_4_3_constants_init(adev); diff --git a/drivers/gpu/drm/amd/amdgpu/nbio_v2_3.c b/drivers/gpu/drm/amd/amdgpu/nbio_v2_3.c index aa761ff3a5fa..4038455d7998 100644 --- a/drivers/gpu/drm/amd/amdgpu/nbio_v2_3.c +++ b/drivers/gpu/drm/amd/amdgpu/nbio_v2_3.c @@ -345,8 +345,8 @@ static void nbio_v2_3_init_registers(struct amdgpu_device *adev) } #define NAVI10_PCIE__LC_L0S_INACTIVITY_DEFAULT 0x00000000 // off by default, no gains over L1 -#define NAVI10_PCIE__LC_L1_INACTIVITY_DEFAULT 0x00000009 // 1=1us, 9=1ms -#define NAVI10_PCIE__LC_L1_INACTIVITY_TBT_DEFAULT 0x0000000E // 4ms +#define NAVI10_PCIE__LC_L1_INACTIVITY_DEFAULT 0x0000000A // 1=1us, 9=1ms, 10=4ms +#define NAVI10_PCIE__LC_L1_INACTIVITY_TBT_DEFAULT 0x0000000E // 400ms static void nbio_v2_3_enable_aspm(struct amdgpu_device *adev, bool enable) @@ -479,9 +479,12 @@ static void nbio_v2_3_program_aspm(struct amdgpu_device *adev) WREG32_SOC15(NBIO, 0, mmRCC_BIF_STRAP5, data); def = data = RREG32_PCIE(smnPCIE_LC_CNTL); - data &= ~PCIE_LC_CNTL__LC_L0S_INACTIVITY_MASK; - data |= 0x9 << PCIE_LC_CNTL__LC_L1_INACTIVITY__SHIFT; - data |= 0x1 << PCIE_LC_CNTL__LC_PMI_TO_L1_DIS__SHIFT; + data |= NAVI10_PCIE__LC_L0S_INACTIVITY_DEFAULT << PCIE_LC_CNTL__LC_L0S_INACTIVITY__SHIFT; + if (pci_is_thunderbolt_attached(adev->pdev)) + data |= NAVI10_PCIE__LC_L1_INACTIVITY_TBT_DEFAULT << PCIE_LC_CNTL__LC_L1_INACTIVITY__SHIFT; + else + data |= NAVI10_PCIE__LC_L1_INACTIVITY_DEFAULT << PCIE_LC_CNTL__LC_L1_INACTIVITY__SHIFT; + data &= ~PCIE_LC_CNTL__LC_PMI_TO_L1_DIS_MASK; if (def != data) WREG32_PCIE(smnPCIE_LC_CNTL, data); diff --git a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c index ea5e12390d18..f413898dda37 100644 --- a/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c +++ b/drivers/gpu/drm/amd/amdgpu/sdma_v4_4_2.c @@ -578,6 +578,9 @@ static void sdma_v4_4_2_inst_enable(struct amdgpu_device *adev, bool enable, return; } + if (adev->firmware.load_type == AMDGPU_FW_LOAD_PSP) + return; + for_each_inst(i, inst_mask) { f32_cntl = RREG32_SDMA(i, regSDMA_F32_CNTL); f32_cntl = REG_SET_FIELD(f32_cntl, SDMA_F32_CNTL, HALT, enable ? 0 : 1); @@ -899,15 +902,12 @@ static int sdma_v4_4_2_inst_start(struct amdgpu_device *adev, WREG32_SDMA(i, regSDMA_CNTL, temp); if (!amdgpu_sriov_vf(adev)) { - ring = &adev->sdma.instance[i].ring; - adev->nbio.funcs->sdma_doorbell_range(adev, i, - ring->use_doorbell, ring->doorbell_index, - adev->doorbell_index.sdma_doorbell_range); - - /* unhalt engine */ - temp = RREG32_SDMA(i, regSDMA_F32_CNTL); - temp = REG_SET_FIELD(temp, SDMA_F32_CNTL, HALT, 0); - WREG32_SDMA(i, regSDMA_F32_CNTL, temp); + if (adev->firmware.load_type != AMDGPU_FW_LOAD_PSP) { + /* unhalt engine */ + temp = RREG32_SDMA(i, regSDMA_F32_CNTL); + temp = REG_SET_FIELD(temp, SDMA_F32_CNTL, HALT, 0); + WREG32_SDMA(i, regSDMA_F32_CNTL, temp); + } } } diff --git a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c index b48bb5212488..259795098173 100644 --- a/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c +++ b/drivers/gpu/drm/amd/amdgpu/vcn_v4_0.c @@ -1424,8 +1424,10 @@ static int vcn_v4_0_start_sriov(struct amdgpu_device *adev) */ static void vcn_v4_0_stop_dpg_mode(struct amdgpu_device *adev, int inst_idx) { + struct dpg_pause_state state = {.fw_based = VCN_DPG_STATE__UNPAUSE}; uint32_t tmp; + vcn_v4_0_pause_dpg_mode(adev, inst_idx, &state); /* Wait for power status to be 1 */ SOC15_WAIT_ON_RREG(VCN, inst_idx, regUVD_POWER_STATUS, 1, UVD_POWER_STATUS__UVD_POWER_STATUS_MASK); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_device.c b/drivers/gpu/drm/amd/amdkfd/kfd_device.c index 9d4abfd8b55e..0b3dc754e06b 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_device.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_device.c @@ -138,9 +138,12 @@ static void kfd_device_info_set_event_interrupt_class(struct kfd_dev *kfd) case IP_VERSION(9, 4, 0): /* VEGA20 */ case IP_VERSION(9, 4, 1): /* ARCTURUS */ case IP_VERSION(9, 4, 2): /* ALDEBARAN */ - case IP_VERSION(9, 4, 3): /* GC 9.4.3 */ kfd->device_info.event_interrupt_class = &event_interrupt_class_v9; break; + case IP_VERSION(9, 4, 3): /* GC 9.4.3 */ + kfd->device_info.event_interrupt_class = + &event_interrupt_class_v9_4_3; + break; case IP_VERSION(10, 3, 1): /* VANGOGH */ case IP_VERSION(10, 3, 3): /* YELLOW_CARP */ case IP_VERSION(10, 3, 6): /* GC 10.3.6 */ @@ -518,6 +521,7 @@ static int kfd_gws_init(struct kfd_node *node) && kfd->mec2_fw_version >= 0x30) || (KFD_GC_VERSION(node) == IP_VERSION(9, 4, 2) && kfd->mec2_fw_version >= 0x28) || + (KFD_GC_VERSION(node) == IP_VERSION(9, 4, 3)) || (KFD_GC_VERSION(node) >= IP_VERSION(10, 3, 0) && KFD_GC_VERSION(node) < IP_VERSION(11, 0, 0) && kfd->mec2_fw_version >= 0x6b)))) @@ -598,6 +602,41 @@ static void kfd_cleanup_nodes(struct kfd_dev *kfd, unsigned int num_nodes) } } +static void kfd_setup_interrupt_bitmap(struct kfd_node *node, + unsigned int kfd_node_idx) +{ + struct amdgpu_device *adev = node->adev; + uint32_t xcc_mask = node->xcc_mask; + uint32_t xcc, mapped_xcc; + /* + * Interrupt bitmap is setup for processing interrupts from + * different XCDs and AIDs. + * Interrupt bitmap is defined as follows: + * 1. Bits 0-15 - correspond to the NodeId field. + * Each bit corresponds to NodeId number. For example, if + * a KFD node has interrupt bitmap set to 0x7, then this + * KFD node will process interrupts with NodeId = 0, 1 and 2 + * in the IH cookie. + * 2. Bits 16-31 - unused. + * + * Please note that the kfd_node_idx argument passed to this + * function is not related to NodeId field received in the + * IH cookie. + * + * In CPX mode, a KFD node will process an interrupt if: + * - the Node Id matches the corresponding bit set in + * Bits 0-15. + * - AND VMID reported in the interrupt lies within the + * VMID range of the node. + */ + for_each_inst(xcc, xcc_mask) { + mapped_xcc = GET_INST(GC, xcc); + node->interrupt_bitmap |= (mapped_xcc % 2 ? 5 : 3) << (4 * (mapped_xcc / 2)); + } + dev_info(kfd_device, "Node: %d, interrupt_bitmap: %x\n", kfd_node_idx, + node->interrupt_bitmap); +} + bool kgd2kfd_device_init(struct kfd_dev *kfd, const struct kgd2kfd_shared_resources *gpu_resources) { @@ -797,6 +836,9 @@ bool kgd2kfd_device_init(struct kfd_dev *kfd, amdgpu_amdkfd_get_local_mem_info(kfd->adev, &node->local_mem_info, node->xcp); + if (KFD_GC_VERSION(kfd) == IP_VERSION(9, 4, 3)) + kfd_setup_interrupt_bitmap(node, i); + /* Initialize the KFD node */ if (kfd_init_node(node)) { dev_err(kfd_device, "Error initializing KFD node\n"); diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c index d5c9f30552e3..f0731a6a5306 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_int_process_v9.c @@ -446,7 +446,36 @@ static void event_interrupt_wq_v9(struct kfd_node *dev, } } +static bool event_interrupt_isr_v9_4_3(struct kfd_node *node, + const uint32_t *ih_ring_entry, + uint32_t *patched_ihre, + bool *patched_flag) +{ + uint16_t node_id, vmid; + + /* + * For GFX 9.4.3, process the interrupt if: + * - NodeID field in IH entry matches the corresponding bit + * set in interrupt_bitmap Bits 0-15. + * OR + * - If partition mode is CPX and interrupt came from + * Node_id 0,4,8,12, then check if the Bit (16 + client id) + * is set in interrupt bitmap Bits 16-31. + */ + node_id = SOC15_NODEID_FROM_IH_ENTRY(ih_ring_entry); + vmid = SOC15_VMID_FROM_IH_ENTRY(ih_ring_entry); + if (kfd_irq_is_from_node(node, node_id, vmid)) + return event_interrupt_isr_v9(node, ih_ring_entry, + patched_ihre, patched_flag); + return false; +} + const struct kfd_event_interrupt_class event_interrupt_class_v9 = { .interrupt_isr = event_interrupt_isr_v9, .interrupt_wq = event_interrupt_wq_v9, }; + +const struct kfd_event_interrupt_class event_interrupt_class_v9_4_3 = { + .interrupt_isr = event_interrupt_isr_v9_4_3, + .interrupt_wq = event_interrupt_wq_v9, +}; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h index 7364a5d77c6e..d4c9ee3f9953 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_priv.h +++ b/drivers/gpu/drm/amd/amdkfd/kfd_priv.h @@ -1444,6 +1444,7 @@ uint64_t kfd_get_number_elems(struct kfd_dev *kfd); /* Events */ extern const struct kfd_event_interrupt_class event_interrupt_class_cik; extern const struct kfd_event_interrupt_class event_interrupt_class_v9; +extern const struct kfd_event_interrupt_class event_interrupt_class_v9_4_3; extern const struct kfd_event_interrupt_class event_interrupt_class_v10; extern const struct kfd_event_interrupt_class event_interrupt_class_v11; diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process.c b/drivers/gpu/drm/amd/amdkfd/kfd_process.c index 3d3611705d41..a844e68211ac 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process.c @@ -2142,6 +2142,7 @@ void kfd_flush_tlb(struct kfd_process_device *pdd, enum TLB_FLUSH_TYPE type) int kfd_process_drain_interrupts(struct kfd_process_device *pdd) { uint32_t irq_drain_fence[8]; + uint8_t node_id = 0; int r = 0; if (!KFD_IS_SOC15(pdd->dev)) @@ -2154,6 +2155,14 @@ int kfd_process_drain_interrupts(struct kfd_process_device *pdd) KFD_IRQ_FENCE_CLIENTID; irq_drain_fence[3] = pdd->process->pasid; + /* + * For GFX 9.4.3, send the NodeId also in IH cookie DW[3] + */ + if (KFD_GC_VERSION(pdd->dev->kfd) == IP_VERSION(9, 4, 3)) { + node_id = ffs(pdd->dev->interrupt_bitmap) - 1; + irq_drain_fence[3] |= node_id << 16; + } + /* ensure stale irqs scheduled KFD interrupts and send drain fence. */ if (amdgpu_amdkfd_send_close_event_drain_irq(pdd->dev->adev, irq_drain_fence)) { diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c index 9ad1a2186a24..ba9d69054119 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_process_queue_manager.c @@ -123,16 +123,24 @@ int pqm_set_gws(struct process_queue_manager *pqm, unsigned int qid, if (!gws && pdd->qpd.num_gws == 0) return -EINVAL; - if (gws) - ret = amdgpu_amdkfd_add_gws_to_process(pdd->process->kgd_process_info, - gws, &mem); - else - ret = amdgpu_amdkfd_remove_gws_from_process(pdd->process->kgd_process_info, - pqn->q->gws); - if (unlikely(ret)) - return ret; + if (KFD_GC_VERSION(dev) != IP_VERSION(9, 4, 3)) { + if (gws) + ret = amdgpu_amdkfd_add_gws_to_process(pdd->process->kgd_process_info, + gws, &mem); + else + ret = amdgpu_amdkfd_remove_gws_from_process(pdd->process->kgd_process_info, + pqn->q->gws); + if (unlikely(ret)) + return ret; + pqn->q->gws = mem; + } else { + /* + * Intentionally set GWS to a non-NULL value + * for GFX 9.4.3. + */ + pqn->q->gws = gws ? ERR_PTR(-ENOMEM) : NULL; + } - pqn->q->gws = mem; pdd->qpd.num_gws = gws ? dev->adev->gds.gws_size : 0; return pqn->q->device->dqm->ops.update_queue(pqn->q->device->dqm, @@ -164,7 +172,8 @@ void pqm_uninit(struct process_queue_manager *pqm) struct process_queue_node *pqn, *next; list_for_each_entry_safe(pqn, next, &pqm->queues, process_queue_list) { - if (pqn->q && pqn->q->gws) + if (pqn->q && pqn->q->gws && + KFD_GC_VERSION(pqn->q->device) != IP_VERSION(9, 4, 3)) amdgpu_amdkfd_remove_gws_from_process(pqm->process->kgd_process_info, pqn->q->gws); kfd_procfs_del_queue(pqn->q); @@ -446,8 +455,10 @@ int pqm_destroy_queue(struct process_queue_manager *pqm, unsigned int qid) } if (pqn->q->gws) { - amdgpu_amdkfd_remove_gws_from_process(pqm->process->kgd_process_info, - pqn->q->gws); + if (KFD_GC_VERSION(pqn->q->device) != IP_VERSION(9, 4, 3)) + amdgpu_amdkfd_remove_gws_from_process( + pqm->process->kgd_process_info, + pqn->q->gws); pdd->qpd.num_gws = 0; } diff --git a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c index 90b86a6ac7bd..61fc62f3e003 100644 --- a/drivers/gpu/drm/amd/amdkfd/kfd_topology.c +++ b/drivers/gpu/drm/amd/amdkfd/kfd_topology.c @@ -2107,6 +2107,10 @@ int kfd_topology_add_device(struct kfd_node *gpu) if (KFD_IS_SVM_API_SUPPORTED(dev->gpu->adev)) dev->node_props.capability |= HSA_CAP_SVMAPI_SUPPORTED; + if (dev->gpu->adev->gmc.is_app_apu || + dev->gpu->adev->gmc.xgmi.connected_to_cpu) + dev->node_props.capability |= HSA_CAP_FLAGS_COHERENTHOSTACCESS; + kfd_debug_print_topology(); kfd_notify_gpu_change(gpu_id, 1); diff --git a/drivers/gpu/drm/amd/amdkfd/soc15_int.h b/drivers/gpu/drm/amd/amdkfd/soc15_int.h index e3f3b0b93a59..10138676f27f 100644 --- a/drivers/gpu/drm/amd/amdkfd/soc15_int.h +++ b/drivers/gpu/drm/amd/amdkfd/soc15_int.h @@ -40,6 +40,7 @@ #define SOC15_VMID_FROM_IH_ENTRY(entry) (le32_to_cpu(entry[0]) >> 24 & 0xf) #define SOC15_VMID_TYPE_FROM_IH_ENTRY(entry) (le32_to_cpu(entry[0]) >> 31 & 0x1) #define SOC15_PASID_FROM_IH_ENTRY(entry) (le32_to_cpu(entry[3]) & 0xffff) +#define SOC15_NODEID_FROM_IH_ENTRY(entry) (le32_to_cpu(entry[3]) >> 16 & 0xff) #define SOC15_CONTEXT_ID0_FROM_IH_ENTRY(entry) (le32_to_cpu(entry[4])) #define SOC15_CONTEXT_ID1_FROM_IH_ENTRY(entry) (le32_to_cpu(entry[5])) #define SOC15_CONTEXT_ID2_FROM_IH_ENTRY(entry) (le32_to_cpu(entry[6])) diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c index 514f6785a020..ff0a217b9d56 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm.c @@ -5063,11 +5063,7 @@ static inline void fill_dc_dirty_rect(struct drm_plane *plane, s32 y, s32 width, s32 height, int *i, bool ffu) { - if (*i > DC_MAX_DIRTY_RECTS) - return; - - if (*i == DC_MAX_DIRTY_RECTS) - goto out; + WARN_ON(*i >= DC_MAX_DIRTY_RECTS); dirty_rect->x = x; dirty_rect->y = y; @@ -5083,7 +5079,6 @@ static inline void fill_dc_dirty_rect(struct drm_plane *plane, "[PLANE:%d] PSR SU dirty rect at (%d, %d) size (%d, %d)", plane->base.id, x, y, width, height); -out: (*i)++; } @@ -5170,6 +5165,9 @@ static void fill_dc_dirty_rects(struct drm_plane *plane, *dirty_regions_changed = bb_changed; + if ((num_clips + (bb_changed ? 2 : 0)) > DC_MAX_DIRTY_RECTS) + goto ffu; + if (bb_changed) { fill_dc_dirty_rect(new_plane_state->plane, &dirty_rects[i], new_plane_state->crtc_x, @@ -5199,9 +5197,6 @@ static void fill_dc_dirty_rects(struct drm_plane *plane, new_plane_state->crtc_h, &i, false); } - if (i > DC_MAX_DIRTY_RECTS) - goto ffu; - flip_addrs->dirty_rect_count = i; return; @@ -7258,13 +7253,7 @@ static int amdgpu_dm_connector_get_modes(struct drm_connector *connector) drm_add_modes_noedid(connector, 1920, 1080); } else { amdgpu_dm_connector_ddc_get_modes(connector, edid); - /* most eDP supports only timings from its edid, - * usually only detailed timings are available - * from eDP edid. timings which are not from edid - * may damage eDP - */ - if (connector->connector_type != DRM_MODE_CONNECTOR_eDP) - amdgpu_dm_connector_add_common_modes(encoder, connector); + amdgpu_dm_connector_add_common_modes(encoder, connector); amdgpu_dm_connector_add_freesync_modes(connector, edid); } amdgpu_dm_fbc_init(connector); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c index 5ea3284b2b77..d63ee636483b 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_debugfs.c @@ -336,6 +336,153 @@ static ssize_t dp_link_settings_write(struct file *f, const char __user *buf, return size; } +static bool dp_mst_is_end_device(struct amdgpu_dm_connector *aconnector) +{ + bool is_end_device = false; + struct drm_dp_mst_topology_mgr *mgr = NULL; + struct drm_dp_mst_port *port = NULL; + + if (aconnector->mst_root && aconnector->mst_root->mst_mgr.mst_state) { + mgr = &aconnector->mst_root->mst_mgr; + port = aconnector->mst_output_port; + + drm_modeset_lock(&mgr->base.lock, NULL); + if (port->pdt == DP_PEER_DEVICE_SST_SINK || + port->pdt == DP_PEER_DEVICE_DP_LEGACY_CONV) + is_end_device = true; + drm_modeset_unlock(&mgr->base.lock); + } + + return is_end_device; +} + +/* Change MST link setting + * + * valid lane count value: 1, 2, 4 + * valid link rate value: + * 06h = 1.62Gbps per lane + * 0Ah = 2.7Gbps per lane + * 0Ch = 3.24Gbps per lane + * 14h = 5.4Gbps per lane + * 1Eh = 8.1Gbps per lane + * 3E8h = 10.0Gbps per lane + * 546h = 13.5Gbps per lane + * 7D0h = 20.0Gbps per lane + * + * debugfs is located at /sys/kernel/debug/dri/0/DP-x/mst_link_settings + * + * for example, to force to 2 lane, 10.0GHz, + * echo 2 0x3e8 > /sys/kernel/debug/dri/0/DP-x/mst_link_settings + * + * Valid input will trigger hotplug event to get new link setting applied + * Invalid input will trigger training setting reset + * + * The usage can be referred to link_settings entry + * + */ +static ssize_t dp_mst_link_setting(struct file *f, const char __user *buf, + size_t size, loff_t *pos) +{ + struct amdgpu_dm_connector *aconnector = file_inode(f)->i_private; + struct dc_link *link = aconnector->dc_link; + struct amdgpu_device *adev = drm_to_adev(aconnector->base.dev); + struct dc *dc = (struct dc *)link->dc; + struct dc_link_settings prefer_link_settings; + char *wr_buf = NULL; + const uint32_t wr_buf_size = 40; + /* 0: lane_count; 1: link_rate */ + int max_param_num = 2; + uint8_t param_nums = 0; + long param[2]; + bool valid_input = true; + + if (!dp_mst_is_end_device(aconnector)) + return -EINVAL; + + if (size == 0) + return -EINVAL; + + wr_buf = kcalloc(wr_buf_size, sizeof(char), GFP_KERNEL); + if (!wr_buf) + return -ENOSPC; + + if (parse_write_buffer_into_params(wr_buf, wr_buf_size, + (long *)param, buf, + max_param_num, + ¶m_nums)) { + kfree(wr_buf); + return -EINVAL; + } + + if (param_nums <= 0) { + kfree(wr_buf); + DRM_DEBUG_DRIVER("user data not be read\n"); + return -EINVAL; + } + + switch (param[0]) { + case LANE_COUNT_ONE: + case LANE_COUNT_TWO: + case LANE_COUNT_FOUR: + break; + default: + valid_input = false; + break; + } + + switch (param[1]) { + case LINK_RATE_LOW: + case LINK_RATE_HIGH: + case LINK_RATE_RBR2: + case LINK_RATE_HIGH2: + case LINK_RATE_HIGH3: + case LINK_RATE_UHBR10: + case LINK_RATE_UHBR13_5: + case LINK_RATE_UHBR20: + break; + default: + valid_input = false; + break; + } + + if (!valid_input) { + kfree(wr_buf); + DRM_DEBUG_DRIVER("Invalid Input value No HW will be programmed\n"); + mutex_lock(&adev->dm.dc_lock); + dc_link_set_preferred_training_settings(dc, NULL, NULL, link, false); + mutex_unlock(&adev->dm.dc_lock); + return -EINVAL; + } + + /* save user force lane_count, link_rate to preferred settings + * spread spectrum will not be changed + */ + prefer_link_settings.link_spread = link->cur_link_settings.link_spread; + prefer_link_settings.use_link_rate_set = false; + prefer_link_settings.lane_count = param[0]; + prefer_link_settings.link_rate = param[1]; + + /* skip immediate retrain, and train to new link setting after hotplug event triggered */ + mutex_lock(&adev->dm.dc_lock); + dc_link_set_preferred_training_settings(dc, &prefer_link_settings, NULL, link, true); + mutex_unlock(&adev->dm.dc_lock); + + mutex_lock(&aconnector->base.dev->mode_config.mutex); + aconnector->base.force = DRM_FORCE_OFF; + mutex_unlock(&aconnector->base.dev->mode_config.mutex); + drm_kms_helper_hotplug_event(aconnector->base.dev); + + msleep(100); + + mutex_lock(&aconnector->base.dev->mode_config.mutex); + aconnector->base.force = DRM_FORCE_UNSPECIFIED; + mutex_unlock(&aconnector->base.dev->mode_config.mutex); + drm_kms_helper_hotplug_event(aconnector->base.dev); + + kfree(wr_buf); + return size; +} + /* function: get current DP PHY settings: voltage swing, pre-emphasis, * post-cursor2 (defined by VESA DP specification) * @@ -2668,6 +2815,12 @@ static const struct file_operations dp_dsc_disable_passthrough_debugfs_fops = { .llseek = default_llseek }; +static const struct file_operations dp_mst_link_settings_debugfs_fops = { + .owner = THIS_MODULE, + .write = dp_mst_link_setting, + .llseek = default_llseek +}; + static const struct { char *name; const struct file_operations *fops; @@ -2691,7 +2844,8 @@ static const struct { {"dsc_disable_passthrough", &dp_dsc_disable_passthrough_debugfs_fops}, {"is_mst_connector", &dp_is_mst_connector_fops}, {"mst_progress_status", &dp_mst_progress_status_fops}, - {"is_dpia_link", &is_dpia_link_fops} + {"is_dpia_link", &is_dpia_link_fops}, + {"mst_link_settings", &dp_mst_link_settings_debugfs_fops} }; static const struct { diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c index cd20cfc04996..d9a482908380 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_helpers.c @@ -44,6 +44,30 @@ #include "dm_helpers.h" #include "ddc_service_types.h" +static u32 edid_extract_panel_id(struct edid *edid) +{ + return (u32)edid->mfg_id[0] << 24 | + (u32)edid->mfg_id[1] << 16 | + (u32)EDID_PRODUCT_ID(edid); +} + +static void apply_edid_quirks(struct edid *edid, struct dc_edid_caps *edid_caps) +{ + uint32_t panel_id = edid_extract_panel_id(edid); + + switch (panel_id) { + /* Workaround for some monitors which does not work well with FAMS */ + case drm_edid_encode_panel_id('S', 'A', 'M', 0x0E5E): + case drm_edid_encode_panel_id('S', 'A', 'M', 0x7053): + case drm_edid_encode_panel_id('S', 'A', 'M', 0x71AC): + DRM_DEBUG_DRIVER("Disabling FAMS on monitor with panel id %X\n", panel_id); + edid_caps->panel_patch.disable_fams = true; + break; + default: + return; + } +} + /* dm_helpers_parse_edid_caps * * Parse edid caps @@ -115,6 +139,8 @@ enum dc_edid_status dm_helpers_parse_edid_caps( else edid_caps->speaker_flags = DEFAULT_SPEAKER_LOCATION; + apply_edid_quirks(edid_buf, edid_caps); + kfree(sads); kfree(sadb); diff --git a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c index d647f68fd563..4f61d4f257cd 100644 --- a/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c +++ b/drivers/gpu/drm/amd/display/amdgpu_dm/amdgpu_dm_psr.c @@ -24,6 +24,7 @@ */ #include "amdgpu_dm_psr.h" +#include "dc_dmub_srv.h" #include "dc.h" #include "dm_helpers.h" #include "amdgpu_dm.h" @@ -50,7 +51,7 @@ static bool link_supports_psrsu(struct dc_link *link) !link->dpcd_caps.psr_info.psr2_su_y_granularity_cap) return false; - return true; + return dc_dmub_check_min_version(dc->ctx->dmub_srv->dmub); } /* diff --git a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c index 6a811755e2e6..cb992aca760d 100644 --- a/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c +++ b/drivers/gpu/drm/amd/display/dc/clk_mgr/dcn32/dcn32_clk_mgr.c @@ -541,9 +541,18 @@ static void dcn32_update_clocks(struct clk_mgr *clk_mgr_base, clk_mgr_base->clks.p_state_change_support = p_state_change_support; /* to disable P-State switching, set UCLK min = max */ - if (!clk_mgr_base->clks.p_state_change_support) - dcn32_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK, - clk_mgr_base->bw_params->clk_table.entries[clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_memclk_levels - 1].memclk_mhz); + if (!clk_mgr_base->clks.p_state_change_support) { + if (dc->clk_mgr->dc_mode_softmax_enabled) { + /* On DCN32x we will never have the functional UCLK min above the softmax + * since we calculate mode support based on softmax being the max UCLK + * frequency. + */ + dcn32_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK, + dc->clk_mgr->bw_params->dc_mode_softmax_memclk); + } else { + dcn32_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK, dc->clk_mgr->bw_params->max_memclk_mhz); + } + } } /* Always update saved value, even if new value not set due to P-State switching unsupported. Also check safe_to_lower for FCLK */ @@ -808,8 +817,7 @@ static void dcn32_set_hard_max_memclk(struct clk_mgr *clk_mgr_base) if (!clk_mgr->smu_present) return; - dcn30_smu_set_hard_max_by_freq(clk_mgr, PPCLK_UCLK, - clk_mgr_base->bw_params->clk_table.entries[clk_mgr_base->bw_params->clk_table.num_entries_per_clk.num_memclk_levels - 1].memclk_mhz); + dcn30_smu_set_hard_max_by_freq(clk_mgr, PPCLK_UCLK, clk_mgr_base->bw_params->max_memclk_mhz); } /* Get current memclk states, update bounding box */ @@ -827,6 +835,7 @@ static void dcn32_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base) &clk_mgr_base->bw_params->clk_table.entries[0].memclk_mhz, &num_entries_per_clk->num_memclk_levels); clk_mgr_base->bw_params->dc_mode_limit.memclk_mhz = dcn30_smu_get_dc_mode_max_dpm_freq(clk_mgr, PPCLK_UCLK); + clk_mgr_base->bw_params->dc_mode_softmax_memclk = clk_mgr_base->bw_params->dc_mode_limit.memclk_mhz; /* memclk must have at least one level */ num_entries_per_clk->num_memclk_levels = num_entries_per_clk->num_memclk_levels ? num_entries_per_clk->num_memclk_levels : 1; @@ -841,7 +850,8 @@ static void dcn32_get_memclk_states_from_smu(struct clk_mgr *clk_mgr_base) } else { num_levels = num_entries_per_clk->num_fclk_levels; } - + clk_mgr_base->bw_params->max_memclk_mhz = + clk_mgr_base->bw_params->clk_table.entries[num_entries_per_clk->num_memclk_levels - 1].memclk_mhz; clk_mgr_base->bw_params->clk_table.num_entries = num_levels ? num_levels : 1; if (clk_mgr->dpm_present && !num_levels) @@ -894,6 +904,25 @@ static bool dcn32_is_smu_present(struct clk_mgr *clk_mgr_base) return clk_mgr->smu_present; } +static void dcn32_set_max_memclk(struct clk_mgr *clk_mgr_base, unsigned int memclk_mhz) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + + if (!clk_mgr->smu_present) + return; + + dcn30_smu_set_hard_max_by_freq(clk_mgr, PPCLK_UCLK, memclk_mhz); +} + +static void dcn32_set_min_memclk(struct clk_mgr *clk_mgr_base, unsigned int memclk_mhz) +{ + struct clk_mgr_internal *clk_mgr = TO_CLK_MGR_INTERNAL(clk_mgr_base); + + if (!clk_mgr->smu_present) + return; + + dcn32_smu_set_hard_min_by_freq(clk_mgr, PPCLK_UCLK, memclk_mhz); +} static struct clk_mgr_funcs dcn32_funcs = { .get_dp_ref_clk_frequency = dce12_get_dp_ref_freq_khz, @@ -904,6 +933,8 @@ static struct clk_mgr_funcs dcn32_funcs = { .notify_wm_ranges = dcn32_notify_wm_ranges, .set_hard_min_memclk = dcn32_set_hard_min_memclk, .set_hard_max_memclk = dcn32_set_hard_max_memclk, + .set_max_memclk = dcn32_set_max_memclk, + .set_min_memclk = dcn32_set_min_memclk, .get_memclk_states_from_smu = dcn32_get_memclk_states_from_smu, .are_clock_states_equal = dcn32_are_clock_states_equal, .enable_pme_wa = dcn32_enable_pme_wa, diff --git a/drivers/gpu/drm/amd/display/dc/core/dc.c b/drivers/gpu/drm/amd/display/dc/core/dc.c index dd3a9d06c6e2..d133e4186a52 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc.c @@ -1629,6 +1629,9 @@ bool dc_validate_boot_timing(const struct dc *dc, return false; } + if (dc->debug.force_odm_combine) + return false; + /* Check for enabled DIG to identify enabled display */ if (!link->link_enc->funcs->is_dig_enabled(link->link_enc)) return false; @@ -3577,6 +3580,13 @@ static void commit_planes_for_stream_fast(struct dc *dc, hwss_execute_sequence(dc, context->block_sequence, context->block_sequence_steps); + /* Clear update flags so next flip doesn't have redundant programming + * (if there's no stream update, the update flags are not cleared). + */ + if (top_pipe_to_program->plane_state) + top_pipe_to_program->plane_state->update_flags.raw = 0; + if (top_pipe_to_program->stream) + top_pipe_to_program->stream->update_flags.raw = 0; } static void commit_planes_for_stream(struct dc *dc, @@ -4233,6 +4243,117 @@ static void update_seamless_boot_flags(struct dc *dc, } } +static void populate_fast_updates(struct dc_fast_update *fast_update, + struct dc_surface_update *srf_updates, + int surface_count, + struct dc_stream_update *stream_update) +{ + int i = 0; + + if (stream_update) { + fast_update[0].out_transfer_func = stream_update->out_transfer_func; + fast_update[0].output_csc_transform = stream_update->output_csc_transform; + } + + for (i = 0; i < surface_count; i++) { + fast_update[i].flip_addr = srf_updates[i].flip_addr; + fast_update[i].gamma = srf_updates[i].gamma; + fast_update[i].gamut_remap_matrix = srf_updates[i].gamut_remap_matrix; + fast_update[i].input_csc_color_matrix = srf_updates[i].input_csc_color_matrix; + fast_update[i].coeff_reduction_factor = srf_updates[i].coeff_reduction_factor; + } +} + +static bool fast_updates_exist(struct dc_fast_update *fast_update, int surface_count) +{ + int i; + + if (fast_update[0].out_transfer_func || + fast_update[0].output_csc_transform) + return true; + + for (i = 0; i < surface_count; i++) { + if (fast_update[i].flip_addr || + fast_update[i].gamma || + fast_update[i].gamut_remap_matrix || + fast_update[i].input_csc_color_matrix || + fast_update[i].coeff_reduction_factor) + return true; + } + + return false; +} + +static bool full_update_required(struct dc_surface_update *srf_updates, + int surface_count, + struct dc_stream_update *stream_update, + struct dc_stream_state *stream) +{ + + int i; + struct dc_stream_status *stream_status; + + for (i = 0; i < surface_count; i++) { + if (srf_updates && + (srf_updates[i].plane_info || + srf_updates[i].scaling_info || + (srf_updates[i].hdr_mult.value && + srf_updates[i].hdr_mult.value != srf_updates->surface->hdr_mult.value) || + srf_updates[i].in_transfer_func || + srf_updates[i].func_shaper || + srf_updates[i].lut3d_func || + srf_updates[i].blend_tf)) + return true; + } + + if (stream_update && + (((stream_update->src.height != 0 && stream_update->src.width != 0) || + (stream_update->dst.height != 0 && stream_update->dst.width != 0) || + stream_update->integer_scaling_update) || + stream_update->hdr_static_metadata || + stream_update->abm_level || + stream_update->periodic_interrupt || + stream_update->vrr_infopacket || + stream_update->vsc_infopacket || + stream_update->vsp_infopacket || + stream_update->hfvsif_infopacket || + stream_update->vtem_infopacket || + stream_update->adaptive_sync_infopacket || + stream_update->dpms_off || + stream_update->allow_freesync || + stream_update->vrr_active_variable || + stream_update->vrr_active_fixed || + stream_update->gamut_remap || + stream_update->output_color_space || + stream_update->dither_option || + stream_update->wb_update || + stream_update->dsc_config || + stream_update->mst_bw_update || + stream_update->func_shaper || + stream_update->lut3d_func || + stream_update->pending_test_pattern || + stream_update->crtc_timing_adjust)) + return true; + + if (stream) { + stream_status = dc_stream_get_status(stream); + if (stream_status == NULL || stream_status->plane_count != surface_count) + return true; + } + + return false; +} + +static bool fast_update_only(struct dc_fast_update *fast_update, + struct dc_surface_update *srf_updates, + int surface_count, + struct dc_stream_update *stream_update, + struct dc_stream_state *stream) +{ + return fast_updates_exist(fast_update, surface_count) + && !full_update_required(srf_updates, surface_count, stream_update, stream); +} + bool dc_update_planes_and_stream(struct dc *dc, struct dc_surface_update *srf_updates, int surface_count, struct dc_stream_state *stream, @@ -4242,6 +4363,7 @@ bool dc_update_planes_and_stream(struct dc *dc, enum surface_update_type update_type; int i; struct mall_temp_config mall_temp_config; + struct dc_fast_update fast_update[MAX_SURFACES] = {0}; /* In cases where MPO and split or ODM are used transitions can * cause underflow. Apply stream configuration with minimal pipe @@ -4250,6 +4372,7 @@ bool dc_update_planes_and_stream(struct dc *dc, bool force_minimal_pipe_splitting; bool is_plane_addition; + populate_fast_updates(fast_update, srf_updates, surface_count, stream_update); force_minimal_pipe_splitting = could_mpcc_tree_change_for_active_pipes( dc, stream, @@ -4300,7 +4423,8 @@ bool dc_update_planes_and_stream(struct dc *dc, } update_seamless_boot_flags(dc, context, surface_count, stream); - if (!dc->debug.enable_legacy_fast_update && update_type == UPDATE_TYPE_FAST) { + if (fast_update_only(fast_update, srf_updates, surface_count, stream_update, stream) && + !dc->debug.enable_legacy_fast_update) { commit_planes_for_stream_fast(dc, srf_updates, surface_count, @@ -4357,7 +4481,9 @@ void dc_commit_updates_for_stream(struct dc *dc, struct dc_state *context; struct dc_context *dc_ctx = dc->ctx; int i, j; + struct dc_fast_update fast_update[MAX_SURFACES] = {0}; + populate_fast_updates(fast_update, srf_updates, surface_count, stream_update); stream_status = dc_stream_get_status(stream); context = dc->current_state; @@ -4443,7 +4569,8 @@ void dc_commit_updates_for_stream(struct dc *dc, TRACE_DC_PIPE_STATE(pipe_ctx, i, MAX_PIPES); update_seamless_boot_flags(dc, context, surface_count, stream); - if (!dc->debug.enable_legacy_fast_update && update_type == UPDATE_TYPE_FAST) { + if (fast_update_only(fast_update, srf_updates, surface_count, stream_update, stream) && + !dc->debug.enable_legacy_fast_update) { commit_planes_for_stream_fast(dc, srf_updates, surface_count, @@ -4753,15 +4880,17 @@ static void blank_and_force_memclk(struct dc *dc, bool apply, unsigned int memcl */ void dc_enable_dcmode_clk_limit(struct dc *dc, bool enable) { - uint32_t hw_internal_rev = dc->ctx->asic_id.hw_internal_rev; - unsigned int softMax, maxDPM, funcMin; + unsigned int softMax = 0, maxDPM = 0, funcMin = 0, i; bool p_state_change_support; - if (!ASICREV_IS_BEIGE_GOBY_P(hw_internal_rev)) + if (!dc->config.dc_mode_clk_limit_support) return; softMax = dc->clk_mgr->bw_params->dc_mode_softmax_memclk; - maxDPM = dc->clk_mgr->bw_params->clk_table.entries[dc->clk_mgr->bw_params->clk_table.num_entries - 1].memclk_mhz; + for (i = 0; i < dc->clk_mgr->bw_params->clk_table.num_entries; i++) { + if (dc->clk_mgr->bw_params->clk_table.entries[i].memclk_mhz > maxDPM) + maxDPM = dc->clk_mgr->bw_params->clk_table.entries[i].memclk_mhz; + } funcMin = (dc->clk_mgr->clks.dramclk_khz + 999) / 1000; p_state_change_support = dc->clk_mgr->clks.p_state_change_support; diff --git a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c index d7d00fefaab9..cb2bf9a466f5 100644 --- a/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/core/dc_hw_sequencer.c @@ -610,7 +610,7 @@ void hwss_build_fast_sequence(struct dc *dc, current_mpc_pipe = current_pipe; while (current_mpc_pipe) { - if (!current_mpc_pipe->bottom_pipe && !pipe_ctx->next_odm_pipe && + if (!current_mpc_pipe->bottom_pipe && !current_mpc_pipe->next_odm_pipe && current_mpc_pipe->stream && current_mpc_pipe->plane_state && current_mpc_pipe->plane_state->update_flags.bits.addr_update && !current_mpc_pipe->plane_state->skip_manual_trigger) { diff --git a/drivers/gpu/drm/amd/display/dc/dc.h b/drivers/gpu/drm/amd/display/dc/dc.h index 26d05e225088..63948170fd6d 100644 --- a/drivers/gpu/drm/amd/display/dc/dc.h +++ b/drivers/gpu/drm/amd/display/dc/dc.h @@ -45,7 +45,7 @@ struct aux_payload; struct set_config_cmd_payload; struct dmub_notification; -#define DC_VER "3.2.239" +#define DC_VER "3.2.241" #define MAX_SURFACES 3 #define MAX_PLANES 6 @@ -416,7 +416,7 @@ struct dc_config { uint8_t force_bios_fixed_vs; int sdpif_request_limit_words_per_umc; bool use_old_fixed_vs_sequence; - bool disable_subvp_drr; + bool dc_mode_clk_limit_support; }; enum visual_confirm { @@ -850,6 +850,7 @@ struct dc_debug_options { /* Enable dmub aux for legacy ddc */ bool enable_dmub_aux_for_legacy_ddc; bool disable_fams; + bool disable_fams_gaming; /* FEC/PSR1 sequence enable delay in 100us */ uint8_t fec_enable_delay_in100us; bool enable_driver_sequence_debug; @@ -1264,6 +1265,16 @@ struct dc_scaling_info { struct scaling_taps scaling_quality; }; +struct dc_fast_update { + const struct dc_flip_addrs *flip_addr; + const struct dc_gamma *gamma; + const struct colorspace_transform *gamut_remap_matrix; + const struct dc_csc_transform *input_csc_color_matrix; + const struct fixed31_32 *coeff_reduction_factor; + struct dc_transfer_func *out_transfer_func; + struct dc_csc_transform *output_csc_transform; +}; + struct dc_surface_update { struct dc_plane_state *surface; @@ -1525,6 +1536,7 @@ struct dc_link { bool dpia_forced_tbt3_mode; bool dongle_mode_timing_override; bool blank_stream_on_ocs_change; + bool read_dpcd204h_on_irq_hpd; } wa_flags; struct link_mst_stream_allocation_table mst_stream_alloc_table; diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c index c52c40b16387..c753c6f30dd7 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.c @@ -1011,3 +1011,10 @@ void dc_send_update_cursor_info_to_dmu( dm_execute_dmub_cmd_list(pCtx->stream->ctx, 2, cmd, DM_DMUB_WAIT_TYPE_WAIT); } } + +bool dc_dmub_check_min_version(struct dmub_srv *srv) +{ + if (!srv->hw_funcs.is_psrsu_supported) + return true; + return srv->hw_funcs.is_psrsu_supported(srv); +} diff --git a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h index a5196a9292b3..099f94b6107c 100644 --- a/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h +++ b/drivers/gpu/drm/amd/display/dc/dc_dmub_srv.h @@ -86,4 +86,5 @@ void dc_dmub_setup_subvp_dmub_command(struct dc *dc, struct dc_state *context, b void dc_dmub_srv_log_diagnostic_data(struct dc_dmub_srv *dc_dmub_srv); void dc_send_update_cursor_info_to_dmu(struct pipe_ctx *pCtx, uint8_t pipe_idx); +bool dc_dmub_check_min_version(struct dmub_srv *srv); #endif /* _DMUB_DC_SRV_H_ */ diff --git a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.h b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.h index e6c06325742a..168cb7094c95 100644 --- a/drivers/gpu/drm/amd/display/dc/dce/dce_abm.h +++ b/drivers/gpu/drm/amd/display/dc/dce/dce_abm.h @@ -266,7 +266,24 @@ type MASTER_COMM_INTERRUPT; \ type MASTER_COMM_CMD_REG_BYTE0; \ type MASTER_COMM_CMD_REG_BYTE1; \ - type MASTER_COMM_CMD_REG_BYTE2 + type MASTER_COMM_CMD_REG_BYTE2; \ + type ABM1_HG_BIN_33_40_SHIFT_INDEX; \ + type ABM1_HG_BIN_33_64_SHIFT_FLAG; \ + type ABM1_HG_BIN_41_48_SHIFT_INDEX; \ + type ABM1_HG_BIN_49_56_SHIFT_INDEX; \ + type ABM1_HG_BIN_57_64_SHIFT_INDEX; \ + type ABM1_HG_RESULT_DATA; \ + type ABM1_HG_RESULT_INDEX; \ + type ABM1_ACE_SLOPE_DATA; \ + type ABM1_ACE_OFFSET_DATA; \ + type ABM1_ACE_OFFSET_SLOPE_INDEX; \ + type ABM1_ACE_THRES_INDEX; \ + type ABM1_ACE_IGNORE_MASTER_LOCK_EN; \ + type ABM1_ACE_READBACK_DB_REG_VALUE_EN; \ + type ABM1_ACE_DBUF_REG_UPDATE_PENDING; \ + type ABM1_ACE_LOCK; \ + type ABM1_ACE_THRES_DATA_1; \ + type ABM1_ACE_THRES_DATA_2 struct dce_abm_shift { ABM_REG_FIELD_LIST(uint8_t); @@ -288,6 +305,16 @@ struct dce_abm_registers { uint32_t DC_ABM1_LS_MIN_MAX_PIXEL_VALUE_THRES; uint32_t DC_ABM1_HGLS_REG_READ_PROGRESS; uint32_t DC_ABM1_ACE_OFFSET_SLOPE_0; + uint32_t DC_ABM1_ACE_OFFSET_SLOPE_DATA; + uint32_t DC_ABM1_ACE_PWL_CNTL; + uint32_t DC_ABM1_HG_BIN_33_40_SHIFT_INDEX; + uint32_t DC_ABM1_HG_BIN_33_64_SHIFT_FLAG; + uint32_t DC_ABM1_HG_BIN_41_48_SHIFT_INDEX; + uint32_t DC_ABM1_HG_BIN_49_56_SHIFT_INDEX; + uint32_t DC_ABM1_HG_BIN_57_64_SHIFT_INDEX; + uint32_t DC_ABM1_HG_RESULT_DATA; + uint32_t DC_ABM1_HG_RESULT_INDEX; + uint32_t DC_ABM1_ACE_THRES_DATA; uint32_t DC_ABM1_ACE_THRES_12; uint32_t MASTER_COMM_CNTL_REG; uint32_t MASTER_COMM_CMD_REG; diff --git a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c index 808855886183..e115ff91aaaa 100644 --- a/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dce112/dce112_resource.c @@ -974,10 +974,12 @@ enum dc_status resource_map_phy_clock_resources( || dc_is_virtual_signal(pipe_ctx->stream->signal)) pipe_ctx->clock_source = dc->res_pool->dp_clock_source; - else - pipe_ctx->clock_source = find_matching_pll( - &context->res_ctx, dc->res_pool, - stream); + else { + if (stream && stream->link && stream->link->link_enc) + pipe_ctx->clock_source = find_matching_pll( + &context->res_ctx, dc->res_pool, + stream); + } if (pipe_ctx->clock_source == NULL) return DC_NO_CLOCK_SOURCE_RESOURCE; diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c index 7a00fe525dfb..3538973bd0c6 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.c @@ -308,7 +308,10 @@ bool cm_helper_convert_to_custom_float( #define NUMBER_REGIONS 32 #define NUMBER_SW_SEGMENTS 16 -bool cm_helper_translate_curve_to_hw_format( +#define DC_LOGGER \ + ctx->logger + +bool cm_helper_translate_curve_to_hw_format(struct dc_context *ctx, const struct dc_transfer_func *output_tf, struct pwl_params *lut_params, bool fixpoint) { @@ -482,10 +485,18 @@ bool cm_helper_translate_curve_to_hw_format( rgb->delta_green = dc_fixpt_sub(rgb_plus_1->green, rgb->green); rgb->delta_blue = dc_fixpt_sub(rgb_plus_1->blue, rgb->blue); + if (fixpoint == true) { - rgb->delta_red_reg = dc_fixpt_clamp_u0d10(rgb->delta_red); - rgb->delta_green_reg = dc_fixpt_clamp_u0d10(rgb->delta_green); - rgb->delta_blue_reg = dc_fixpt_clamp_u0d10(rgb->delta_blue); + uint32_t red_clamp = dc_fixpt_clamp_u0d14(rgb->delta_red); + uint32_t green_clamp = dc_fixpt_clamp_u0d14(rgb->delta_green); + uint32_t blue_clamp = dc_fixpt_clamp_u0d14(rgb->delta_blue); + + if (red_clamp >> 10 || green_clamp >> 10 || blue_clamp >> 10) + DC_LOG_WARNING("Losing delta precision while programming shaper LUT."); + + rgb->delta_red_reg = red_clamp & 0x3ff; + rgb->delta_green_reg = green_clamp & 0x3ff; + rgb->delta_blue_reg = blue_clamp & 0x3ff; rgb->red_reg = dc_fixpt_clamp_u0d14(rgb->red); rgb->green_reg = dc_fixpt_clamp_u0d14(rgb->green); rgb->blue_reg = dc_fixpt_clamp_u0d14(rgb->blue); diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.h b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.h index 3b8cd7410498..0a68b63d6126 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.h +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_cm_common.h @@ -106,6 +106,7 @@ bool cm_helper_convert_to_custom_float( bool fixpoint); bool cm_helper_translate_curve_to_hw_format( + struct dc_context *ctx, const struct dc_transfer_func *output_tf, struct pwl_params *lut_params, bool fixpoint); diff --git a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c index 20a1582be0b1..a50309039d08 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c +++ b/drivers/gpu/drm/amd/display/dc/dcn10/dcn10_hw_sequencer.c @@ -1843,7 +1843,7 @@ bool dcn10_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, /* dcn10_translate_regamma_to_hw_format takes 750us, only do it when full * update. */ - else if (cm_helper_translate_curve_to_hw_format( + else if (cm_helper_translate_curve_to_hw_format(dc->ctx, stream->out_transfer_func, &dpp->regamma_params, false)) { dpp->funcs->dpp_program_regamma_pwl( diff --git a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c index eaf9e9ccad2a..4492bc2392b6 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn20/dcn20_hwseq.c @@ -867,7 +867,7 @@ bool dcn20_set_output_transfer_func(struct dc *dc, struct pipe_ctx *pipe_ctx, params = &stream->out_transfer_func->pwl; else if (pipe_ctx->stream->out_transfer_func->type == TF_TYPE_DISTRIBUTED_POINTS && - cm_helper_translate_curve_to_hw_format( + cm_helper_translate_curve_to_hw_format(dc->ctx, stream->out_transfer_func, &mpc->blender_params, false)) params = &mpc->blender_params; @@ -896,7 +896,7 @@ bool dcn20_set_blend_lut( if (plane_state->blend_tf->type == TF_TYPE_HWPWL) blend_lut = &plane_state->blend_tf->pwl; else if (plane_state->blend_tf->type == TF_TYPE_DISTRIBUTED_POINTS) { - cm_helper_translate_curve_to_hw_format( + cm_helper_translate_curve_to_hw_format(plane_state->ctx, plane_state->blend_tf, &dpp_base->regamma_params, false); blend_lut = &dpp_base->regamma_params; @@ -918,7 +918,7 @@ bool dcn20_set_shaper_3dlut( if (plane_state->in_shaper_func->type == TF_TYPE_HWPWL) shaper_lut = &plane_state->in_shaper_func->pwl; else if (plane_state->in_shaper_func->type == TF_TYPE_DISTRIBUTED_POINTS) { - cm_helper_translate_curve_to_hw_format( + cm_helper_translate_curve_to_hw_format(plane_state->ctx, plane_state->in_shaper_func, &dpp_base->shaper_params, true); shaper_lut = &dpp_base->shaper_params; @@ -1764,8 +1764,9 @@ static void dcn20_program_pipe( hws->funcs.set_hdr_multiplier(pipe_ctx); if (pipe_ctx->update_flags.bits.enable || - pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || - pipe_ctx->plane_state->update_flags.bits.gamma_change) + pipe_ctx->plane_state->update_flags.bits.in_transfer_func_change || + pipe_ctx->plane_state->update_flags.bits.gamma_change || + pipe_ctx->plane_state->update_flags.bits.lut_3d) hws->funcs.set_input_transfer_func(dc, pipe_ctx, pipe_ctx->plane_state); /* dcn10_translate_regamma_to_hw_format takes 750us to finish diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dwb_cm.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dwb_cm.c index 6a3d3a0ec0a3..701c7d8bc038 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dwb_cm.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_dwb_cm.c @@ -280,7 +280,7 @@ bool dwb3_ogam_set_input_transfer_func( dwb_ogam_lut = kzalloc(sizeof(*dwb_ogam_lut), GFP_KERNEL); if (dwb_ogam_lut) { - cm_helper_translate_curve_to_hw_format( + cm_helper_translate_curve_to_hw_format(dwbc->ctx, in_transfer_func_dwb_ogam, dwb_ogam_lut, false); diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c index b9753867d97b..bf8864bc8a99 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_hwseq.c @@ -106,7 +106,7 @@ static bool dcn30_set_mpc_shaper_3dlut(struct pipe_ctx *pipe_ctx, if (stream->func_shaper->type == TF_TYPE_HWPWL) { shaper_lut = &stream->func_shaper->pwl; } else if (stream->func_shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { - cm_helper_translate_curve_to_hw_format(stream->func_shaper, + cm_helper_translate_curve_to_hw_format(stream->ctx, stream->func_shaper, &dpp_base->shaper_params, true); shaper_lut = &dpp_base->shaper_params; } diff --git a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c index 1a0284a068b2..abe4c12a10b5 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn30/dcn30_resource.c @@ -725,7 +725,8 @@ static const struct dc_debug_options debug_defaults_drv = { .dwb_fi_phase = -1, // -1 = disable, .dmub_command_table = true, .use_max_lb = true, - .exit_idle_opt_for_cursor_updates = true + .exit_idle_opt_for_cursor_updates = true, + .enable_legacy_fast_update = false, }; static const struct dc_panel_config panel_config_defaults = { @@ -1986,11 +1987,10 @@ bool dcn30_can_support_mclk_switch_using_fw_based_vblank_stretch(struct dc *dc, if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(context)) return false; - // check if freesync enabled if (!context->streams[0]->allow_freesync) return false; - if (context->streams[0]->vrr_active_variable) + if (context->streams[0]->vrr_active_variable && dc->debug.disable_fams_gaming) return false; context->streams[0]->fpo_in_use = true; diff --git a/drivers/gpu/drm/amd/display/dc/dcn302/dcn302_resource.c b/drivers/gpu/drm/amd/display/dc/dcn302/dcn302_resource.c index 7dc065ea247a..5ad6a22ee47d 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn302/dcn302_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn302/dcn302_resource.c @@ -95,7 +95,8 @@ static const struct dc_debug_options debug_defaults_drv = { .dwb_fi_phase = -1, // -1 = disable, .dmub_command_table = true, .use_max_lb = true, - .exit_idle_opt_for_cursor_updates = true + .exit_idle_opt_for_cursor_updates = true, + .enable_legacy_fast_update = false, }; static const struct dc_panel_config panel_config_defaults = { diff --git a/drivers/gpu/drm/amd/display/dc/dcn303/dcn303_resource.c b/drivers/gpu/drm/amd/display/dc/dcn303/dcn303_resource.c index 6d9761395288..45956ef6f3f9 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn303/dcn303_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn303/dcn303_resource.c @@ -1190,6 +1190,7 @@ static bool dcn303_resource_construct( dc->caps.dp_hdmi21_pcon_support = true; + dc->config.dc_mode_clk_limit_support = true; /* read VBIOS LTTPR caps */ if (ctx->dc_bios->funcs->get_lttpr_caps) { enum bp_result bp_query_result; diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_dccg.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_dccg.c index cf23d7bc560a..0746ed31d1d1 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_dccg.c +++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_dccg.c @@ -332,7 +332,7 @@ static void dccg314_dpp_root_clock_control( { struct dcn_dccg *dccg_dcn = TO_DCN_DCCG(dccg); - if (dccg->dpp_clock_gated[dpp_inst] == clock_on) + if (dccg->dpp_clock_gated[dpp_inst] != clock_on) return; if (clock_on) { diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.c index 7a43f8868500..4d2820ffe468 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.c @@ -337,13 +337,14 @@ void dcn314_enable_power_gating_plane(struct dce_hwseq *hws, bool enable) REG_SET(DC_IP_REQUEST_CNTL, 0, IP_REQUEST_EN, 0); } -void dcn314_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div) +unsigned int dcn314_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div) { struct dc_stream_state *stream = pipe_ctx->stream; + unsigned int odm_combine_factor = 0; bool two_pix_per_container = false; two_pix_per_container = optc2_is_two_pixels_per_containter(&stream->timing); - get_odm_config(pipe_ctx, NULL); + odm_combine_factor = get_odm_config(pipe_ctx, NULL); if (stream->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { *k1_div = PIXEL_RATE_DIV_BY_1; @@ -361,11 +362,15 @@ void dcn314_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int } else { *k1_div = PIXEL_RATE_DIV_BY_1; *k2_div = PIXEL_RATE_DIV_BY_4; + if (odm_combine_factor == 2) + *k2_div = PIXEL_RATE_DIV_BY_2; } } if ((*k1_div == PIXEL_RATE_DIV_NA) && (*k2_div == PIXEL_RATE_DIV_NA)) ASSERT(false); + + return odm_combine_factor; } void dcn314_set_pixels_per_cycle(struct pipe_ctx *pipe_ctx) @@ -424,27 +429,6 @@ void dcn314_dpp_root_clock_control(struct dce_hwseq *hws, unsigned int dpp_inst, hws->ctx->dc->res_pool->dccg, dpp_inst, clock_on); } -void dcn314_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on) -{ - struct dc_context *ctx = hws->ctx; - union dmub_rb_cmd cmd; - - if (hws->ctx->dc->debug.disable_hubp_power_gate) - return; - - PERF_TRACE(); - - memset(&cmd, 0, sizeof(cmd)); - cmd.domain_control.header.type = DMUB_CMD__VBIOS; - cmd.domain_control.header.sub_type = DMUB_CMD__VBIOS_DOMAIN_CONTROL; - cmd.domain_control.header.payload_bytes = sizeof(cmd.domain_control.data); - cmd.domain_control.data.inst = hubp_inst; - cmd.domain_control.data.power_gate = !power_on; - - dm_execute_dmub_cmd(ctx, &cmd, DM_DMUB_WAIT_TYPE_WAIT); - - PERF_TRACE(); -} static void apply_symclk_on_tx_off_wa(struct dc_link *link) { /* There are use cases where SYMCLK is referenced by OTG. For instance diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.h b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.h index 96035c75e0df..eafcc4ea6d24 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_hwseq.h @@ -37,14 +37,12 @@ void dcn314_dsc_pg_control(struct dce_hwseq *hws, unsigned int dsc_inst, bool po void dcn314_enable_power_gating_plane(struct dce_hwseq *hws, bool enable); -void dcn314_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div); +unsigned int dcn314_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div); void dcn314_set_pixels_per_cycle(struct pipe_ctx *pipe_ctx); void dcn314_resync_fifo_dccg_dio(struct dce_hwseq *hws, struct dc *dc, struct dc_state *context); -void dcn314_hubp_pg_control(struct dce_hwseq *hws, unsigned int hubp_inst, bool power_on); - void dcn314_dpp_root_clock_control(struct dce_hwseq *hws, unsigned int dpp_inst, bool clock_on); void dcn314_disable_link_output(struct dc_link *link, const struct link_resource *link_res, enum signal_type signal); diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.c index 86d6a514dec0..ca8fe55c33b8 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.c +++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_init.c @@ -139,7 +139,7 @@ static const struct hwseq_private_funcs dcn314_private_funcs = { .plane_atomic_power_down = dcn10_plane_atomic_power_down, .enable_power_gating_plane = dcn314_enable_power_gating_plane, .dpp_root_clock_control = dcn314_dpp_root_clock_control, - .hubp_pg_control = dcn314_hubp_pg_control, + .hubp_pg_control = dcn31_hubp_pg_control, .program_all_writeback_pipes_in_tree = dcn30_program_all_writeback_pipes_in_tree, .update_odm = dcn314_update_odm, .dsc_pg_control = dcn314_dsc_pg_control, diff --git a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c index a840b008d660..6a9024aa3285 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn314/dcn314_resource.c @@ -1883,13 +1883,6 @@ static bool dcn314_resource_construct( /* Use pipe context based otg sync logic */ dc->config.use_pipe_ctx_sync_logic = true; - /* Disable pipe power gating when unsupported */ - if (ctx->asic_id.hw_internal_rev == 0x01 || - ctx->asic_id.hw_internal_rev == 0x80) { - dc->debug.disable_dpp_power_gate = true; - dc->debug.disable_hubp_power_gate = true; - } - /* read VBIOS LTTPR caps */ { if (ctx->dc_bios->funcs->get_lttpr_caps) { @@ -1910,6 +1903,14 @@ static bool dcn314_resource_construct( dc->debug = debug_defaults_drv; else dc->debug = debug_defaults_diags; + + /* Disable pipe power gating */ + dc->debug.disable_dpp_power_gate = true; + dc->debug.disable_hubp_power_gate = true; + + /* Disable root clock optimization */ + dc->debug.root_clock_optimization.u32All = 0; + // Init the vm_helper if (dc->vm_helper) vm_helper_init(dc->vm_helper, 16); diff --git a/drivers/gpu/drm/amd/display/dc/dcn315/dcn315_resource.c b/drivers/gpu/drm/amd/display/dc/dcn315/dcn315_resource.c index f1153941907e..df3a438abda8 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn315/dcn315_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn315/dcn315_resource.c @@ -1610,7 +1610,7 @@ static int source_format_to_bpp (enum source_format_class SourcePixelFormat) { if (SourcePixelFormat == dm_444_64) return 8; - else if (SourcePixelFormat == dm_444_16 || SourcePixelFormat == dm_444_16) + else if (SourcePixelFormat == dm_444_16) return 2; else if (SourcePixelFormat == dm_444_8) return 1; diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubp.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubp.c index 2d604f7ee782..ca5b4b28a664 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubp.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hubp.c @@ -179,6 +179,7 @@ static struct hubp_funcs dcn32_hubp_funcs = { .hubp_setup_interdependent = hubp2_setup_interdependent, .hubp_set_vm_system_aperture_settings = hubp3_set_vm_system_aperture_settings, .set_blank = hubp2_set_blank, + .set_blank_regs = hubp2_set_blank_regs, .dcc_control = hubp3_dcc_control, .mem_program_viewport = min_set_viewport, .set_cursor_attributes = hubp32_cursor_set_attributes, diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c index c586468872e2..d52d5feeb311 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.c @@ -448,7 +448,7 @@ bool dcn32_set_mpc_shaper_3dlut( if (stream->func_shaper->type == TF_TYPE_HWPWL) shaper_lut = &stream->func_shaper->pwl; else if (stream->func_shaper->type == TF_TYPE_DISTRIBUTED_POINTS) { - cm_helper_translate_curve_to_hw_format( + cm_helper_translate_curve_to_hw_format(stream->ctx, stream->func_shaper, &dpp_base->shaper_params, true); shaper_lut = &dpp_base->shaper_params; @@ -484,7 +484,7 @@ bool dcn32_set_mcm_luts( if (plane_state->blend_tf->type == TF_TYPE_HWPWL) lut_params = &plane_state->blend_tf->pwl; else if (plane_state->blend_tf->type == TF_TYPE_DISTRIBUTED_POINTS) { - cm_helper_translate_curve_to_hw_format( + cm_helper_translate_curve_to_hw_format(plane_state->ctx, plane_state->blend_tf, &dpp_base->regamma_params, false); lut_params = &dpp_base->regamma_params; @@ -499,7 +499,7 @@ bool dcn32_set_mcm_luts( else if (plane_state->in_shaper_func->type == TF_TYPE_DISTRIBUTED_POINTS) { // TODO: dpp_base replace ASSERT(false); - cm_helper_translate_curve_to_hw_format( + cm_helper_translate_curve_to_hw_format(plane_state->ctx, plane_state->in_shaper_func, &dpp_base->shaper_params, true); lut_params = &dpp_base->shaper_params; @@ -1141,16 +1141,14 @@ void dcn32_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx * } } -void dcn32_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div) +unsigned int dcn32_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div) { struct dc_stream_state *stream = pipe_ctx->stream; + unsigned int odm_combine_factor = 0; bool two_pix_per_container = false; - // For phantom pipes, use the same programming as the main pipes - if (pipe_ctx->stream->mall_stream_config.type == SUBVP_PHANTOM) { - stream = pipe_ctx->stream->mall_stream_config.paired_stream; - } two_pix_per_container = optc2_is_two_pixels_per_containter(&stream->timing); + odm_combine_factor = get_odm_config(pipe_ctx, NULL); if (stream->ctx->dc->link_srv->dp_is_128b_132b_signal(pipe_ctx)) { *k1_div = PIXEL_RATE_DIV_BY_1; @@ -1168,13 +1166,15 @@ void dcn32_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int * } else { *k1_div = PIXEL_RATE_DIV_BY_1; *k2_div = PIXEL_RATE_DIV_BY_4; - if (dcn32_is_dp_dig_pixel_rate_div_policy(pipe_ctx)) + if ((odm_combine_factor == 2) || dcn32_is_dp_dig_pixel_rate_div_policy(pipe_ctx)) *k2_div = PIXEL_RATE_DIV_BY_2; } } if ((*k1_div == PIXEL_RATE_DIV_NA) && (*k2_div == PIXEL_RATE_DIV_NA)) ASSERT(false); + + return odm_combine_factor; } void dcn32_set_pixels_per_cycle(struct pipe_ctx *pipe_ctx) diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.h b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.h index bf9bffabe0c0..2d2628f31bed 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.h +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_hwseq.h @@ -71,7 +71,7 @@ void dcn32_update_force_pstate(struct dc *dc, struct dc_state *context); void dcn32_update_odm(struct dc *dc, struct dc_state *context, struct pipe_ctx *pipe_ctx); -void dcn32_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div); +unsigned int dcn32_calculate_dccg_k1_k2_values(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div); void dcn32_set_pixels_per_cycle(struct pipe_ctx *pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c index c2490e16a66a..777b2fac20c4 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_init.c @@ -56,6 +56,7 @@ static const struct hw_sequencer_funcs dcn32_funcs = { .enable_audio_stream = dce110_enable_audio_stream, .disable_audio_stream = dce110_disable_audio_stream, .disable_plane = dcn20_disable_plane, + .disable_pixel_data = dcn20_disable_pixel_data, .pipe_control_lock = dcn20_pipe_control_lock, .interdependent_update_lock = dcn10_lock_all_pipes, .cursor_lock = dcn10_cursor_lock, diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c index 19f134caa8ad..1cc09799f92d 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource.c @@ -732,6 +732,7 @@ static const struct dc_debug_options debug_defaults_drv = { .disable_dp_plus_plus_wa = true, .fpo_vactive_min_active_margin_us = 200, .fpo_vactive_max_blank_us = 1000, + .enable_legacy_fast_update = false, }; static struct dce_aux *dcn32_aux_engine_create( @@ -2214,6 +2215,7 @@ static bool dcn32_resource_construct( /* Use pipe context based otg sync logic */ dc->config.use_pipe_ctx_sync_logic = true; + dc->config.dc_mode_clk_limit_support = true; /* read VBIOS LTTPR caps */ { if (ctx->dc_bios->funcs->get_lttpr_caps) { diff --git a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c index a9c41ef0751f..5be242a1b82c 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c +++ b/drivers/gpu/drm/amd/display/dc/dcn32/dcn32_resource_helpers.c @@ -595,11 +595,10 @@ struct dc_stream_state *dcn32_can_support_mclk_switch_using_fw_based_vblank_stre if (!is_refresh_rate_support_mclk_switch_using_fw_based_vblank_stretch(fpo_candidate_stream, fpo_vactive_margin_us)) return NULL; - // check if freesync enabled if (!fpo_candidate_stream->allow_freesync) return NULL; - if (fpo_candidate_stream->vrr_active_variable) + if (fpo_candidate_stream->vrr_active_variable && dc->debug.disable_fams_gaming) return NULL; return fpo_candidate_stream; diff --git a/drivers/gpu/drm/amd/display/dc/dcn321/dcn321_resource.c b/drivers/gpu/drm/amd/display/dc/dcn321/dcn321_resource.c index ea204742ad35..a53478e15ce3 100644 --- a/drivers/gpu/drm/amd/display/dc/dcn321/dcn321_resource.c +++ b/drivers/gpu/drm/amd/display/dc/dcn321/dcn321_resource.c @@ -730,6 +730,8 @@ static const struct dc_debug_options debug_defaults_drv = { .disable_subvp_high_refresh = false, .fpo_vactive_min_active_margin_us = 200, .fpo_vactive_max_blank_us = 1000, + .enable_legacy_fast_update = false, + .disable_dc_mode_overwrite = true, }; static struct dce_aux *dcn321_aux_engine_create( @@ -1754,6 +1756,7 @@ static bool dcn321_resource_construct( dc->caps.color.mpc.ogam_rom_caps.hlg = 0; dc->caps.color.mpc.ocsc = 1; + dc->config.dc_mode_clk_limit_support = true; /* read VBIOS LTTPR caps */ { if (ctx->dc_bios->funcs->get_lttpr_caps) { diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20.c b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20.c index 6266b0788387..7bf4bb7ad044 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn20/display_mode_vba_20.c @@ -4356,12 +4356,16 @@ void dml20_ModeSupportAndSystemConfigurationFull(struct display_mode_lib *mode_l locals->PSCL_FACTOR[k] / locals->ReturnBWPerState[i][0], locals->EffectiveLBLatencyHidingSourceLinesLuma), locals->SwathHeightYPerState[i][j][k]); - - locals->EffectiveDETLBLinesChroma = dml_floor(locals->LinesInDETChroma + dml_min( - locals->LinesInDETChroma * locals->RequiredDISPCLK[i][j] * locals->BytePerPixelInDETC[k] * - locals->PSCL_FACTOR_CHROMA[k] / locals->ReturnBWPerState[i][0], - locals->EffectiveLBLatencyHidingSourceLinesChroma), - locals->SwathHeightCPerState[i][j][k]); + if (locals->LinesInDETChroma) { + locals->EffectiveDETLBLinesChroma = dml_floor(locals->LinesInDETChroma + + dml_min(locals->LinesInDETChroma * locals->RequiredDISPCLK[i][j] * + locals->BytePerPixelInDETC[k] * + locals->PSCL_FACTOR_CHROMA[k] / locals->ReturnBWPerState[i][0], + locals->EffectiveLBLatencyHidingSourceLinesChroma), + locals->SwathHeightCPerState[i][j][k]); + } else { + locals->EffectiveDETLBLinesChroma = 0; + } if (locals->BytePerPixelInDETC[k] == 0) { locals->UrgentLatencySupportUsPerState[i][j][k] = locals->EffectiveDETLBLinesLuma * (locals->HTotal[k] / locals->PixelClock[k]) diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c index c9afddd11589..d9e049e7ff0a 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn314/dcn314_fpu.c @@ -33,7 +33,7 @@ #include "dml/display_mode_vba.h" struct _vcs_dpi_ip_params_st dcn3_14_ip = { - .VBlankNomDefaultUS = 800, + .VBlankNomDefaultUS = 668, .gpuvm_enable = 1, .gpuvm_max_page_table_levels = 1, .hostvm_enable = 1, diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c index e2bb2b9971f3..a95034801712 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.c @@ -485,24 +485,20 @@ static void get_optimal_ntuple(struct _vcs_dpi_voltage_scaling_st *entry) } } -void insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table, +static void insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries, struct _vcs_dpi_voltage_scaling_st *entry) { int i = 0; int index = 0; - float net_bw_of_new_state = 0; dc_assert_fp_enabled(); - get_optimal_ntuple(entry); - if (*num_entries == 0) { table[0] = *entry; (*num_entries)++; } else { - net_bw_of_new_state = calculate_net_bw_in_kbytes_sec(entry); - while (net_bw_of_new_state > calculate_net_bw_in_kbytes_sec(&table[index])) { + while (entry->net_bw_in_kbytes_sec > table[index].net_bw_in_kbytes_sec) { index++; if (index >= *num_entries) break; @@ -2349,6 +2345,63 @@ void dcn32_patch_dpm_table(struct clk_bw_params *bw_params) bw_params->clk_table.entries[0].memclk_mhz = dcn3_2_soc.clock_limits[0].dram_speed_mts / 16; } +static void swap_table_entries(struct _vcs_dpi_voltage_scaling_st *first_entry, + struct _vcs_dpi_voltage_scaling_st *second_entry) +{ + struct _vcs_dpi_voltage_scaling_st temp_entry = *first_entry; + *first_entry = *second_entry; + *second_entry = temp_entry; +} + +/* + * sort_entries_with_same_bw - Sort entries sharing the same bandwidth by DCFCLK + */ +static void sort_entries_with_same_bw(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries) +{ + unsigned int start_index = 0; + unsigned int end_index = 0; + unsigned int current_bw = 0; + + for (int i = 0; i < (*num_entries - 1); i++) { + if (table[i].net_bw_in_kbytes_sec == table[i+1].net_bw_in_kbytes_sec) { + current_bw = table[i].net_bw_in_kbytes_sec; + start_index = i; + end_index = ++i; + + while ((i < (*num_entries - 1)) && (table[i+1].net_bw_in_kbytes_sec == current_bw)) + end_index = ++i; + } + + if (start_index != end_index) { + for (int j = start_index; j < end_index; j++) { + for (int k = start_index; k < end_index; k++) { + if (table[k].dcfclk_mhz > table[k+1].dcfclk_mhz) + swap_table_entries(&table[k], &table[k+1]); + } + } + } + + start_index = 0; + end_index = 0; + + } +} + +/* + * remove_inconsistent_entries - Ensure entries with the same bandwidth have MEMCLK and FCLK monotonically increasing + * and remove entries that do not + */ +static void remove_inconsistent_entries(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries) +{ + for (int i = 0; i < (*num_entries - 1); i++) { + if (table[i].net_bw_in_kbytes_sec == table[i+1].net_bw_in_kbytes_sec) { + if ((table[i].dram_speed_mts > table[i+1].dram_speed_mts) || + (table[i].fabricclk_mhz > table[i+1].fabricclk_mhz)) + remove_entry_from_table_at_index(table, num_entries, i); + } + } +} + /* * override_max_clk_values - Overwrite the max clock frequencies with the max DC mode timings * Input: @@ -2480,6 +2533,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = 0; entry.dram_speed_mts = 0; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); insert_entry_into_table_sorted(table, num_entries, &entry); } @@ -2488,6 +2543,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = 0; entry.dram_speed_mts = 0; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); insert_entry_into_table_sorted(table, num_entries, &entry); // Insert the UCLK DPMS @@ -2496,6 +2553,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = 0; entry.dram_speed_mts = bw_params->clk_table.entries[i].memclk_mhz * 16; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); insert_entry_into_table_sorted(table, num_entries, &entry); } @@ -2506,6 +2565,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = bw_params->clk_table.entries[i].fclk_mhz; entry.dram_speed_mts = 0; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); insert_entry_into_table_sorted(table, num_entries, &entry); } } @@ -2515,6 +2576,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = max_clk_data.fclk_mhz; entry.dram_speed_mts = 0; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); insert_entry_into_table_sorted(table, num_entries, &entry); } @@ -2530,6 +2593,21 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk remove_entry_from_table_at_index(table, num_entries, i); } + // Insert entry with all max dc limits without bandwidth matching + if (!disable_dc_mode_overwrite) { + struct _vcs_dpi_voltage_scaling_st max_dc_limits_entry = entry; + + max_dc_limits_entry.dcfclk_mhz = max_clk_data.dcfclk_mhz; + max_dc_limits_entry.fabricclk_mhz = max_clk_data.fclk_mhz; + max_dc_limits_entry.dram_speed_mts = max_clk_data.memclk_mhz * 16; + + max_dc_limits_entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&max_dc_limits_entry); + insert_entry_into_table_sorted(table, num_entries, &max_dc_limits_entry); + + sort_entries_with_same_bw(table, num_entries); + remove_inconsistent_entries(table, num_entries); + } + // At this point, the table only contains supported points of interest // it could be used as is, but some states may be redundant due to // coarse grained nature of some clocks, so we want to round up to diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h index a4206b71d650..defbee866be6 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn32/dcn32_fpu.h @@ -39,10 +39,6 @@ void dcn32_helper_populate_phantom_dlg_params(struct dc *dc, uint8_t dcn32_predict_pipe_split(struct dc_state *context, display_e2e_pipe_params_st *pipe_e2e); -void insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table, - unsigned int *num_entries, - struct _vcs_dpi_voltage_scaling_st *entry); - void dcn32_set_phantom_stream_timing(struct dc *dc, struct dc_state *context, struct pipe_ctx *ref_pipe, diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.c b/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.c index f0683fd9d3f0..b26fcf86014c 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.c +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.c @@ -207,24 +207,20 @@ static float calculate_net_bw_in_kbytes_sec(struct _vcs_dpi_voltage_scaling_st * return limiting_bw_kbytes_sec; } -void dcn321_insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table, +static void dcn321_insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries, struct _vcs_dpi_voltage_scaling_st *entry) { int i = 0; int index = 0; - float net_bw_of_new_state = 0; dc_assert_fp_enabled(); - get_optimal_ntuple(entry); - if (*num_entries == 0) { table[0] = *entry; (*num_entries)++; } else { - net_bw_of_new_state = calculate_net_bw_in_kbytes_sec(entry); - while (net_bw_of_new_state > calculate_net_bw_in_kbytes_sec(&table[index])) { + while (entry->net_bw_in_kbytes_sec > table[index].net_bw_in_kbytes_sec) { index++; if (index >= *num_entries) break; @@ -252,6 +248,63 @@ static void remove_entry_from_table_at_index(struct _vcs_dpi_voltage_scaling_st memset(&table[--(*num_entries)], 0, sizeof(struct _vcs_dpi_voltage_scaling_st)); } +static void swap_table_entries(struct _vcs_dpi_voltage_scaling_st *first_entry, + struct _vcs_dpi_voltage_scaling_st *second_entry) +{ + struct _vcs_dpi_voltage_scaling_st temp_entry = *first_entry; + *first_entry = *second_entry; + *second_entry = temp_entry; +} + +/* + * sort_entries_with_same_bw - Sort entries sharing the same bandwidth by DCFCLK + */ +static void sort_entries_with_same_bw(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries) +{ + unsigned int start_index = 0; + unsigned int end_index = 0; + unsigned int current_bw = 0; + + for (int i = 0; i < (*num_entries - 1); i++) { + if (table[i].net_bw_in_kbytes_sec == table[i+1].net_bw_in_kbytes_sec) { + current_bw = table[i].net_bw_in_kbytes_sec; + start_index = i; + end_index = ++i; + + while ((i < (*num_entries - 1)) && (table[i+1].net_bw_in_kbytes_sec == current_bw)) + end_index = ++i; + } + + if (start_index != end_index) { + for (int j = start_index; j < end_index; j++) { + for (int k = start_index; k < end_index; k++) { + if (table[k].dcfclk_mhz > table[k+1].dcfclk_mhz) + swap_table_entries(&table[k], &table[k+1]); + } + } + } + + start_index = 0; + end_index = 0; + + } +} + +/* + * remove_inconsistent_entries - Ensure entries with the same bandwidth have MEMCLK and FCLK monotonically increasing + * and remove entries that do not follow this order + */ +static void remove_inconsistent_entries(struct _vcs_dpi_voltage_scaling_st *table, unsigned int *num_entries) +{ + for (int i = 0; i < (*num_entries - 1); i++) { + if (table[i].net_bw_in_kbytes_sec == table[i+1].net_bw_in_kbytes_sec) { + if ((table[i].dram_speed_mts > table[i+1].dram_speed_mts) || + (table[i].fabricclk_mhz > table[i+1].fabricclk_mhz)) + remove_entry_from_table_at_index(table, num_entries, i); + } + } +} + /* * override_max_clk_values - Overwrite the max clock frequencies with the max DC mode timings * Input: @@ -362,11 +415,11 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk if (max_clk_data.fclk_mhz == 0) max_clk_data.fclk_mhz = max_clk_data.dcfclk_mhz * - dcn3_2_soc.pct_ideal_sdp_bw_after_urgent / - dcn3_2_soc.pct_ideal_fabric_bw_after_urgent; + dcn3_21_soc.pct_ideal_sdp_bw_after_urgent / + dcn3_21_soc.pct_ideal_fabric_bw_after_urgent; if (max_clk_data.phyclk_mhz == 0) - max_clk_data.phyclk_mhz = dcn3_2_soc.clock_limits[0].phyclk_mhz; + max_clk_data.phyclk_mhz = dcn3_21_soc.clock_limits[0].phyclk_mhz; *num_entries = 0; entry.dispclk_mhz = max_clk_data.dispclk_mhz; @@ -374,8 +427,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.dppclk_mhz = max_clk_data.dppclk_mhz; entry.dtbclk_mhz = max_clk_data.dtbclk_mhz; entry.phyclk_mhz = max_clk_data.phyclk_mhz; - entry.phyclk_d18_mhz = dcn3_2_soc.clock_limits[0].phyclk_d18_mhz; - entry.phyclk_d32_mhz = dcn3_2_soc.clock_limits[0].phyclk_d32_mhz; + entry.phyclk_d18_mhz = dcn3_21_soc.clock_limits[0].phyclk_d18_mhz; + entry.phyclk_d32_mhz = dcn3_21_soc.clock_limits[0].phyclk_d32_mhz; // Insert all the DCFCLK STAs for (i = 0; i < num_dcfclk_stas; i++) { @@ -383,6 +436,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = 0; entry.dram_speed_mts = 0; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); dcn321_insert_entry_into_table_sorted(table, num_entries, &entry); } @@ -391,6 +446,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = 0; entry.dram_speed_mts = 0; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); dcn321_insert_entry_into_table_sorted(table, num_entries, &entry); // Insert the UCLK DPMS @@ -399,6 +456,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = 0; entry.dram_speed_mts = bw_params->clk_table.entries[i].memclk_mhz * 16; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); dcn321_insert_entry_into_table_sorted(table, num_entries, &entry); } @@ -409,6 +468,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = bw_params->clk_table.entries[i].fclk_mhz; entry.dram_speed_mts = 0; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); dcn321_insert_entry_into_table_sorted(table, num_entries, &entry); } } @@ -418,6 +479,8 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk entry.fabricclk_mhz = max_clk_data.fclk_mhz; entry.dram_speed_mts = 0; + get_optimal_ntuple(&entry); + entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&entry); dcn321_insert_entry_into_table_sorted(table, num_entries, &entry); } @@ -433,6 +496,23 @@ static int build_synthetic_soc_states(bool disable_dc_mode_overwrite, struct clk remove_entry_from_table_at_index(table, num_entries, i); } + // Insert entry with all max dc limits without bandwitch matching + if (!disable_dc_mode_overwrite) { + struct _vcs_dpi_voltage_scaling_st max_dc_limits_entry = entry; + + max_dc_limits_entry.dcfclk_mhz = max_clk_data.dcfclk_mhz; + max_dc_limits_entry.fabricclk_mhz = max_clk_data.fclk_mhz; + max_dc_limits_entry.dram_speed_mts = max_clk_data.memclk_mhz * 16; + + max_dc_limits_entry.net_bw_in_kbytes_sec = calculate_net_bw_in_kbytes_sec(&max_dc_limits_entry); + dcn321_insert_entry_into_table_sorted(table, num_entries, &max_dc_limits_entry); + + sort_entries_with_same_bw(table, num_entries); + remove_inconsistent_entries(table, num_entries); + } + + + // At this point, the table only contains supported points of interest // it could be used as is, but some states may be redundant due to // coarse grained nature of some clocks, so we want to round up to diff --git a/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.h b/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.h index e8fad9b4be69..c6623b3705ca 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.h +++ b/drivers/gpu/drm/amd/display/dc/dml/dcn321/dcn321_fpu.h @@ -29,10 +29,6 @@ #include "dml/display_mode_vba.h" -void dcn321_insert_entry_into_table_sorted(struct _vcs_dpi_voltage_scaling_st *table, - unsigned int *num_entries, - struct _vcs_dpi_voltage_scaling_st *entry); - void dcn321_update_bw_bounding_box_fpu(struct dc *dc, struct clk_bw_params *bw_params); #endif diff --git a/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h b/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h index ff0246a9458f..fb17f8868cb4 100644 --- a/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h +++ b/drivers/gpu/drm/amd/display/dc/dml/display_mode_structs.h @@ -167,6 +167,7 @@ struct _vcs_dpi_voltage_scaling_st { double phyclk_mhz; double dppclk_mhz; double dtbclk_mhz; + float net_bw_in_kbytes_sec; }; /** diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h index 6faf40fa5c69..ecb7bcc39469 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw/clk_mgr.h @@ -230,6 +230,7 @@ struct clk_bw_params { unsigned int dram_channel_width_bytes; unsigned int dispclk_vco_khz; unsigned int dc_mode_softmax_memclk; + unsigned int max_memclk_mhz; struct clk_limit_table clk_table; struct wm_table wm_table; struct dummy_pstate_entry dummy_pstate_table[4]; diff --git a/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer_private.h b/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer_private.h index a151865a3a20..4ca4192c1e12 100644 --- a/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer_private.h +++ b/drivers/gpu/drm/amd/display/dc/inc/hw_sequencer_private.h @@ -156,7 +156,7 @@ struct hwseq_private_funcs { void (*program_mall_pipe_config)(struct dc *dc, struct dc_state *context); void (*update_force_pstate)(struct dc *dc, struct dc_state *context); void (*update_mall_sel)(struct dc *dc, struct dc_state *context); - void (*calculate_dccg_k1_k2_values)(struct pipe_ctx *pipe_ctx, + unsigned int (*calculate_dccg_k1_k2_values)(struct pipe_ctx *pipe_ctx, unsigned int *k1_div, unsigned int *k2_div); void (*set_pixels_per_cycle)(struct pipe_ctx *pipe_ctx); diff --git a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c index ba95facc4ee8..ef8739df91bc 100644 --- a/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c +++ b/drivers/gpu/drm/amd/display/dc/link/protocols/link_dp_irq_handler.c @@ -82,8 +82,15 @@ bool dp_parse_link_loss_status( } /* Check interlane align.*/ - if (sink_status_changed || - !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) { + if (link_dp_get_encoding_format(&link->cur_link_settings) == DP_128b_132b_ENCODING && + (!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b || + !hpd_irq_dpcd_data->bytes.lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b)) { + sink_status_changed = true; + } else if (!hpd_irq_dpcd_data->bytes.lane_status_updated.bits.INTERLANE_ALIGN_DONE) { + sink_status_changed = true; + } + + if (sink_status_changed) { DC_LOG_HW_HPD_IRQ("%s: Link Status changed.\n", __func__); @@ -201,6 +208,25 @@ void dp_handle_link_loss(struct dc_link *link) } } +static void read_dpcd204h_on_irq_hpd(struct dc_link *link, union hpd_irq_data *irq_data) +{ + enum dc_status retval; + union lane_align_status_updated dpcd_lane_status_updated; + + retval = core_link_read_dpcd( + link, + DP_LANE_ALIGN_STATUS_UPDATED, + &dpcd_lane_status_updated.raw, + sizeof(union lane_align_status_updated)); + + if (retval == DC_OK) { + irq_data->bytes.lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b = + dpcd_lane_status_updated.bits.EQ_INTERLANE_ALIGN_DONE_128b_132b; + irq_data->bytes.lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b = + dpcd_lane_status_updated.bits.CDS_INTERLANE_ALIGN_DONE_128b_132b; + } +} + enum dc_status dp_read_hpd_rx_irq_data( struct dc_link *link, union hpd_irq_data *irq_data) @@ -242,6 +268,13 @@ enum dc_status dp_read_hpd_rx_irq_data( irq_data->bytes.lane23_status.raw = tmp[DP_LANE2_3_STATUS_ESI - DP_SINK_COUNT_ESI]; irq_data->bytes.lane_status_updated.raw = tmp[DP_LANE_ALIGN_STATUS_UPDATED_ESI - DP_SINK_COUNT_ESI]; irq_data->bytes.sink_status.raw = tmp[DP_SINK_STATUS_ESI - DP_SINK_COUNT_ESI]; + + /* + * This display doesn't have correct values in DPCD200Eh. + * Read and check DPCD204h instead. + */ + if (link->wa_flags.read_dpcd204h_on_irq_hpd) + read_dpcd204h_on_irq_hpd(link, irq_data); } return retval; diff --git a/drivers/gpu/drm/amd/display/dmub/dmub_srv.h b/drivers/gpu/drm/amd/display/dmub/dmub_srv.h index 7c9a2b34bd05..4585e0419da6 100644 --- a/drivers/gpu/drm/amd/display/dmub/dmub_srv.h +++ b/drivers/gpu/drm/amd/display/dmub/dmub_srv.h @@ -367,6 +367,8 @@ struct dmub_srv_hw_funcs { bool (*is_supported)(struct dmub_srv *dmub); + bool (*is_psrsu_supported)(struct dmub_srv *dmub); + bool (*is_hw_init)(struct dmub_srv *dmub); void (*enable_dmub_boot_options)(struct dmub_srv *dmub, @@ -492,7 +494,7 @@ struct dmub_notification { * of a firmware to know if feature or functionality is supported or present. */ #define DMUB_FW_VERSION(major, minor, revision) \ - ((((major) & 0xFF) << 24) | (((minor) & 0xFF) << 16) | ((revision) & 0xFFFF)) + ((((major) & 0xFF) << 24) | (((minor) & 0xFF) << 16) | (((revision) & 0xFF) << 8)) /** * dmub_srv_create() - creates the DMUB service. diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c index ebf7aeec4029..5e952541e72d 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.c @@ -302,6 +302,11 @@ bool dmub_dcn31_is_supported(struct dmub_srv *dmub) return supported; } +bool dmub_dcn31_is_psrsu_supported(struct dmub_srv *dmub) +{ + return dmub->fw_version >= DMUB_FW_VERSION(4, 0, 59); +} + void dmub_dcn31_set_gpint(struct dmub_srv *dmub, union dmub_gpint_data_register reg) { diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.h b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.h index 7d5c10ee539b..89c5a948b67d 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.h +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn31.h @@ -221,6 +221,8 @@ bool dmub_dcn31_is_hw_init(struct dmub_srv *dmub); bool dmub_dcn31_is_supported(struct dmub_srv *dmub); +bool dmub_dcn31_is_psrsu_supported(struct dmub_srv *dmub); + void dmub_dcn31_set_gpint(struct dmub_srv *dmub, union dmub_gpint_data_register reg); diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.c index 48a06dbd9be7..f161aeb7e7c4 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.c @@ -60,3 +60,8 @@ const struct dmub_srv_dcn31_regs dmub_srv_dcn314_regs = { { DMUB_DCN31_FIELDS() }, #undef DMUB_SF }; + +bool dmub_dcn314_is_psrsu_supported(struct dmub_srv *dmub) +{ + return dmub->fw_version >= DMUB_FW_VERSION(8, 0, 16); +} diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.h b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.h index 674267a2940e..f213bd82c911 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.h +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_dcn314.h @@ -30,4 +30,6 @@ extern const struct dmub_srv_dcn31_regs dmub_srv_dcn314_regs; +bool dmub_dcn314_is_psrsu_supported(struct dmub_srv *dmub); + #endif /* _DMUB_DCN314_H_ */ diff --git a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c index 9e9a6a44a7ac..bdaf43892f47 100644 --- a/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c +++ b/drivers/gpu/drm/amd/display/dmub/src/dmub_srv.c @@ -226,14 +226,17 @@ static bool dmub_srv_hw_setup(struct dmub_srv *dmub, enum dmub_asic asic) case DMUB_ASIC_DCN314: case DMUB_ASIC_DCN315: case DMUB_ASIC_DCN316: - if (asic == DMUB_ASIC_DCN314) + if (asic == DMUB_ASIC_DCN314) { dmub->regs_dcn31 = &dmub_srv_dcn314_regs; - else if (asic == DMUB_ASIC_DCN315) + funcs->is_psrsu_supported = dmub_dcn314_is_psrsu_supported; + } else if (asic == DMUB_ASIC_DCN315) { dmub->regs_dcn31 = &dmub_srv_dcn315_regs; - else if (asic == DMUB_ASIC_DCN316) + } else if (asic == DMUB_ASIC_DCN316) { dmub->regs_dcn31 = &dmub_srv_dcn316_regs; - else + } else { dmub->regs_dcn31 = &dmub_srv_dcn31_regs; + funcs->is_psrsu_supported = dmub_dcn31_is_psrsu_supported; + } funcs->reset = dmub_dcn31_reset; funcs->reset_release = dmub_dcn31_reset_release; funcs->backdoor_load = dmub_dcn31_backdoor_load; diff --git a/drivers/gpu/drm/amd/pm/amdgpu_pm.c b/drivers/gpu/drm/amd/pm/amdgpu_pm.c index a57952b93e73..9ef88a0b1b57 100644 --- a/drivers/gpu/drm/amd/pm/amdgpu_pm.c +++ b/drivers/gpu/drm/amd/pm/amdgpu_pm.c @@ -35,44 +35,6 @@ #include <linux/pm_runtime.h> #include <asm/processor.h> -static const struct cg_flag_name clocks[] = { - {AMD_CG_SUPPORT_GFX_FGCG, "Graphics Fine Grain Clock Gating"}, - {AMD_CG_SUPPORT_GFX_MGCG, "Graphics Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_GFX_MGLS, "Graphics Medium Grain memory Light Sleep"}, - {AMD_CG_SUPPORT_GFX_CGCG, "Graphics Coarse Grain Clock Gating"}, - {AMD_CG_SUPPORT_GFX_CGLS, "Graphics Coarse Grain memory Light Sleep"}, - {AMD_CG_SUPPORT_GFX_CGTS, "Graphics Coarse Grain Tree Shader Clock Gating"}, - {AMD_CG_SUPPORT_GFX_CGTS_LS, "Graphics Coarse Grain Tree Shader Light Sleep"}, - {AMD_CG_SUPPORT_GFX_CP_LS, "Graphics Command Processor Light Sleep"}, - {AMD_CG_SUPPORT_GFX_RLC_LS, "Graphics Run List Controller Light Sleep"}, - {AMD_CG_SUPPORT_GFX_3D_CGCG, "Graphics 3D Coarse Grain Clock Gating"}, - {AMD_CG_SUPPORT_GFX_3D_CGLS, "Graphics 3D Coarse Grain memory Light Sleep"}, - {AMD_CG_SUPPORT_MC_LS, "Memory Controller Light Sleep"}, - {AMD_CG_SUPPORT_MC_MGCG, "Memory Controller Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_SDMA_LS, "System Direct Memory Access Light Sleep"}, - {AMD_CG_SUPPORT_SDMA_MGCG, "System Direct Memory Access Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_BIF_MGCG, "Bus Interface Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_BIF_LS, "Bus Interface Light Sleep"}, - {AMD_CG_SUPPORT_UVD_MGCG, "Unified Video Decoder Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_VCE_MGCG, "Video Compression Engine Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_HDP_LS, "Host Data Path Light Sleep"}, - {AMD_CG_SUPPORT_HDP_MGCG, "Host Data Path Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_DRM_MGCG, "Digital Right Management Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_DRM_LS, "Digital Right Management Light Sleep"}, - {AMD_CG_SUPPORT_ROM_MGCG, "Rom Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_DF_MGCG, "Data Fabric Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_VCN_MGCG, "VCN Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_HDP_DS, "Host Data Path Deep Sleep"}, - {AMD_CG_SUPPORT_HDP_SD, "Host Data Path Shutdown"}, - {AMD_CG_SUPPORT_IH_CG, "Interrupt Handler Clock Gating"}, - {AMD_CG_SUPPORT_JPEG_MGCG, "JPEG Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_REPEATER_FGCG, "Repeater Fine Grain Clock Gating"}, - {AMD_CG_SUPPORT_GFX_PERF_CLK, "Perfmon Clock Gating"}, - {AMD_CG_SUPPORT_ATHUB_MGCG, "Address Translation Hub Medium Grain Clock Gating"}, - {AMD_CG_SUPPORT_ATHUB_LS, "Address Translation Hub Light Sleep"}, - {0, NULL}, -}; - static const struct hwmon_temp_label { enum PP_HWMON_TEMP channel; const char *label; @@ -2110,6 +2072,7 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ case IP_VERSION(9, 4, 0): case IP_VERSION(9, 4, 1): case IP_VERSION(9, 4, 2): + case IP_VERSION(9, 4, 3): case IP_VERSION(10, 3, 0): case IP_VERSION(11, 0, 0): case IP_VERSION(11, 0, 1): @@ -2120,7 +2083,9 @@ static int default_attr_update(struct amdgpu_device *adev, struct amdgpu_device_ *states = ATTR_STATE_UNSUPPORTED; } } else if (DEVICE_ATTR_IS(pp_features)) { - if (adev->flags & AMD_IS_APU || gc_ver < IP_VERSION(9, 0, 0)) + if ((adev->flags & AMD_IS_APU && + gc_ver != IP_VERSION(9, 4, 3)) || + gc_ver < IP_VERSION(9, 0, 0)) *states = ATTR_STATE_UNSUPPORTED; } else if (DEVICE_ATTR_IS(gpu_metrics)) { if (gc_ver < IP_VERSION(9, 1, 0)) @@ -3684,6 +3649,44 @@ static int amdgpu_debugfs_pm_info_pp(struct seq_file *m, struct amdgpu_device *a return 0; } +static const struct cg_flag_name clocks[] = { + {AMD_CG_SUPPORT_GFX_FGCG, "Graphics Fine Grain Clock Gating"}, + {AMD_CG_SUPPORT_GFX_MGCG, "Graphics Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_GFX_MGLS, "Graphics Medium Grain memory Light Sleep"}, + {AMD_CG_SUPPORT_GFX_CGCG, "Graphics Coarse Grain Clock Gating"}, + {AMD_CG_SUPPORT_GFX_CGLS, "Graphics Coarse Grain memory Light Sleep"}, + {AMD_CG_SUPPORT_GFX_CGTS, "Graphics Coarse Grain Tree Shader Clock Gating"}, + {AMD_CG_SUPPORT_GFX_CGTS_LS, "Graphics Coarse Grain Tree Shader Light Sleep"}, + {AMD_CG_SUPPORT_GFX_CP_LS, "Graphics Command Processor Light Sleep"}, + {AMD_CG_SUPPORT_GFX_RLC_LS, "Graphics Run List Controller Light Sleep"}, + {AMD_CG_SUPPORT_GFX_3D_CGCG, "Graphics 3D Coarse Grain Clock Gating"}, + {AMD_CG_SUPPORT_GFX_3D_CGLS, "Graphics 3D Coarse Grain memory Light Sleep"}, + {AMD_CG_SUPPORT_MC_LS, "Memory Controller Light Sleep"}, + {AMD_CG_SUPPORT_MC_MGCG, "Memory Controller Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_SDMA_LS, "System Direct Memory Access Light Sleep"}, + {AMD_CG_SUPPORT_SDMA_MGCG, "System Direct Memory Access Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_BIF_MGCG, "Bus Interface Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_BIF_LS, "Bus Interface Light Sleep"}, + {AMD_CG_SUPPORT_UVD_MGCG, "Unified Video Decoder Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_VCE_MGCG, "Video Compression Engine Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_HDP_LS, "Host Data Path Light Sleep"}, + {AMD_CG_SUPPORT_HDP_MGCG, "Host Data Path Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_DRM_MGCG, "Digital Right Management Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_DRM_LS, "Digital Right Management Light Sleep"}, + {AMD_CG_SUPPORT_ROM_MGCG, "Rom Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_DF_MGCG, "Data Fabric Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_VCN_MGCG, "VCN Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_HDP_DS, "Host Data Path Deep Sleep"}, + {AMD_CG_SUPPORT_HDP_SD, "Host Data Path Shutdown"}, + {AMD_CG_SUPPORT_IH_CG, "Interrupt Handler Clock Gating"}, + {AMD_CG_SUPPORT_JPEG_MGCG, "JPEG Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_REPEATER_FGCG, "Repeater Fine Grain Clock Gating"}, + {AMD_CG_SUPPORT_GFX_PERF_CLK, "Perfmon Clock Gating"}, + {AMD_CG_SUPPORT_ATHUB_MGCG, "Address Translation Hub Medium Grain Clock Gating"}, + {AMD_CG_SUPPORT_ATHUB_LS, "Address Translation Hub Light Sleep"}, + {0, NULL}, +}; + static void amdgpu_parse_cg_state(struct seq_file *m, u64 flags) { int i; diff --git a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h index d178f3f44081..42172b00be66 100644 --- a/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h +++ b/drivers/gpu/drm/amd/pm/inc/amdgpu_dpm.h @@ -89,6 +89,8 @@ struct amdgpu_dpm_thermal { int max_mem_crit_temp; /* memory max emergency(shutdown) temp */ int max_mem_emergency_temp; + /* SWCTF threshold */ + int sw_ctf_threshold; /* was last interrupt low to high or high to low */ bool high_to_low; /* interrupt source */ diff --git a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c index 11b7b4cffaae..ff360c699171 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c +++ b/drivers/gpu/drm/amd/pm/powerplay/amd_powerplay.c @@ -26,6 +26,7 @@ #include <linux/gfp.h> #include <linux/slab.h> #include <linux/firmware.h> +#include <linux/reboot.h> #include "amd_shared.h" #include "amd_powerplay.h" #include "power_state.h" @@ -91,6 +92,45 @@ static int pp_early_init(void *handle) return 0; } +static void pp_swctf_delayed_work_handler(struct work_struct *work) +{ + struct pp_hwmgr *hwmgr = + container_of(work, struct pp_hwmgr, swctf_delayed_work.work); + struct amdgpu_device *adev = hwmgr->adev; + struct amdgpu_dpm_thermal *range = + &adev->pm.dpm.thermal; + uint32_t gpu_temperature, size; + int ret; + + /* + * If the hotspot/edge temperature is confirmed as below SW CTF setting point + * after the delay enforced, nothing will be done. + * Otherwise, a graceful shutdown will be performed to prevent further damage. + */ + if (range->sw_ctf_threshold && + hwmgr->hwmgr_func->read_sensor) { + ret = hwmgr->hwmgr_func->read_sensor(hwmgr, + AMDGPU_PP_SENSOR_HOTSPOT_TEMP, + &gpu_temperature, + &size); + /* + * For some legacy ASICs, hotspot temperature retrieving might be not + * supported. Check the edge temperature instead then. + */ + if (ret == -EOPNOTSUPP) + ret = hwmgr->hwmgr_func->read_sensor(hwmgr, + AMDGPU_PP_SENSOR_EDGE_TEMP, + &gpu_temperature, + &size); + if (!ret && gpu_temperature / 1000 < range->sw_ctf_threshold) + return; + } + + dev_emerg(adev->dev, "ERROR: GPU over temperature range(SW CTF) detected!\n"); + dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU SW CTF!\n"); + orderly_poweroff(true); +} + static int pp_sw_init(void *handle) { struct amdgpu_device *adev = handle; @@ -101,6 +141,10 @@ static int pp_sw_init(void *handle) pr_debug("powerplay sw init %s\n", ret ? "failed" : "successfully"); + if (!ret) + INIT_DELAYED_WORK(&hwmgr->swctf_delayed_work, + pp_swctf_delayed_work_handler); + return ret; } @@ -135,6 +179,8 @@ static int pp_hw_fini(void *handle) struct amdgpu_device *adev = handle; struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle; + cancel_delayed_work_sync(&hwmgr->swctf_delayed_work); + hwmgr_hw_fini(hwmgr); return 0; @@ -221,6 +267,8 @@ static int pp_suspend(void *handle) struct amdgpu_device *adev = handle; struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle; + cancel_delayed_work_sync(&hwmgr->swctf_delayed_work); + return hwmgr_suspend(hwmgr); } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c index 981dc8c7112d..90452b66e107 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/hardwaremanager.c @@ -241,7 +241,8 @@ int phm_start_thermal_controller(struct pp_hwmgr *hwmgr) TEMP_RANGE_MAX, TEMP_RANGE_MIN, TEMP_RANGE_MAX, - TEMP_RANGE_MAX}; + TEMP_RANGE_MAX, + 0}; struct amdgpu_device *adev = hwmgr->adev; if (!hwmgr->not_vf) @@ -265,6 +266,7 @@ int phm_start_thermal_controller(struct pp_hwmgr *hwmgr) adev->pm.dpm.thermal.min_mem_temp = range.mem_min; adev->pm.dpm.thermal.max_mem_crit_temp = range.mem_crit_max; adev->pm.dpm.thermal.max_mem_emergency_temp = range.mem_emergency_max; + adev->pm.dpm.thermal.sw_ctf_threshold = range.sw_ctf_threshold; return ret; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c index e10cc5e7928e..6841a4bce186 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu7_hwmgr.c @@ -5432,6 +5432,8 @@ static int smu7_get_thermal_temperature_range(struct pp_hwmgr *hwmgr, thermal_data->max = data->thermal_temp_setting.temperature_shutdown * PP_TEMPERATURE_UNITS_PER_CENTIGRADES; + thermal_data->sw_ctf_threshold = thermal_data->max; + return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c index bfe80ac0ad8c..d0b1ab6c4523 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/smu_helper.c @@ -603,21 +603,17 @@ int phm_irq_process(struct amdgpu_device *adev, struct amdgpu_irq_src *source, struct amdgpu_iv_entry *entry) { + struct pp_hwmgr *hwmgr = adev->powerplay.pp_handle; uint32_t client_id = entry->client_id; uint32_t src_id = entry->src_id; if (client_id == AMDGPU_IRQ_CLIENTID_LEGACY) { if (src_id == VISLANDS30_IV_SRCID_CG_TSS_THERMAL_LOW_TO_HIGH) { - dev_emerg(adev->dev, "ERROR: GPU over temperature range(SW CTF) detected!\n"); - /* - * SW CTF just occurred. - * Try to do a graceful shutdown to prevent further damage. - */ - dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU SW CTF!\n"); - orderly_poweroff(true); - } else if (src_id == VISLANDS30_IV_SRCID_CG_TSS_THERMAL_HIGH_TO_LOW) + schedule_delayed_work(&hwmgr->swctf_delayed_work, + msecs_to_jiffies(AMDGPU_SWCTF_EXTRA_DELAY)); + } else if (src_id == VISLANDS30_IV_SRCID_CG_TSS_THERMAL_HIGH_TO_LOW) { dev_emerg(adev->dev, "ERROR: GPU under temperature range detected!\n"); - else if (src_id == VISLANDS30_IV_SRCID_GPIO_19) { + } else if (src_id == VISLANDS30_IV_SRCID_GPIO_19) { dev_emerg(adev->dev, "ERROR: GPU HW Critical Temperature Fault(aka CTF) detected!\n"); /* * HW CTF just occurred. Shutdown to prevent further damage. @@ -626,15 +622,10 @@ int phm_irq_process(struct amdgpu_device *adev, orderly_poweroff(true); } } else if (client_id == SOC15_IH_CLIENTID_THM) { - if (src_id == 0) { - dev_emerg(adev->dev, "ERROR: GPU over temperature range(SW CTF) detected!\n"); - /* - * SW CTF just occurred. - * Try to do a graceful shutdown to prevent further damage. - */ - dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU SW CTF!\n"); - orderly_poweroff(true); - } else + if (src_id == 0) + schedule_delayed_work(&hwmgr->swctf_delayed_work, + msecs_to_jiffies(AMDGPU_SWCTF_EXTRA_DELAY)); + else dev_emerg(adev->dev, "ERROR: GPU under temperature range detected!\n"); } else if (client_id == SOC15_IH_CLIENTID_ROM_SMUIO) { dev_emerg(adev->dev, "ERROR: GPU HW Critical Temperature Fault(aka CTF) detected!\n"); diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c index 99cd2e63afdd..c51dd4c74fe9 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega10_hwmgr.c @@ -5241,6 +5241,9 @@ static int vega10_get_thermal_temperature_range(struct pp_hwmgr *hwmgr, { struct vega10_hwmgr *data = hwmgr->backend; PPTable_t *pp_table = &(data->smc_state_table.pp_table); + struct phm_ppt_v2_information *pp_table_info = + (struct phm_ppt_v2_information *)(hwmgr->pptable); + struct phm_tdp_table *tdp_table = pp_table_info->tdp_table; memcpy(thermal_data, &SMU7ThermalWithDelayPolicy[0], sizeof(struct PP_TemperatureRange)); @@ -5257,6 +5260,13 @@ static int vega10_get_thermal_temperature_range(struct pp_hwmgr *hwmgr, thermal_data->mem_emergency_max = (pp_table->ThbmLimit + CTF_OFFSET_HBM)* PP_TEMPERATURE_UNITS_PER_CENTIGRADES; + if (tdp_table->usSoftwareShutdownTemp > pp_table->ThotspotLimit && + tdp_table->usSoftwareShutdownTemp < VEGA10_THERMAL_MAXIMUM_ALERT_TEMP) + thermal_data->sw_ctf_threshold = tdp_table->usSoftwareShutdownTemp; + else + thermal_data->sw_ctf_threshold = VEGA10_THERMAL_MAXIMUM_ALERT_TEMP; + thermal_data->sw_ctf_threshold *= PP_TEMPERATURE_UNITS_PER_CENTIGRADES; + return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c index e9db137cd1c6..1937be1cf5b4 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_hwmgr.c @@ -2763,6 +2763,8 @@ static int vega12_notify_cac_buffer_info(struct pp_hwmgr *hwmgr, static int vega12_get_thermal_temperature_range(struct pp_hwmgr *hwmgr, struct PP_TemperatureRange *thermal_data) { + struct phm_ppt_v3_information *pptable_information = + (struct phm_ppt_v3_information *)hwmgr->pptable; struct vega12_hwmgr *data = (struct vega12_hwmgr *)(hwmgr->backend); PPTable_t *pp_table = &(data->smc_state_table.pp_table); @@ -2781,6 +2783,8 @@ static int vega12_get_thermal_temperature_range(struct pp_hwmgr *hwmgr, PP_TEMPERATURE_UNITS_PER_CENTIGRADES; thermal_data->mem_emergency_max = (pp_table->ThbmLimit + CTF_OFFSET_HBM)* PP_TEMPERATURE_UNITS_PER_CENTIGRADES; + thermal_data->sw_ctf_threshold = pptable_information->us_software_shutdown_temp * + PP_TEMPERATURE_UNITS_PER_CENTIGRADES; return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.c index ed3dff0b52d2..ae342c58cd3e 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega12_thermal.c @@ -192,7 +192,9 @@ static int vega12_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_IH_HW_ENA, 1); val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTH, high); val = REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTL, low); - val = val & (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK); + val &= ~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK; + val &= ~THM_THERMAL_INT_CTRL__THERM_INTH_MASK_MASK; + val &= ~THM_THERMAL_INT_CTRL__THERM_INTL_MASK_MASK; WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL, val); diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c index 0d4d4811527c..4e19ccbdb807 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_hwmgr.c @@ -4206,6 +4206,8 @@ static int vega20_notify_cac_buffer_info(struct pp_hwmgr *hwmgr, static int vega20_get_thermal_temperature_range(struct pp_hwmgr *hwmgr, struct PP_TemperatureRange *thermal_data) { + struct phm_ppt_v3_information *pptable_information = + (struct phm_ppt_v3_information *)hwmgr->pptable; struct vega20_hwmgr *data = (struct vega20_hwmgr *)(hwmgr->backend); PPTable_t *pp_table = &(data->smc_state_table.pp_table); @@ -4224,6 +4226,8 @@ static int vega20_get_thermal_temperature_range(struct pp_hwmgr *hwmgr, PP_TEMPERATURE_UNITS_PER_CENTIGRADES; thermal_data->mem_emergency_max = (pp_table->ThbmLimit + CTF_OFFSET_HBM)* PP_TEMPERATURE_UNITS_PER_CENTIGRADES; + thermal_data->sw_ctf_threshold = pptable_information->us_software_shutdown_temp * + PP_TEMPERATURE_UNITS_PER_CENTIGRADES; return 0; } diff --git a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.c b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.c index f4f4efdbda79..e9737ca8418a 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.c +++ b/drivers/gpu/drm/amd/pm/powerplay/hwmgr/vega20_thermal.c @@ -263,7 +263,9 @@ static int vega20_thermal_set_temperature_range(struct pp_hwmgr *hwmgr, val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, THERM_IH_HW_ENA, 1); val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTH, high); val = CGS_REG_SET_FIELD(val, THM_THERMAL_INT_CTRL, DIG_THERM_INTL, low); - val = val & (~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK); + val &= ~THM_THERMAL_INT_CTRL__THERM_TRIGGER_MASK_MASK; + val &= ~THM_THERMAL_INT_CTRL__THERM_INTH_MASK_MASK; + val &= ~THM_THERMAL_INT_CTRL__THERM_INTL_MASK_MASK; WREG32_SOC15(THM, 0, mmTHM_THERMAL_INT_CTRL, val); diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h index f1580a26a850..612d66aeaab9 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/hwmgr.h @@ -811,6 +811,8 @@ struct pp_hwmgr { bool gfxoff_state_changed_by_workload; uint32_t pstate_sclk_peak; uint32_t pstate_mclk_peak; + + struct delayed_work swctf_delayed_work; }; int hwmgr_early_init(struct pp_hwmgr *hwmgr); diff --git a/drivers/gpu/drm/amd/pm/powerplay/inc/power_state.h b/drivers/gpu/drm/amd/pm/powerplay/inc/power_state.h index a5f2227a3971..0ffc2347829d 100644 --- a/drivers/gpu/drm/amd/pm/powerplay/inc/power_state.h +++ b/drivers/gpu/drm/amd/pm/powerplay/inc/power_state.h @@ -131,6 +131,7 @@ struct PP_TemperatureRange { int mem_min; int mem_crit_max; int mem_emergency_max; + int sw_ctf_threshold; }; struct PP_StateValidationBlock { diff --git a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c index 4dea79a0c5b5..ce41a8309582 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c +++ b/drivers/gpu/drm/amd/pm/swsmu/amdgpu_smu.c @@ -24,6 +24,7 @@ #include <linux/firmware.h> #include <linux/pci.h> +#include <linux/reboot.h> #include "amdgpu.h" #include "amdgpu_smu.h" @@ -1078,6 +1079,34 @@ static void smu_interrupt_work_fn(struct work_struct *work) smu->ppt_funcs->interrupt_work(smu); } +static void smu_swctf_delayed_work_handler(struct work_struct *work) +{ + struct smu_context *smu = + container_of(work, struct smu_context, swctf_delayed_work.work); + struct smu_temperature_range *range = + &smu->thermal_range; + struct amdgpu_device *adev = smu->adev; + uint32_t hotspot_tmp, size; + + /* + * If the hotspot temperature is confirmed as below SW CTF setting point + * after the delay enforced, nothing will be done. + * Otherwise, a graceful shutdown will be performed to prevent further damage. + */ + if (range->software_shutdown_temp && + smu->ppt_funcs->read_sensor && + !smu->ppt_funcs->read_sensor(smu, + AMDGPU_PP_SENSOR_HOTSPOT_TEMP, + &hotspot_tmp, + &size) && + hotspot_tmp / 1000 < range->software_shutdown_temp) + return; + + dev_emerg(adev->dev, "ERROR: GPU over temperature range(SW CTF) detected!\n"); + dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU SW CTF!\n"); + orderly_poweroff(true); +} + static int smu_sw_init(void *handle) { struct amdgpu_device *adev = (struct amdgpu_device *)handle; @@ -1120,6 +1149,9 @@ static int smu_sw_init(void *handle) smu->smu_dpm.dpm_level = AMD_DPM_FORCED_LEVEL_AUTO; smu->smu_dpm.requested_dpm_level = AMD_DPM_FORCED_LEVEL_AUTO; + INIT_DELAYED_WORK(&smu->swctf_delayed_work, + smu_swctf_delayed_work_handler); + ret = smu_smc_table_sw_init(smu); if (ret) { dev_err(adev->dev, "Failed to sw init smc table!\n"); @@ -1600,6 +1632,8 @@ static int smu_smc_hw_cleanup(struct smu_context *smu) return ret; } + cancel_delayed_work_sync(&smu->swctf_delayed_work); + ret = smu_disable_dpms(smu); if (ret) { dev_err(adev->dev, "Fail to disable dpm features!\n"); diff --git a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h index 09469c750a96..6e2069dcb6b9 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h +++ b/drivers/gpu/drm/amd/pm/swsmu/inc/amdgpu_smu.h @@ -573,6 +573,8 @@ struct smu_context u32 debug_param_reg; u32 debug_msg_reg; u32 debug_resp_reg; + + struct delayed_work swctf_delayed_work; }; struct i2c_adapter; diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c index 275f708db636..c94d825a871b 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/navi10_ppt.c @@ -1654,7 +1654,7 @@ static int navi10_force_clk_levels(struct smu_context *smu, enum smu_clk_type clk_type, uint32_t mask) { - int ret = 0, size = 0; + int ret = 0; uint32_t soft_min_level = 0, soft_max_level = 0, min_freq = 0, max_freq = 0; soft_min_level = mask ? (ffs(mask) - 1) : 0; @@ -1675,15 +1675,15 @@ static int navi10_force_clk_levels(struct smu_context *smu, ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, soft_min_level, &min_freq); if (ret) - return size; + return 0; ret = smu_v11_0_get_dpm_freq_by_index(smu, clk_type, soft_max_level, &max_freq); if (ret) - return size; + return 0; ret = smu_v11_0_set_soft_freq_limited_range(smu, clk_type, min_freq, max_freq); if (ret) - return size; + return 0; break; case SMU_DCEFCLK: dev_info(smu->adev->dev,"Setting DCEFCLK min/max dpm level is not supported!\n"); @@ -1693,7 +1693,7 @@ static int navi10_force_clk_levels(struct smu_context *smu, break; } - return size; + return 0; } static int navi10_populate_umd_state_clk(struct smu_context *smu) diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c index e1ef88ee1ed3..aa4a5498a12f 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu11/smu_v11_0.c @@ -1412,13 +1412,8 @@ static int smu_v11_0_irq_process(struct amdgpu_device *adev, if (client_id == SOC15_IH_CLIENTID_THM) { switch (src_id) { case THM_11_0__SRCID__THM_DIG_THERM_L2H: - dev_emerg(adev->dev, "ERROR: GPU over temperature range(SW CTF) detected!\n"); - /* - * SW CTF just occurred. - * Try to do a graceful shutdown to prevent further damage. - */ - dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU SW CTF!\n"); - orderly_poweroff(true); + schedule_delayed_work(&smu->swctf_delayed_work, + msecs_to_jiffies(AMDGPU_SWCTF_EXTRA_DELAY)); break; case THM_11_0__SRCID__THM_DIG_THERM_H2L: dev_emerg(adev->dev, "ERROR: GPU under temperature range detected\n"); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c index e52c563f0dac..3856da6c3f3d 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0.c @@ -1353,13 +1353,8 @@ static int smu_v13_0_irq_process(struct amdgpu_device *adev, if (client_id == SOC15_IH_CLIENTID_THM) { switch (src_id) { case THM_11_0__SRCID__THM_DIG_THERM_L2H: - dev_emerg(adev->dev, "ERROR: GPU over temperature range(SW CTF) detected!\n"); - /* - * SW CTF just occurred. - * Try to do a graceful shutdown to prevent further damage. - */ - dev_emerg(adev->dev, "ERROR: System is going to shutdown due to GPU SW CTF!\n"); - orderly_poweroff(true); + schedule_delayed_work(&smu->swctf_delayed_work, + msecs_to_jiffies(AMDGPU_SWCTF_EXTRA_DELAY)); break; case THM_11_0__SRCID__THM_DIG_THERM_H2L: dev_emerg(adev->dev, "ERROR: GPU under temperature range detected\n"); diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c index a6083957ae51..124287cbbff8 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_0_ppt.c @@ -1710,6 +1710,7 @@ static int smu_v13_0_0_get_thermal_temperature_range(struct smu_context *smu, range->mem_emergency_max = (pptable->SkuTable.TemperatureLimit[TEMP_MEM] + CTF_OFFSET_MEM)* SMU_TEMPERATURE_UNITS_PER_CENTIGRADES; range->software_shutdown_temp = powerplay_table->software_shutdown_temp; + range->software_shutdown_temp_offset = pptable->SkuTable.FanAbnormalTempLimitOffset; return 0; } diff --git a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c index a92ea4601ea4..6ef12252beb5 100644 --- a/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c +++ b/drivers/gpu/drm/amd/pm/swsmu/smu13/smu_v13_0_6_ppt.c @@ -200,7 +200,6 @@ struct PPTable_t { }; #define SMUQ10_TO_UINT(x) ((x) >> 10) -#define SMUQ16_TO_UINT(x) ((x) >> 16) struct smu_v13_0_6_dpm_map { enum smu_clk_type clk_type; @@ -1994,8 +1993,9 @@ static ssize_t smu_v13_0_6_get_gpu_metrics(struct smu_context *smu, void **table gpu_metrics->average_socket_power = SMUQ10_TO_UINT(metrics->SocketPower); + /* Energy is reported in 15.625mJ units */ gpu_metrics->energy_accumulator = - SMUQ16_TO_UINT(metrics->SocketEnergyAcc); + SMUQ10_TO_UINT(metrics->SocketEnergyAcc); gpu_metrics->current_gfxclk = SMUQ10_TO_UINT(metrics->GfxclkFrequency[xcc0]); diff --git a/drivers/gpu/drm/i915/display/intel_cx0_phy.c b/drivers/gpu/drm/i915/display/intel_cx0_phy.c index 0600fdcd06ef..719447ce86e7 100644 --- a/drivers/gpu/drm/i915/display/intel_cx0_phy.c +++ b/drivers/gpu/drm/i915/display/intel_cx0_phy.c @@ -2435,7 +2435,8 @@ static void intel_program_port_clock_ctl(struct intel_encoder *encoder, intel_de_rmw(i915, XELPDP_PORT_CLOCK_CTL(encoder->port), XELPDP_LANE1_PHY_CLOCK_SELECT | XELPDP_FORWARD_CLOCK_UNGATE | - XELPDP_DDI_CLOCK_SELECT_MASK | XELPDP_SSC_ENABLE_PLLB, val); + XELPDP_DDI_CLOCK_SELECT_MASK | XELPDP_SSC_ENABLE_PLLA | + XELPDP_SSC_ENABLE_PLLB, val); } static u32 intel_cx0_get_powerdown_update(u8 lane_mask) diff --git a/drivers/gpu/drm/i915/display/intel_display_power.h b/drivers/gpu/drm/i915/display/intel_display_power.h index be1a87bde0c9..df38632c6237 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power.h +++ b/drivers/gpu/drm/i915/display/intel_display_power.h @@ -6,6 +6,9 @@ #ifndef __INTEL_DISPLAY_POWER_H__ #define __INTEL_DISPLAY_POWER_H__ +#include <linux/mutex.h> +#include <linux/workqueue.h> + #include "intel_wakeref.h" enum aux_ch; @@ -16,6 +19,7 @@ enum port; struct drm_i915_private; struct i915_power_well; struct intel_encoder; +struct seq_file; /* * Keep the pipe, transcoder, port (DDI_LANES,DDI_IO,AUX) domain instances diff --git a/drivers/gpu/drm/i915/display/intel_display_power_map.c b/drivers/gpu/drm/i915/display/intel_display_power_map.c index 1118ee9d224c..5ad04cd42c15 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_map.c +++ b/drivers/gpu/drm/i915/display/intel_display_power_map.c @@ -1252,10 +1252,18 @@ I915_DECL_PW_DOMAINS(xelpd_pwdoms_pw_a, POWER_DOMAIN_INIT); #define XELPD_DC_OFF_PORT_POWER_DOMAINS \ + POWER_DOMAIN_PORT_DDI_LANES_C, \ + POWER_DOMAIN_PORT_DDI_LANES_D, \ + POWER_DOMAIN_PORT_DDI_LANES_E, \ POWER_DOMAIN_PORT_DDI_LANES_TC1, \ POWER_DOMAIN_PORT_DDI_LANES_TC2, \ POWER_DOMAIN_PORT_DDI_LANES_TC3, \ POWER_DOMAIN_PORT_DDI_LANES_TC4, \ + POWER_DOMAIN_VGA, \ + POWER_DOMAIN_AUDIO_PLAYBACK, \ + POWER_DOMAIN_AUX_IO_C, \ + POWER_DOMAIN_AUX_IO_D, \ + POWER_DOMAIN_AUX_IO_E, \ POWER_DOMAIN_AUX_C, \ POWER_DOMAIN_AUX_D, \ POWER_DOMAIN_AUX_E, \ @@ -1272,14 +1280,6 @@ I915_DECL_PW_DOMAINS(xelpd_pwdoms_pw_a, XELPD_PW_B_POWER_DOMAINS, \ XELPD_PW_C_POWER_DOMAINS, \ XELPD_PW_D_POWER_DOMAINS, \ - POWER_DOMAIN_PORT_DDI_LANES_C, \ - POWER_DOMAIN_PORT_DDI_LANES_D, \ - POWER_DOMAIN_PORT_DDI_LANES_E, \ - POWER_DOMAIN_VGA, \ - POWER_DOMAIN_AUDIO_PLAYBACK, \ - POWER_DOMAIN_AUX_IO_C, \ - POWER_DOMAIN_AUX_IO_D, \ - POWER_DOMAIN_AUX_IO_E, \ XELPD_DC_OFF_PORT_POWER_DOMAINS I915_DECL_PW_DOMAINS(xelpd_pwdoms_pw_2, diff --git a/drivers/gpu/drm/i915/display/intel_display_power_well.h b/drivers/gpu/drm/i915/display/intel_display_power_well.h index e494df379e6c..1015bba4af01 100644 --- a/drivers/gpu/drm/i915/display/intel_display_power_well.h +++ b/drivers/gpu/drm/i915/display/intel_display_power_well.h @@ -12,6 +12,8 @@ struct drm_i915_private; struct i915_power_well; +struct i915_power_well_ops; +struct intel_encoder; #define for_each_power_well(__dev_priv, __power_well) \ for ((__power_well) = (__dev_priv)->display.power.domains.power_wells; \ diff --git a/drivers/gpu/drm/i915/display/intel_hdcp.c b/drivers/gpu/drm/i915/display/intel_hdcp.c index 5ed450111f77..34fabadefaf6 100644 --- a/drivers/gpu/drm/i915/display/intel_hdcp.c +++ b/drivers/gpu/drm/i915/display/intel_hdcp.c @@ -2358,7 +2358,7 @@ int intel_hdcp_enable(struct intel_atomic_state *state, mutex_lock(&dig_port->hdcp_mutex); drm_WARN_ON(&i915->drm, hdcp->value == DRM_MODE_CONTENT_PROTECTION_ENABLED); - hdcp->content_type = (u8)conn_state->content_type; + hdcp->content_type = (u8)conn_state->hdcp_content_type; if (intel_crtc_has_type(pipe_config, INTEL_OUTPUT_DP_MST)) { hdcp->cpu_transcoder = pipe_config->mst_master_transcoder; diff --git a/drivers/gpu/drm/i915/display/intel_psr.c b/drivers/gpu/drm/i915/display/intel_psr.c index d58ed9b62e67..56c17283ba2d 100644 --- a/drivers/gpu/drm/i915/display/intel_psr.c +++ b/drivers/gpu/drm/i915/display/intel_psr.c @@ -933,9 +933,9 @@ static bool _compute_psr2_wake_times(struct intel_dp *intel_dp, } io_wake_lines = intel_usecs_to_scanlines( - &crtc_state->uapi.adjusted_mode, io_wake_time); + &crtc_state->hw.adjusted_mode, io_wake_time); fast_wake_lines = intel_usecs_to_scanlines( - &crtc_state->uapi.adjusted_mode, fast_wake_time); + &crtc_state->hw.adjusted_mode, fast_wake_time); if (io_wake_lines > max_wake_lines || fast_wake_lines > max_wake_lines) diff --git a/drivers/gpu/drm/i915/display/intel_psr_regs.h b/drivers/gpu/drm/i915/display/intel_psr_regs.h index 0f7db617425a..8750cb0d8d9d 100644 --- a/drivers/gpu/drm/i915/display/intel_psr_regs.h +++ b/drivers/gpu/drm/i915/display/intel_psr_regs.h @@ -81,7 +81,7 @@ #define _SRD_AUX_DATA_A 0x60814 #define _SRD_AUX_DATA_EDP 0x6f814 -#define EDP_PSR_AUX_DATA(tran, i) _MMIO_TRANS2(tran, _SRD_AUX_DATA_A + (i) + 4) /* 5 registers */ +#define EDP_PSR_AUX_DATA(tran, i) _MMIO_TRANS2(tran, _SRD_AUX_DATA_A + (i) * 4) /* 5 registers */ #define _SRD_STATUS_A 0x60840 #define _SRD_STATUS_EDP 0x6f840 diff --git a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c index 01b75529311c..ee9f83af7cf6 100644 --- a/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c +++ b/drivers/gpu/drm/i915/gt/uc/intel_guc_slpc.c @@ -606,7 +606,7 @@ static int slpc_set_softlimits(struct intel_guc_slpc *slpc) if (unlikely(ret)) return ret; slpc_to_gt(slpc)->defaults.min_freq = slpc->min_freq_softlimit; - } else if (slpc->min_freq_softlimit != slpc->min_freq) { + } else { return intel_guc_slpc_set_min_freq(slpc, slpc->min_freq_softlimit); } diff --git a/drivers/gpu/drm/i915/selftests/mock_gem_device.c b/drivers/gpu/drm/i915/selftests/mock_gem_device.c index 09d4bbcdcdbf..4de6a4e8280d 100644 --- a/drivers/gpu/drm/i915/selftests/mock_gem_device.c +++ b/drivers/gpu/drm/i915/selftests/mock_gem_device.c @@ -118,15 +118,31 @@ static void mock_gt_probe(struct drm_i915_private *i915) i915->gt[0]->name = "Mock GT"; } +static const struct intel_device_info mock_info = { + .__runtime.graphics.ip.ver = -1, + .__runtime.page_sizes = (I915_GTT_PAGE_SIZE_4K | + I915_GTT_PAGE_SIZE_64K | + I915_GTT_PAGE_SIZE_2M), + .__runtime.memory_regions = REGION_SMEM, + .__runtime.platform_engine_mask = BIT(0), + + /* simply use legacy cache level for mock device */ + .max_pat_index = 3, + .cachelevel_to_pat = { + [I915_CACHE_NONE] = 0, + [I915_CACHE_LLC] = 1, + [I915_CACHE_L3_LLC] = 2, + [I915_CACHE_WT] = 3, + }, +}; + struct drm_i915_private *mock_gem_device(void) { #if IS_ENABLED(CONFIG_IOMMU_API) && defined(CONFIG_INTEL_IOMMU) static struct dev_iommu fake_iommu = { .priv = (void *)-1 }; #endif struct drm_i915_private *i915; - struct intel_device_info *i915_info; struct pci_dev *pdev; - unsigned int i; int ret; pdev = kzalloc(sizeof(*pdev), GFP_KERNEL); @@ -159,15 +175,18 @@ struct drm_i915_private *mock_gem_device(void) pci_set_drvdata(pdev, i915); + /* Device parameters start as a copy of module parameters. */ + i915_params_copy(&i915->params, &i915_modparams); + + /* Set up device info and initial runtime info. */ + intel_device_info_driver_create(i915, pdev->device, &mock_info); + dev_pm_domain_set(&pdev->dev, &pm_domain); pm_runtime_enable(&pdev->dev); pm_runtime_dont_use_autosuspend(&pdev->dev); if (pm_runtime_enabled(&pdev->dev)) WARN_ON(pm_runtime_get_sync(&pdev->dev)); - - i915_params_copy(&i915->params, &i915_modparams); - intel_runtime_pm_init_early(&i915->runtime_pm); /* wakeref tracking has significant overhead */ i915->runtime_pm.no_wakeref_tracking = true; @@ -175,21 +194,6 @@ struct drm_i915_private *mock_gem_device(void) /* Using the global GTT may ask questions about KMS users, so prepare */ drm_mode_config_init(&i915->drm); - RUNTIME_INFO(i915)->graphics.ip.ver = -1; - - RUNTIME_INFO(i915)->page_sizes = - I915_GTT_PAGE_SIZE_4K | - I915_GTT_PAGE_SIZE_64K | - I915_GTT_PAGE_SIZE_2M; - - RUNTIME_INFO(i915)->memory_regions = REGION_SMEM; - - /* simply use legacy cache level for mock device */ - i915_info = (struct intel_device_info *)INTEL_INFO(i915); - i915_info->max_pat_index = 3; - for (i = 0; i < I915_MAX_CACHE_LEVEL; i++) - i915_info->cachelevel_to_pat[i] = i; - intel_memory_regions_hw_probe(i915); spin_lock_init(&i915->gpu_error.lock); @@ -223,7 +227,6 @@ struct drm_i915_private *mock_gem_device(void) mock_init_ggtt(to_gt(i915)); to_gt(i915)->vm = i915_vm_get(&to_gt(i915)->ggtt->vm); - RUNTIME_INFO(i915)->platform_engine_mask = BIT(0); to_gt(i915)->info.engine_mask = BIT(0); to_gt(i915)->engine[RCS0] = mock_engine(i915, "mock", RCS0); diff --git a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c index 3cc9fb0d4f5d..dc276c346fd1 100644 --- a/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c +++ b/drivers/gpu/drm/panel/panel-boe-tv101wum-nl6.c @@ -2139,9 +2139,9 @@ static const struct panel_desc starry_himax83102_j02_desc = { static const struct drm_display_mode starry_ili9882t_default_mode = { .clock = 165280, .hdisplay = 1200, - .hsync_start = 1200 + 32, - .hsync_end = 1200 + 32 + 30, - .htotal = 1200 + 32 + 30 + 32, + .hsync_start = 1200 + 72, + .hsync_end = 1200 + 72 + 30, + .htotal = 1200 + 72 + 30 + 72, .vdisplay = 1920, .vsync_start = 1920 + 68, .vsync_end = 1920 + 68 + 2, diff --git a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c index 6f0d332ccf51..06bdcf072d10 100644 --- a/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c +++ b/drivers/hid/amd-sfh-hid/sfh1_1/amd_sfh_desc.c @@ -132,29 +132,45 @@ static void get_common_inputs(struct common_input_property *common, int report_i common->event_type = HID_USAGE_SENSOR_EVENT_DATA_UPDATED_ENUM; } -static int float_to_int(u32 float32) +static int float_to_int(u32 flt32_val) { int fraction, shift, mantissa, sign, exp, zeropre; - mantissa = float32 & GENMASK(22, 0); - sign = (float32 & BIT(31)) ? -1 : 1; - exp = (float32 & ~BIT(31)) >> 23; + mantissa = flt32_val & GENMASK(22, 0); + sign = (flt32_val & BIT(31)) ? -1 : 1; + exp = (flt32_val & ~BIT(31)) >> 23; if (!exp && !mantissa) return 0; + /* + * Calculate the exponent and fraction part of floating + * point representation. + */ exp -= 127; if (exp < 0) { exp = -exp; + if (exp >= BITS_PER_TYPE(u32)) + return 0; zeropre = (((BIT(23) + mantissa) * 100) >> 23) >> exp; return zeropre >= 50 ? sign : 0; } shift = 23 - exp; - float32 = BIT(exp) + (mantissa >> shift); - fraction = mantissa & GENMASK(shift - 1, 0); + if (abs(shift) >= BITS_PER_TYPE(u32)) + return 0; + + if (shift < 0) { + shift = -shift; + flt32_val = BIT(exp) + (mantissa << shift); + shift = 0; + } else { + flt32_val = BIT(exp) + (mantissa >> shift); + } + + fraction = (shift == 0) ? 0 : mantissa & GENMASK(shift - 1, 0); - return (((fraction * 100) >> shift) >= 50) ? sign * (float32 + 1) : sign * float32; + return (((fraction * 100) >> shift) >= 50) ? sign * (flt32_val + 1) : sign * flt32_val; } static u8 get_input_rep(u8 current_index, int sensor_idx, int report_id, diff --git a/drivers/hid/hid-hyperv.c b/drivers/hid/hid-hyperv.c index 49d4a26895e7..f33485d83d24 100644 --- a/drivers/hid/hid-hyperv.c +++ b/drivers/hid/hid-hyperv.c @@ -258,19 +258,17 @@ static void mousevsc_on_receive(struct hv_device *device, switch (hid_msg_hdr->type) { case SYNTH_HID_PROTOCOL_RESPONSE: + len = struct_size(pipe_msg, data, pipe_msg->size); + /* * While it will be impossible for us to protect against * malicious/buggy hypervisor/host, add a check here to * ensure we don't corrupt memory. */ - if (struct_size(pipe_msg, data, pipe_msg->size) - > sizeof(struct mousevsc_prt_msg)) { - WARN_ON(1); + if (WARN_ON(len > sizeof(struct mousevsc_prt_msg))) break; - } - memcpy(&input_dev->protocol_resp, pipe_msg, - struct_size(pipe_msg, data, pipe_msg->size)); + memcpy(&input_dev->protocol_resp, pipe_msg, len); complete(&input_dev->wait_event); break; diff --git a/drivers/hid/hid-input.c b/drivers/hid/hid-input.c index a1d2690a1a0d..851ee86eff32 100644 --- a/drivers/hid/hid-input.c +++ b/drivers/hid/hid-input.c @@ -1093,6 +1093,10 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x074: map_key_clear(KEY_BRIGHTNESS_MAX); break; case 0x075: map_key_clear(KEY_BRIGHTNESS_AUTO); break; + case 0x076: map_key_clear(KEY_CAMERA_ACCESS_ENABLE); break; + case 0x077: map_key_clear(KEY_CAMERA_ACCESS_DISABLE); break; + case 0x078: map_key_clear(KEY_CAMERA_ACCESS_TOGGLE); break; + case 0x079: map_key_clear(KEY_KBDILLUMUP); break; case 0x07a: map_key_clear(KEY_KBDILLUMDOWN); break; case 0x07c: map_key_clear(KEY_KBDILLUMTOGGLE); break; @@ -1139,9 +1143,6 @@ static void hidinput_configure_usage(struct hid_input *hidinput, struct hid_fiel case 0x0cd: map_key_clear(KEY_PLAYPAUSE); break; case 0x0cf: map_key_clear(KEY_VOICECOMMAND); break; - case 0x0d5: map_key_clear(KEY_CAMERA_ACCESS_ENABLE); break; - case 0x0d6: map_key_clear(KEY_CAMERA_ACCESS_DISABLE); break; - case 0x0d7: map_key_clear(KEY_CAMERA_ACCESS_TOGGLE); break; case 0x0d8: map_key_clear(KEY_DICTATE); break; case 0x0d9: map_key_clear(KEY_EMOJI_PICKER); break; diff --git a/drivers/hid/hid-logitech-hidpp.c b/drivers/hid/hid-logitech-hidpp.c index dfe8e09a18de..129b01be488d 100644 --- a/drivers/hid/hid-logitech-hidpp.c +++ b/drivers/hid/hid-logitech-hidpp.c @@ -4598,6 +4598,8 @@ static const struct hid_device_id hidpp_devices[] = { { /* Logitech G403 Wireless Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC082) }, + { /* Logitech G502 Lightspeed Wireless Gaming Mouse over USB */ + HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC08D) }, { /* Logitech G703 Gaming Mouse over USB */ HID_USB_DEVICE(USB_VENDOR_ID_LOGITECH, 0xC087) }, { /* Logitech G703 Hero Gaming Mouse over USB */ diff --git a/drivers/hid/hid-nvidia-shield.c b/drivers/hid/hid-nvidia-shield.c index 85700cec5eac..a928ad2be62d 100644 --- a/drivers/hid/hid-nvidia-shield.c +++ b/drivers/hid/hid-nvidia-shield.c @@ -63,12 +63,12 @@ static_assert(sizeof(enum thunderstrike_led_state) == 1); struct thunderstrike_hostcmd_board_info { __le16 revision; __le16 serial[7]; -}; +} __packed; struct thunderstrike_hostcmd_haptics { u8 motor_left; u8 motor_right; -}; +} __packed; struct thunderstrike_hostcmd_resp_report { u8 report_id; /* THUNDERSTRIKE_HOSTCMD_RESP_REPORT_ID */ @@ -81,7 +81,7 @@ struct thunderstrike_hostcmd_resp_report { __le16 fw_version; enum thunderstrike_led_state led_state; u8 payload[30]; - }; + } __packed; } __packed; static_assert(sizeof(struct thunderstrike_hostcmd_resp_report) == THUNDERSTRIKE_HOSTCMD_REPORT_SIZE); @@ -92,15 +92,15 @@ struct thunderstrike_hostcmd_req_report { u8 reserved_at_10; union { - struct { + struct __packed { u8 update; enum thunderstrike_led_state state; } led; - struct { + struct __packed { u8 update; struct thunderstrike_hostcmd_haptics motors; } haptics; - }; + } __packed; u8 reserved_at_30[27]; } __packed; static_assert(sizeof(struct thunderstrike_hostcmd_req_report) == diff --git a/drivers/i2c/busses/i2c-mpc.c b/drivers/i2c/busses/i2c-mpc.c index fb1b640f33b7..f460a7fb4eae 100644 --- a/drivers/i2c/busses/i2c-mpc.c +++ b/drivers/i2c/busses/i2c-mpc.c @@ -307,7 +307,6 @@ static void mpc_i2c_setup_512x(struct device_node *node, { struct device_node *node_ctrl; void __iomem *ctrl; - const u32 *pval; u32 idx; /* Enable I2C interrupts for mpc5121 */ diff --git a/drivers/i2c/busses/i2c-nomadik.c b/drivers/i2c/busses/i2c-nomadik.c index 1e5fd23ef45c..212f412f1c74 100644 --- a/drivers/i2c/busses/i2c-nomadik.c +++ b/drivers/i2c/busses/i2c-nomadik.c @@ -1038,7 +1038,6 @@ static int nmk_i2c_probe(struct amba_device *adev, const struct amba_id *id) static void nmk_i2c_remove(struct amba_device *adev) { - struct resource *res = &adev->res; struct nmk_i2c_dev *dev = amba_get_drvdata(adev); i2c_del_adapter(&dev->adap); @@ -1047,7 +1046,6 @@ static void nmk_i2c_remove(struct amba_device *adev) clear_all_interrupts(dev); /* disable the controller */ i2c_clr_bit(dev->virtbase + I2C_CR, I2C_CR_PE); - release_mem_region(res->start, resource_size(res)); } static struct i2c_vendor_data vendor_stn8815 = { diff --git a/drivers/i2c/busses/i2c-scmi.c b/drivers/i2c/busses/i2c-scmi.c index 104570292241..421735acfa14 100644 --- a/drivers/i2c/busses/i2c-scmi.c +++ b/drivers/i2c/busses/i2c-scmi.c @@ -13,9 +13,6 @@ #include <linux/i2c.h> #include <linux/acpi.h> -/* SMBUS HID definition as supported by Microsoft Windows */ -#define ACPI_SMBUS_MS_HID "SMB0001" - struct smbus_methods_t { char *mt_info; char *mt_sbr; diff --git a/drivers/i2c/busses/i2c-xiic.c b/drivers/i2c/busses/i2c-xiic.c index f879af4def5e..b3bb97762c85 100644 --- a/drivers/i2c/busses/i2c-xiic.c +++ b/drivers/i2c/busses/i2c-xiic.c @@ -721,6 +721,8 @@ static irqreturn_t xiic_process(int irq, void *dev_id) wakeup_req = 1; wakeup_code = STATE_ERROR; } + /* don't try to handle other events */ + goto out; } if (pend & XIIC_INTR_RX_FULL_MASK) { /* Receive register/FIFO is full */ diff --git a/drivers/md/dm-verity-loadpin.c b/drivers/md/dm-verity-loadpin.c index 4f78cc55c251..0666699b6858 100644 --- a/drivers/md/dm-verity-loadpin.c +++ b/drivers/md/dm-verity-loadpin.c @@ -58,6 +58,9 @@ bool dm_verity_loadpin_is_bdev_trusted(struct block_device *bdev) int srcu_idx; bool trusted = false; + if (bdev == NULL) + return false; + if (list_empty(&dm_verity_loadpin_trusted_root_digests)) return false; diff --git a/drivers/memory/ti-emif-sram-pm.S b/drivers/memory/ti-emif-sram-pm.S index d60a8cfd63f3..7756b3971244 100644 --- a/drivers/memory/ti-emif-sram-pm.S +++ b/drivers/memory/ti-emif-sram-pm.S @@ -8,7 +8,7 @@ #include <linux/linkage.h> #include <asm/assembler.h> -#include <asm/memory.h> +#include <asm/page.h> #include "emif.h" #include "ti-emif-asm-offsets.h" diff --git a/drivers/mmc/core/core.c b/drivers/mmc/core/core.c index ec4108a3e5b9..3d3e0ca52614 100644 --- a/drivers/mmc/core/core.c +++ b/drivers/mmc/core/core.c @@ -2199,8 +2199,10 @@ int mmc_card_alternative_gpt_sector(struct mmc_card *card, sector_t *gpt_sector) } EXPORT_SYMBOL(mmc_card_alternative_gpt_sector); -static void __mmc_rescan(struct mmc_host *host) +void mmc_rescan(struct work_struct *work) { + struct mmc_host *host = + container_of(work, struct mmc_host, detect.work); int i; if (host->rescan_disable) @@ -2272,14 +2274,6 @@ static void __mmc_rescan(struct mmc_host *host) mmc_schedule_delayed_work(&host->detect, HZ); } -void mmc_rescan(struct work_struct *work) -{ - struct mmc_host *host = - container_of(work, struct mmc_host, detect.work); - - __mmc_rescan(host); -} - void mmc_start_host(struct mmc_host *host) { host->f_init = max(min(freqs[0], host->f_max), host->f_min); @@ -2292,8 +2286,7 @@ void mmc_start_host(struct mmc_host *host) } mmc_gpiod_request_cd_irq(host); - host->detect_change = 1; - __mmc_rescan(host); + _mmc_detect_change(host, 0, false); } void __mmc_stop_host(struct mmc_host *host) diff --git a/drivers/net/dsa/ocelot/felix.c b/drivers/net/dsa/ocelot/felix.c index 70c0e2b1936b..8da46d284e35 100644 --- a/drivers/net/dsa/ocelot/felix.c +++ b/drivers/net/dsa/ocelot/felix.c @@ -1286,7 +1286,6 @@ static int felix_parse_ports_node(struct felix *felix, if (err < 0) { dev_info(dev, "Unsupported PHY mode %s on port %d\n", phy_modes(phy_mode), port); - of_node_put(child); /* Leave port_phy_modes[port] = 0, which is also * PHY_INTERFACE_MODE_NA. This will perform a @@ -1786,16 +1785,15 @@ static int felix_change_mtu(struct dsa_switch *ds, int port, int new_mtu) { struct ocelot *ocelot = ds->priv; struct ocelot_port *ocelot_port = ocelot->ports[port]; - struct felix *felix = ocelot_to_felix(ocelot); ocelot_port_set_maxlen(ocelot, port, new_mtu); - mutex_lock(&ocelot->tas_lock); + mutex_lock(&ocelot->fwd_domain_lock); - if (ocelot_port->taprio && felix->info->tas_guard_bands_update) - felix->info->tas_guard_bands_update(ocelot, port); + if (ocelot_port->taprio && ocelot->ops->tas_guard_bands_update) + ocelot->ops->tas_guard_bands_update(ocelot, port); - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); return 0; } diff --git a/drivers/net/dsa/ocelot/felix.h b/drivers/net/dsa/ocelot/felix.h index 96008c046da5..1d4befe7cfe8 100644 --- a/drivers/net/dsa/ocelot/felix.h +++ b/drivers/net/dsa/ocelot/felix.h @@ -57,7 +57,6 @@ struct felix_info { void (*mdio_bus_free)(struct ocelot *ocelot); int (*port_setup_tc)(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data); - void (*tas_guard_bands_update)(struct ocelot *ocelot, int port); void (*port_sched_speed_set)(struct ocelot *ocelot, int port, u32 speed); void (*phylink_mac_config)(struct ocelot *ocelot, int port, diff --git a/drivers/net/dsa/ocelot/felix_vsc9959.c b/drivers/net/dsa/ocelot/felix_vsc9959.c index bb39fedd46c7..1c113957fcf4 100644 --- a/drivers/net/dsa/ocelot/felix_vsc9959.c +++ b/drivers/net/dsa/ocelot/felix_vsc9959.c @@ -1209,15 +1209,17 @@ static u32 vsc9959_tas_tc_max_sdu(struct tc_taprio_qopt_offload *taprio, int tc) static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) { struct ocelot_port *ocelot_port = ocelot->ports[port]; + struct ocelot_mm_state *mm = &ocelot->mm[port]; struct tc_taprio_qopt_offload *taprio; u64 min_gate_len[OCELOT_NUM_TC]; + u32 val, maxlen, add_frag_size; + u64 needed_min_frag_time_ps; int speed, picos_per_byte; u64 needed_bit_time_ps; - u32 val, maxlen; u8 tas_speed; int tc; - lockdep_assert_held(&ocelot->tas_lock); + lockdep_assert_held(&ocelot->fwd_domain_lock); taprio = ocelot_port->taprio; @@ -1253,14 +1255,21 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) */ needed_bit_time_ps = (u64)(maxlen + 24) * picos_per_byte; + /* Preemptible TCs don't need to pass a full MTU, the port will + * automatically emit a HOLD request when a preemptible TC gate closes + */ + val = ocelot_read_rix(ocelot, QSYS_PREEMPTION_CFG, port); + add_frag_size = QSYS_PREEMPTION_CFG_MM_ADD_FRAG_SIZE_X(val); + needed_min_frag_time_ps = picos_per_byte * + (u64)(24 + 2 * ethtool_mm_frag_size_add_to_min(add_frag_size)); + dev_dbg(ocelot->dev, - "port %d: max frame size %d needs %llu ps at speed %d\n", - port, maxlen, needed_bit_time_ps, speed); + "port %d: max frame size %d needs %llu ps, %llu ps for mPackets at speed %d\n", + port, maxlen, needed_bit_time_ps, needed_min_frag_time_ps, + speed); vsc9959_tas_min_gate_lengths(taprio, min_gate_len); - mutex_lock(&ocelot->fwd_domain_lock); - for (tc = 0; tc < OCELOT_NUM_TC; tc++) { u32 requested_max_sdu = vsc9959_tas_tc_max_sdu(taprio, tc); u64 remaining_gate_len_ps; @@ -1269,7 +1278,9 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) remaining_gate_len_ps = vsc9959_tas_remaining_gate_len_ps(min_gate_len[tc]); - if (remaining_gate_len_ps > needed_bit_time_ps) { + if ((mm->active_preemptible_tcs & BIT(tc)) ? + remaining_gate_len_ps > needed_min_frag_time_ps : + remaining_gate_len_ps > needed_bit_time_ps) { /* Setting QMAXSDU_CFG to 0 disables oversized frame * dropping. */ @@ -1323,8 +1334,6 @@ static void vsc9959_tas_guard_bands_update(struct ocelot *ocelot, int port) ocelot_write_rix(ocelot, maxlen, QSYS_PORT_MAX_SDU, port); ocelot->ops->cut_through_fwd(ocelot); - - mutex_unlock(&ocelot->fwd_domain_lock); } static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port, @@ -1351,7 +1360,7 @@ static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port, break; } - mutex_lock(&ocelot->tas_lock); + mutex_lock(&ocelot->fwd_domain_lock); ocelot_rmw_rix(ocelot, QSYS_TAG_CONFIG_LINK_SPEED(tas_speed), @@ -1361,7 +1370,7 @@ static void vsc9959_sched_speed_set(struct ocelot *ocelot, int port, if (ocelot_port->taprio) vsc9959_tas_guard_bands_update(ocelot, port); - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); } static void vsc9959_new_base_time(struct ocelot *ocelot, ktime_t base_time, @@ -1409,7 +1418,7 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, int ret, i; u32 val; - mutex_lock(&ocelot->tas_lock); + mutex_lock(&ocelot->fwd_domain_lock); if (taprio->cmd == TAPRIO_CMD_DESTROY) { ocelot_port_mqprio(ocelot, port, &taprio->mqprio); @@ -1421,7 +1430,7 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, vsc9959_tas_guard_bands_update(ocelot, port); - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); return 0; } else if (taprio->cmd != TAPRIO_CMD_REPLACE) { ret = -EOPNOTSUPP; @@ -1504,7 +1513,7 @@ static int vsc9959_qos_port_tas_set(struct ocelot *ocelot, int port, ocelot_port->taprio = taprio_offload_get(taprio); vsc9959_tas_guard_bands_update(ocelot, port); - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); return 0; @@ -1512,7 +1521,7 @@ err_reset_tc: taprio->mqprio.qopt.num_tc = 0; ocelot_port_mqprio(ocelot, port, &taprio->mqprio); err_unlock: - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); return ret; } @@ -1525,7 +1534,7 @@ static void vsc9959_tas_clock_adjust(struct ocelot *ocelot) int port; u32 val; - mutex_lock(&ocelot->tas_lock); + mutex_lock(&ocelot->fwd_domain_lock); for (port = 0; port < ocelot->num_phys_ports; port++) { ocelot_port = ocelot->ports[port]; @@ -1563,7 +1572,7 @@ static void vsc9959_tas_clock_adjust(struct ocelot *ocelot) QSYS_TAG_CONFIG_ENABLE, QSYS_TAG_CONFIG, port); } - mutex_unlock(&ocelot->tas_lock); + mutex_unlock(&ocelot->fwd_domain_lock); } static int vsc9959_qos_port_cbs_set(struct dsa_switch *ds, int port, @@ -1634,6 +1643,18 @@ static int vsc9959_qos_query_caps(struct tc_query_caps_base *base) } } +static int vsc9959_qos_port_mqprio(struct ocelot *ocelot, int port, + struct tc_mqprio_qopt_offload *mqprio) +{ + int ret; + + mutex_lock(&ocelot->fwd_domain_lock); + ret = ocelot_port_mqprio(ocelot, port, mqprio); + mutex_unlock(&ocelot->fwd_domain_lock); + + return ret; +} + static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port, enum tc_setup_type type, void *type_data) @@ -1646,7 +1667,7 @@ static int vsc9959_port_setup_tc(struct dsa_switch *ds, int port, case TC_SETUP_QDISC_TAPRIO: return vsc9959_qos_port_tas_set(ocelot, port, type_data); case TC_SETUP_QDISC_MQPRIO: - return ocelot_port_mqprio(ocelot, port, type_data); + return vsc9959_qos_port_mqprio(ocelot, port, type_data); case TC_SETUP_QDISC_CBS: return vsc9959_qos_port_cbs_set(ds, port, type_data); default: @@ -2591,6 +2612,7 @@ static const struct ocelot_ops vsc9959_ops = { .cut_through_fwd = vsc9959_cut_through_fwd, .tas_clock_adjust = vsc9959_tas_clock_adjust, .update_stats = vsc9959_update_stats, + .tas_guard_bands_update = vsc9959_tas_guard_bands_update, }; static const struct felix_info felix_info_vsc9959 = { @@ -2616,7 +2638,6 @@ static const struct felix_info felix_info_vsc9959 = { .port_modes = vsc9959_port_modes, .port_setup_tc = vsc9959_port_setup_tc, .port_sched_speed_set = vsc9959_sched_speed_set, - .tas_guard_bands_update = vsc9959_tas_guard_bands_update, }; /* The INTB interrupt is shared between for PTP TX timestamp availability diff --git a/drivers/net/dsa/qca/qca8k-8xxx.c b/drivers/net/dsa/qca/qca8k-8xxx.c index f7d7cfb2fd86..09b80644c11b 100644 --- a/drivers/net/dsa/qca/qca8k-8xxx.c +++ b/drivers/net/dsa/qca/qca8k-8xxx.c @@ -588,6 +588,9 @@ qca8k_phy_eth_busy_wait(struct qca8k_mgmt_eth_data *mgmt_eth_data, bool ack; int ret; + if (!skb) + return -ENOMEM; + reinit_completion(&mgmt_eth_data->rw_done); /* Increment seq_num and set it in the copy pkt */ diff --git a/drivers/net/ethernet/amazon/ena/ena_com.c b/drivers/net/ethernet/amazon/ena/ena_com.c index 451c3a1b6255..633b321d7fdd 100644 --- a/drivers/net/ethernet/amazon/ena/ena_com.c +++ b/drivers/net/ethernet/amazon/ena/ena_com.c @@ -35,6 +35,8 @@ #define ENA_REGS_ADMIN_INTR_MASK 1 +#define ENA_MAX_BACKOFF_DELAY_EXP 16U + #define ENA_MIN_ADMIN_POLL_US 100 #define ENA_MAX_ADMIN_POLL_US 5000 @@ -536,6 +538,7 @@ static int ena_com_comp_status_to_errno(struct ena_com_admin_queue *admin_queue, static void ena_delay_exponential_backoff_us(u32 exp, u32 delay_us) { + exp = min_t(u32, exp, ENA_MAX_BACKOFF_DELAY_EXP); delay_us = max_t(u32, ENA_MIN_ADMIN_POLL_US, delay_us); delay_us = min_t(u32, delay_us * (1U << exp), ENA_MAX_ADMIN_POLL_US); usleep_range(delay_us, 2 * delay_us); diff --git a/drivers/net/ethernet/broadcom/bgmac.c b/drivers/net/ethernet/broadcom/bgmac.c index 1761df8fb7f9..10c7c232cc4e 100644 --- a/drivers/net/ethernet/broadcom/bgmac.c +++ b/drivers/net/ethernet/broadcom/bgmac.c @@ -1492,8 +1492,6 @@ int bgmac_enet_probe(struct bgmac *bgmac) bgmac->in_init = true; - bgmac_chip_intrs_off(bgmac); - net_dev->irq = bgmac->irq; SET_NETDEV_DEV(net_dev, bgmac->dev); dev_set_drvdata(bgmac->dev, bgmac); @@ -1511,6 +1509,8 @@ int bgmac_enet_probe(struct bgmac *bgmac) */ bgmac_clk_enable(bgmac, 0); + bgmac_chip_intrs_off(bgmac); + /* This seems to be fixing IRQ by assigning OOB #6 to the core */ if (!(bgmac->feature_flags & BGMAC_FEAT_IDM_MASK)) { if (bgmac->feature_flags & BGMAC_FEAT_IRQ_ID_OOB_6) diff --git a/drivers/net/ethernet/freescale/fec.h b/drivers/net/ethernet/freescale/fec.h index 9939ccafb556..63a053dea819 100644 --- a/drivers/net/ethernet/freescale/fec.h +++ b/drivers/net/ethernet/freescale/fec.h @@ -355,7 +355,7 @@ struct bufdesc_ex { #define RX_RING_SIZE (FEC_ENET_RX_FRPPG * FEC_ENET_RX_PAGES) #define FEC_ENET_TX_FRSIZE 2048 #define FEC_ENET_TX_FRPPG (PAGE_SIZE / FEC_ENET_TX_FRSIZE) -#define TX_RING_SIZE 512 /* Must be power of two */ +#define TX_RING_SIZE 1024 /* Must be power of two */ #define TX_RING_MOD_MASK 511 /* for this to work */ #define BD_ENET_RX_INT 0x00800000 @@ -544,10 +544,23 @@ enum { XDP_STATS_TOTAL, }; +enum fec_txbuf_type { + FEC_TXBUF_T_SKB, + FEC_TXBUF_T_XDP_NDO, +}; + +struct fec_tx_buffer { + union { + struct sk_buff *skb; + struct xdp_frame *xdp; + }; + enum fec_txbuf_type type; +}; + struct fec_enet_priv_tx_q { struct bufdesc_prop bd; unsigned char *tx_bounce[TX_RING_SIZE]; - struct sk_buff *tx_skbuff[TX_RING_SIZE]; + struct fec_tx_buffer tx_buf[TX_RING_SIZE]; unsigned short tx_stop_threshold; unsigned short tx_wake_threshold; diff --git a/drivers/net/ethernet/freescale/fec_main.c b/drivers/net/ethernet/freescale/fec_main.c index 1280da699fa3..1b990a486059 100644 --- a/drivers/net/ethernet/freescale/fec_main.c +++ b/drivers/net/ethernet/freescale/fec_main.c @@ -397,7 +397,7 @@ static void fec_dump(struct net_device *ndev) fec16_to_cpu(bdp->cbd_sc), fec32_to_cpu(bdp->cbd_bufaddr), fec16_to_cpu(bdp->cbd_datlen), - txq->tx_skbuff[index]); + txq->tx_buf[index].skb); bdp = fec_enet_get_nextdesc(bdp, &txq->bd); index++; } while (bdp != txq->bd.base); @@ -654,7 +654,7 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, index = fec_enet_get_bd_index(last_bdp, &txq->bd); /* Save skb pointer */ - txq->tx_skbuff[index] = skb; + txq->tx_buf[index].skb = skb; /* Make sure the updates to rest of the descriptor are performed before * transferring ownership. @@ -672,9 +672,7 @@ static int fec_enet_txq_submit_skb(struct fec_enet_priv_tx_q *txq, skb_tx_timestamp(skb); - /* Make sure the update to bdp and tx_skbuff are performed before - * txq->bd.cur. - */ + /* Make sure the update to bdp is performed before txq->bd.cur. */ wmb(); txq->bd.cur = bdp; @@ -862,7 +860,7 @@ static int fec_enet_txq_submit_tso(struct fec_enet_priv_tx_q *txq, } /* Save skb pointer */ - txq->tx_skbuff[index] = skb; + txq->tx_buf[index].skb = skb; skb_tx_timestamp(skb); txq->bd.cur = bdp; @@ -952,16 +950,33 @@ static void fec_enet_bd_init(struct net_device *dev) for (i = 0; i < txq->bd.ring_size; i++) { /* Initialize the BD for every fragment in the page. */ bdp->cbd_sc = cpu_to_fec16(0); - if (bdp->cbd_bufaddr && - !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) - dma_unmap_single(&fep->pdev->dev, - fec32_to_cpu(bdp->cbd_bufaddr), - fec16_to_cpu(bdp->cbd_datlen), - DMA_TO_DEVICE); - if (txq->tx_skbuff[i]) { - dev_kfree_skb_any(txq->tx_skbuff[i]); - txq->tx_skbuff[i] = NULL; + if (txq->tx_buf[i].type == FEC_TXBUF_T_SKB) { + if (bdp->cbd_bufaddr && + !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) + dma_unmap_single(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + fec16_to_cpu(bdp->cbd_datlen), + DMA_TO_DEVICE); + if (txq->tx_buf[i].skb) { + dev_kfree_skb_any(txq->tx_buf[i].skb); + txq->tx_buf[i].skb = NULL; + } + } else { + if (bdp->cbd_bufaddr) + dma_unmap_single(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + fec16_to_cpu(bdp->cbd_datlen), + DMA_TO_DEVICE); + + if (txq->tx_buf[i].xdp) { + xdp_return_frame(txq->tx_buf[i].xdp); + txq->tx_buf[i].xdp = NULL; + } + + /* restore default tx buffer type: FEC_TXBUF_T_SKB */ + txq->tx_buf[i].type = FEC_TXBUF_T_SKB; } + bdp->cbd_bufaddr = cpu_to_fec32(0); bdp = fec_enet_get_nextdesc(bdp, &txq->bd); } @@ -1360,6 +1375,7 @@ static void fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) { struct fec_enet_private *fep; + struct xdp_frame *xdpf; struct bufdesc *bdp; unsigned short status; struct sk_buff *skb; @@ -1387,16 +1403,31 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) index = fec_enet_get_bd_index(bdp, &txq->bd); - skb = txq->tx_skbuff[index]; - txq->tx_skbuff[index] = NULL; - if (!IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) - dma_unmap_single(&fep->pdev->dev, - fec32_to_cpu(bdp->cbd_bufaddr), - fec16_to_cpu(bdp->cbd_datlen), - DMA_TO_DEVICE); - bdp->cbd_bufaddr = cpu_to_fec32(0); - if (!skb) - goto skb_done; + if (txq->tx_buf[index].type == FEC_TXBUF_T_SKB) { + skb = txq->tx_buf[index].skb; + txq->tx_buf[index].skb = NULL; + if (bdp->cbd_bufaddr && + !IS_TSO_HEADER(txq, fec32_to_cpu(bdp->cbd_bufaddr))) + dma_unmap_single(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + fec16_to_cpu(bdp->cbd_datlen), + DMA_TO_DEVICE); + bdp->cbd_bufaddr = cpu_to_fec32(0); + if (!skb) + goto tx_buf_done; + } else { + xdpf = txq->tx_buf[index].xdp; + if (bdp->cbd_bufaddr) + dma_unmap_single(&fep->pdev->dev, + fec32_to_cpu(bdp->cbd_bufaddr), + fec16_to_cpu(bdp->cbd_datlen), + DMA_TO_DEVICE); + bdp->cbd_bufaddr = cpu_to_fec32(0); + if (!xdpf) { + txq->tx_buf[index].type = FEC_TXBUF_T_SKB; + goto tx_buf_done; + } + } /* Check for errors. */ if (status & (BD_ENET_TX_HB | BD_ENET_TX_LC | @@ -1415,21 +1446,11 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) ndev->stats.tx_carrier_errors++; } else { ndev->stats.tx_packets++; - ndev->stats.tx_bytes += skb->len; - } - - /* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who - * are to time stamp the packet, so we still need to check time - * stamping enabled flag. - */ - if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && - fep->hwts_tx_en) && - fep->bufdesc_ex) { - struct skb_shared_hwtstamps shhwtstamps; - struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; - fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); - skb_tstamp_tx(skb, &shhwtstamps); + if (txq->tx_buf[index].type == FEC_TXBUF_T_SKB) + ndev->stats.tx_bytes += skb->len; + else + ndev->stats.tx_bytes += xdpf->len; } /* Deferred means some collisions occurred during transmit, @@ -1438,10 +1459,32 @@ fec_enet_tx_queue(struct net_device *ndev, u16 queue_id) if (status & BD_ENET_TX_DEF) ndev->stats.collisions++; - /* Free the sk buffer associated with this last transmit */ - dev_kfree_skb_any(skb); -skb_done: - /* Make sure the update to bdp and tx_skbuff are performed + if (txq->tx_buf[index].type == FEC_TXBUF_T_SKB) { + /* NOTE: SKBTX_IN_PROGRESS being set does not imply it's we who + * are to time stamp the packet, so we still need to check time + * stamping enabled flag. + */ + if (unlikely(skb_shinfo(skb)->tx_flags & SKBTX_IN_PROGRESS && + fep->hwts_tx_en) && fep->bufdesc_ex) { + struct skb_shared_hwtstamps shhwtstamps; + struct bufdesc_ex *ebdp = (struct bufdesc_ex *)bdp; + + fec_enet_hwtstamp(fep, fec32_to_cpu(ebdp->ts), &shhwtstamps); + skb_tstamp_tx(skb, &shhwtstamps); + } + + /* Free the sk buffer associated with this last transmit */ + dev_kfree_skb_any(skb); + } else { + xdp_return_frame(xdpf); + + txq->tx_buf[index].xdp = NULL; + /* restore default tx buffer type: FEC_TXBUF_T_SKB */ + txq->tx_buf[index].type = FEC_TXBUF_T_SKB; + } + +tx_buf_done: + /* Make sure the update to bdp and tx_buf are performed * before dirty_tx */ wmb(); @@ -3249,9 +3292,19 @@ static void fec_enet_free_buffers(struct net_device *ndev) for (i = 0; i < txq->bd.ring_size; i++) { kfree(txq->tx_bounce[i]); txq->tx_bounce[i] = NULL; - skb = txq->tx_skbuff[i]; - txq->tx_skbuff[i] = NULL; - dev_kfree_skb(skb); + + if (txq->tx_buf[i].type == FEC_TXBUF_T_SKB) { + skb = txq->tx_buf[i].skb; + txq->tx_buf[i].skb = NULL; + dev_kfree_skb(skb); + } else { + if (txq->tx_buf[i].xdp) { + xdp_return_frame(txq->tx_buf[i].xdp); + txq->tx_buf[i].xdp = NULL; + } + + txq->tx_buf[i].type = FEC_TXBUF_T_SKB; + } } } } @@ -3296,8 +3349,7 @@ static int fec_enet_alloc_queue(struct net_device *ndev) fep->total_tx_ring_size += fep->tx_queue[i]->bd.ring_size; txq->tx_stop_threshold = FEC_MAX_SKB_DESCS; - txq->tx_wake_threshold = - (txq->bd.ring_size - txq->tx_stop_threshold) / 2; + txq->tx_wake_threshold = FEC_MAX_SKB_DESCS + 2 * MAX_SKB_FRAGS; txq->tso_hdrs = dma_alloc_coherent(&fep->pdev->dev, txq->bd.ring_size * TSO_HEADER_SIZE, @@ -3732,12 +3784,18 @@ static int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf) if (fep->quirks & FEC_QUIRK_SWAP_FRAME) return -EOPNOTSUPP; + if (!bpf->prog) + xdp_features_clear_redirect_target(dev); + if (is_run) { napi_disable(&fep->napi); netif_tx_disable(dev); } old_prog = xchg(&fep->xdp_prog, bpf->prog); + if (old_prog) + bpf_prog_put(old_prog); + fec_restart(dev); if (is_run) { @@ -3745,8 +3803,8 @@ static int fec_enet_bpf(struct net_device *dev, struct netdev_bpf *bpf) netif_tx_start_all_queues(dev); } - if (old_prog) - bpf_prog_put(old_prog); + if (bpf->prog) + xdp_features_set_redirect_target(dev, false); return 0; @@ -3778,7 +3836,7 @@ static int fec_enet_txq_xmit_frame(struct fec_enet_private *fep, entries_free = fec_enet_get_free_txdesc_num(txq); if (entries_free < MAX_SKB_FRAGS + 1) { - netdev_err(fep->netdev, "NOT enough BD for SG!\n"); + netdev_err_once(fep->netdev, "NOT enough BD for SG!\n"); return -EBUSY; } @@ -3811,7 +3869,8 @@ static int fec_enet_txq_xmit_frame(struct fec_enet_private *fep, ebdp->cbd_esc = cpu_to_fec32(estatus); } - txq->tx_skbuff[index] = NULL; + txq->tx_buf[index].type = FEC_TXBUF_T_XDP_NDO; + txq->tx_buf[index].xdp = frame; /* Make sure the updates to rest of the descriptor are performed before * transferring ownership. @@ -4016,8 +4075,7 @@ static int fec_enet_init(struct net_device *ndev) if (!(fep->quirks & FEC_QUIRK_SWAP_FRAME)) ndev->xdp_features = NETDEV_XDP_ACT_BASIC | - NETDEV_XDP_ACT_REDIRECT | - NETDEV_XDP_ACT_NDO_XMIT; + NETDEV_XDP_ACT_REDIRECT; fec_restart(ndev); diff --git a/drivers/net/ethernet/google/gve/gve.h b/drivers/net/ethernet/google/gve/gve.h index 98eb78d98e9f..4b425bf71ede 100644 --- a/drivers/net/ethernet/google/gve/gve.h +++ b/drivers/net/ethernet/google/gve/gve.h @@ -964,5 +964,6 @@ void gve_handle_report_stats(struct gve_priv *priv); /* exported by ethtool.c */ extern const struct ethtool_ops gve_ethtool_ops; /* needed by ethtool */ +extern char gve_driver_name[]; extern const char gve_version_str[]; #endif /* _GVE_H_ */ diff --git a/drivers/net/ethernet/google/gve/gve_ethtool.c b/drivers/net/ethernet/google/gve/gve_ethtool.c index cfd4b8d284d1..233e5946905e 100644 --- a/drivers/net/ethernet/google/gve/gve_ethtool.c +++ b/drivers/net/ethernet/google/gve/gve_ethtool.c @@ -15,7 +15,7 @@ static void gve_get_drvinfo(struct net_device *netdev, { struct gve_priv *priv = netdev_priv(netdev); - strscpy(info->driver, "gve", sizeof(info->driver)); + strscpy(info->driver, gve_driver_name, sizeof(info->driver)); strscpy(info->version, gve_version_str, sizeof(info->version)); strscpy(info->bus_info, pci_name(priv->pdev), sizeof(info->bus_info)); } @@ -590,6 +590,9 @@ static int gve_get_link_ksettings(struct net_device *netdev, err = gve_adminq_report_link_speed(priv); cmd->base.speed = priv->link_speed; + + cmd->base.duplex = DUPLEX_FULL; + return err; } diff --git a/drivers/net/ethernet/google/gve/gve_main.c b/drivers/net/ethernet/google/gve/gve_main.c index 8fb70db63b8b..e6f1711d9be0 100644 --- a/drivers/net/ethernet/google/gve/gve_main.c +++ b/drivers/net/ethernet/google/gve/gve_main.c @@ -33,6 +33,7 @@ #define MIN_TX_TIMEOUT_GAP (1000 * 10) #define DQO_TX_MAX 0x3FFFF +char gve_driver_name[] = "gve"; const char gve_version_str[] = GVE_VERSION; static const char gve_version_prefix[] = GVE_VERSION_PREFIX; @@ -2200,7 +2201,7 @@ static int gve_probe(struct pci_dev *pdev, const struct pci_device_id *ent) if (err) return err; - err = pci_request_regions(pdev, "gvnic-cfg"); + err = pci_request_regions(pdev, gve_driver_name); if (err) goto abort_with_enabled; @@ -2393,8 +2394,8 @@ static const struct pci_device_id gve_id_table[] = { { } }; -static struct pci_driver gvnic_driver = { - .name = "gvnic", +static struct pci_driver gve_driver = { + .name = gve_driver_name, .id_table = gve_id_table, .probe = gve_probe, .remove = gve_remove, @@ -2405,10 +2406,10 @@ static struct pci_driver gvnic_driver = { #endif }; -module_pci_driver(gvnic_driver); +module_pci_driver(gve_driver); MODULE_DEVICE_TABLE(pci, gve_id_table); MODULE_AUTHOR("Google, Inc."); -MODULE_DESCRIPTION("gVNIC Driver"); +MODULE_DESCRIPTION("Google Virtual NIC Driver"); MODULE_LICENSE("Dual MIT/GPL"); MODULE_VERSION(GVE_VERSION); diff --git a/drivers/net/ethernet/intel/ice/ice_main.c b/drivers/net/ethernet/intel/ice/ice_main.c index 93979ab18bc1..19a5e7f3a075 100644 --- a/drivers/net/ethernet/intel/ice/ice_main.c +++ b/drivers/net/ethernet/intel/ice/ice_main.c @@ -5739,6 +5739,13 @@ ice_set_tx_maxrate(struct net_device *netdev, int queue_index, u32 maxrate) q_handle = vsi->tx_rings[queue_index]->q_handle; tc = ice_dcb_get_tc(vsi, queue_index); + vsi = ice_locate_vsi_using_queue(vsi, queue_index); + if (!vsi) { + netdev_err(netdev, "Invalid VSI for given queue %d\n", + queue_index); + return -EINVAL; + } + /* Set BW back to default, when user set maxrate to 0 */ if (!maxrate) status = ice_cfg_q_bw_dflt_lmt(vsi->port_info, vsi->idx, tc, @@ -7872,10 +7879,10 @@ static int ice_validate_mqprio_qopt(struct ice_vsi *vsi, struct tc_mqprio_qopt_offload *mqprio_qopt) { - u64 sum_max_rate = 0, sum_min_rate = 0; int non_power_of_2_qcount = 0; struct ice_pf *pf = vsi->back; int max_rss_q_cnt = 0; + u64 sum_min_rate = 0; struct device *dev; int i, speed; u8 num_tc; @@ -7891,6 +7898,7 @@ ice_validate_mqprio_qopt(struct ice_vsi *vsi, dev = ice_pf_to_dev(pf); vsi->ch_rss_size = 0; num_tc = mqprio_qopt->qopt.num_tc; + speed = ice_get_link_speed_kbps(vsi); for (i = 0; num_tc; i++) { int qcount = mqprio_qopt->qopt.count[i]; @@ -7931,7 +7939,6 @@ ice_validate_mqprio_qopt(struct ice_vsi *vsi, */ max_rate = mqprio_qopt->max_rate[i]; max_rate = div_u64(max_rate, ICE_BW_KBPS_DIVISOR); - sum_max_rate += max_rate; /* min_rate is minimum guaranteed rate and it can't be zero */ min_rate = mqprio_qopt->min_rate[i]; @@ -7944,6 +7951,12 @@ ice_validate_mqprio_qopt(struct ice_vsi *vsi, return -EINVAL; } + if (max_rate && max_rate > speed) { + dev_err(dev, "TC%d: max_rate(%llu Kbps) > link speed of %u Kbps\n", + i, max_rate, speed); + return -EINVAL; + } + iter_div_u64_rem(min_rate, ICE_MIN_BW_LIMIT, &rem); if (rem) { dev_err(dev, "TC%d: Min Rate not multiple of %u Kbps", @@ -7981,12 +7994,6 @@ ice_validate_mqprio_qopt(struct ice_vsi *vsi, (mqprio_qopt->qopt.offset[i] + mqprio_qopt->qopt.count[i])) return -EINVAL; - speed = ice_get_link_speed_kbps(vsi); - if (sum_max_rate && sum_max_rate > (u64)speed) { - dev_err(dev, "Invalid max Tx rate(%llu) Kbps > speed(%u) Kbps specified\n", - sum_max_rate, speed); - return -EINVAL; - } if (sum_min_rate && sum_min_rate > (u64)speed) { dev_err(dev, "Invalid min Tx rate(%llu) Kbps > speed (%u) Kbps specified\n", sum_min_rate, speed); diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.c b/drivers/net/ethernet/intel/ice/ice_tc_lib.c index b54052ef6050..4a34ef5f58d3 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.c +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.c @@ -750,17 +750,16 @@ exit: /** * ice_locate_vsi_using_queue - locate VSI using queue (forward to queue action) * @vsi: Pointer to VSI - * @tc_fltr: Pointer to tc_flower_filter + * @queue: Queue index * - * Locate the VSI using specified queue. When ADQ is not enabled, always - * return input VSI, otherwise locate corresponding VSI based on per channel - * offset and qcount + * Locate the VSI using specified "queue". When ADQ is not enabled, + * always return input VSI, otherwise locate corresponding + * VSI based on per channel "offset" and "qcount" */ -static struct ice_vsi * -ice_locate_vsi_using_queue(struct ice_vsi *vsi, - struct ice_tc_flower_fltr *tc_fltr) +struct ice_vsi * +ice_locate_vsi_using_queue(struct ice_vsi *vsi, int queue) { - int num_tc, tc, queue; + int num_tc, tc; /* if ADQ is not active, passed VSI is the candidate VSI */ if (!ice_is_adq_active(vsi->back)) @@ -770,7 +769,6 @@ ice_locate_vsi_using_queue(struct ice_vsi *vsi, * upon queue number) */ num_tc = vsi->mqprio_qopt.qopt.num_tc; - queue = tc_fltr->action.fwd.q.queue; for (tc = 0; tc < num_tc; tc++) { int qcount = vsi->mqprio_qopt.qopt.count[tc]; @@ -812,6 +810,7 @@ ice_tc_forward_action(struct ice_vsi *vsi, struct ice_tc_flower_fltr *tc_fltr) struct ice_pf *pf = vsi->back; struct device *dev; u32 tc_class; + int q; dev = ice_pf_to_dev(pf); @@ -840,7 +839,8 @@ ice_tc_forward_action(struct ice_vsi *vsi, struct ice_tc_flower_fltr *tc_fltr) /* Determine destination VSI even though the action is * FWD_TO_QUEUE, because QUEUE is associated with VSI */ - dest_vsi = tc_fltr->dest_vsi; + q = tc_fltr->action.fwd.q.queue; + dest_vsi = ice_locate_vsi_using_queue(vsi, q); break; default: dev_err(dev, @@ -1716,7 +1716,7 @@ ice_tc_forward_to_queue(struct ice_vsi *vsi, struct ice_tc_flower_fltr *fltr, /* If ADQ is configured, and the queue belongs to ADQ VSI, then prepare * ADQ switch filter */ - ch_vsi = ice_locate_vsi_using_queue(vsi, fltr); + ch_vsi = ice_locate_vsi_using_queue(vsi, fltr->action.fwd.q.queue); if (!ch_vsi) return -EINVAL; fltr->dest_vsi = ch_vsi; diff --git a/drivers/net/ethernet/intel/ice/ice_tc_lib.h b/drivers/net/ethernet/intel/ice/ice_tc_lib.h index 8bbc1a62bdb1..65d387163a46 100644 --- a/drivers/net/ethernet/intel/ice/ice_tc_lib.h +++ b/drivers/net/ethernet/intel/ice/ice_tc_lib.h @@ -204,6 +204,7 @@ static inline int ice_chnl_dmac_fltr_cnt(struct ice_pf *pf) return pf->num_dmac_chnl_fltrs; } +struct ice_vsi *ice_locate_vsi_using_queue(struct ice_vsi *vsi, int queue); int ice_add_cls_flower(struct net_device *netdev, struct ice_vsi *vsi, struct flow_cls_offload *cls_flower); diff --git a/drivers/net/ethernet/intel/igc/igc.h b/drivers/net/ethernet/intel/igc/igc.h index 00a5ee487812..9db384f66a8e 100644 --- a/drivers/net/ethernet/intel/igc/igc.h +++ b/drivers/net/ethernet/intel/igc/igc.h @@ -14,6 +14,7 @@ #include <linux/timecounter.h> #include <linux/net_tstamp.h> #include <linux/bitfield.h> +#include <linux/hrtimer.h> #include "igc_hw.h" @@ -101,6 +102,8 @@ struct igc_ring { u32 start_time; u32 end_time; u32 max_sdu; + bool oper_gate_closed; /* Operating gate. True if the TX Queue is closed */ + bool admin_gate_closed; /* Future gate. True if the TX Queue will be closed */ /* CBS parameters */ bool cbs_enable; /* indicates if CBS is enabled */ @@ -160,6 +163,7 @@ struct igc_adapter { struct timer_list watchdog_timer; struct timer_list dma_err_timer; struct timer_list phy_info_timer; + struct hrtimer hrtimer; u32 wol; u32 en_mng_pt; @@ -184,10 +188,13 @@ struct igc_adapter { u32 max_frame_size; u32 min_frame_size; + int tc_setup_type; ktime_t base_time; ktime_t cycle_time; - bool qbv_enable; + bool taprio_offload_enable; u32 qbv_config_change_errors; + bool qbv_transition; + unsigned int qbv_count; /* OS defined structs */ struct pci_dev *pdev; diff --git a/drivers/net/ethernet/intel/igc/igc_ethtool.c b/drivers/net/ethernet/intel/igc/igc_ethtool.c index 0e2cb00622d1..93bce729be76 100644 --- a/drivers/net/ethernet/intel/igc/igc_ethtool.c +++ b/drivers/net/ethernet/intel/igc/igc_ethtool.c @@ -1708,6 +1708,8 @@ static int igc_ethtool_get_link_ksettings(struct net_device *netdev, /* twisted pair */ cmd->base.port = PORT_TP; cmd->base.phy_address = hw->phy.addr; + ethtool_link_ksettings_add_link_mode(cmd, supported, TP); + ethtool_link_ksettings_add_link_mode(cmd, advertising, TP); /* advertising link modes */ if (hw->phy.autoneg_advertised & ADVERTISE_10_HALF) diff --git a/drivers/net/ethernet/intel/igc/igc_main.c b/drivers/net/ethernet/intel/igc/igc_main.c index 019ce91c45aa..9f93f0f4f752 100644 --- a/drivers/net/ethernet/intel/igc/igc_main.c +++ b/drivers/net/ethernet/intel/igc/igc_main.c @@ -711,7 +711,6 @@ static void igc_configure_tx_ring(struct igc_adapter *adapter, /* disable the queue */ wr32(IGC_TXDCTL(reg_idx), 0); wrfl(); - mdelay(10); wr32(IGC_TDLEN(reg_idx), ring->count * sizeof(union igc_adv_tx_desc)); @@ -1017,7 +1016,7 @@ static __le32 igc_tx_launchtime(struct igc_ring *ring, ktime_t txtime, ktime_t base_time = adapter->base_time; ktime_t now = ktime_get_clocktai(); ktime_t baset_est, end_of_cycle; - u32 launchtime; + s32 launchtime; s64 n; n = div64_s64(ktime_sub_ns(now, base_time), cycle_time); @@ -1030,7 +1029,7 @@ static __le32 igc_tx_launchtime(struct igc_ring *ring, ktime_t txtime, *first_flag = true; ring->last_ff_cycle = baset_est; - if (ktime_compare(txtime, ring->last_tx_cycle) > 0) + if (ktime_compare(end_of_cycle, ring->last_tx_cycle) > 0) *insert_empty = true; } } @@ -1573,16 +1572,12 @@ done: first->bytecount = skb->len; first->gso_segs = 1; - if (tx_ring->max_sdu > 0) { - u32 max_sdu = 0; - - max_sdu = tx_ring->max_sdu + - (skb_vlan_tagged(first->skb) ? VLAN_HLEN : 0); + if (adapter->qbv_transition || tx_ring->oper_gate_closed) + goto out_drop; - if (first->bytecount > max_sdu) { - adapter->stats.txdrop++; - goto out_drop; - } + if (tx_ring->max_sdu > 0 && first->bytecount > tx_ring->max_sdu) { + adapter->stats.txdrop++; + goto out_drop; } if (unlikely(test_bit(IGC_RING_FLAG_TX_HWTSTAMP, &tx_ring->flags) && @@ -3012,8 +3007,8 @@ static bool igc_clean_tx_irq(struct igc_q_vector *q_vector, int napi_budget) time_after(jiffies, tx_buffer->time_stamp + (adapter->tx_timeout_factor * HZ)) && !(rd32(IGC_STATUS) & IGC_STATUS_TXOFF) && - (rd32(IGC_TDH(tx_ring->reg_idx)) != - readl(tx_ring->tail))) { + (rd32(IGC_TDH(tx_ring->reg_idx)) != readl(tx_ring->tail)) && + !tx_ring->oper_gate_closed) { /* detected Tx unit hang */ netdev_err(tx_ring->netdev, "Detected Tx Unit Hang\n" @@ -6102,7 +6097,10 @@ static int igc_tsn_clear_schedule(struct igc_adapter *adapter) adapter->base_time = 0; adapter->cycle_time = NSEC_PER_SEC; + adapter->taprio_offload_enable = false; adapter->qbv_config_change_errors = 0; + adapter->qbv_transition = false; + adapter->qbv_count = 0; for (i = 0; i < adapter->num_tx_queues; i++) { struct igc_ring *ring = adapter->tx_ring[i]; @@ -6110,6 +6108,8 @@ static int igc_tsn_clear_schedule(struct igc_adapter *adapter) ring->start_time = 0; ring->end_time = NSEC_PER_SEC; ring->max_sdu = 0; + ring->oper_gate_closed = false; + ring->admin_gate_closed = false; } return 0; @@ -6121,27 +6121,20 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter, bool queue_configured[IGC_MAX_TX_QUEUES] = { }; struct igc_hw *hw = &adapter->hw; u32 start_time = 0, end_time = 0; + struct timespec64 now; size_t n; int i; - switch (qopt->cmd) { - case TAPRIO_CMD_REPLACE: - adapter->qbv_enable = true; - break; - case TAPRIO_CMD_DESTROY: - adapter->qbv_enable = false; - break; - default: - return -EOPNOTSUPP; - } - - if (!adapter->qbv_enable) + if (qopt->cmd == TAPRIO_CMD_DESTROY) return igc_tsn_clear_schedule(adapter); + if (qopt->cmd != TAPRIO_CMD_REPLACE) + return -EOPNOTSUPP; + if (qopt->base_time < 0) return -ERANGE; - if (igc_is_device_id_i225(hw) && adapter->base_time) + if (igc_is_device_id_i225(hw) && adapter->taprio_offload_enable) return -EALREADY; if (!validate_schedule(adapter, qopt)) @@ -6149,6 +6142,9 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter, adapter->cycle_time = qopt->cycle_time; adapter->base_time = qopt->base_time; + adapter->taprio_offload_enable = true; + + igc_ptp_read(adapter, &now); for (n = 0; n < qopt->num_entries; n++) { struct tc_taprio_sched_entry *e = &qopt->entries[n]; @@ -6184,7 +6180,10 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter, ring->start_time = start_time; ring->end_time = end_time; - queue_configured[i] = true; + if (ring->start_time >= adapter->cycle_time) + queue_configured[i] = false; + else + queue_configured[i] = true; } start_time += e->interval; @@ -6194,8 +6193,20 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter, * If not, set the start and end time to be end time. */ for (i = 0; i < adapter->num_tx_queues; i++) { + struct igc_ring *ring = adapter->tx_ring[i]; + + if (!is_base_time_past(qopt->base_time, &now)) { + ring->admin_gate_closed = false; + } else { + ring->oper_gate_closed = false; + ring->admin_gate_closed = false; + } + if (!queue_configured[i]) { - struct igc_ring *ring = adapter->tx_ring[i]; + if (!is_base_time_past(qopt->base_time, &now)) + ring->admin_gate_closed = true; + else + ring->oper_gate_closed = true; ring->start_time = end_time; ring->end_time = end_time; @@ -6207,7 +6218,7 @@ static int igc_save_qbv_schedule(struct igc_adapter *adapter, struct net_device *dev = adapter->netdev; if (qopt->max_sdu[i]) - ring->max_sdu = qopt->max_sdu[i] + dev->hard_header_len; + ring->max_sdu = qopt->max_sdu[i] + dev->hard_header_len - ETH_TLEN; else ring->max_sdu = 0; } @@ -6327,6 +6338,8 @@ static int igc_setup_tc(struct net_device *dev, enum tc_setup_type type, { struct igc_adapter *adapter = netdev_priv(dev); + adapter->tc_setup_type = type; + switch (type) { case TC_QUERY_CAPS: return igc_tc_query_caps(adapter, type_data); @@ -6574,6 +6587,27 @@ static const struct xdp_metadata_ops igc_xdp_metadata_ops = { .xmo_rx_timestamp = igc_xdp_rx_timestamp, }; +static enum hrtimer_restart igc_qbv_scheduling_timer(struct hrtimer *timer) +{ + struct igc_adapter *adapter = container_of(timer, struct igc_adapter, + hrtimer); + unsigned int i; + + adapter->qbv_transition = true; + for (i = 0; i < adapter->num_tx_queues; i++) { + struct igc_ring *tx_ring = adapter->tx_ring[i]; + + if (tx_ring->admin_gate_closed) { + tx_ring->admin_gate_closed = false; + tx_ring->oper_gate_closed = true; + } else { + tx_ring->oper_gate_closed = false; + } + } + adapter->qbv_transition = false; + return HRTIMER_NORESTART; +} + /** * igc_probe - Device Initialization Routine * @pdev: PCI device information struct @@ -6752,6 +6786,9 @@ static int igc_probe(struct pci_dev *pdev, INIT_WORK(&adapter->reset_task, igc_reset_task); INIT_WORK(&adapter->watchdog_task, igc_watchdog_task); + hrtimer_init(&adapter->hrtimer, CLOCK_MONOTONIC, HRTIMER_MODE_REL); + adapter->hrtimer.function = &igc_qbv_scheduling_timer; + /* Initialize link properties that are user-changeable */ adapter->fc_autoneg = true; hw->mac.autoneg = true; @@ -6855,6 +6892,7 @@ static void igc_remove(struct pci_dev *pdev) cancel_work_sync(&adapter->reset_task); cancel_work_sync(&adapter->watchdog_task); + hrtimer_cancel(&adapter->hrtimer); /* Release control of h/w to f/w. If f/w is AMT enabled, this * would have already happened in close and is redundant. diff --git a/drivers/net/ethernet/intel/igc/igc_ptp.c b/drivers/net/ethernet/intel/igc/igc_ptp.c index 32ef112f8291..f0b979a70655 100644 --- a/drivers/net/ethernet/intel/igc/igc_ptp.c +++ b/drivers/net/ethernet/intel/igc/igc_ptp.c @@ -356,16 +356,35 @@ static int igc_ptp_feature_enable_i225(struct ptp_clock_info *ptp, tsim &= ~IGC_TSICR_TT0; } if (on) { + struct timespec64 safe_start; int i = rq->perout.index; igc_pin_perout(igc, i, pin, use_freq); - igc->perout[i].start.tv_sec = rq->perout.start.sec; + igc_ptp_read(igc, &safe_start); + + /* PPS output start time is triggered by Target time(TT) + * register. Programming any past time value into TT + * register will cause PPS to never start. Need to make + * sure we program the TT register a time ahead in + * future. There isn't a stringent need to fire PPS out + * right away. Adding +2 seconds should take care of + * corner cases. Let's say if the SYSTIML is close to + * wrap up and the timer keeps ticking as we program the + * register, adding +2seconds is safe bet. + */ + safe_start.tv_sec += 2; + + if (rq->perout.start.sec < safe_start.tv_sec) + igc->perout[i].start.tv_sec = safe_start.tv_sec; + else + igc->perout[i].start.tv_sec = rq->perout.start.sec; igc->perout[i].start.tv_nsec = rq->perout.start.nsec; igc->perout[i].period.tv_sec = ts.tv_sec; igc->perout[i].period.tv_nsec = ts.tv_nsec; - wr32(trgttimh, rq->perout.start.sec); + wr32(trgttimh, (u32)igc->perout[i].start.tv_sec); /* For now, always select timer 0 as source. */ - wr32(trgttiml, rq->perout.start.nsec | IGC_TT_IO_TIMER_SEL_SYSTIM0); + wr32(trgttiml, (u32)(igc->perout[i].start.tv_nsec | + IGC_TT_IO_TIMER_SEL_SYSTIM0)); if (use_freq) wr32(freqout, ns); tsauxc |= tsauxc_mask; diff --git a/drivers/net/ethernet/intel/igc/igc_tsn.c b/drivers/net/ethernet/intel/igc/igc_tsn.c index 94a2b0dfb54d..a9c08321aca9 100644 --- a/drivers/net/ethernet/intel/igc/igc_tsn.c +++ b/drivers/net/ethernet/intel/igc/igc_tsn.c @@ -37,7 +37,7 @@ static unsigned int igc_tsn_new_flags(struct igc_adapter *adapter) { unsigned int new_flags = adapter->flags & ~IGC_FLAG_TSN_ANY_ENABLED; - if (adapter->qbv_enable) + if (adapter->taprio_offload_enable) new_flags |= IGC_FLAG_TSN_QBV_ENABLED; if (is_any_launchtime(adapter)) @@ -114,7 +114,6 @@ static int igc_tsn_disable_offload(struct igc_adapter *adapter) static int igc_tsn_enable_offload(struct igc_adapter *adapter) { struct igc_hw *hw = &adapter->hw; - bool tsn_mode_reconfig = false; u32 tqavctrl, baset_l, baset_h; u32 sec, nsec, cycle; ktime_t base_time, systim; @@ -133,8 +132,28 @@ static int igc_tsn_enable_offload(struct igc_adapter *adapter) wr32(IGC_STQT(i), ring->start_time); wr32(IGC_ENDQT(i), ring->end_time); - txqctl |= IGC_TXQCTL_STRICT_CYCLE | - IGC_TXQCTL_STRICT_END; + if (adapter->taprio_offload_enable) { + /* If taprio_offload_enable is set we are in "taprio" + * mode and we need to be strict about the + * cycles: only transmit a packet if it can be + * completed during that cycle. + * + * If taprio_offload_enable is NOT true when + * enabling TSN offload, the cycle should have + * no external effects, but is only used internally + * to adapt the base time register after a second + * has passed. + * + * Enabling strict mode in this case would + * unnecessarily prevent the transmission of + * certain packets (i.e. at the boundary of a + * second) and thus interfere with the launchtime + * feature that promises transmission at a + * certain point in time. + */ + txqctl |= IGC_TXQCTL_STRICT_CYCLE | + IGC_TXQCTL_STRICT_END; + } if (ring->launchtime_enable) txqctl |= IGC_TXQCTL_QUEUE_MODE_LAUNCHT; @@ -228,11 +247,10 @@ skip_cbs: tqavctrl = rd32(IGC_TQAVCTRL) & ~IGC_TQAVCTRL_FUTSCDDIS; - if (tqavctrl & IGC_TQAVCTRL_TRANSMIT_MODE_TSN) - tsn_mode_reconfig = true; - tqavctrl |= IGC_TQAVCTRL_TRANSMIT_MODE_TSN | IGC_TQAVCTRL_ENHANCED_QAV; + adapter->qbv_count++; + cycle = adapter->cycle_time; base_time = adapter->base_time; @@ -249,17 +267,29 @@ skip_cbs: * Gate Control List (GCL) is running. */ if ((rd32(IGC_BASET_H) || rd32(IGC_BASET_L)) && - tsn_mode_reconfig) + (adapter->tc_setup_type == TC_SETUP_QDISC_TAPRIO) && + (adapter->qbv_count > 1)) adapter->qbv_config_change_errors++; } else { - /* According to datasheet section 7.5.2.9.3.3, FutScdDis bit - * has to be configured before the cycle time and base time. - * Tx won't hang if there is a GCL is already running, - * so in this case we don't need to set FutScdDis. - */ - if (igc_is_device_id_i226(hw) && - !(rd32(IGC_BASET_H) || rd32(IGC_BASET_L))) - tqavctrl |= IGC_TQAVCTRL_FUTSCDDIS; + if (igc_is_device_id_i226(hw)) { + ktime_t adjust_time, expires_time; + + /* According to datasheet section 7.5.2.9.3.3, FutScdDis bit + * has to be configured before the cycle time and base time. + * Tx won't hang if a GCL is already running, + * so in this case we don't need to set FutScdDis. + */ + if (!(rd32(IGC_BASET_H) || rd32(IGC_BASET_L))) + tqavctrl |= IGC_TQAVCTRL_FUTSCDDIS; + + nsec = rd32(IGC_SYSTIML); + sec = rd32(IGC_SYSTIMH); + systim = ktime_set(sec, nsec); + + adjust_time = adapter->base_time; + expires_time = ktime_sub_ns(adjust_time, systim); + hrtimer_start(&adapter->hrtimer, expires_time, HRTIMER_MODE_REL); + } } wr32(IGC_TQAVCTRL, tqavctrl); @@ -305,7 +335,11 @@ int igc_tsn_offload_apply(struct igc_adapter *adapter) { struct igc_hw *hw = &adapter->hw; - if (netif_running(adapter->netdev) && igc_is_device_id_i225(hw)) { + /* Per I225/6 HW Design Section 7.5.2.1, transmit mode + * cannot be changed dynamically. Require reset the adapter. + */ + if (netif_running(adapter->netdev) && + (igc_is_device_id_i225(hw) || !adapter->qbv_count)) { schedule_work(&adapter->reset_task); return 0; } diff --git a/drivers/net/ethernet/marvell/mvneta.c b/drivers/net/ethernet/marvell/mvneta.c index ff5647bcdfca..acf4f6ba73a6 100644 --- a/drivers/net/ethernet/marvell/mvneta.c +++ b/drivers/net/ethernet/marvell/mvneta.c @@ -1511,7 +1511,7 @@ static void mvneta_defaults_set(struct mvneta_port *pp) */ if (txq_number == 1) txq_map = (cpu == pp->rxq_def) ? - MVNETA_CPU_TXQ_ACCESS(1) : 0; + MVNETA_CPU_TXQ_ACCESS(0) : 0; } else { txq_map = MVNETA_CPU_TXQ_ACCESS_ALL_MASK; @@ -4356,7 +4356,7 @@ static void mvneta_percpu_elect(struct mvneta_port *pp) */ if (txq_number == 1) txq_map = (cpu == elected_cpu) ? - MVNETA_CPU_TXQ_ACCESS(1) : 0; + MVNETA_CPU_TXQ_ACCESS(0) : 0; else txq_map = mvreg_read(pp, MVNETA_CPU_MAP(cpu)) & MVNETA_CPU_TXQ_ACCESS_ALL_MASK; diff --git a/drivers/net/ethernet/marvell/octeontx2/af/ptp.c b/drivers/net/ethernet/marvell/octeontx2/af/ptp.c index 3411e2e47d46..0ee420a489fc 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/ptp.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/ptp.c @@ -208,7 +208,7 @@ struct ptp *ptp_get(void) /* Check driver is bound to PTP block */ if (!ptp) ptp = ERR_PTR(-EPROBE_DEFER); - else + else if (!IS_ERR(ptp)) pci_dev_get(ptp->pdev); return ptp; @@ -388,11 +388,10 @@ static int ptp_extts_on(struct ptp *ptp, int on) static int ptp_probe(struct pci_dev *pdev, const struct pci_device_id *ent) { - struct device *dev = &pdev->dev; struct ptp *ptp; int err; - ptp = devm_kzalloc(dev, sizeof(*ptp), GFP_KERNEL); + ptp = kzalloc(sizeof(*ptp), GFP_KERNEL); if (!ptp) { err = -ENOMEM; goto error; @@ -428,20 +427,19 @@ static int ptp_probe(struct pci_dev *pdev, return 0; error_free: - devm_kfree(dev, ptp); + kfree(ptp); error: /* For `ptp_get()` we need to differentiate between the case * when the core has not tried to probe this device and the case when - * the probe failed. In the later case we pretend that the - * initialization was successful and keep the error in + * the probe failed. In the later case we keep the error in * `dev->driver_data`. */ pci_set_drvdata(pdev, ERR_PTR(err)); if (!first_ptp_block) first_ptp_block = ERR_PTR(err); - return 0; + return err; } static void ptp_remove(struct pci_dev *pdev) @@ -449,16 +447,17 @@ static void ptp_remove(struct pci_dev *pdev) struct ptp *ptp = pci_get_drvdata(pdev); u64 clock_cfg; - if (cn10k_ptp_errata(ptp) && hrtimer_active(&ptp->hrtimer)) - hrtimer_cancel(&ptp->hrtimer); - if (IS_ERR_OR_NULL(ptp)) return; + if (cn10k_ptp_errata(ptp) && hrtimer_active(&ptp->hrtimer)) + hrtimer_cancel(&ptp->hrtimer); + /* Disable PTP clock */ clock_cfg = readq(ptp->reg_base + PTP_CLOCK_CFG); clock_cfg &= ~PTP_CLOCK_CFG_PTP_EN; writeq(clock_cfg, ptp->reg_base + PTP_CLOCK_CFG); + kfree(ptp); } static const struct pci_device_id ptp_id_table[] = { diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c index 8dbc35c481f6..73df2d564545 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu.c @@ -3252,7 +3252,7 @@ static int rvu_probe(struct pci_dev *pdev, const struct pci_device_id *id) rvu->ptp = ptp_get(); if (IS_ERR(rvu->ptp)) { err = PTR_ERR(rvu->ptp); - if (err == -EPROBE_DEFER) + if (err) goto err_release_regions; rvu->ptp = NULL; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c index 0d745ae1cc9a..04b0e885f9d2 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_nix.c @@ -4069,21 +4069,14 @@ int rvu_mbox_handler_nix_set_rx_mode(struct rvu *rvu, struct nix_rx_mode *req, } /* install/uninstall promisc entry */ - if (promisc) { + if (promisc) rvu_npc_install_promisc_entry(rvu, pcifunc, nixlf, pfvf->rx_chan_base, pfvf->rx_chan_cnt); - - if (rvu_npc_exact_has_match_table(rvu)) - rvu_npc_exact_promisc_enable(rvu, pcifunc); - } else { + else if (!nix_rx_multicast) rvu_npc_enable_promisc_entry(rvu, pcifunc, nixlf, false); - if (rvu_npc_exact_has_match_table(rvu)) - rvu_npc_exact_promisc_disable(rvu, pcifunc); - } - return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c index 9f11c1e40737..6fe67f3a7f6f 100644 --- a/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c +++ b/drivers/net/ethernet/marvell/octeontx2/af/rvu_npc_hash.c @@ -1164,8 +1164,10 @@ static u16 __rvu_npc_exact_cmd_rules_cnt_update(struct rvu *rvu, int drop_mcam_i { struct npc_exact_table *table; u16 *cnt, old_cnt; + bool promisc; table = rvu->hw->table; + promisc = table->promisc_mode[drop_mcam_idx]; cnt = &table->cnt_cmd_rules[drop_mcam_idx]; old_cnt = *cnt; @@ -1177,13 +1179,18 @@ static u16 __rvu_npc_exact_cmd_rules_cnt_update(struct rvu *rvu, int drop_mcam_i *enable_or_disable_cam = false; - /* If all rules are deleted, disable cam */ + if (promisc) + goto done; + + /* If all rules are deleted and not already in promisc mode; + * disable cam + */ if (!*cnt && val < 0) { *enable_or_disable_cam = true; goto done; } - /* If rule got added, enable cam */ + /* If rule got added and not already in promisc mode; enable cam */ if (!old_cnt && val > 0) { *enable_or_disable_cam = true; goto done; @@ -1462,6 +1469,12 @@ int rvu_npc_exact_promisc_disable(struct rvu *rvu, u16 pcifunc) *promisc = false; mutex_unlock(&table->lock); + /* Enable drop rule */ + rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, + true); + + dev_dbg(rvu->dev, "%s: disabled promisc mode (cgx=%d lmac=%d)\n", + __func__, cgx_id, lmac_id); return 0; } @@ -1503,6 +1516,12 @@ int rvu_npc_exact_promisc_enable(struct rvu *rvu, u16 pcifunc) *promisc = true; mutex_unlock(&table->lock); + /* disable drop rule */ + rvu_npc_enable_mcam_by_entry_index(rvu, drop_mcam_idx, NIX_INTF_RX, + false); + + dev_dbg(rvu->dev, "%s: Enabled promisc mode (cgx=%d lmac=%d)\n", + __func__, cgx_id, lmac_id); return 0; } diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c index 10e11262d48a..2d7713a1a153 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_flows.c @@ -872,6 +872,14 @@ static int otx2_prepare_flow_request(struct ethtool_rx_flow_spec *fsp, return -EINVAL; vlan_etype = be16_to_cpu(fsp->h_ext.vlan_etype); + + /* Drop rule with vlan_etype == 802.1Q + * and vlan_id == 0 is not supported + */ + if (vlan_etype == ETH_P_8021Q && !fsp->m_ext.vlan_tci && + fsp->ring_cookie == RX_CLS_FLOW_DISC) + return -EINVAL; + /* Only ETH_P_8021Q and ETH_P_802AD types supported */ if (vlan_etype != ETH_P_8021Q && vlan_etype != ETH_P_8021AD) diff --git a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c index 8a13df592af6..5e56b6c3e60a 100644 --- a/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c +++ b/drivers/net/ethernet/marvell/octeontx2/nic/otx2_tc.c @@ -597,6 +597,21 @@ static int otx2_tc_prepare_flow(struct otx2_nic *nic, struct otx2_tc_flow *node, return -EOPNOTSUPP; } + if (!match.mask->vlan_id) { + struct flow_action_entry *act; + int i; + + flow_action_for_each(i, act, &rule->action) { + if (act->id == FLOW_ACTION_DROP) { + netdev_err(nic->netdev, + "vlan tpid 0x%x with vlan_id %d is not supported for DROP rule.\n", + ntohs(match.key->vlan_tpid), + match.key->vlan_id); + return -EOPNOTSUPP; + } + } + } + if (match.mask->vlan_id || match.mask->vlan_dei || match.mask->vlan_priority) { diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c index 03cb79adf912..be83ad9db82a 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/fs_tt_redirect.c @@ -594,7 +594,7 @@ int mlx5e_fs_tt_redirect_any_create(struct mlx5e_flow_steering *fs) err = fs_any_create_table(fs); if (err) - return err; + goto err_free_any; err = fs_any_enable(fs); if (err) @@ -606,8 +606,8 @@ int mlx5e_fs_tt_redirect_any_create(struct mlx5e_flow_steering *fs) err_destroy_table: fs_any_destroy_table(fs_any); - - kfree(fs_any); +err_free_any: mlx5e_fs_set_any(fs, NULL); + kfree(fs_any); return err; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c index 3cbebfba582b..b0b429a0321e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/ptp.c @@ -729,8 +729,10 @@ int mlx5e_ptp_open(struct mlx5e_priv *priv, struct mlx5e_params *params, c = kvzalloc_node(sizeof(*c), GFP_KERNEL, dev_to_node(mlx5_core_dma_dev(mdev))); cparams = kvzalloc(sizeof(*cparams), GFP_KERNEL); - if (!c || !cparams) - return -ENOMEM; + if (!c || !cparams) { + err = -ENOMEM; + goto err_free; + } c->priv = priv; c->mdev = priv->mdev; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c index a254e728ac95..fadfa8b50beb 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.c @@ -1545,7 +1545,8 @@ mlx5_tc_ct_parse_action(struct mlx5_tc_ct_priv *priv, attr->ct_attr.ct_action |= act->ct.action; /* So we can have clear + ct */ attr->ct_attr.zone = act->ct.zone; - attr->ct_attr.nf_ft = act->ct.flow_table; + if (!(act->ct.action & TCA_CT_ACT_CLEAR)) + attr->ct_attr.nf_ft = act->ct.flow_table; attr->ct_attr.act_miss_cookie = act->miss_cookie; return 0; @@ -1990,6 +1991,9 @@ mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *priv, struct mlx5_flow_attr *att if (!priv) return -EOPNOTSUPP; + if (attr->ct_attr.offloaded) + return 0; + if (attr->ct_attr.ct_action & TCA_CT_ACT_CLEAR) { err = mlx5_tc_ct_entry_set_registers(priv, &attr->parse_attr->mod_hdr_acts, 0, 0, 0, 0); @@ -1999,11 +2003,15 @@ mlx5_tc_ct_flow_offload(struct mlx5_tc_ct_priv *priv, struct mlx5_flow_attr *att attr->action |= MLX5_FLOW_CONTEXT_ACTION_MOD_HDR; } - if (!attr->ct_attr.nf_ft) /* means only ct clear action, and not ct_clear,ct() */ + if (!attr->ct_attr.nf_ft) { /* means only ct clear action, and not ct_clear,ct() */ + attr->ct_attr.offloaded = true; return 0; + } mutex_lock(&priv->control_lock); err = __mlx5_tc_ct_flow_offload(priv, attr); + if (!err) + attr->ct_attr.offloaded = true; mutex_unlock(&priv->control_lock); return err; @@ -2021,7 +2029,7 @@ void mlx5_tc_ct_delete_flow(struct mlx5_tc_ct_priv *priv, struct mlx5_flow_attr *attr) { - if (!attr->ct_attr.ft) /* no ct action, return */ + if (!attr->ct_attr.offloaded) /* no ct action, return */ return; if (!attr->ct_attr.nf_ft) /* means only ct clear action, and not ct_clear,ct() */ return; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.h b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.h index 8e9316fa46d4..b66c5f98067f 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.h +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/tc_ct.h @@ -29,6 +29,7 @@ struct mlx5_ct_attr { u32 ct_labels_id; u32 act_miss_mapping; u64 act_miss_cookie; + bool offloaded; struct mlx5_ct_ft *ft; }; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c index f0e6095809fa..40589cebb773 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en/xdp.c @@ -662,8 +662,7 @@ static void mlx5e_free_xdpsq_desc(struct mlx5e_xdpsq *sq, /* No need to check ((page->pp_magic & ~0x3UL) == PP_SIGNATURE) * as we know this is a page_pool page. */ - page_pool_put_defragged_page(page->pp, - page, -1, true); + page_pool_recycle_direct(page->pp, page); } while (++n < num); break; diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c index 88a5aed9d678..c7d191f66ad1 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_accel/fs_tcp.c @@ -190,6 +190,7 @@ static int accel_fs_tcp_create_groups(struct mlx5e_flow_table *ft, in = kvzalloc(inlen, GFP_KERNEL); if (!in || !ft->g) { kfree(ft->g); + ft->g = NULL; kvfree(in); return -ENOMEM; } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c index 704b022cd1f0..41d37159e027 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_rx.c @@ -390,10 +390,18 @@ static void mlx5e_dealloc_rx_wqe(struct mlx5e_rq *rq, u16 ix) { struct mlx5e_wqe_frag_info *wi = get_frag(rq, ix); - if (rq->xsk_pool) + if (rq->xsk_pool) { mlx5e_xsk_free_rx_wqe(wi); - else + } else { mlx5e_free_rx_wqe(rq, wi); + + /* Avoid a second release of the wqe pages: dealloc is called + * for the same missing wqes on regular RQ flush and on regular + * RQ close. This happens when XSK RQs come into play. + */ + for (int i = 0; i < rq->wqe.info.num_frags; i++, wi++) + wi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE); + } } static void mlx5e_xsk_free_rx_wqes(struct mlx5e_rq *rq, u16 ix, int wqe_bulk) @@ -1743,11 +1751,11 @@ mlx5e_skb_from_cqe_nonlinear(struct mlx5e_rq *rq, struct mlx5e_wqe_frag_info *wi prog = rcu_dereference(rq->xdp_prog); if (prog && mlx5e_xdp_handle(rq, prog, &mxbuf)) { - if (test_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) { + if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) { struct mlx5e_wqe_frag_info *pwi; for (pwi = head_wi; pwi < wi; pwi++) - pwi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE); + pwi->frag_page->frags++; } return NULL; /* page/packet was consumed by XDP */ } @@ -1817,12 +1825,8 @@ static void mlx5e_handle_rx_cqe(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) rq, wi, cqe, cqe_bcnt); if (!skb) { /* probably for XDP */ - if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) { - /* do not return page to cache, - * it will be returned on XDP_TX completion. - */ - wi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE); - } + if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) + wi->frag_page->frags++; goto wq_cyc_pop; } @@ -1868,12 +1872,8 @@ static void mlx5e_handle_rx_cqe_rep(struct mlx5e_rq *rq, struct mlx5_cqe64 *cqe) rq, wi, cqe, cqe_bcnt); if (!skb) { /* probably for XDP */ - if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) { - /* do not return page to cache, - * it will be returned on XDP_TX completion. - */ - wi->flags |= BIT(MLX5E_WQE_FRAG_SKIP_RELEASE); - } + if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) + wi->frag_page->frags++; goto wq_cyc_pop; } @@ -2052,12 +2052,12 @@ mlx5e_skb_from_cqe_mpwrq_nonlinear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *w if (prog) { if (mlx5e_xdp_handle(rq, prog, &mxbuf)) { if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) { - int i; + struct mlx5e_frag_page *pfp; + + for (pfp = head_page; pfp < frag_page; pfp++) + pfp->frags++; - for (i = 0; i < sinfo->nr_frags; i++) - /* non-atomic */ - __set_bit(page_idx + i, wi->skip_release_bitmap); - return NULL; + wi->linear_page.frags++; } mlx5e_page_release_fragmented(rq, &wi->linear_page); return NULL; /* page/packet was consumed by XDP */ @@ -2155,7 +2155,7 @@ mlx5e_skb_from_cqe_mpwrq_linear(struct mlx5e_rq *rq, struct mlx5e_mpw_info *wi, cqe_bcnt, &mxbuf); if (mlx5e_xdp_handle(rq, prog, &mxbuf)) { if (__test_and_clear_bit(MLX5E_RQ_FLAG_XDP_XMIT, rq->flags)) - __set_bit(page_idx, wi->skip_release_bitmap); /* non-atomic */ + frag_page->frags++; return NULL; /* page/packet was consumed by XDP */ } diff --git a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c index 41dc26800f48..8d0a3f69693e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/en_tc.c @@ -1639,7 +1639,8 @@ static void remove_unready_flow(struct mlx5e_tc_flow *flow) uplink_priv = &rpriv->uplink_priv; mutex_lock(&uplink_priv->unready_flows_lock); - unready_flow_del(flow); + if (flow_flag_test(flow, NOT_READY)) + unready_flow_del(flow); mutex_unlock(&uplink_priv->unready_flows_lock); } @@ -1932,8 +1933,7 @@ static void mlx5e_tc_del_fdb_flow(struct mlx5e_priv *priv, esw_attr = attr->esw_attr; mlx5e_put_flow_tunnel_id(flow); - if (flow_flag_test(flow, NOT_READY)) - remove_unready_flow(flow); + remove_unready_flow(flow); if (mlx5e_is_offloaded_flow(flow)) { if (flow_flag_test(flow, SLOW)) diff --git a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c index faec7d7a4400..243c455f1029 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/eswitch.c @@ -807,6 +807,9 @@ static int mlx5_esw_vport_caps_get(struct mlx5_eswitch *esw, struct mlx5_vport * hca_caps = MLX5_ADDR_OF(query_hca_cap_out, query_ctx, capability); vport->info.roce_enabled = MLX5_GET(cmd_hca_cap, hca_caps, roce); + if (!MLX5_CAP_GEN_MAX(esw->dev, hca_cap_2)) + goto out_free; + memset(query_ctx, 0, query_out_sz); err = mlx5_vport_get_other_func_cap(esw->dev, vport->vport, query_ctx, MLX5_CAP_GENERAL_2); diff --git a/drivers/net/ethernet/mellanox/mlx5/core/thermal.c b/drivers/net/ethernet/mellanox/mlx5/core/thermal.c index 20bb5eb266c1..52199d39657e 100644 --- a/drivers/net/ethernet/mellanox/mlx5/core/thermal.c +++ b/drivers/net/ethernet/mellanox/mlx5/core/thermal.c @@ -68,14 +68,19 @@ static struct thermal_zone_device_ops mlx5_thermal_ops = { int mlx5_thermal_init(struct mlx5_core_dev *mdev) { + char data[THERMAL_NAME_LENGTH]; struct mlx5_thermal *thermal; - struct thermal_zone_device *tzd; - const char *data = "mlx5"; + int err; - tzd = thermal_zone_get_zone_by_name(data); - if (!IS_ERR(tzd)) + if (!mlx5_core_is_pf(mdev) && !mlx5_core_is_ecpf(mdev)) return 0; + err = snprintf(data, sizeof(data), "mlx5_%s", dev_name(mdev->device)); + if (err < 0 || err >= sizeof(data)) { + mlx5_core_err(mdev, "Failed to setup thermal zone name, %d\n", err); + return -EINVAL; + } + thermal = kzalloc(sizeof(*thermal), GFP_KERNEL); if (!thermal) return -ENOMEM; @@ -89,10 +94,10 @@ int mlx5_thermal_init(struct mlx5_core_dev *mdev) &mlx5_thermal_ops, NULL, 0, MLX5_THERMAL_POLL_INT_MSEC); if (IS_ERR(thermal->tzdev)) { - dev_err(mdev->device, "Failed to register thermal zone device (%s) %ld\n", - data, PTR_ERR(thermal->tzdev)); + err = PTR_ERR(thermal->tzdev); + mlx5_core_err(mdev, "Failed to register thermal zone device (%s) %d\n", data, err); kfree(thermal); - return -EINVAL; + return err; } mdev->thermal = thermal; diff --git a/drivers/net/ethernet/microchip/Kconfig b/drivers/net/ethernet/microchip/Kconfig index 24c994baad13..329e374b9539 100644 --- a/drivers/net/ethernet/microchip/Kconfig +++ b/drivers/net/ethernet/microchip/Kconfig @@ -46,7 +46,7 @@ config LAN743X tristate "LAN743x support" depends on PCI depends on PTP_1588_CLOCK_OPTIONAL - select PHYLIB + select FIXED_PHY select CRC16 select CRC32 help diff --git a/drivers/net/ethernet/mscc/ocelot.c b/drivers/net/ethernet/mscc/ocelot.c index 2fa833d041ba..56ccbd4c37fe 100644 --- a/drivers/net/ethernet/mscc/ocelot.c +++ b/drivers/net/ethernet/mscc/ocelot.c @@ -2927,7 +2927,6 @@ int ocelot_init(struct ocelot *ocelot) mutex_init(&ocelot->mact_lock); mutex_init(&ocelot->fwd_domain_lock); - mutex_init(&ocelot->tas_lock); spin_lock_init(&ocelot->ptp_clock_lock); spin_lock_init(&ocelot->ts_id_lock); diff --git a/drivers/net/ethernet/mscc/ocelot_mm.c b/drivers/net/ethernet/mscc/ocelot_mm.c index fb3145118d68..c815ae64e39d 100644 --- a/drivers/net/ethernet/mscc/ocelot_mm.c +++ b/drivers/net/ethernet/mscc/ocelot_mm.c @@ -67,10 +67,13 @@ void ocelot_port_update_active_preemptible_tcs(struct ocelot *ocelot, int port) val = mm->preemptible_tcs; /* Cut through switching doesn't work for preemptible priorities, - * so first make sure it is disabled. + * so first make sure it is disabled. Also, changing the preemptible + * TCs affects the oversized frame dropping logic, so that needs to be + * re-triggered. And since tas_guard_bands_update() also implicitly + * calls cut_through_fwd(), we don't need to explicitly call it. */ mm->active_preemptible_tcs = val; - ocelot->ops->cut_through_fwd(ocelot); + ocelot->ops->tas_guard_bands_update(ocelot, port); dev_dbg(ocelot->dev, "port %d %s/%s, MM TX %s, preemptible TCs 0x%x, active 0x%x\n", @@ -89,17 +92,14 @@ void ocelot_port_change_fp(struct ocelot *ocelot, int port, { struct ocelot_mm_state *mm = &ocelot->mm[port]; - mutex_lock(&ocelot->fwd_domain_lock); + lockdep_assert_held(&ocelot->fwd_domain_lock); if (mm->preemptible_tcs == preemptible_tcs) - goto out_unlock; + return; mm->preemptible_tcs = preemptible_tcs; ocelot_port_update_active_preemptible_tcs(ocelot, port); - -out_unlock: - mutex_unlock(&ocelot->fwd_domain_lock); } static void ocelot_mm_update_port_status(struct ocelot *ocelot, int port) diff --git a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c index b8678da1cce5..ab7d217b98b3 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_bus_pci.c @@ -353,12 +353,6 @@ err_out_reset: ionic_reset(ionic); err_out_teardown: ionic_dev_teardown(ionic); - pci_clear_master(pdev); - /* Don't fail the probe for these errors, keep - * the hw interface around for inspection - */ - return 0; - err_out_unmap_bars: ionic_unmap_bars(ionic); err_out_pci_release_regions: diff --git a/drivers/net/ethernet/pensando/ionic/ionic_lif.c b/drivers/net/ethernet/pensando/ionic/ionic_lif.c index 7c20a44e549b..612b0015dc43 100644 --- a/drivers/net/ethernet/pensando/ionic/ionic_lif.c +++ b/drivers/net/ethernet/pensando/ionic/ionic_lif.c @@ -475,11 +475,6 @@ static void ionic_qcqs_free(struct ionic_lif *lif) static void ionic_link_qcq_interrupts(struct ionic_qcq *src_qcq, struct ionic_qcq *n_qcq) { - if (WARN_ON(n_qcq->flags & IONIC_QCQ_F_INTR)) { - ionic_intr_free(n_qcq->cq.lif->ionic, n_qcq->intr.index); - n_qcq->flags &= ~IONIC_QCQ_F_INTR; - } - n_qcq->intr.vector = src_qcq->intr.vector; n_qcq->intr.index = src_qcq->intr.index; n_qcq->napi_qcq = src_qcq->napi_qcq; diff --git a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c index 12405d71c5ee..0772eb14eabf 100644 --- a/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c +++ b/drivers/net/ethernet/wangxun/txgbe/txgbe_hw.c @@ -186,9 +186,6 @@ static int txgbe_calc_eeprom_checksum(struct wx *wx, u16 *checksum) if (eeprom_ptrs) kvfree(eeprom_ptrs); - if (*checksum > TXGBE_EEPROM_SUM) - return -EINVAL; - *checksum = TXGBE_EEPROM_SUM - *checksum; return 0; diff --git a/drivers/net/netdevsim/dev.c b/drivers/net/netdevsim/dev.c index 6045bece2654..b4d3b9cde8bd 100644 --- a/drivers/net/netdevsim/dev.c +++ b/drivers/net/netdevsim/dev.c @@ -184,13 +184,10 @@ static ssize_t nsim_dev_trap_fa_cookie_write(struct file *file, cookie_len = (count - 1) / 2; if ((count - 1) % 2) return -EINVAL; - buf = kmalloc(count, GFP_KERNEL | __GFP_NOWARN); - if (!buf) - return -ENOMEM; - ret = simple_write_to_buffer(buf, count, ppos, data, count); - if (ret < 0) - goto free_buf; + buf = memdup_user(data, count); + if (IS_ERR(buf)) + return PTR_ERR(buf); fa_cookie = kmalloc(sizeof(*fa_cookie) + cookie_len, GFP_KERNEL | __GFP_NOWARN); diff --git a/drivers/net/ntb_netdev.c b/drivers/net/ntb_netdev.c index 85dbe7f73e31..536bd6564f8b 100644 --- a/drivers/net/ntb_netdev.c +++ b/drivers/net/ntb_netdev.c @@ -493,7 +493,7 @@ static int __init ntb_netdev_init_module(void) return 0; } -module_init(ntb_netdev_init_module); +late_initcall(ntb_netdev_init_module); static void __exit ntb_netdev_exit_module(void) { diff --git a/drivers/net/wireless/cisco/airo.c b/drivers/net/wireless/cisco/airo.c index 7c4cc5f5e1eb..dbd13f7aa3e6 100644 --- a/drivers/net/wireless/cisco/airo.c +++ b/drivers/net/wireless/cisco/airo.c @@ -6157,8 +6157,11 @@ static int airo_get_rate(struct net_device *dev, struct iw_param *vwrq = &wrqu->bitrate; struct airo_info *local = dev->ml_priv; StatusRid status_rid; /* Card status info */ + int ret; - readStatusRid(local, &status_rid, 1); + ret = readStatusRid(local, &status_rid, 1); + if (ret) + return -EBUSY; vwrq->value = le16_to_cpu(status_rid.currentXmitRate) * 500000; /* If more than one rate, set auto */ diff --git a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c index aa4320ca4c30..d594694206b3 100644 --- a/drivers/net/wireless/intel/iwlwifi/cfg/22000.c +++ b/drivers/net/wireless/intel/iwlwifi/cfg/22000.c @@ -84,7 +84,6 @@ const struct iwl_ht_params iwl_22000_ht_params = { .mac_addr_from_csr = 0x380, \ .ht_params = &iwl_22000_ht_params, \ .nvm_ver = IWL_22000_NVM_VERSION, \ - .trans.use_tfh = true, \ .trans.rf_id = true, \ .trans.gen2 = true, \ .nvm_type = IWL_NVM_EXT, \ @@ -122,7 +121,6 @@ const struct iwl_ht_params iwl_22000_ht_params = { const struct iwl_cfg_trans_params iwl_qu_trans_cfg = { .mq_rx_supported = true, - .use_tfh = true, .rf_id = true, .gen2 = true, .device_family = IWL_DEVICE_FAMILY_22000, @@ -134,7 +132,6 @@ const struct iwl_cfg_trans_params iwl_qu_trans_cfg = { const struct iwl_cfg_trans_params iwl_qu_medium_latency_trans_cfg = { .mq_rx_supported = true, - .use_tfh = true, .rf_id = true, .gen2 = true, .device_family = IWL_DEVICE_FAMILY_22000, @@ -146,7 +143,6 @@ const struct iwl_cfg_trans_params iwl_qu_medium_latency_trans_cfg = { const struct iwl_cfg_trans_params iwl_qu_long_latency_trans_cfg = { .mq_rx_supported = true, - .use_tfh = true, .rf_id = true, .gen2 = true, .device_family = IWL_DEVICE_FAMILY_22000, @@ -200,7 +196,6 @@ const struct iwl_cfg_trans_params iwl_ax200_trans_cfg = { .device_family = IWL_DEVICE_FAMILY_22000, .base_params = &iwl_22000_base_params, .mq_rx_supported = true, - .use_tfh = true, .rf_id = true, .gen2 = true, .bisr_workaround = 1, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-config.h b/drivers/net/wireless/intel/iwlwifi/iwl-config.h index 742096c5a36a..241a9e3f2a1a 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-config.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-config.h @@ -256,7 +256,6 @@ enum iwl_cfg_trans_ltr_delay { * @xtal_latency: power up latency to get the xtal stabilized * @extra_phy_cfg_flags: extra configuration flags to pass to the PHY * @rf_id: need to read rf_id to determine the firmware image - * @use_tfh: use TFH * @gen2: 22000 and on transport operation * @mq_rx_supported: multi-queue rx support * @integrated: discrete or integrated @@ -271,7 +270,6 @@ struct iwl_cfg_trans_params { u32 xtal_latency; u32 extra_phy_cfg_flags; u32 rf_id:1, - use_tfh:1, gen2:1, mq_rx_supported:1, integrated:1, diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h index bedd78a47f67..4e4a60ddf9b2 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-fh.h +++ b/drivers/net/wireless/intel/iwlwifi/iwl-fh.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2005-2014, 2018-2021 Intel Corporation + * Copyright (C) 2005-2014, 2018-2021, 2023 Intel Corporation * Copyright (C) 2015-2017 Intel Deutschland GmbH */ #ifndef __iwl_fh_h__ @@ -71,7 +71,7 @@ static inline unsigned int FH_MEM_CBBC_QUEUE(struct iwl_trans *trans, unsigned int chnl) { - if (trans->trans_cfg->use_tfh) { + if (trans->trans_cfg->gen2) { WARN_ON_ONCE(chnl >= 64); return TFH_TFDQ_CBB_TABLE + 8 * chnl; } diff --git a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c index b1af9359cea5..4bd759432d44 100644 --- a/drivers/net/wireless/intel/iwlwifi/iwl-trans.c +++ b/drivers/net/wireless/intel/iwlwifi/iwl-trans.c @@ -2,7 +2,7 @@ /* * Copyright (C) 2015 Intel Mobile Communications GmbH * Copyright (C) 2016-2017 Intel Deutschland GmbH - * Copyright (C) 2019-2021 Intel Corporation + * Copyright (C) 2019-2021, 2023 Intel Corporation */ #include <linux/kernel.h> #include <linux/bsearch.h> @@ -42,7 +42,7 @@ struct iwl_trans *iwl_trans_alloc(unsigned int priv_size, WARN_ON(!ops->wait_txq_empty && !ops->wait_tx_queues_empty); - if (trans->trans_cfg->use_tfh) { + if (trans->trans_cfg->gen2) { trans->txqs.tfd.addr_size = 64; trans->txqs.tfd.max_tbs = IWL_TFH_NUM_TBS; trans->txqs.tfd.size = sizeof(struct iwl_tfh_tfd); @@ -101,7 +101,7 @@ int iwl_trans_init(struct iwl_trans *trans) /* Some things must not change even if the config does */ WARN_ON(trans->txqs.tfd.addr_size != - (trans->trans_cfg->use_tfh ? 64 : 36)); + (trans->trans_cfg->gen2 ? 64 : 36)); snprintf(trans->dev_cmd_pool_name, sizeof(trans->dev_cmd_pool_name), "iwl_cmd_pool:%s", dev_name(trans->dev)); diff --git a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h index b83df0631279..b18c91c5dd5d 100644 --- a/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h +++ b/drivers/net/wireless/intel/iwlwifi/mvm/mvm.h @@ -1450,7 +1450,7 @@ static inline bool iwl_mvm_has_new_station_api(const struct iwl_fw *fw) static inline bool iwl_mvm_has_new_tx_api(struct iwl_mvm *mvm) { /* TODO - replace with TLV once defined */ - return mvm->trans->trans_cfg->use_tfh; + return mvm->trans->trans_cfg->gen2; } static inline bool iwl_mvm_has_unified_ucode(struct iwl_mvm *mvm) diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c index eacbbdbffb5e..3e988da44973 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/trans.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/trans.c @@ -819,7 +819,7 @@ static int iwl_pcie_load_cpu_sections_8000(struct iwl_trans *trans, iwl_enable_interrupts(trans); - if (trans->trans_cfg->use_tfh) { + if (trans->trans_cfg->gen2) { if (cpu == 1) iwl_write_prph(trans, UREG_UCODE_LOAD_STATUS, 0xFFFF); @@ -3394,7 +3394,7 @@ iwl_trans_pcie_dump_data(struct iwl_trans *trans, u8 tfdidx; u32 caplen, cmdlen; - if (trans->trans_cfg->use_tfh) + if (trans->trans_cfg->gen2) tfdidx = idx; else tfdidx = ptr; diff --git a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c index 1337fa95f657..790e5b124740 100644 --- a/drivers/net/wireless/intel/iwlwifi/pcie/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/pcie/tx.c @@ -364,7 +364,7 @@ void iwl_trans_pcie_tx_reset(struct iwl_trans *trans) for (txq_id = 0; txq_id < trans->trans_cfg->base_params->num_of_queues; txq_id++) { struct iwl_txq *txq = trans->txqs.txq[txq_id]; - if (trans->trans_cfg->use_tfh) + if (trans->trans_cfg->gen2) iwl_write_direct64(trans, FH_MEM_CBBC_QUEUE(trans, txq_id), txq->dma_addr); diff --git a/drivers/net/wireless/intel/iwlwifi/queue/tx.c b/drivers/net/wireless/intel/iwlwifi/queue/tx.c index fbacbe9ada15..5bb3cc3367c9 100644 --- a/drivers/net/wireless/intel/iwlwifi/queue/tx.c +++ b/drivers/net/wireless/intel/iwlwifi/queue/tx.c @@ -985,7 +985,7 @@ void iwl_txq_log_scd_error(struct iwl_trans *trans, struct iwl_txq *txq) bool active; u8 fifo; - if (trans->trans_cfg->use_tfh) { + if (trans->trans_cfg->gen2) { IWL_ERR(trans, "Queue %d is stuck %d %d\n", txq_id, txq->read_ptr, txq->write_ptr); /* TODO: access new SCD registers and dump them */ @@ -1040,7 +1040,7 @@ int iwl_txq_alloc(struct iwl_trans *trans, struct iwl_txq *txq, int slots_num, if (WARN_ON(txq->entries || txq->tfds)) return -EINVAL; - if (trans->trans_cfg->use_tfh) + if (trans->trans_cfg->gen2) tfd_sz = trans->txqs.tfd.size * slots_num; timer_setup(&txq->stuck_timer, iwl_txq_stuck_timer, 0); @@ -1347,7 +1347,7 @@ static inline dma_addr_t iwl_txq_gen1_tfd_tb_get_addr(struct iwl_trans *trans, dma_addr_t addr; dma_addr_t hi_len; - if (trans->trans_cfg->use_tfh) { + if (trans->trans_cfg->gen2) { struct iwl_tfh_tfd *tfh_tfd = _tfd; struct iwl_tfh_tb *tfh_tb = &tfh_tfd->tbs[idx]; @@ -1408,7 +1408,7 @@ void iwl_txq_gen1_tfd_unmap(struct iwl_trans *trans, meta->tbs = 0; - if (trans->trans_cfg->use_tfh) { + if (trans->trans_cfg->gen2) { struct iwl_tfh_tfd *tfd_fh = (void *)tfd; tfd_fh->num_tbs = 0; @@ -1625,7 +1625,7 @@ void iwl_txq_reclaim(struct iwl_trans *trans, int txq_id, int ssn, txq->entries[read_ptr].skb = NULL; - if (!trans->trans_cfg->use_tfh) + if (!trans->trans_cfg->gen2) iwl_txq_gen1_inval_byte_cnt_tbl(trans, txq); iwl_txq_free_tfd(trans, txq); diff --git a/drivers/net/wireless/intel/iwlwifi/queue/tx.h b/drivers/net/wireless/intel/iwlwifi/queue/tx.h index eca53bfd326d..1e4a24ab9bab 100644 --- a/drivers/net/wireless/intel/iwlwifi/queue/tx.h +++ b/drivers/net/wireless/intel/iwlwifi/queue/tx.h @@ -1,6 +1,6 @@ /* SPDX-License-Identifier: GPL-2.0 OR BSD-3-Clause */ /* - * Copyright (C) 2020-2022 Intel Corporation + * Copyright (C) 2020-2023 Intel Corporation */ #ifndef __iwl_trans_queue_tx_h__ #define __iwl_trans_queue_tx_h__ @@ -38,7 +38,7 @@ static inline void iwl_wake_queue(struct iwl_trans *trans, static inline void *iwl_txq_get_tfd(struct iwl_trans *trans, struct iwl_txq *txq, int idx) { - if (trans->trans_cfg->use_tfh) + if (trans->trans_cfg->gen2) idx = iwl_txq_get_cmd_index(txq, idx); return (u8 *)txq->tfds + trans->txqs.tfd.size * idx; @@ -135,7 +135,7 @@ static inline u8 iwl_txq_gen1_tfd_get_num_tbs(struct iwl_trans *trans, { struct iwl_tfd *tfd; - if (trans->trans_cfg->use_tfh) { + if (trans->trans_cfg->gen2) { struct iwl_tfh_tfd *tfh_tfd = _tfd; return le16_to_cpu(tfh_tfd->num_tbs) & 0x1f; @@ -151,7 +151,7 @@ static inline u16 iwl_txq_gen1_tfd_tb_get_len(struct iwl_trans *trans, struct iwl_tfd *tfd; struct iwl_tfd_tb *tb; - if (trans->trans_cfg->use_tfh) { + if (trans->trans_cfg->gen2) { struct iwl_tfh_tfd *tfh_tfd = _tfd; struct iwl_tfh_tb *tfh_tb = &tfh_tfd->tbs[idx]; diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/dma.c b/drivers/net/wireless/mediatek/mt76/mt7921/dma.c index f0a80c2b476a..4153cd6c2a01 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/dma.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/dma.c @@ -231,10 +231,6 @@ int mt7921_dma_init(struct mt7921_dev *dev) if (ret) return ret; - ret = mt7921_wfsys_reset(dev); - if (ret) - return ret; - /* init tx queue */ ret = mt76_connac_init_tx_queues(dev->phy.mt76, MT7921_TXQ_BAND0, MT7921_TX_RING_SIZE, diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c index c69ce6df4956..f55caa00ac69 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/mcu.c @@ -476,12 +476,6 @@ static int mt7921_load_firmware(struct mt7921_dev *dev) { int ret; - ret = mt76_get_field(dev, MT_CONN_ON_MISC, MT_TOP_MISC2_FW_N9_RDY); - if (ret && mt76_is_mmio(&dev->mt76)) { - dev_dbg(dev->mt76.dev, "Firmware is already download\n"); - goto fw_loaded; - } - ret = mt76_connac2_load_patch(&dev->mt76, mt7921_patch_name(dev)); if (ret) return ret; @@ -504,8 +498,6 @@ static int mt7921_load_firmware(struct mt7921_dev *dev) return -EIO; } -fw_loaded: - #ifdef CONFIG_PM dev->mt76.hw->wiphy->wowlan = &mt76_connac_wowlan_support; #endif /* CONFIG_PM */ diff --git a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c index ddb1fa4ee01d..95610a117d2f 100644 --- a/drivers/net/wireless/mediatek/mt76/mt7921/pci.c +++ b/drivers/net/wireless/mediatek/mt76/mt7921/pci.c @@ -325,6 +325,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev, bus_ops->rmw = mt7921_rmw; dev->mt76.bus = bus_ops; + ret = mt7921e_mcu_fw_pmctrl(dev); + if (ret) + goto err_free_dev; + ret = __mt7921e_mcu_drv_pmctrl(dev); if (ret) goto err_free_dev; @@ -333,6 +337,10 @@ static int mt7921_pci_probe(struct pci_dev *pdev, (mt7921_l1_rr(dev, MT_HW_REV) & 0xff); dev_info(mdev->dev, "ASIC revision: %04x\n", mdev->rev); + ret = mt7921_wfsys_reset(dev); + if (ret) + goto err_free_dev; + mt76_wr(dev, MT_WFDMA0_HOST_INT_ENA, 0); mt76_wr(dev, MT_PCIE_MAC_INT_ENABLE, 0xff); diff --git a/drivers/net/wireless/realtek/rtw89/debug.c b/drivers/net/wireless/realtek/rtw89/debug.c index 1db2d59d33ff..a4bbac916e22 100644 --- a/drivers/net/wireless/realtek/rtw89/debug.c +++ b/drivers/net/wireless/realtek/rtw89/debug.c @@ -3026,17 +3026,18 @@ static ssize_t rtw89_debug_priv_send_h2c_set(struct file *filp, struct rtw89_debugfs_priv *debugfs_priv = filp->private_data; struct rtw89_dev *rtwdev = debugfs_priv->rtwdev; u8 *h2c; + int ret; u16 h2c_len = count / 2; h2c = rtw89_hex2bin_user(rtwdev, user_buf, count); if (IS_ERR(h2c)) return -EFAULT; - rtw89_fw_h2c_raw(rtwdev, h2c, h2c_len); + ret = rtw89_fw_h2c_raw(rtwdev, h2c, h2c_len); kfree(h2c); - return count; + return ret ? ret : count; } static int diff --git a/drivers/net/xen-netback/netback.c b/drivers/net/xen-netback/netback.c index 3d79b35eb577..c8d20cddf658 100644 --- a/drivers/net/xen-netback/netback.c +++ b/drivers/net/xen-netback/netback.c @@ -689,7 +689,7 @@ static void xenvif_fill_frags(struct xenvif_queue *queue, struct sk_buff *skb) prev_pending_idx = pending_idx; txp = &queue->pending_tx_info[pending_idx].req; - page = virt_to_page(idx_to_kaddr(queue, pending_idx)); + page = virt_to_page((void *)idx_to_kaddr(queue, pending_idx)); __skb_fill_page_desc(skb, i, page, txp->offset, txp->size); skb->len += txp->size; skb->data_len += txp->size; diff --git a/drivers/ntb/hw/amd/ntb_hw_amd.c b/drivers/ntb/hw/amd/ntb_hw_amd.c index 04550b1f984c..4940b6301d83 100644 --- a/drivers/ntb/hw/amd/ntb_hw_amd.c +++ b/drivers/ntb/hw/amd/ntb_hw_amd.c @@ -941,7 +941,7 @@ static void ndev_init_debugfs(struct amd_ntb_dev *ndev) ndev->debugfs_dir = debugfs_create_dir(pci_name(ndev->ntb.pdev), debugfs_dir); - if (!ndev->debugfs_dir) + if (IS_ERR(ndev->debugfs_dir)) ndev->debugfs_info = NULL; else ndev->debugfs_info = @@ -1194,7 +1194,6 @@ static int amd_ntb_init_pci(struct amd_ntb_dev *ndev, return 0; err_dma_mask: - pci_clear_master(pdev); pci_release_regions(pdev); err_pci_regions: pci_disable_device(pdev); @@ -1209,7 +1208,6 @@ static void amd_ntb_deinit_pci(struct amd_ntb_dev *ndev) pci_iounmap(pdev, ndev->self_mmio); - pci_clear_master(pdev); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); @@ -1338,12 +1336,17 @@ static struct pci_driver amd_ntb_pci_driver = { static int __init amd_ntb_pci_driver_init(void) { + int ret; pr_info("%s %s\n", NTB_DESC, NTB_VER); if (debugfs_initialized()) debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); - return pci_register_driver(&amd_ntb_pci_driver); + ret = pci_register_driver(&amd_ntb_pci_driver); + if (ret) + debugfs_remove_recursive(debugfs_dir); + + return ret; } module_init(amd_ntb_pci_driver_init); diff --git a/drivers/ntb/hw/epf/ntb_hw_epf.c b/drivers/ntb/hw/epf/ntb_hw_epf.c index 3ece49cb18ff..b640aa0bf45e 100644 --- a/drivers/ntb/hw/epf/ntb_hw_epf.c +++ b/drivers/ntb/hw/epf/ntb_hw_epf.c @@ -591,7 +591,7 @@ static int ntb_epf_init_pci(struct ntb_epf_dev *ndev, ret = dma_set_mask_and_coherent(dev, DMA_BIT_MASK(32)); if (ret) { dev_err(dev, "Cannot set DMA mask\n"); - goto err_dma_mask; + goto err_pci_regions; } dev_warn(&pdev->dev, "Cannot DMA highmem\n"); } @@ -599,14 +599,14 @@ static int ntb_epf_init_pci(struct ntb_epf_dev *ndev, ndev->ctrl_reg = pci_iomap(pdev, ndev->ctrl_reg_bar, 0); if (!ndev->ctrl_reg) { ret = -EIO; - goto err_dma_mask; + goto err_pci_regions; } if (ndev->peer_spad_reg_bar) { ndev->peer_spad_reg = pci_iomap(pdev, ndev->peer_spad_reg_bar, 0); if (!ndev->peer_spad_reg) { ret = -EIO; - goto err_dma_mask; + goto err_pci_regions; } } else { spad_sz = 4 * readl(ndev->ctrl_reg + NTB_EPF_SPAD_COUNT); @@ -617,14 +617,11 @@ static int ntb_epf_init_pci(struct ntb_epf_dev *ndev, ndev->db_reg = pci_iomap(pdev, ndev->db_reg_bar, 0); if (!ndev->db_reg) { ret = -EIO; - goto err_dma_mask; + goto err_pci_regions; } return 0; -err_dma_mask: - pci_clear_master(pdev); - err_pci_regions: pci_disable_device(pdev); @@ -642,7 +639,6 @@ static void ntb_epf_deinit_pci(struct ntb_epf_dev *ndev) pci_iounmap(pdev, ndev->peer_spad_reg); pci_iounmap(pdev, ndev->db_reg); - pci_clear_master(pdev); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); diff --git a/drivers/ntb/hw/idt/ntb_hw_idt.c b/drivers/ntb/hw/idt/ntb_hw_idt.c index 0ed6f809ff2e..48823b53ede3 100644 --- a/drivers/ntb/hw/idt/ntb_hw_idt.c +++ b/drivers/ntb/hw/idt/ntb_hw_idt.c @@ -2651,20 +2651,18 @@ static int idt_init_pci(struct idt_ntb_dev *ndev) } /* - * Enable the device advanced error reporting. It's not critical to + * The PCI core enables device error reporting. It's not critical to * have AER disabled in the kernel. + * + * Cleanup nonfatal error status before getting to init. */ - ret = pci_enable_pcie_error_reporting(pdev); - if (ret != 0) - dev_warn(&pdev->dev, "PCIe AER capability disabled\n"); - else /* Cleanup nonfatal error status before getting to init */ - pci_aer_clear_nonfatal_status(pdev); + pci_aer_clear_nonfatal_status(pdev); /* First enable the PCI device */ ret = pcim_enable_device(pdev); if (ret != 0) { dev_err(&pdev->dev, "Failed to enable PCIe device\n"); - goto err_disable_aer; + return ret; } /* @@ -2692,8 +2690,6 @@ static int idt_init_pci(struct idt_ntb_dev *ndev) err_clear_master: pci_clear_master(pdev); -err_disable_aer: - (void)pci_disable_pcie_error_reporting(pdev); return ret; } @@ -2714,9 +2710,6 @@ static void idt_deinit_pci(struct idt_ntb_dev *ndev) /* Clear the bus master disabling the Request TLPs translation */ pci_clear_master(pdev); - /* Disable the AER capability */ - (void)pci_disable_pcie_error_reporting(pdev); - dev_dbg(&pdev->dev, "NT-function PCIe interface cleared"); } @@ -2891,6 +2884,7 @@ static struct pci_driver idt_pci_driver = { static int __init idt_pci_driver_init(void) { + int ret; pr_info("%s %s\n", NTB_DESC, NTB_VER); /* Create the top DebugFS directory if the FS is initialized */ @@ -2898,7 +2892,11 @@ static int __init idt_pci_driver_init(void) dbgfs_topdir = debugfs_create_dir(KBUILD_MODNAME, NULL); /* Register the NTB hardware driver to handle the PCI device */ - return pci_register_driver(&idt_pci_driver); + ret = pci_register_driver(&idt_pci_driver); + if (ret) + debugfs_remove_recursive(dbgfs_topdir); + + return ret; } module_init(idt_pci_driver_init); diff --git a/drivers/ntb/hw/intel/ntb_hw_gen1.c b/drivers/ntb/hw/intel/ntb_hw_gen1.c index 84772013812b..9ab836d0d4f1 100644 --- a/drivers/ntb/hw/intel/ntb_hw_gen1.c +++ b/drivers/ntb/hw/intel/ntb_hw_gen1.c @@ -1791,7 +1791,6 @@ static int intel_ntb_init_pci(struct intel_ntb_dev *ndev, struct pci_dev *pdev) err_mmio: err_dma_mask: - pci_clear_master(pdev); pci_release_regions(pdev); err_pci_regions: pci_disable_device(pdev); @@ -1808,7 +1807,6 @@ static void intel_ntb_deinit_pci(struct intel_ntb_dev *ndev) pci_iounmap(pdev, ndev->peer_mmio); pci_iounmap(pdev, ndev->self_mmio); - pci_clear_master(pdev); pci_release_regions(pdev); pci_disable_device(pdev); pci_set_drvdata(pdev, NULL); @@ -2064,12 +2062,17 @@ static struct pci_driver intel_ntb_pci_driver = { static int __init intel_ntb_pci_driver_init(void) { + int ret; pr_info("%s %s\n", NTB_DESC, NTB_VER); if (debugfs_initialized()) debugfs_dir = debugfs_create_dir(KBUILD_MODNAME, NULL); - return pci_register_driver(&intel_ntb_pci_driver); + ret = pci_register_driver(&intel_ntb_pci_driver); + if (ret) + debugfs_remove_recursive(debugfs_dir); + + return ret; } module_init(intel_ntb_pci_driver_init); diff --git a/drivers/ntb/ntb_transport.c b/drivers/ntb/ntb_transport.c index a9b97ebc71ac..2abd2235bbca 100644 --- a/drivers/ntb/ntb_transport.c +++ b/drivers/ntb/ntb_transport.c @@ -410,7 +410,7 @@ int ntb_transport_register_client_dev(char *device_name) rc = device_register(dev); if (rc) { - kfree(client_dev); + put_device(dev); goto err; } diff --git a/drivers/ntb/test/ntb_tool.c b/drivers/ntb/test/ntb_tool.c index 5ee0afa621a9..eeeb4b1c97d2 100644 --- a/drivers/ntb/test/ntb_tool.c +++ b/drivers/ntb/test/ntb_tool.c @@ -998,6 +998,8 @@ static int tool_init_mws(struct tool_ctx *tc) tc->peers[pidx].outmws = devm_kcalloc(&tc->ntb->dev, tc->peers[pidx].outmw_cnt, sizeof(*tc->peers[pidx].outmws), GFP_KERNEL); + if (tc->peers[pidx].outmws == NULL) + return -ENOMEM; for (widx = 0; widx < tc->peers[pidx].outmw_cnt; widx++) { tc->peers[pidx].outmws[widx].pidx = pidx; diff --git a/drivers/pci/controller/dwc/pcie-designware.c b/drivers/pci/controller/dwc/pcie-designware.c index df092229e97d..c87848cd8686 100644 --- a/drivers/pci/controller/dwc/pcie-designware.c +++ b/drivers/pci/controller/dwc/pcie-designware.c @@ -834,7 +834,7 @@ static int dw_pcie_edma_irq_vector(struct device *dev, unsigned int nr) return platform_get_irq_byname_optional(pdev, name); } -static struct dw_edma_core_ops dw_pcie_edma_ops = { +static struct dw_edma_plat_ops dw_pcie_edma_ops = { .irq_vector = dw_pcie_edma_irq_vector, }; diff --git a/drivers/pci/endpoint/functions/pci-epf-vntb.c b/drivers/pci/endpoint/functions/pci-epf-vntb.c index 0f5c8f8be847..c8b423c3c26e 100644 --- a/drivers/pci/endpoint/functions/pci-epf-vntb.c +++ b/drivers/pci/endpoint/functions/pci-epf-vntb.c @@ -1285,6 +1285,7 @@ static int pci_vntb_probe(struct pci_dev *pdev, const struct pci_device_id *id) return 0; err_register_dev: + put_device(&ndev->ntb.dev); return -EINVAL; } diff --git a/drivers/phy/amlogic/phy-meson-g12a-usb2.c b/drivers/phy/amlogic/phy-meson-g12a-usb2.c index 9d1efa0d9394..ec2555bb83d5 100644 --- a/drivers/phy/amlogic/phy-meson-g12a-usb2.c +++ b/drivers/phy/amlogic/phy-meson-g12a-usb2.c @@ -172,10 +172,16 @@ static int phy_meson_g12a_usb2_init(struct phy *phy) int ret; unsigned int value; - ret = reset_control_reset(priv->reset); + ret = clk_prepare_enable(priv->clk); if (ret) return ret; + ret = reset_control_reset(priv->reset); + if (ret) { + clk_disable_unprepare(priv->clk); + return ret; + } + udelay(RESET_COMPLETE_TIME); /* usb2_otg_aca_en == 0 */ @@ -277,8 +283,13 @@ static int phy_meson_g12a_usb2_init(struct phy *phy) static int phy_meson_g12a_usb2_exit(struct phy *phy) { struct phy_meson_g12a_usb2_priv *priv = phy_get_drvdata(phy); + int ret; + + ret = reset_control_reset(priv->reset); + if (!ret) + clk_disable_unprepare(priv->clk); - return reset_control_reset(priv->reset); + return ret; } /* set_mode is not needed, mode setting is handled via the UTMI bus */ diff --git a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c index 76cf4280d7ed..4c10cafded4e 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init-synopsys.c @@ -59,6 +59,8 @@ #define USB_CTLR_TP_DIAG1_wake_MASK BIT(1) #define USB_CTRL_CTLR_CSHCR 0x50 #define USB_CTRL_CTLR_CSHCR_ctl_pme_en_MASK BIT(18) +#define USB_CTRL_P0_U2PHY_CFG1 0x68 +#define USB_CTRL_P0_U2PHY_CFG1_COMMONONN_MASK BIT(10) /* Register definitions for the USB_PHY block in 7211b0 */ #define USB_PHY_PLL_CTL 0x00 @@ -90,6 +92,8 @@ #define BDC_EC_AXIRDA_RTS_MASK GENMASK(31, 28) #define BDC_EC_AXIRDA_RTS_SHIFT 28 +#define USB_XHCI_GBL_GUSB2PHYCFG 0x100 +#define USB_XHCI_GBL_GUSB2PHYCFG_U2_FREECLK_EXISTS_MASK BIT(30) static void usb_mdio_write_7211b0(struct brcm_usb_init_params *params, uint8_t addr, uint16_t data) @@ -140,13 +144,17 @@ static void xhci_soft_reset(struct brcm_usb_init_params *params, int on_off) { void __iomem *ctrl = params->regs[BRCM_REGS_CTRL]; + void __iomem *xhci_gbl = params->regs[BRCM_REGS_XHCI_GBL]; /* Assert reset */ - if (on_off) + if (on_off) { USB_CTRL_UNSET(ctrl, USB_PM, XHC_SOFT_RESETB); /* De-assert reset */ - else + } else { USB_CTRL_SET(ctrl, USB_PM, XHC_SOFT_RESETB); + /* Required for COMMONONN to be set */ + USB_XHCI_GBL_UNSET(xhci_gbl, GUSB2PHYCFG, U2_FREECLK_EXISTS); + } } static void usb_init_ipp(struct brcm_usb_init_params *params) @@ -320,6 +328,9 @@ static void usb_init_common_7216(struct brcm_usb_init_params *params) /* 1 millisecond - for USB clocks to settle down */ usleep_range(1000, 2000); + /* Disable PHY when port is suspended */ + USB_CTRL_SET(ctrl, P0_U2PHY_CFG1, COMMONONN); + usb_wake_enable_7216(params, false); usb_init_common(params); } diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.c b/drivers/phy/broadcom/phy-brcm-usb-init.c index a1ca83308f98..39536b6d96a9 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.c +++ b/drivers/phy/broadcom/phy-brcm-usb-init.c @@ -35,6 +35,11 @@ #define USB_CTRL_SETUP_STRAP_IPP_SEL_MASK BIT(25) /* option */ #define USB_CTRL_SETUP_CC_DRD_MODE_ENABLE_MASK BIT(26) /* option */ #define USB_CTRL_SETUP_STRAP_CC_DRD_MODE_ENABLE_SEL_MASK BIT(27) /* opt */ +#define USB_CTRL_SETUP_OC_DISABLE_PORT0_MASK BIT(28) +#define USB_CTRL_SETUP_OC_DISABLE_PORT1_MASK BIT(29) +#define USB_CTRL_SETUP_OC_DISABLE_MASK GENMASK(29, 28) /* option */ +#define USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK BIT(30) +#define USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK BIT(31) #define USB_CTRL_SETUP_OC3_DISABLE_MASK GENMASK(31, 30) /* option */ #define USB_CTRL_PLL_CTL 0x04 #define USB_CTRL_PLL_CTL_PLL_SUSPEND_EN_MASK BIT(27) @@ -114,6 +119,8 @@ enum { USB_CTRL_SETUP_SCB2_EN_SELECTOR, USB_CTRL_SETUP_SS_EHCI64BIT_EN_SELECTOR, USB_CTRL_SETUP_STRAP_IPP_SEL_SELECTOR, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_SELECTOR, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_SELECTOR, USB_CTRL_SETUP_OC3_DISABLE_SELECTOR, USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_SELECTOR, USB_CTRL_USB_PM_BDC_SOFT_RESETB_SELECTOR, @@ -190,6 +197,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -232,6 +241,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -253,6 +264,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, @@ -274,6 +287,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -295,6 +310,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -316,6 +333,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK */ + 0, /* USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK */ 0, /* USB_CTRL_SETUP_OC3_DISABLE_MASK */ USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -337,6 +356,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, @@ -358,6 +379,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { USB_CTRL_SETUP_SCB2_EN_MASK, USB_CTRL_SETUP_SS_EHCI64BIT_EN_VAR_MASK, 0, /* USB_CTRL_SETUP_STRAP_IPP_SEL_MASK */ + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK, 0, /* USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK */ @@ -379,6 +402,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK, USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, @@ -400,6 +425,8 @@ usb_reg_bits_map_table[BRCM_FAMILY_COUNT][USB_CTRL_SELECTOR_COUNT] = { 0, /* USB_CTRL_SETUP_SCB2_EN_MASK */ 0, /*USB_CTRL_SETUP_SS_EHCI64BIT_EN_MASK */ USB_CTRL_SETUP_STRAP_IPP_SEL_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT0_MASK, + USB_CTRL_SETUP_OC3_DISABLE_PORT1_MASK, USB_CTRL_SETUP_OC3_DISABLE_MASK, 0, /* USB_CTRL_PLL_CTL_PLL_IDDQ_PWRDN_MASK */ USB_CTRL_USB_PM_BDC_SOFT_RESETB_MASK, @@ -872,6 +899,13 @@ static void usb_init_common(struct brcm_usb_init_params *params) brcmusb_memc_fix(params); + /* Workaround for false positive OC for 7439b2 in DRD/Device mode */ + if ((params->family_id == 0x74390012) && + (params->supported_port_modes != USB_CTLR_MODE_HOST)) { + USB_CTRL_SET(ctrl, SETUP, OC_DISABLE_PORT1); + USB_CTRL_SET_FAMILY(params, SETUP, OC3_DISABLE_PORT1); + } + if (USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, PORT_MODE)) { reg = brcm_usb_readl(USB_CTRL_REG(ctrl, USB_DEVICE_CTL1)); reg &= ~USB_CTRL_MASK_FAMILY(params, USB_DEVICE_CTL1, diff --git a/drivers/phy/broadcom/phy-brcm-usb-init.h b/drivers/phy/broadcom/phy-brcm-usb-init.h index f9fbf8fb80e5..c1a88f5cd4cd 100644 --- a/drivers/phy/broadcom/phy-brcm-usb-init.h +++ b/drivers/phy/broadcom/phy-brcm-usb-init.h @@ -34,6 +34,14 @@ enum brcmusb_reg_sel { brcm_usb_ctrl_unset(USB_CTRL_REG(base, reg), \ USB_CTRL_##reg##_##field##_MASK) +#define USB_XHCI_GBL_REG(base, reg) ((void __iomem *)base + USB_XHCI_GBL_##reg) +#define USB_XHCI_GBL_SET(base, reg, field) \ + brcm_usb_ctrl_set(USB_XHCI_GBL_REG(base, reg), \ + USB_XHCI_GBL_##reg##_##field##_MASK) +#define USB_XHCI_GBL_UNSET(base, reg, field) \ + brcm_usb_ctrl_unset(USB_XHCI_GBL_REG(base, reg), \ + USB_XHCI_GBL_##reg##_##field##_MASK) + struct brcm_usb_init_params; struct brcm_usb_init_ops { diff --git a/drivers/phy/cadence/phy-cadence-salvo.c b/drivers/phy/cadence/phy-cadence-salvo.c index e569f5f67578..f461585c84c6 100644 --- a/drivers/phy/cadence/phy-cadence-salvo.c +++ b/drivers/phy/cadence/phy-cadence-salvo.c @@ -6,6 +6,7 @@ * Copyright (c) 2019-2020 NXP */ +#include <linux/bitfield.h> #include <linux/clk.h> #include <linux/io.h> #include <linux/module.h> @@ -15,7 +16,9 @@ #include <linux/of.h> #include <linux/of_platform.h> -/* PHY register definition */ +#define USB3_PHY_OFFSET 0x0 +#define USB2_PHY_OFFSET 0x38000 +/* USB3 PHY register definition */ #define PHY_PMA_CMN_CTRL1 0xC800 #define TB_ADDR_CMN_DIAG_HSCLK_SEL 0x01e0 #define TB_ADDR_CMN_PLL0_VCOCAL_INIT_TMR 0x0084 @@ -87,8 +90,35 @@ #define TB_ADDR_XCVR_DIAG_LANE_FCM_EN_MGN_TMR 0x40f2 #define TB_ADDR_TX_RCVDETSC_CTRL 0x4124 +/* USB2 PHY register definition */ +#define UTMI_REG15 0xaf +#define UTMI_AFE_RX_REG0 0x0d +#define UTMI_AFE_RX_REG5 0x12 +#define UTMI_AFE_BC_REG4 0x29 + +/* Align UTMI_AFE_RX_REG0 bit[7:6] define */ +enum usb2_disconn_threshold { + USB2_DISCONN_THRESHOLD_575 = 0x0, + USB2_DISCONN_THRESHOLD_610 = 0x1, + USB2_DISCONN_THRESHOLD_645 = 0x3, +}; + +#define RX_USB2_DISCONN_MASK GENMASK(7, 6) + /* TB_ADDR_TX_RCVDETSC_CTRL */ #define RXDET_IN_P3_32KHZ BIT(0) +/* + * UTMI_REG15 + * + * Gate how many us for the txvalid signal until analog + * HS/FS transmitters have powered up + */ +#define TXVALID_GATE_THRESHOLD_HS_MASK (BIT(4) | BIT(5)) +/* 0us, txvalid is ready just after HS/FS transmitters have powered up */ +#define TXVALID_GATE_THRESHOLD_HS_0US (BIT(4) | BIT(5)) + +#define SET_B_SESSION_VALID (BIT(6) | BIT(5)) +#define CLR_B_SESSION_VALID (BIT(6)) struct cdns_reg_pairs { u16 val; @@ -106,19 +136,27 @@ struct cdns_salvo_phy { struct clk *clk; void __iomem *base; struct cdns_salvo_data *data; + enum usb2_disconn_threshold usb2_disconn; }; static const struct of_device_id cdns_salvo_phy_of_match[]; -static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 reg) +static const struct cdns_salvo_data cdns_nxp_salvo_data; + +static bool cdns_is_nxp_phy(struct cdns_salvo_phy *salvo_phy) +{ + return salvo_phy->data == &cdns_nxp_salvo_data; +} + +static u16 cdns_salvo_read(struct cdns_salvo_phy *salvo_phy, u32 offset, u32 reg) { - return (u16)readl(salvo_phy->base + + return (u16)readl(salvo_phy->base + offset + reg * (1 << salvo_phy->data->reg_offset_shift)); } -static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy, +static void cdns_salvo_write(struct cdns_salvo_phy *salvo_phy, u32 offset, u32 reg, u16 val) { - writel(val, salvo_phy->base + + writel(val, salvo_phy->base + offset + reg * (1 << salvo_phy->data->reg_offset_shift)); } @@ -219,15 +257,27 @@ static int cdns_salvo_phy_init(struct phy *phy) for (i = 0; i < data->init_sequence_length; i++) { const struct cdns_reg_pairs *reg_pair = data->init_sequence_val + i; - cdns_salvo_write(salvo_phy, reg_pair->off, reg_pair->val); + cdns_salvo_write(salvo_phy, USB3_PHY_OFFSET, reg_pair->off, reg_pair->val); } /* RXDET_IN_P3_32KHZ, Receiver detect slow clock enable */ - value = cdns_salvo_read(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL); + value = cdns_salvo_read(salvo_phy, USB3_PHY_OFFSET, TB_ADDR_TX_RCVDETSC_CTRL); value |= RXDET_IN_P3_32KHZ; - cdns_salvo_write(salvo_phy, TB_ADDR_TX_RCVDETSC_CTRL, + cdns_salvo_write(salvo_phy, USB3_PHY_OFFSET, TB_ADDR_TX_RCVDETSC_CTRL, RXDET_IN_P3_32KHZ); + value = cdns_salvo_read(salvo_phy, USB2_PHY_OFFSET, UTMI_REG15); + value &= ~TXVALID_GATE_THRESHOLD_HS_MASK; + cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_REG15, + value | TXVALID_GATE_THRESHOLD_HS_0US); + + cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG5, 0x5); + + value = cdns_salvo_read(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG0); + value &= ~RX_USB2_DISCONN_MASK; + value = FIELD_PREP(RX_USB2_DISCONN_MASK, salvo_phy->usb2_disconn); + cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_RX_REG0, value); + udelay(10); clk_disable_unprepare(salvo_phy->clk); @@ -251,11 +301,29 @@ static int cdns_salvo_phy_power_off(struct phy *phy) return 0; } +static int cdns_salvo_set_mode(struct phy *phy, enum phy_mode mode, int submode) +{ + struct cdns_salvo_phy *salvo_phy = phy_get_drvdata(phy); + + if (!cdns_is_nxp_phy(salvo_phy)) + return 0; + + if (mode == PHY_MODE_USB_DEVICE) + cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_BC_REG4, + SET_B_SESSION_VALID); + else + cdns_salvo_write(salvo_phy, USB2_PHY_OFFSET, UTMI_AFE_BC_REG4, + CLR_B_SESSION_VALID); + + return 0; +} + static const struct phy_ops cdns_salvo_phy_ops = { .init = cdns_salvo_phy_init, .power_on = cdns_salvo_phy_power_on, .power_off = cdns_salvo_phy_power_off, .owner = THIS_MODULE, + .set_mode = cdns_salvo_set_mode, }; static int cdns_salvo_phy_probe(struct platform_device *pdev) @@ -264,6 +332,7 @@ static int cdns_salvo_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct cdns_salvo_phy *salvo_phy; struct cdns_salvo_data *data; + u32 val; data = (struct cdns_salvo_data *)of_device_get_match_data(dev); salvo_phy = devm_kzalloc(dev, sizeof(*salvo_phy), GFP_KERNEL); @@ -275,6 +344,16 @@ static int cdns_salvo_phy_probe(struct platform_device *pdev) if (IS_ERR(salvo_phy->clk)) return PTR_ERR(salvo_phy->clk); + if (of_property_read_u32(dev->of_node, "cdns,usb2-disconnect-threshold-microvolt", &val)) + val = 575; + + if (val < 610) + salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_575; + else if (val < 645) + salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_610; + else + salvo_phy->usb2_disconn = USB2_DISCONN_THRESHOLD_645; + salvo_phy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(salvo_phy->base)) return PTR_ERR(salvo_phy->base); diff --git a/drivers/phy/cadence/phy-cadence-torrent.c b/drivers/phy/cadence/phy-cadence-torrent.c index 62e59d1bb9c3..37b6b5c05be8 100644 --- a/drivers/phy/cadence/phy-cadence-torrent.c +++ b/drivers/phy/cadence/phy-cadence-torrent.c @@ -38,6 +38,9 @@ #define POLL_TIMEOUT_US 5000 #define PLL_LOCK_TIMEOUT 100000 +#define DP_PLL0 BIT(0) +#define DP_PLL1 BIT(1) + #define TORRENT_COMMON_CDB_OFFSET 0x0 #define TORRENT_TX_LANE_CDB_OFFSET(ln, block_offset, reg_offset) \ @@ -66,16 +69,11 @@ */ #define PHY_AUX_CTRL 0x04 #define PHY_RESET 0x20 -#define PMA_TX_ELEC_IDLE_MASK 0xF0U #define PMA_TX_ELEC_IDLE_SHIFT 4 -#define PHY_L00_RESET_N_MASK 0x01U #define PHY_PMA_XCVR_PLLCLK_EN 0x24 #define PHY_PMA_XCVR_PLLCLK_EN_ACK 0x28 #define PHY_PMA_XCVR_POWER_STATE_REQ 0x2c -#define PHY_POWER_STATE_LN_0 0x0000 -#define PHY_POWER_STATE_LN_1 0x0008 -#define PHY_POWER_STATE_LN_2 0x0010 -#define PHY_POWER_STATE_LN_3 0x0018 +#define PHY_POWER_STATE_LN(ln) ((ln) * 8) #define PMA_XCVR_POWER_STATE_REQ_LN_MASK 0x3FU #define PHY_PMA_XCVR_POWER_STATE_ACK 0x30 #define PHY_PMA_CMN_READY 0x34 @@ -323,6 +321,7 @@ struct cdns_torrent_phy { void __iomem *base; /* DPTX registers base */ void __iomem *sd_base; /* SD0801 registers base */ u32 max_bit_rate; /* Maximum link bit rate to use (in Mbps) */ + u32 dp_pll; struct reset_control *phy_rst; struct reset_control *apb_rst; struct device *dev; @@ -905,88 +904,129 @@ void cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(struct cdns_torrent_phy *cdns_phy, /* Setting VCO for 10.8GHz */ case 2700: case 5400: - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0028); - cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_FBH_OVRD_M0, 0x0022); - cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBH_OVRD_M0, 0x0022); - cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBL_OVRD_M0, 0x000C); + if (cdns_phy->dp_pll & DP_PLL0) + cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_FBH_OVRD_M0, 0x0022); + + if (cdns_phy->dp_pll & DP_PLL1) { + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0028); + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBH_OVRD_M0, 0x0022); + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_FBL_OVRD_M0, 0x000C); + } break; /* Setting VCO for 9.72GHz */ case 1620: case 2430: case 3240: - cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004); - cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08); - cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0061); - cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0061); - cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x3333); - cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x3333); - cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); - cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); - cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0042); - cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0042); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + if (cdns_phy->dp_pll & DP_PLL0) { + cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0061); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x3333); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0042); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + } + if (cdns_phy->dp_pll & DP_PLL1) { + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0061); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x3333); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0042); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + } break; /* Setting VCO for 8.64GHz */ case 2160: case 4320: - cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004); - cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08); - cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0056); - cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0056); - cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x6666); - cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x6666); - cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); - cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); - cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x003A); - cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x003A); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + if (cdns_phy->dp_pll & DP_PLL0) { + cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0056); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVL_M0, 0x6666); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x003A); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + } + if (cdns_phy->dp_pll & DP_PLL1) { + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0056); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVL_M0, 0x6666); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x003A); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + } break; /* Setting VCO for 8.1GHz */ case 8100: - cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004); - cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08); - cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0051); - cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0051); - cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); - cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); - cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0036); - cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0036); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); - cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + if (cdns_phy->dp_pll & DP_PLL0) { + cdns_torrent_phy_write(regmap, CMN_PLL0_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PLL0_INTDIV_M0, 0x0051); + cdns_torrent_phy_write(regmap, CMN_PLL0_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL0_HIGH_THR_M0, 0x0036); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL0_CTRL_M0, 0x0002); + } + if (cdns_phy->dp_pll & DP_PLL1) { + cdns_torrent_phy_write(regmap, CMN_PLL1_DSM_DIAG_M0, 0x0004); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_PADJ_M0, 0x0509); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CP_IADJ_M0, 0x0F00); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_FILT_PADJ_M0, 0x0F08); + cdns_torrent_phy_write(regmap, CMN_PLL1_INTDIV_M0, 0x0051); + cdns_torrent_phy_write(regmap, CMN_PLL1_FRACDIVH_M0, 0x0002); + cdns_torrent_phy_write(regmap, CMN_PLL1_HIGH_THR_M0, 0x0036); + cdns_torrent_phy_write(regmap, CMN_PDIAG_PLL1_CTRL_M0, 0x0002); + } break; } } +/* Set PLL used for DP configuration */ +static int cdns_torrent_dp_get_pll(struct cdns_torrent_phy *cdns_phy, + enum cdns_torrent_phy_type phy_t2) +{ + switch (phy_t2) { + case TYPE_PCIE: + case TYPE_USB: + cdns_phy->dp_pll = DP_PLL1; + break; + case TYPE_SGMII: + case TYPE_QSGMII: + cdns_phy->dp_pll = DP_PLL0; + break; + case TYPE_NONE: + cdns_phy->dp_pll = DP_PLL0 | DP_PLL1; + break; + default: + dev_err(cdns_phy->dev, "Unsupported PHY configuration\n"); + return -EINVAL; + } + + return 0; +} + /* * Enable or disable PLL for selected lanes. */ static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, struct phy_configure_opts_dp *dp, bool enable) { - u32 rd_val; - u32 ret; struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; + u32 rd_val, pll_ack_val; + int ret; /* * Used to determine, which bits to check for or enable in @@ -996,28 +1036,18 @@ static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy, /* Used to enable or disable lanes. */ u32 pll_val; - /* Select values of registers and mask, depending on enabled lane - * count. - */ - switch (dp->lanes) { - /* lane 0 */ - case (1): - pll_bits = 0x00000001; - break; - /* lanes 0-1 */ - case (2): - pll_bits = 0x00000003; - break; - /* lanes 0-3, all */ - default: - pll_bits = 0x0000000F; - break; - } + /* Select values of registers and mask, depending on enabled lane count. */ + pll_val = cdns_torrent_dp_read(regmap, PHY_PMA_XCVR_PLLCLK_EN); - if (enable) - pll_val = pll_bits; - else - pll_val = 0x00000000; + if (enable) { + pll_bits = ((1 << dp->lanes) - 1); + pll_val |= pll_bits; + pll_ack_val = pll_bits; + } else { + pll_bits = ((1 << inst->num_lanes) - 1); + pll_val &= (~pll_bits); + pll_ack_val = 0; + } cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, pll_val); @@ -1025,22 +1055,23 @@ static int cdns_torrent_dp_set_pll_en(struct cdns_torrent_phy *cdns_phy, ret = regmap_read_poll_timeout(regmap, PHY_PMA_XCVR_PLLCLK_EN_ACK, rd_val, - (rd_val & pll_bits) == pll_val, + (rd_val & pll_bits) == pll_ack_val, 0, POLL_TIMEOUT_US); ndelay(100); return ret; } static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, u32 num_lanes, enum phy_powerstate powerstate) { /* Register value for power state for a single byte. */ - u32 value_part; - u32 value; - u32 mask; + u32 value_part, i; + u32 value = 0; + u32 mask = 0; u32 read_val; - u32 ret; + int ret; struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; switch (powerstate) { @@ -1056,29 +1087,11 @@ static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy, break; } - /* Select values of registers and mask, depending on enabled - * lane count. - */ - switch (num_lanes) { - /* lane 0 */ - case (1): - value = value_part; - mask = 0x0000003FU; - break; - /* lanes 0-1 */ - case (2): - value = (value_part - | (value_part << 8)); - mask = 0x00003F3FU; - break; - /* lanes 0-3, all */ - default: - value = (value_part - | (value_part << 8) - | (value_part << 16) - | (value_part << 24)); - mask = 0x3F3F3F3FU; - break; + /* Select values of registers and mask, depending on enabled lane count. */ + + for (i = 0; i < num_lanes; i++) { + value |= (value_part << PHY_POWER_STATE_LN(i)); + mask |= (PMA_XCVR_POWER_STATE_REQ_LN_MASK << PHY_POWER_STATE_LN(i)); } /* Set power state A<n>. */ @@ -1093,7 +1106,8 @@ static int cdns_torrent_dp_set_power_state(struct cdns_torrent_phy *cdns_phy, return ret; } -static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes) +static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, u32 num_lanes) { unsigned int read_val; int ret; @@ -1114,12 +1128,12 @@ static int cdns_torrent_dp_run(struct cdns_torrent_phy *cdns_phy, u32 num_lanes) ndelay(100); - ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes, + ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, num_lanes, POWERSTATE_A2); if (ret) return ret; - ret = cdns_torrent_dp_set_power_state(cdns_phy, num_lanes, + ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, num_lanes, POWERSTATE_A0); return ret; @@ -1143,6 +1157,7 @@ static int cdns_torrent_dp_wait_pma_cmn_ready(struct cdns_torrent_phy *cdns_phy) } static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, u32 rate, u32 num_lanes) { unsigned int clk_sel_val = 0; @@ -1175,14 +1190,17 @@ static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy, break; } - cdns_torrent_phy_write(cdns_phy->regmap_common_cdb, - CMN_PDIAG_PLL0_CLK_SEL_M0, clk_sel_val); - cdns_torrent_phy_write(cdns_phy->regmap_common_cdb, - CMN_PDIAG_PLL1_CLK_SEL_M0, clk_sel_val); + if (cdns_phy->dp_pll & DP_PLL0) + cdns_torrent_phy_write(cdns_phy->regmap_common_cdb, + CMN_PDIAG_PLL0_CLK_SEL_M0, clk_sel_val); + + if (cdns_phy->dp_pll & DP_PLL1) + cdns_torrent_phy_write(cdns_phy->regmap_common_cdb, + CMN_PDIAG_PLL1_CLK_SEL_M0, clk_sel_val); /* PMA lane configuration to deal with multi-link operation */ for (i = 0; i < num_lanes; i++) - cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[i], + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + i], XCVR_DIAG_HSCLK_DIV, hsclk_div_val); } @@ -1191,23 +1209,44 @@ static void cdns_torrent_dp_pma_cmn_rate(struct cdns_torrent_phy *cdns_phy, * set and PLL disable request was processed. */ static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, struct phy_configure_opts_dp *dp) { - u32 read_val, ret; + u32 read_val, field_val; + int ret; - /* Disable the cmn_pll0_en before re-programming the new data rate. */ - regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x0); + /* + * Disable the associated PLL (cmn_pll0_en or cmn_pll1_en) before + * re-programming the new data rate. + */ + ret = regmap_field_read(cdns_phy->phy_pma_pll_raw_ctrl, &field_val); + if (ret) + return ret; + field_val &= ~(cdns_phy->dp_pll); + regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, field_val); /* * Wait for PLL ready de-assertion. * For PLL0 - PHY_PMA_CMN_CTRL2[2] == 1 + * For PLL1 - PHY_PMA_CMN_CTRL2[3] == 1 */ - ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2, - read_val, - ((read_val >> 2) & 0x01) != 0, - 0, POLL_TIMEOUT_US); - if (ret) - return ret; + if (cdns_phy->dp_pll & DP_PLL0) { + ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2, + read_val, + ((read_val >> 2) & 0x01) != 0, + 0, POLL_TIMEOUT_US); + if (ret) + return ret; + } + + if ((cdns_phy->dp_pll & DP_PLL1) && cdns_phy->nsubnodes != 1) { + ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2, + read_val, + ((read_val >> 3) & 0x01) != 0, + 0, POLL_TIMEOUT_US); + if (ret) + return ret; + } ndelay(200); /* DP Rate Change - VCO Output settings. */ @@ -1221,19 +1260,35 @@ static int cdns_torrent_dp_configure_rate(struct cdns_torrent_phy *cdns_phy, /* PMA common configuration 100MHz */ cdns_torrent_dp_pma_cmn_vco_cfg_100mhz(cdns_phy, dp->link_rate, dp->ssc); - cdns_torrent_dp_pma_cmn_rate(cdns_phy, dp->link_rate, dp->lanes); + cdns_torrent_dp_pma_cmn_rate(cdns_phy, inst, dp->link_rate, dp->lanes); - /* Enable the cmn_pll0_en. */ - regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, 0x3); + /* Enable the associated PLL (cmn_pll0_en or cmn_pll1_en) */ + ret = regmap_field_read(cdns_phy->phy_pma_pll_raw_ctrl, &field_val); + if (ret) + return ret; + field_val |= cdns_phy->dp_pll; + regmap_field_write(cdns_phy->phy_pma_pll_raw_ctrl, field_val); /* * Wait for PLL ready assertion. * For PLL0 - PHY_PMA_CMN_CTRL2[0] == 1 + * For PLL1 - PHY_PMA_CMN_CTRL2[1] == 1 */ - ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2, - read_val, - (read_val & 0x01) != 0, - 0, POLL_TIMEOUT_US); + if (cdns_phy->dp_pll & DP_PLL0) { + ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2, + read_val, + (read_val & 0x01) != 0, + 0, POLL_TIMEOUT_US); + if (ret) + return ret; + } + + if ((cdns_phy->dp_pll & DP_PLL1) && cdns_phy->nsubnodes != 1) + ret = regmap_field_read_poll_timeout(cdns_phy->phy_pma_cmn_ctrl_2, + read_val, + ((read_val >> 1) & 0x01) != 0, + 0, POLL_TIMEOUT_US); + return ret; } @@ -1301,6 +1356,7 @@ static int cdns_torrent_dp_verify_config(struct cdns_torrent_inst *inst, /* Set power state A0 and PLL clock enable to 0 on enabled lanes. */ static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, u32 num_lanes) { struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; @@ -1308,27 +1364,13 @@ static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy, PHY_PMA_XCVR_POWER_STATE_REQ); u32 pll_clk_en = cdns_torrent_dp_read(regmap, PHY_PMA_XCVR_PLLCLK_EN); + u32 i; - /* Lane 0 is always enabled. */ - pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK << - PHY_POWER_STATE_LN_0); - pll_clk_en &= ~0x01U; - - if (num_lanes > 1) { - /* lane 1 */ - pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK << - PHY_POWER_STATE_LN_1); - pll_clk_en &= ~(0x01U << 1); - } + for (i = 0; i < num_lanes; i++) { + pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK + << PHY_POWER_STATE_LN(inst->mlane + i)); - if (num_lanes > 2) { - /* lanes 2 and 3 */ - pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK << - PHY_POWER_STATE_LN_2); - pwr_state &= ~(PMA_XCVR_POWER_STATE_REQ_LN_MASK << - PHY_POWER_STATE_LN_3); - pll_clk_en &= ~(0x01U << 2); - pll_clk_en &= ~(0x01U << 3); + pll_clk_en &= ~(0x01U << (inst->mlane + i)); } cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_POWER_STATE_REQ, pwr_state); @@ -1337,36 +1379,57 @@ static void cdns_torrent_dp_set_a0_pll(struct cdns_torrent_phy *cdns_phy, /* Configure lane count as required. */ static int cdns_torrent_dp_set_lanes(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, struct phy_configure_opts_dp *dp) { - u32 value; - u32 ret; + u32 value, i; + int ret; struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; u8 lane_mask = (1 << dp->lanes) - 1; + u8 pma_tx_elec_idle_mask = 0; + u32 clane = inst->mlane; + + lane_mask <<= clane; value = cdns_torrent_dp_read(regmap, PHY_RESET); /* clear pma_tx_elec_idle_ln_* bits. */ - value &= ~PMA_TX_ELEC_IDLE_MASK; + pma_tx_elec_idle_mask = ((1 << inst->num_lanes) - 1) << clane; + + pma_tx_elec_idle_mask <<= PMA_TX_ELEC_IDLE_SHIFT; + + value &= ~pma_tx_elec_idle_mask; + /* Assert pma_tx_elec_idle_ln_* for disabled lanes. */ value |= ((~lane_mask) << PMA_TX_ELEC_IDLE_SHIFT) & - PMA_TX_ELEC_IDLE_MASK; + pma_tx_elec_idle_mask; + cdns_torrent_dp_write(regmap, PHY_RESET, value); - /* reset the link by asserting phy_l00_reset_n low */ + /* reset the link by asserting master lane phy_l0*_reset_n low */ cdns_torrent_dp_write(regmap, PHY_RESET, - value & (~PHY_L00_RESET_N_MASK)); + value & (~(1 << clane))); /* - * Assert lane reset on unused lanes and lane 0 so they remain in reset + * Assert lane reset on unused lanes and master lane so they remain in reset * and powered down when re-enabling the link */ - value = (value & 0x0000FFF0) | (0x0000000E & lane_mask); + for (i = 0; i < inst->num_lanes; i++) + value &= (~(1 << (clane + i))); + + for (i = 1; i < inst->num_lanes; i++) + value |= ((1 << (clane + i)) & lane_mask); + cdns_torrent_dp_write(regmap, PHY_RESET, value); - cdns_torrent_dp_set_a0_pll(cdns_phy, dp->lanes); + cdns_torrent_dp_set_a0_pll(cdns_phy, inst, dp->lanes); /* release phy_l0*_reset_n based on used laneCount */ - value = (value & 0x0000FFF0) | (0x0000000F & lane_mask); + for (i = 0; i < inst->num_lanes; i++) + value &= (~(1 << (clane + i))); + + for (i = 0; i < inst->num_lanes; i++) + value |= ((1 << (clane + i)) & lane_mask); + cdns_torrent_dp_write(regmap, PHY_RESET, value); /* Wait, until PHY gets ready after releasing PHY reset signal. */ @@ -1377,41 +1440,44 @@ static int cdns_torrent_dp_set_lanes(struct cdns_torrent_phy *cdns_phy, ndelay(100); /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */ - cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001); + value = cdns_torrent_dp_read(regmap, PHY_PMA_XCVR_PLLCLK_EN); + value |= (1 << clane); + cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, value); - ret = cdns_torrent_dp_run(cdns_phy, dp->lanes); + ret = cdns_torrent_dp_run(cdns_phy, inst, dp->lanes); return ret; } /* Configure link rate as required. */ static int cdns_torrent_dp_set_rate(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, struct phy_configure_opts_dp *dp) { - u32 ret; + int ret; - ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes, + ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, dp->lanes, POWERSTATE_A3); if (ret) return ret; - ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, false); + ret = cdns_torrent_dp_set_pll_en(cdns_phy, inst, dp, false); if (ret) return ret; ndelay(200); - ret = cdns_torrent_dp_configure_rate(cdns_phy, dp); + ret = cdns_torrent_dp_configure_rate(cdns_phy, inst, dp); if (ret) return ret; ndelay(200); - ret = cdns_torrent_dp_set_pll_en(cdns_phy, dp, true); + ret = cdns_torrent_dp_set_pll_en(cdns_phy, inst, dp, true); if (ret) return ret; - ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes, + ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, dp->lanes, POWERSTATE_A2); if (ret) return ret; - ret = cdns_torrent_dp_set_power_state(cdns_phy, dp->lanes, + ret = cdns_torrent_dp_set_power_state(cdns_phy, inst, dp->lanes, POWERSTATE_A0); if (ret) return ret; @@ -1422,44 +1488,45 @@ static int cdns_torrent_dp_set_rate(struct cdns_torrent_phy *cdns_phy, /* Configure voltage swing and pre-emphasis for all enabled lanes. */ static void cdns_torrent_dp_set_voltages(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, struct phy_configure_opts_dp *dp) { u8 lane; u16 val; for (lane = 0; lane < dp->lanes; lane++) { - val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane], + val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane], TX_DIAG_ACYA); /* * Write 1 to register bit TX_DIAG_ACYA[0] to freeze the * current state of the analog TX driver. */ val |= TX_DIAG_ACYA_HBDC_MASK; - cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane], TX_DIAG_ACYA, val); - cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane], TX_TXCC_CTRL, 0x08A4); val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].diag_tx_drv; - cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane], DRV_DIAG_TX_DRV, val); val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].mgnfs_mult; - cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane], TX_TXCC_MGNFS_MULT_000, val); val = vltg_coeff[dp->voltage[lane]][dp->pre[lane]].cpost_mult; - cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane], TX_TXCC_CPOST_MULT_00, val); - val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[lane], + val = cdns_torrent_phy_read(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane], TX_DIAG_ACYA); /* * Write 0 to register bit TX_DIAG_ACYA[0] to allow the state of * analog TX driver to reflect the new programmed one. */ val &= ~TX_DIAG_ACYA_HBDC_MASK; - cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[lane], + cdns_torrent_phy_write(cdns_phy->regmap_tx_lane_cdb[inst->mlane + lane], TX_DIAG_ACYA, val); } }; @@ -1478,7 +1545,7 @@ static int cdns_torrent_dp_configure(struct phy *phy, } if (opts->dp.set_lanes) { - ret = cdns_torrent_dp_set_lanes(cdns_phy, &opts->dp); + ret = cdns_torrent_dp_set_lanes(cdns_phy, inst, &opts->dp); if (ret) { dev_err(&phy->dev, "cdns_torrent_dp_set_lanes failed\n"); return ret; @@ -1486,7 +1553,7 @@ static int cdns_torrent_dp_configure(struct phy *phy, } if (opts->dp.set_rate) { - ret = cdns_torrent_dp_set_rate(cdns_phy, &opts->dp); + ret = cdns_torrent_dp_set_rate(cdns_phy, inst, &opts->dp); if (ret) { dev_err(&phy->dev, "cdns_torrent_dp_set_rate failed\n"); return ret; @@ -1494,7 +1561,7 @@ static int cdns_torrent_dp_configure(struct phy *phy, } if (opts->dp.set_voltages) - cdns_torrent_dp_set_voltages(cdns_phy, &opts->dp); + cdns_torrent_dp_set_voltages(cdns_phy, inst, &opts->dp); return ret; } @@ -1562,6 +1629,7 @@ static void cdns_torrent_dp_common_init(struct cdns_torrent_phy *cdns_phy, { struct regmap *regmap = cdns_phy->regmap_dptx_phy_reg; unsigned char lane_bits; + u32 val; cdns_torrent_dp_write(regmap, PHY_AUX_CTRL, 0x0003); /* enable AUX */ @@ -1569,18 +1637,23 @@ static void cdns_torrent_dp_common_init(struct cdns_torrent_phy *cdns_phy, * Set lines power state to A0 * Set lines pll clk enable to 0 */ - cdns_torrent_dp_set_a0_pll(cdns_phy, inst->num_lanes); + cdns_torrent_dp_set_a0_pll(cdns_phy, inst, inst->num_lanes); /* * release phy_l0*_reset_n and pma_tx_elec_idle_ln_* based on * used lanes */ lane_bits = (1 << inst->num_lanes) - 1; - cdns_torrent_dp_write(regmap, PHY_RESET, - ((0xF & ~lane_bits) << 4) | (0xF & lane_bits)); + + val = cdns_torrent_dp_read(regmap, PHY_RESET); + val |= (0xF & lane_bits); + val &= ~(lane_bits << 4); + cdns_torrent_dp_write(regmap, PHY_RESET, val); /* release pma_xcvr_pllclk_en_ln_*, only for the master lane */ - cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, 0x0001); + val = cdns_torrent_dp_read(regmap, PHY_PMA_XCVR_PLLCLK_EN); + val |= 1; + cdns_torrent_dp_write(regmap, PHY_PMA_XCVR_PLLCLK_EN, val); /* * PHY PMA registers configuration functions @@ -1599,7 +1672,7 @@ static void cdns_torrent_dp_common_init(struct cdns_torrent_phy *cdns_phy, cdns_phy->max_bit_rate, false); - cdns_torrent_dp_pma_cmn_rate(cdns_phy, cdns_phy->max_bit_rate, + cdns_torrent_dp_pma_cmn_rate(cdns_phy, inst, cdns_phy->max_bit_rate, inst->num_lanes); /* take out of reset */ @@ -1612,13 +1685,15 @@ static int cdns_torrent_dp_start(struct cdns_torrent_phy *cdns_phy, { int ret; - cdns_torrent_phy_on(phy); + ret = cdns_torrent_phy_on(phy); + if (ret) + return ret; ret = cdns_torrent_dp_wait_pma_cmn_ready(cdns_phy); if (ret) return ret; - ret = cdns_torrent_dp_run(cdns_phy, inst->num_lanes); + ret = cdns_torrent_dp_run(cdns_phy, inst, inst->num_lanes); return ret; } @@ -1627,6 +1702,7 @@ static int cdns_torrent_dp_init(struct phy *phy) { struct cdns_torrent_inst *inst = phy_get_drvdata(phy); struct cdns_torrent_phy *cdns_phy = dev_get_drvdata(phy->dev.parent); + int ret; switch (cdns_phy->ref_clk_rate) { case CLK_19_2_MHZ: @@ -1639,6 +1715,24 @@ static int cdns_torrent_dp_init(struct phy *phy) return -EINVAL; } + ret = cdns_torrent_dp_get_pll(cdns_phy, TYPE_NONE); + if (ret) + return ret; + + cdns_torrent_dp_common_init(cdns_phy, inst); + + return cdns_torrent_dp_start(cdns_phy, inst, phy); +} + +static int cdns_torrent_dp_multilink_init(struct cdns_torrent_phy *cdns_phy, + struct cdns_torrent_inst *inst, + struct phy *phy) +{ + if (cdns_phy->ref_clk_rate != CLK_100_MHZ) { + dev_err(cdns_phy->dev, "Unsupported Ref Clock Rate\n"); + return -EINVAL; + } + cdns_torrent_dp_common_init(cdns_phy, inst); return cdns_torrent_dp_start(cdns_phy, inst, phy); @@ -2156,8 +2250,11 @@ static int cdns_torrent_phy_init(struct phy *phy) u32 num_regs; int i, j; - if (cdns_phy->nsubnodes > 1) + if (cdns_phy->nsubnodes > 1) { + if (phy_type == TYPE_DP) + return cdns_torrent_dp_multilink_init(cdns_phy, inst, phy); return 0; + } /** * Spread spectrum generation is not required or supported @@ -2399,6 +2496,12 @@ int cdns_torrent_phy_configure_multilink(struct cdns_torrent_phy *cdns_phy) } } + if (phy_t1 == TYPE_DP) { + ret = cdns_torrent_dp_get_pll(cdns_phy, phy_t2); + if (ret) + return ret; + } + reset_control_deassert(cdns_phy->phys[node].lnk_rst); } @@ -2794,6 +2897,109 @@ static void cdns_torrent_phy_remove(struct platform_device *pdev) cdns_torrent_clk_cleanup(cdns_phy); } +/* USB and DP link configuration */ +static struct cdns_reg_pairs usb_dp_link_cmn_regs[] = { + {0x0002, PHY_PLL_CFG}, + {0x8600, CMN_PDIAG_PLL0_CLK_SEL_M0} +}; + +static struct cdns_reg_pairs usb_dp_xcvr_diag_ln_regs[] = { + {0x0000, XCVR_DIAG_HSCLK_SEL}, + {0x0001, XCVR_DIAG_HSCLK_DIV}, + {0x0041, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_reg_pairs dp_usb_xcvr_diag_ln_regs[] = { + {0x0001, XCVR_DIAG_HSCLK_SEL}, + {0x0009, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_torrent_vals usb_dp_link_cmn_vals = { + .reg_pairs = usb_dp_link_cmn_regs, + .num_regs = ARRAY_SIZE(usb_dp_link_cmn_regs), +}; + +static struct cdns_torrent_vals usb_dp_xcvr_diag_ln_vals = { + .reg_pairs = usb_dp_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(usb_dp_xcvr_diag_ln_regs), +}; + +static struct cdns_torrent_vals dp_usb_xcvr_diag_ln_vals = { + .reg_pairs = dp_usb_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(dp_usb_xcvr_diag_ln_regs), +}; + +/* PCIe and DP link configuration */ +static struct cdns_reg_pairs pcie_dp_link_cmn_regs[] = { + {0x0003, PHY_PLL_CFG}, + {0x0601, CMN_PDIAG_PLL0_CLK_SEL_M0}, + {0x0400, CMN_PDIAG_PLL0_CLK_SEL_M1} +}; + +static struct cdns_reg_pairs pcie_dp_xcvr_diag_ln_regs[] = { + {0x0000, XCVR_DIAG_HSCLK_SEL}, + {0x0001, XCVR_DIAG_HSCLK_DIV}, + {0x0012, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_reg_pairs dp_pcie_xcvr_diag_ln_regs[] = { + {0x0001, XCVR_DIAG_HSCLK_SEL}, + {0x0009, XCVR_DIAG_PLLDRC_CTRL} +}; + +static struct cdns_torrent_vals pcie_dp_link_cmn_vals = { + .reg_pairs = pcie_dp_link_cmn_regs, + .num_regs = ARRAY_SIZE(pcie_dp_link_cmn_regs), +}; + +static struct cdns_torrent_vals pcie_dp_xcvr_diag_ln_vals = { + .reg_pairs = pcie_dp_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(pcie_dp_xcvr_diag_ln_regs), +}; + +static struct cdns_torrent_vals dp_pcie_xcvr_diag_ln_vals = { + .reg_pairs = dp_pcie_xcvr_diag_ln_regs, + .num_regs = ARRAY_SIZE(dp_pcie_xcvr_diag_ln_regs), +}; + +/* DP Multilink, 100 MHz Ref clk, no SSC */ +static struct cdns_reg_pairs dp_100_no_ssc_cmn_regs[] = { + {0x007F, CMN_TXPUCAL_TUNE}, + {0x007F, CMN_TXPDCAL_TUNE} +}; + +static struct cdns_reg_pairs dp_100_no_ssc_tx_ln_regs[] = { + {0x00FB, TX_PSC_A0}, + {0x04AA, TX_PSC_A2}, + {0x04AA, TX_PSC_A3}, + {0x000F, XCVR_DIAG_BIDI_CTRL} +}; + +static struct cdns_reg_pairs dp_100_no_ssc_rx_ln_regs[] = { + {0x0000, RX_PSC_A0}, + {0x0000, RX_PSC_A2}, + {0x0000, RX_PSC_A3}, + {0x0000, RX_PSC_CAL}, + {0x0000, RX_REE_GCSM1_CTRL}, + {0x0000, RX_REE_GCSM2_CTRL}, + {0x0000, RX_REE_PERGCSM_CTRL} +}; + +static struct cdns_torrent_vals dp_100_no_ssc_cmn_vals = { + .reg_pairs = dp_100_no_ssc_cmn_regs, + .num_regs = ARRAY_SIZE(dp_100_no_ssc_cmn_regs), +}; + +static struct cdns_torrent_vals dp_100_no_ssc_tx_ln_vals = { + .reg_pairs = dp_100_no_ssc_tx_ln_regs, + .num_regs = ARRAY_SIZE(dp_100_no_ssc_tx_ln_regs), +}; + +static struct cdns_torrent_vals dp_100_no_ssc_rx_ln_vals = { + .reg_pairs = dp_100_no_ssc_rx_ln_regs, + .num_regs = ARRAY_SIZE(dp_100_no_ssc_rx_ln_regs), +}; + /* Single DisplayPort(DP) link configuration */ static struct cdns_reg_pairs sl_dp_link_cmn_regs[] = { {0x0000, PHY_PLL_CFG}, @@ -3736,6 +3942,12 @@ static const struct cdns_torrent_data cdns_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_link_cmn_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &pcie_dp_link_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &usb_dp_link_cmn_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -3758,6 +3970,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = &pcie_dp_link_cmn_vals, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -3810,6 +4025,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_dp_link_cmn_vals, + }, }, }, .xcvr_diag_vals = { @@ -3817,6 +4035,12 @@ static const struct cdns_torrent_data cdns_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_xcvr_diag_ln_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &dp_pcie_xcvr_diag_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &dp_usb_xcvr_diag_ln_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -3839,6 +4063,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, [INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &pcie_dp_xcvr_diag_ln_vals, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -3891,6 +4118,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_dp_xcvr_diag_ln_vals, + }, }, }, .pcs_cmn_vals = { @@ -3915,6 +4145,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + }, }, }, .cmn_vals = { @@ -3937,6 +4170,12 @@ static const struct cdns_torrent_data cdns_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &dp_100_no_ssc_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -3959,6 +4198,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = NULL, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -4011,6 +4253,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_100_no_ssc_cmn_vals, + }, }, }, }, @@ -4034,6 +4279,12 @@ static const struct cdns_torrent_data cdns_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &dp_100_no_ssc_tx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &dp_100_no_ssc_tx_ln_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -4056,6 +4307,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = NULL, [INTERNAL_SSC] = NULL, }, + [TYPE_DP] = { + [NO_SSC] = NULL, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -4108,6 +4362,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, }, }, }, @@ -4131,6 +4388,12 @@ static const struct cdns_torrent_data cdns_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &dp_100_no_ssc_rx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &dp_100_no_ssc_rx_ln_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -4153,6 +4416,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -4205,6 +4471,9 @@ static const struct cdns_torrent_data cdns_map_torrent = { [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, }, }, }, @@ -4218,6 +4487,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_link_cmn_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &pcie_dp_link_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &usb_dp_link_cmn_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -4240,6 +4515,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &pcie_usb_link_cmn_vals, [INTERNAL_SSC] = &pcie_usb_link_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = &pcie_dp_link_cmn_vals, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -4292,6 +4570,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &usb_sgmii_link_cmn_vals, [INTERNAL_SSC] = &usb_sgmii_link_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_dp_link_cmn_vals, + }, }, }, .xcvr_diag_vals = { @@ -4299,6 +4580,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_xcvr_diag_ln_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &dp_pcie_xcvr_diag_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &dp_usb_xcvr_diag_ln_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -4321,6 +4608,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, [INTERNAL_SSC] = &pcie_usb_xcvr_diag_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &pcie_dp_xcvr_diag_ln_vals, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -4373,6 +4663,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, [INTERNAL_SSC] = &usb_sgmii_xcvr_diag_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_dp_xcvr_diag_ln_vals, + }, }, }, .pcs_cmn_vals = { @@ -4397,6 +4690,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &usb_phy_pcs_cmn_vals, [INTERNAL_SSC] = &usb_phy_pcs_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_phy_pcs_cmn_vals, + }, }, }, .cmn_vals = { @@ -4419,6 +4715,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &dp_100_no_ssc_cmn_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &sl_dp_100_no_ssc_cmn_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -4441,6 +4743,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &pcie_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &pcie_100_int_ssc_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = NULL, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -4493,6 +4798,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &sl_usb_100_no_ssc_cmn_vals, [INTERNAL_SSC] = &sl_usb_100_int_ssc_cmn_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_100_no_ssc_cmn_vals, + }, }, }, }, @@ -4516,6 +4824,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_100_no_ssc_tx_ln_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &dp_100_no_ssc_tx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &dp_100_no_ssc_tx_ln_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -4538,6 +4852,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = NULL, [INTERNAL_SSC] = NULL, }, + [TYPE_DP] = { + [NO_SSC] = NULL, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -4590,6 +4907,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, [INTERNAL_SSC] = &usb_100_no_ssc_tx_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_100_no_ssc_tx_ln_vals, + }, }, }, }, @@ -4613,6 +4933,12 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [TYPE_NONE] = { [NO_SSC] = &sl_dp_100_no_ssc_rx_ln_vals, }, + [TYPE_PCIE] = { + [NO_SSC] = &dp_100_no_ssc_rx_ln_vals, + }, + [TYPE_USB] = { + [NO_SSC] = &dp_100_no_ssc_rx_ln_vals, + }, }, [TYPE_PCIE] = { [TYPE_NONE] = { @@ -4635,6 +4961,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, [INTERNAL_SSC] = &pcie_100_no_ssc_rx_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &pcie_100_no_ssc_rx_ln_vals, + }, }, [TYPE_SGMII] = { [TYPE_NONE] = { @@ -4687,6 +5016,9 @@ static const struct cdns_torrent_data ti_j721e_map_torrent = { [EXTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, [INTERNAL_SSC] = &usb_100_no_ssc_rx_ln_vals, }, + [TYPE_DP] = { + [NO_SSC] = &usb_100_no_ssc_rx_ln_vals, + }, }, }, }, diff --git a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c index afc63552ecaf..d4c92498ad1e 100644 --- a/drivers/phy/freescale/phy-fsl-imx8m-pcie.c +++ b/drivers/phy/freescale/phy-fsl-imx8m-pcie.c @@ -206,7 +206,6 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct imx8_pcie_phy *imx8_phy; - struct resource *res; imx8_phy = devm_kzalloc(dev, sizeof(*imx8_phy), GFP_KERNEL); if (!imx8_phy) @@ -259,8 +258,7 @@ static int imx8_pcie_phy_probe(struct platform_device *pdev) "Failed to get PCIE PHY PERST control\n"); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - imx8_phy->base = devm_ioremap_resource(dev, res); + imx8_phy->base = devm_platform_ioremap_resource(pdev, 0); if (IS_ERR(imx8_phy->base)) return PTR_ERR(imx8_phy->base); diff --git a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c index a29b4a6f7c24..88826ceb72f8 100644 --- a/drivers/phy/freescale/phy-fsl-imx8mq-usb.c +++ b/drivers/phy/freescale/phy-fsl-imx8mq-usb.c @@ -27,17 +27,231 @@ #define PHY_CTRL2_TXENABLEN0 BIT(8) #define PHY_CTRL2_OTG_DISABLE BIT(9) +#define PHY_CTRL3 0xc +#define PHY_CTRL3_COMPDISTUNE_MASK GENMASK(2, 0) +#define PHY_CTRL3_TXPREEMP_TUNE_MASK GENMASK(16, 15) +#define PHY_CTRL3_TXRISE_TUNE_MASK GENMASK(21, 20) +#define PHY_CTRL3_TXVREF_TUNE_MASK GENMASK(25, 22) +#define PHY_CTRL3_TX_VBOOST_LEVEL_MASK GENMASK(31, 29) + +#define PHY_CTRL4 0x10 +#define PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK GENMASK(20, 15) + +#define PHY_CTRL5 0x14 +#define PHY_CTRL5_DMPWD_OVERRIDE_SEL BIT(23) +#define PHY_CTRL5_DMPWD_OVERRIDE BIT(22) +#define PHY_CTRL5_DPPWD_OVERRIDE_SEL BIT(21) +#define PHY_CTRL5_DPPWD_OVERRIDE BIT(20) +#define PHY_CTRL5_PCS_TX_SWING_FULL_MASK GENMASK(6, 0) + #define PHY_CTRL6 0x18 #define PHY_CTRL6_ALT_CLK_EN BIT(1) #define PHY_CTRL6_ALT_CLK_SEL BIT(0) +#define PHY_TUNE_DEFAULT 0xffffffff + struct imx8mq_usb_phy { struct phy *phy; struct clk *clk; void __iomem *base; struct regulator *vbus; + u32 pcs_tx_swing_full; + u32 pcs_tx_deemph_3p5db; + u32 tx_vref_tune; + u32 tx_rise_tune; + u32 tx_preemp_amp_tune; + u32 tx_vboost_level; + u32 comp_dis_tune; }; +static u32 phy_tx_vref_tune_from_property(u32 percent) +{ + percent = clamp(percent, 94U, 124U); + + return DIV_ROUND_CLOSEST(percent - 94U, 2); +} + +static u32 phy_tx_rise_tune_from_property(u32 percent) +{ + switch (percent) { + case 0 ... 98: + return 3; + case 99: + return 2; + case 100 ... 101: + return 1; + default: + return 0; + } +} + +static u32 phy_tx_preemp_amp_tune_from_property(u32 microamp) +{ + microamp = min(microamp, 1800U); + + return microamp / 600; +} + +static u32 phy_tx_vboost_level_from_property(u32 microvolt) +{ + switch (microvolt) { + case 0 ... 960: + return 0; + case 961 ... 1160: + return 2; + default: + return 3; + } +} + +static u32 phy_pcs_tx_deemph_3p5db_from_property(u32 decibel) +{ + return min(decibel, 36U); +} + +static u32 phy_comp_dis_tune_from_property(u32 percent) +{ + switch (percent) { + case 0 ... 92: + return 0; + case 93 ... 95: + return 1; + case 96 ... 97: + return 2; + case 98 ... 102: + return 3; + case 103 ... 105: + return 4; + case 106 ... 109: + return 5; + case 110 ... 113: + return 6; + default: + return 7; + } +} +static u32 phy_pcs_tx_swing_full_from_property(u32 percent) +{ + percent = min(percent, 100U); + + return (percent * 127) / 100; +} + +static void imx8m_get_phy_tuning_data(struct imx8mq_usb_phy *imx_phy) +{ + struct device *dev = imx_phy->phy->dev.parent; + + if (device_property_read_u32(dev, "fsl,phy-tx-vref-tune-percent", + &imx_phy->tx_vref_tune)) + imx_phy->tx_vref_tune = PHY_TUNE_DEFAULT; + else + imx_phy->tx_vref_tune = + phy_tx_vref_tune_from_property(imx_phy->tx_vref_tune); + + if (device_property_read_u32(dev, "fsl,phy-tx-rise-tune-percent", + &imx_phy->tx_rise_tune)) + imx_phy->tx_rise_tune = PHY_TUNE_DEFAULT; + else + imx_phy->tx_rise_tune = + phy_tx_rise_tune_from_property(imx_phy->tx_rise_tune); + + if (device_property_read_u32(dev, "fsl,phy-tx-preemp-amp-tune-microamp", + &imx_phy->tx_preemp_amp_tune)) + imx_phy->tx_preemp_amp_tune = PHY_TUNE_DEFAULT; + else + imx_phy->tx_preemp_amp_tune = + phy_tx_preemp_amp_tune_from_property(imx_phy->tx_preemp_amp_tune); + + if (device_property_read_u32(dev, "fsl,phy-tx-vboost-level-microvolt", + &imx_phy->tx_vboost_level)) + imx_phy->tx_vboost_level = PHY_TUNE_DEFAULT; + else + imx_phy->tx_vboost_level = + phy_tx_vboost_level_from_property(imx_phy->tx_vboost_level); + + if (device_property_read_u32(dev, "fsl,phy-comp-dis-tune-percent", + &imx_phy->comp_dis_tune)) + imx_phy->comp_dis_tune = PHY_TUNE_DEFAULT; + else + imx_phy->comp_dis_tune = + phy_comp_dis_tune_from_property(imx_phy->comp_dis_tune); + + if (device_property_read_u32(dev, "fsl,pcs-tx-deemph-3p5db-attenuation-db", + &imx_phy->pcs_tx_deemph_3p5db)) + imx_phy->pcs_tx_deemph_3p5db = PHY_TUNE_DEFAULT; + else + imx_phy->pcs_tx_deemph_3p5db = + phy_pcs_tx_deemph_3p5db_from_property(imx_phy->pcs_tx_deemph_3p5db); + + if (device_property_read_u32(dev, "fsl,phy-pcs-tx-swing-full-percent", + &imx_phy->pcs_tx_swing_full)) + imx_phy->pcs_tx_swing_full = PHY_TUNE_DEFAULT; + else + imx_phy->pcs_tx_swing_full = + phy_pcs_tx_swing_full_from_property(imx_phy->pcs_tx_swing_full); +} + +static void imx8m_phy_tune(struct imx8mq_usb_phy *imx_phy) +{ + u32 value; + + /* PHY tuning */ + if (imx_phy->pcs_tx_deemph_3p5db != PHY_TUNE_DEFAULT) { + value = readl(imx_phy->base + PHY_CTRL4); + value &= ~PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK; + value |= FIELD_PREP(PHY_CTRL4_PCS_TX_DEEMPH_3P5DB_MASK, + imx_phy->pcs_tx_deemph_3p5db); + writel(value, imx_phy->base + PHY_CTRL4); + } + + if (imx_phy->pcs_tx_swing_full != PHY_TUNE_DEFAULT) { + value = readl(imx_phy->base + PHY_CTRL5); + value |= FIELD_PREP(PHY_CTRL5_PCS_TX_SWING_FULL_MASK, + imx_phy->pcs_tx_swing_full); + writel(value, imx_phy->base + PHY_CTRL5); + } + + if ((imx_phy->tx_vref_tune & imx_phy->tx_rise_tune & + imx_phy->tx_preemp_amp_tune & imx_phy->comp_dis_tune & + imx_phy->tx_vboost_level) == PHY_TUNE_DEFAULT) + /* If all are the default values, no need update. */ + return; + + value = readl(imx_phy->base + PHY_CTRL3); + + if (imx_phy->tx_vref_tune != PHY_TUNE_DEFAULT) { + value &= ~PHY_CTRL3_TXVREF_TUNE_MASK; + value |= FIELD_PREP(PHY_CTRL3_TXVREF_TUNE_MASK, + imx_phy->tx_vref_tune); + } + + if (imx_phy->tx_rise_tune != PHY_TUNE_DEFAULT) { + value &= ~PHY_CTRL3_TXRISE_TUNE_MASK; + value |= FIELD_PREP(PHY_CTRL3_TXRISE_TUNE_MASK, + imx_phy->tx_rise_tune); + } + + if (imx_phy->tx_preemp_amp_tune != PHY_TUNE_DEFAULT) { + value &= ~PHY_CTRL3_TXPREEMP_TUNE_MASK; + value |= FIELD_PREP(PHY_CTRL3_TXPREEMP_TUNE_MASK, + imx_phy->tx_preemp_amp_tune); + } + + if (imx_phy->comp_dis_tune != PHY_TUNE_DEFAULT) { + value &= ~PHY_CTRL3_COMPDISTUNE_MASK; + value |= FIELD_PREP(PHY_CTRL3_COMPDISTUNE_MASK, + imx_phy->comp_dis_tune); + } + + if (imx_phy->tx_vboost_level != PHY_TUNE_DEFAULT) { + value &= ~PHY_CTRL3_TX_VBOOST_LEVEL_MASK; + value |= FIELD_PREP(PHY_CTRL3_TX_VBOOST_LEVEL_MASK, + imx_phy->tx_vboost_level); + } + + writel(value, imx_phy->base + PHY_CTRL3); +} + static int imx8mq_usb_phy_init(struct phy *phy) { struct imx8mq_usb_phy *imx_phy = phy_get_drvdata(phy); @@ -99,6 +313,8 @@ static int imx8mp_usb_phy_init(struct phy *phy) value &= ~(PHY_CTRL1_RESET | PHY_CTRL1_ATERESET); writel(value, imx_phy->base + PHY_CTRL1); + imx8m_phy_tune(imx_phy); + return 0; } @@ -182,6 +398,8 @@ static int imx8mq_usb_phy_probe(struct platform_device *pdev) phy_set_drvdata(imx_phy->phy, imx_phy); + imx8m_get_phy_tuning_data(imx_phy); + phy_provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); return PTR_ERR_OR_ZERO(phy_provider); diff --git a/drivers/phy/hisilicon/Kconfig b/drivers/phy/hisilicon/Kconfig index d3b92c288554..6c89136fc8c2 100644 --- a/drivers/phy/hisilicon/Kconfig +++ b/drivers/phy/hisilicon/Kconfig @@ -54,7 +54,7 @@ config PHY_HISTB_COMBPHY config PHY_HISI_INNO_USB2 tristate "HiSilicon INNO USB2 PHY support" - depends on (ARCH_HISI && ARM64) || COMPILE_TEST + depends on ARCH_HISI || COMPILE_TEST select GENERIC_PHY select MFD_SYSCON help diff --git a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c index b133ae06757a..15dafe359552 100644 --- a/drivers/phy/hisilicon/phy-hisi-inno-usb2.c +++ b/drivers/phy/hisilicon/phy-hisi-inno-usb2.c @@ -9,7 +9,7 @@ #include <linux/delay.h> #include <linux/io.h> #include <linux/module.h> -#include <linux/platform_device.h> +#include <linux/of_device.h> #include <linux/phy/phy.h> #include <linux/reset.h> @@ -20,12 +20,25 @@ #define PHY_CLK_STABLE_TIME 2 /* unit:ms */ #define UTMI_RST_COMPLETE_TIME 2 /* unit:ms */ #define POR_RST_COMPLETE_TIME 300 /* unit:us */ + +#define PHY_TYPE_0 0 +#define PHY_TYPE_1 1 + #define PHY_TEST_DATA GENMASK(7, 0) -#define PHY_TEST_ADDR GENMASK(15, 8) -#define PHY_TEST_PORT GENMASK(18, 16) -#define PHY_TEST_WREN BIT(21) -#define PHY_TEST_CLK BIT(22) /* rising edge active */ -#define PHY_TEST_RST BIT(23) /* low active */ +#define PHY_TEST_ADDR_OFFSET 8 +#define PHY0_TEST_ADDR GENMASK(15, 8) +#define PHY0_TEST_PORT_OFFSET 16 +#define PHY0_TEST_PORT GENMASK(18, 16) +#define PHY0_TEST_WREN BIT(21) +#define PHY0_TEST_CLK BIT(22) /* rising edge active */ +#define PHY0_TEST_RST BIT(23) /* low active */ +#define PHY1_TEST_ADDR GENMASK(11, 8) +#define PHY1_TEST_PORT_OFFSET 12 +#define PHY1_TEST_PORT BIT(12) +#define PHY1_TEST_WREN BIT(13) +#define PHY1_TEST_CLK BIT(14) /* rising edge active */ +#define PHY1_TEST_RST BIT(15) /* low active */ + #define PHY_CLK_ENABLE BIT(2) struct hisi_inno_phy_port { @@ -37,6 +50,7 @@ struct hisi_inno_phy_priv { void __iomem *mmio; struct clk *ref_clk; struct reset_control *por_rst; + unsigned int type; struct hisi_inno_phy_port ports[INNO_PHY_PORT_NUM]; }; @@ -45,17 +59,27 @@ static void hisi_inno_phy_write_reg(struct hisi_inno_phy_priv *priv, { void __iomem *reg = priv->mmio; u32 val; - - val = (data & PHY_TEST_DATA) | - ((addr << 8) & PHY_TEST_ADDR) | - ((port << 16) & PHY_TEST_PORT) | - PHY_TEST_WREN | PHY_TEST_RST; + u32 value; + + if (priv->type == PHY_TYPE_0) + val = (data & PHY_TEST_DATA) | + ((addr << PHY_TEST_ADDR_OFFSET) & PHY0_TEST_ADDR) | + ((port << PHY0_TEST_PORT_OFFSET) & PHY0_TEST_PORT) | + PHY0_TEST_WREN | PHY0_TEST_RST; + else + val = (data & PHY_TEST_DATA) | + ((addr << PHY_TEST_ADDR_OFFSET) & PHY1_TEST_ADDR) | + ((port << PHY1_TEST_PORT_OFFSET) & PHY1_TEST_PORT) | + PHY1_TEST_WREN | PHY1_TEST_RST; writel(val, reg); - val |= PHY_TEST_CLK; - writel(val, reg); + value = val; + if (priv->type == PHY_TYPE_0) + value |= PHY0_TEST_CLK; + else + value |= PHY1_TEST_CLK; + writel(value, reg); - val &= ~PHY_TEST_CLK; writel(val, reg); } @@ -135,6 +159,8 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) if (IS_ERR(priv->por_rst)) return PTR_ERR(priv->por_rst); + priv->type = (uintptr_t) of_device_get_match_data(dev); + for_each_child_of_node(np, child) { struct reset_control *rst; struct phy *phy; @@ -170,8 +196,12 @@ static int hisi_inno_phy_probe(struct platform_device *pdev) } static const struct of_device_id hisi_inno_phy_of_match[] = { - { .compatible = "hisilicon,inno-usb2-phy", }, - { .compatible = "hisilicon,hi3798cv200-usb2-phy", }, + { .compatible = "hisilicon,inno-usb2-phy", + .data = (void *) PHY_TYPE_0 }, + { .compatible = "hisilicon,hi3798cv200-usb2-phy", + .data = (void *) PHY_TYPE_0 }, + { .compatible = "hisilicon,hi3798mv100-usb2-phy", + .data = (void *) PHY_TYPE_1 }, { }, }; MODULE_DEVICE_TABLE(of, hisi_inno_phy_of_match); diff --git a/drivers/phy/mediatek/phy-mtk-tphy.c b/drivers/phy/mediatek/phy-mtk-tphy.c index e906a82791bd..0d110e50bbfd 100644 --- a/drivers/phy/mediatek/phy-mtk-tphy.c +++ b/drivers/phy/mediatek/phy-mtk-tphy.c @@ -7,6 +7,7 @@ #include <dt-bindings/phy/phy.h> #include <linux/clk.h> +#include <linux/debugfs.h> #include <linux/delay.h> #include <linux/iopoll.h> #include <linux/mfd/syscon.h> @@ -264,6 +265,8 @@ #define TPHY_CLKS_CNT 2 +#define USER_BUF_LEN(count) min_t(size_t, 8, (count)) + enum mtk_phy_version { MTK_PHY_V1 = 1, MTK_PHY_V2, @@ -336,6 +339,358 @@ struct mtk_tphy { int src_coef; /* coefficient for slew rate calibrate */ }; +#if IS_ENABLED(CONFIG_DEBUG_FS) + +enum u2_phy_params { + U2P_EYE_VRT = 0, + U2P_EYE_TERM, + U2P_EFUSE_EN, + U2P_EFUSE_INTR, + U2P_DISCTH, + U2P_PRE_EMPHASIS, +}; + +enum u3_phy_params { + U3P_EFUSE_EN = 0, + U3P_EFUSE_INTR, + U3P_EFUSE_TX_IMP, + U3P_EFUSE_RX_IMP, +}; + +static const char *const u2_phy_files[] = { + [U2P_EYE_VRT] = "vrt", + [U2P_EYE_TERM] = "term", + [U2P_EFUSE_EN] = "efuse", + [U2P_EFUSE_INTR] = "intr", + [U2P_DISCTH] = "discth", + [U2P_PRE_EMPHASIS] = "preemph", +}; + +static const char *const u3_phy_files[] = { + [U3P_EFUSE_EN] = "efuse", + [U3P_EFUSE_INTR] = "intr", + [U3P_EFUSE_TX_IMP] = "tx-imp", + [U3P_EFUSE_RX_IMP] = "rx-imp", +}; + +static int u2_phy_params_show(struct seq_file *sf, void *unused) +{ + struct mtk_phy_instance *inst = sf->private; + const char *fname = file_dentry(sf->file)->d_iname; + struct u2phy_banks *u2_banks = &inst->u2_banks; + void __iomem *com = u2_banks->com; + u32 max = 0; + u32 tmp = 0; + u32 val = 0; + int ret; + + ret = match_string(u2_phy_files, ARRAY_SIZE(u2_phy_files), fname); + if (ret < 0) + return ret; + + switch (ret) { + case U2P_EYE_VRT: + tmp = readl(com + U3P_USBPHYACR1); + val = FIELD_GET(PA1_RG_VRT_SEL, tmp); + max = FIELD_MAX(PA1_RG_VRT_SEL); + break; + + case U2P_EYE_TERM: + tmp = readl(com + U3P_USBPHYACR1); + val = FIELD_GET(PA1_RG_TERM_SEL, tmp); + max = FIELD_MAX(PA1_RG_TERM_SEL); + break; + + case U2P_EFUSE_EN: + if (u2_banks->misc) { + tmp = readl(u2_banks->misc + U3P_MISC_REG1); + max = 1; + } + + val = !!(tmp & MR1_EFUSE_AUTO_LOAD_DIS); + break; + + case U2P_EFUSE_INTR: + tmp = readl(com + U3P_USBPHYACR1); + val = FIELD_GET(PA1_RG_INTR_CAL, tmp); + max = FIELD_MAX(PA1_RG_INTR_CAL); + break; + + case U2P_DISCTH: + tmp = readl(com + U3P_USBPHYACR6); + val = FIELD_GET(PA6_RG_U2_DISCTH, tmp); + max = FIELD_MAX(PA6_RG_U2_DISCTH); + break; + + case U2P_PRE_EMPHASIS: + tmp = readl(com + U3P_USBPHYACR6); + val = FIELD_GET(PA6_RG_U2_PRE_EMP, tmp); + max = FIELD_MAX(PA6_RG_U2_PRE_EMP); + break; + + default: + seq_printf(sf, "invalid, %d\n", ret); + break; + } + + seq_printf(sf, "%s : %d [0, %d]\n", fname, val, max); + + return 0; +} + +static int u2_phy_params_open(struct inode *inode, struct file *file) +{ + return single_open(file, u2_phy_params_show, inode->i_private); +} + +static ssize_t u2_phy_params_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + const char *fname = file_dentry(file)->d_iname; + struct seq_file *sf = file->private_data; + struct mtk_phy_instance *inst = sf->private; + struct u2phy_banks *u2_banks = &inst->u2_banks; + void __iomem *com = u2_banks->com; + ssize_t rc; + u32 val; + int ret; + + rc = kstrtouint_from_user(ubuf, USER_BUF_LEN(count), 0, &val); + if (rc) + return rc; + + ret = match_string(u2_phy_files, ARRAY_SIZE(u2_phy_files), fname); + if (ret < 0) + return (ssize_t)ret; + + switch (ret) { + case U2P_EYE_VRT: + mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_VRT_SEL, val); + break; + + case U2P_EYE_TERM: + mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_TERM_SEL, val); + break; + + case U2P_EFUSE_EN: + if (u2_banks->misc) + mtk_phy_update_field(u2_banks->misc + U3P_MISC_REG1, + MR1_EFUSE_AUTO_LOAD_DIS, !!val); + break; + + case U2P_EFUSE_INTR: + mtk_phy_update_field(com + U3P_USBPHYACR1, PA1_RG_INTR_CAL, val); + break; + + case U2P_DISCTH: + mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_DISCTH, val); + break; + + case U2P_PRE_EMPHASIS: + mtk_phy_update_field(com + U3P_USBPHYACR6, PA6_RG_U2_PRE_EMP, val); + break; + + default: + break; + } + + return count; +} + +static const struct file_operations u2_phy_fops = { + .open = u2_phy_params_open, + .write = u2_phy_params_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void u2_phy_dbgfs_files_create(struct mtk_phy_instance *inst) +{ + u32 count = ARRAY_SIZE(u2_phy_files); + int i; + + for (i = 0; i < count; i++) + debugfs_create_file(u2_phy_files[i], 0644, inst->phy->debugfs, + inst, &u2_phy_fops); +} + +static int u3_phy_params_show(struct seq_file *sf, void *unused) +{ + struct mtk_phy_instance *inst = sf->private; + const char *fname = file_dentry(sf->file)->d_iname; + struct u3phy_banks *u3_banks = &inst->u3_banks; + u32 val = 0; + u32 max = 0; + u32 tmp; + int ret; + + ret = match_string(u3_phy_files, ARRAY_SIZE(u3_phy_files), fname); + if (ret < 0) + return ret; + + switch (ret) { + case U3P_EFUSE_EN: + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_RSV); + val = !!(tmp & P3D_RG_EFUSE_AUTO_LOAD_DIS); + max = 1; + break; + + case U3P_EFUSE_INTR: + tmp = readl(u3_banks->phya + U3P_U3_PHYA_REG0); + val = FIELD_GET(P3A_RG_IEXT_INTR, tmp); + max = FIELD_MAX(P3A_RG_IEXT_INTR); + break; + + case U3P_EFUSE_TX_IMP: + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL0); + val = FIELD_GET(P3D_RG_TX_IMPEL, tmp); + max = FIELD_MAX(P3D_RG_TX_IMPEL); + break; + + case U3P_EFUSE_RX_IMP: + tmp = readl(u3_banks->phyd + U3P_U3_PHYD_IMPCAL1); + val = FIELD_GET(P3D_RG_RX_IMPEL, tmp); + max = FIELD_MAX(P3D_RG_RX_IMPEL); + break; + + default: + seq_printf(sf, "invalid, %d\n", ret); + break; + } + + seq_printf(sf, "%s : %d [0, %d]\n", fname, val, max); + + return 0; +} + +static int u3_phy_params_open(struct inode *inode, struct file *file) +{ + return single_open(file, u3_phy_params_show, inode->i_private); +} + +static ssize_t u3_phy_params_write(struct file *file, const char __user *ubuf, + size_t count, loff_t *ppos) +{ + const char *fname = file_dentry(file)->d_iname; + struct seq_file *sf = file->private_data; + struct mtk_phy_instance *inst = sf->private; + struct u3phy_banks *u3_banks = &inst->u3_banks; + void __iomem *phyd = u3_banks->phyd; + ssize_t rc; + u32 val; + int ret; + + rc = kstrtouint_from_user(ubuf, USER_BUF_LEN(count), 0, &val); + if (rc) + return rc; + + ret = match_string(u3_phy_files, ARRAY_SIZE(u3_phy_files), fname); + if (ret < 0) + return (ssize_t)ret; + + switch (ret) { + case U3P_EFUSE_EN: + mtk_phy_update_field(phyd + U3P_U3_PHYD_RSV, + P3D_RG_EFUSE_AUTO_LOAD_DIS, !!val); + break; + + case U3P_EFUSE_INTR: + mtk_phy_update_field(u3_banks->phya + U3P_U3_PHYA_REG0, + P3A_RG_IEXT_INTR, val); + break; + + case U3P_EFUSE_TX_IMP: + mtk_phy_update_field(phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_TX_IMPEL, val); + mtk_phy_set_bits(phyd + U3P_U3_PHYD_IMPCAL0, P3D_RG_FORCE_TX_IMPEL); + break; + + case U3P_EFUSE_RX_IMP: + mtk_phy_update_field(phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_RX_IMPEL, val); + mtk_phy_set_bits(phyd + U3P_U3_PHYD_IMPCAL1, P3D_RG_FORCE_RX_IMPEL); + break; + + default: + break; + } + + return count; +} + +static const struct file_operations u3_phy_fops = { + .open = u3_phy_params_open, + .write = u3_phy_params_write, + .read = seq_read, + .llseek = seq_lseek, + .release = single_release, +}; + +static void u3_phy_dbgfs_files_create(struct mtk_phy_instance *inst) +{ + u32 count = ARRAY_SIZE(u3_phy_files); + int i; + + for (i = 0; i < count; i++) + debugfs_create_file(u3_phy_files[i], 0644, inst->phy->debugfs, + inst, &u3_phy_fops); +} + +static int phy_type_show(struct seq_file *sf, void *unused) +{ + struct mtk_phy_instance *inst = sf->private; + const char *type; + + switch (inst->type) { + case PHY_TYPE_USB2: + type = "USB2"; + break; + case PHY_TYPE_USB3: + type = "USB3"; + break; + case PHY_TYPE_PCIE: + type = "PCIe"; + break; + case PHY_TYPE_SGMII: + type = "SGMII"; + break; + case PHY_TYPE_SATA: + type = "SATA"; + break; + default: + type = ""; + } + + seq_printf(sf, "%s\n", type); + + return 0; +} +DEFINE_SHOW_ATTRIBUTE(phy_type); + +/* these files will be removed when phy is released by phy core */ +static void phy_debugfs_init(struct mtk_phy_instance *inst) +{ + debugfs_create_file("type", 0444, inst->phy->debugfs, inst, &phy_type_fops); + + switch (inst->type) { + case PHY_TYPE_USB2: + u2_phy_dbgfs_files_create(inst); + break; + case PHY_TYPE_USB3: + case PHY_TYPE_PCIE: + u3_phy_dbgfs_files_create(inst); + break; + default: + break; + } +} + +#else + +static void phy_debugfs_init(struct mtk_phy_instance *inst) +{} + +#endif + static void hs_slew_rate_calibrate(struct mtk_tphy *tphy, struct mtk_phy_instance *instance) { @@ -1140,6 +1495,7 @@ static struct phy *mtk_phy_xlate(struct device *dev, phy_parse_property(tphy, instance); phy_type_set(instance); + phy_debugfs_init(instance); return instance->phy; } diff --git a/drivers/phy/microchip/sparx5_serdes.c b/drivers/phy/microchip/sparx5_serdes.c index ab1b0986aa67..01bd5ea620c5 100644 --- a/drivers/phy/microchip/sparx5_serdes.c +++ b/drivers/phy/microchip/sparx5_serdes.c @@ -25,12 +25,17 @@ #define SPX5_SERDES_10G_START 13 #define SPX5_SERDES_25G_START 25 +#define SPX5_SERDES_6G10G_CNT SPX5_SERDES_25G_START + +/* Optimal power settings from GUC */ +#define SPX5_SERDES_QUIET_MODE_VAL 0x01ef4e0c enum sparx5_10g28cmu_mode { SPX5_SD10G28_CMU_MAIN = 0, SPX5_SD10G28_CMU_AUX1 = 1, SPX5_SD10G28_CMU_AUX2 = 3, SPX5_SD10G28_CMU_NONE = 4, + SPX5_SD10G28_CMU_MAX, }; enum sparx5_sd25g28_mode_preset_type { @@ -922,6 +927,222 @@ static void sparx5_sd10g28_get_params(struct sparx5_serdes_macro *macro, *params = init; } +static int sparx5_cmu_apply_cfg(struct sparx5_serdes_private *priv, + u32 cmu_idx, + void __iomem *cmu_tgt, + void __iomem *cmu_cfg_tgt, + u32 spd10g) +{ + void __iomem **regs = priv->regs; + struct device *dev = priv->dev; + int value; + + cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx); + cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx); + + if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 || + cmu_idx == 10 || cmu_idx == 13) { + spd10g = 0; + } + + sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(1), + SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, + cmu_cfg_tgt, + SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(0), + SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, + cmu_cfg_tgt, + SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(1), + SD_CMU_CFG_SD_CMU_CFG_CMU_RST, + cmu_cfg_tgt, + SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_SET(0x1) | + SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_SET(0x1) | + SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_SET(0x1) | + SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_SET(0x1) | + SD_CMU_CMU_45_R_EN_RATECHG_CTRL_SET(0x0), + SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT | + SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT | + SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT | + SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT | + SD_CMU_CMU_45_R_EN_RATECHG_CTRL, + cmu_tgt, + SD_CMU_CMU_45(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_SET(0), + SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0, + cmu_tgt, + SD_CMU_CMU_47(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_1B_CFG_RESERVE_7_0_SET(0), + SD_CMU_CMU_1B_CFG_RESERVE_7_0, + cmu_tgt, + SD_CMU_CMU_1B(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_JC_BYP_SET(0x1), + SD_CMU_CMU_0D_CFG_JC_BYP, + cmu_tgt, + SD_CMU_CMU_0D(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_1F_CFG_VTUNE_SEL_SET(1), + SD_CMU_CMU_1F_CFG_VTUNE_SEL, + cmu_tgt, + SD_CMU_CMU_1F(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_SET(3), + SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0, + cmu_tgt, + SD_CMU_CMU_00(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_SET(3), + SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, + cmu_tgt, + SD_CMU_CMU_05(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_30_R_PLL_DLOL_EN_SET(1), + SD_CMU_CMU_30_R_PLL_DLOL_EN, + cmu_tgt, + SD_CMU_CMU_30(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_09_CFG_SW_10G_SET(spd10g), + SD_CMU_CMU_09_CFG_SW_10G, + cmu_tgt, + SD_CMU_CMU_09(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(0), + SD_CMU_CFG_SD_CMU_CFG_CMU_RST, + cmu_cfg_tgt, + SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); + + msleep(20); + + sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(0), + SD_CMU_CMU_44_R_PLL_RSTN, + cmu_tgt, + SD_CMU_CMU_44(cmu_idx)); + + sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(1), + SD_CMU_CMU_44_R_PLL_RSTN, + cmu_tgt, + SD_CMU_CMU_44(cmu_idx)); + + msleep(20); + + value = readl(sdx5_addr(regs, SD_CMU_CMU_E0(cmu_idx))); + value = SD_CMU_CMU_E0_PLL_LOL_UDL_GET(value); + + if (value) { + dev_err(dev, "CMU PLL Loss of Lock: 0x%x\n", value); + return -EINVAL; + } + sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_SET(0), + SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD, + cmu_tgt, + SD_CMU_CMU_0D(cmu_idx)); + return 0; +} + +static int sparx5_cmu_cfg(struct sparx5_serdes_private *priv, u32 cmu_idx) +{ + void __iomem *cmu_tgt, *cmu_cfg_tgt; + u32 spd10g = 1; + + if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 || + cmu_idx == 10 || cmu_idx == 13) { + spd10g = 0; + } + + cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx); + cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx); + + return sparx5_cmu_apply_cfg(priv, cmu_idx, cmu_tgt, cmu_cfg_tgt, spd10g); +} + +/* Map of 6G/10G serdes mode and index to CMU index. */ +static const int +sparx5_serdes_cmu_map[SPX5_SD10G28_CMU_MAX][SPX5_SERDES_6G10G_CNT] = { + [SPX5_SD10G28_CMU_MAIN] = { 2, 2, 2, 2, 2, + 2, 2, 2, 5, 5, + 5, 5, 5, 5, 5, + 5, 8, 11, 11, 11, + 11, 11, 11, 11, 11 }, + [SPX5_SD10G28_CMU_AUX1] = { 0, 0, 3, 3, 3, + 3, 3, 3, 3, 3, + 6, 6, 6, 6, 6, + 6, 6, 9, 9, 12, + 12, 12, 12, 12, 12 }, + [SPX5_SD10G28_CMU_AUX2] = { 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4, + 4, 4, 7, 7, 7, + 7, 7, 10, 10, 10, + 10, 13, 13, 13, 13 }, + [SPX5_SD10G28_CMU_NONE] = { 1, 1, 1, 1, 4, + 4, 4, 4, 4, 4, + 4, 4, 7, 7, 7, + 7, 7, 10, 10, 10, + 10, 13, 13, 13, 13 }, +}; + +/* Get the index of the CMU which provides the clock for the specified serdes + * mode and index. + */ +static int sparx5_serdes_cmu_get(enum sparx5_10g28cmu_mode mode, int sd_index) +{ + return sparx5_serdes_cmu_map[mode][sd_index]; +} + +static void sparx5_serdes_cmu_power_off(struct sparx5_serdes_private *priv) +{ + void __iomem *cmu_inst, *cmu_cfg_inst; + int i; + + /* Power down each CMU */ + for (i = 0; i < SPX5_CMU_MAX; i++) { + cmu_inst = sdx5_inst_get(priv, TARGET_SD_CMU, i); + cmu_cfg_inst = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, i); + + sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(0), + SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, cmu_cfg_inst, + SD_CMU_CFG_SD_CMU_CFG(0)); + + sdx5_inst_rmw(SD_CMU_CMU_05_CFG_REFCK_TERM_EN_SET(0), + SD_CMU_CMU_05_CFG_REFCK_TERM_EN, cmu_inst, + SD_CMU_CMU_05(0)); + + sdx5_inst_rmw(SD_CMU_CMU_09_CFG_EN_TX_CK_DN_SET(0), + SD_CMU_CMU_09_CFG_EN_TX_CK_DN, cmu_inst, + SD_CMU_CMU_09(0)); + + sdx5_inst_rmw(SD_CMU_CMU_06_CFG_VCO_PD_SET(1), + SD_CMU_CMU_06_CFG_VCO_PD, cmu_inst, + SD_CMU_CMU_06(0)); + + sdx5_inst_rmw(SD_CMU_CMU_09_CFG_EN_TX_CK_UP_SET(0), + SD_CMU_CMU_09_CFG_EN_TX_CK_UP, cmu_inst, + SD_CMU_CMU_09(0)); + + sdx5_inst_rmw(SD_CMU_CMU_08_CFG_CK_TREE_PD_SET(1), + SD_CMU_CMU_08_CFG_CK_TREE_PD, cmu_inst, + SD_CMU_CMU_08(0)); + + sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_REFCK_PD_SET(1) | + SD_CMU_CMU_0D_CFG_PD_DIV64_SET(1) | + SD_CMU_CMU_0D_CFG_PD_DIV66_SET(1), + SD_CMU_CMU_0D_CFG_REFCK_PD | + SD_CMU_CMU_0D_CFG_PD_DIV64 | + SD_CMU_CMU_0D_CFG_PD_DIV66, cmu_inst, + SD_CMU_CMU_0D(0)); + + sdx5_inst_rmw(SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD_SET(1), + SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD, cmu_inst, + SD_CMU_CMU_06(0)); + } +} + static void sparx5_sd25g28_reset(void __iomem *regs[], struct sparx5_sd25g28_params *params, u32 sd_index) @@ -1422,7 +1643,17 @@ static int sparx5_sd10g28_apply_params(struct sparx5_serdes_macro *macro, u32 lane_index = macro->sidx; u32 sd_index = macro->stpidx; void __iomem *sd_inst; - u32 value; + u32 value, cmu_idx; + int err; + + /* Do not configure serdes if CMU is not to be configured too */ + if (params->skip_cmu_cfg) + return 0; + + cmu_idx = sparx5_serdes_cmu_get(params->cmu_sel, lane_index); + err = sparx5_cmu_cfg(priv, cmu_idx); + if (err) + return err; if (params->is_6g) sd_inst = sdx5_inst_get(priv, TARGET_SD6G_LANE, sd_index); @@ -1884,6 +2115,7 @@ static int sparx5_sd10g28_config(struct sparx5_serdes_macro *macro, bool reset) .rxinvert = 1, .txswing = 240, .reg_rst = reset, + .skip_cmu_cfg = reset, }; int err; @@ -1899,7 +2131,7 @@ static int sparx5_sd10g28_config(struct sparx5_serdes_macro *macro, bool reset) static int sparx5_serdes_power_save(struct sparx5_serdes_macro *macro, u32 pwdn) { struct sparx5_serdes_private *priv = macro->priv; - void __iomem *sd_inst; + void __iomem *sd_inst, *sd_lane_inst; if (macro->serdestype == SPX5_SDT_6G) sd_inst = sdx5_inst_get(priv, TARGET_SD6G_LANE, macro->stpidx); @@ -1909,12 +2141,36 @@ static int sparx5_serdes_power_save(struct sparx5_serdes_macro *macro, u32 pwdn) sd_inst = sdx5_inst_get(priv, TARGET_SD25G_LANE, macro->stpidx); if (macro->serdestype == SPX5_SDT_25G) { + sd_lane_inst = sdx5_inst_get(priv, TARGET_SD_LANE_25G, + macro->stpidx); + /* Take serdes out of reset */ + sdx5_inst_rmw(SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST_SET(0), + SD_LANE_25G_SD_LANE_CFG_EXT_CFG_RST, sd_lane_inst, + SD_LANE_25G_SD_LANE_CFG(0)); + + /* Configure optimal settings for quiet mode */ + sdx5_inst_rmw(SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE_SET(SPX5_SERDES_QUIET_MODE_VAL), + SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE, + sd_lane_inst, SD_LANE_25G_QUIET_MODE_6G(0)); + sdx5_inst_rmw(SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER_SET(pwdn), SD25G_LANE_LANE_04_LN_CFG_PD_DRIVER, sd_inst, SD25G_LANE_LANE_04(0)); } else { /* 6G and 10G */ + sd_lane_inst = sdx5_inst_get(priv, TARGET_SD_LANE, macro->sidx); + + /* Take serdes out of reset */ + sdx5_inst_rmw(SD_LANE_SD_LANE_CFG_EXT_CFG_RST_SET(0), + SD_LANE_SD_LANE_CFG_EXT_CFG_RST, sd_lane_inst, + SD_LANE_SD_LANE_CFG(0)); + + /* Configure optimal settings for quiet mode */ + sdx5_inst_rmw(SD_LANE_QUIET_MODE_6G_QUIET_MODE_SET(SPX5_SERDES_QUIET_MODE_VAL), + SD_LANE_QUIET_MODE_6G_QUIET_MODE, sd_lane_inst, + SD_LANE_QUIET_MODE_6G(0)); + sdx5_inst_rmw(SD10G_LANE_LANE_06_CFG_PD_DRIVER_SET(pwdn), SD10G_LANE_LANE_06_CFG_PD_DRIVER, sd_inst, @@ -1939,159 +2195,6 @@ static int sparx5_serdes_clock_config(struct sparx5_serdes_macro *macro) return 0; } -static int sparx5_cmu_apply_cfg(struct sparx5_serdes_private *priv, - u32 cmu_idx, - void __iomem *cmu_tgt, - void __iomem *cmu_cfg_tgt, - u32 spd10g) -{ - void __iomem **regs = priv->regs; - struct device *dev = priv->dev; - int value; - - cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx); - cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx); - - if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 || - cmu_idx == 10 || cmu_idx == 13) { - spd10g = 0; - } - - sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(1), - SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, - cmu_cfg_tgt, - SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST_SET(0), - SD_CMU_CFG_SD_CMU_CFG_EXT_CFG_RST, - cmu_cfg_tgt, - SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(1), - SD_CMU_CFG_SD_CMU_CFG_CMU_RST, - cmu_cfg_tgt, - SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT_SET(0x1) | - SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT_SET(0x1) | - SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT_SET(0x1) | - SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT_SET(0x1) | - SD_CMU_CMU_45_R_EN_RATECHG_CTRL_SET(0x0), - SD_CMU_CMU_45_R_DWIDTHCTRL_FROM_HWT | - SD_CMU_CMU_45_R_REFCK_SSC_EN_FROM_HWT | - SD_CMU_CMU_45_R_LINK_BUF_EN_FROM_HWT | - SD_CMU_CMU_45_R_BIAS_EN_FROM_HWT | - SD_CMU_CMU_45_R_EN_RATECHG_CTRL, - cmu_tgt, - SD_CMU_CMU_45(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0_SET(0), - SD_CMU_CMU_47_R_PCS2PMA_PHYMODE_4_0, - cmu_tgt, - SD_CMU_CMU_47(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_1B_CFG_RESERVE_7_0_SET(0), - SD_CMU_CMU_1B_CFG_RESERVE_7_0, - cmu_tgt, - SD_CMU_CMU_1B(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_JC_BYP_SET(0x1), - SD_CMU_CMU_0D_CFG_JC_BYP, - cmu_tgt, - SD_CMU_CMU_0D(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_1F_CFG_VTUNE_SEL_SET(1), - SD_CMU_CMU_1F_CFG_VTUNE_SEL, - cmu_tgt, - SD_CMU_CMU_1F(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0_SET(3), - SD_CMU_CMU_00_CFG_PLL_TP_SEL_1_0, - cmu_tgt, - SD_CMU_CMU_00(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_SET(3), - SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, - cmu_tgt, - SD_CMU_CMU_05(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_30_R_PLL_DLOL_EN_SET(1), - SD_CMU_CMU_30_R_PLL_DLOL_EN, - cmu_tgt, - SD_CMU_CMU_30(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_09_CFG_SW_10G_SET(spd10g), - SD_CMU_CMU_09_CFG_SW_10G, - cmu_tgt, - SD_CMU_CMU_09(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CFG_SD_CMU_CFG_CMU_RST_SET(0), - SD_CMU_CFG_SD_CMU_CFG_CMU_RST, - cmu_cfg_tgt, - SD_CMU_CFG_SD_CMU_CFG(cmu_idx)); - - msleep(20); - - sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(0), - SD_CMU_CMU_44_R_PLL_RSTN, - cmu_tgt, - SD_CMU_CMU_44(cmu_idx)); - - sdx5_inst_rmw(SD_CMU_CMU_44_R_PLL_RSTN_SET(1), - SD_CMU_CMU_44_R_PLL_RSTN, - cmu_tgt, - SD_CMU_CMU_44(cmu_idx)); - - msleep(20); - - value = readl(sdx5_addr(regs, SD_CMU_CMU_E0(cmu_idx))); - value = SD_CMU_CMU_E0_PLL_LOL_UDL_GET(value); - - if (value) { - dev_err(dev, "CMU PLL Loss of Lock: 0x%x\n", value); - return -EINVAL; - } - sdx5_inst_rmw(SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD_SET(0), - SD_CMU_CMU_0D_CFG_PMA_TX_CK_PD, - cmu_tgt, - SD_CMU_CMU_0D(cmu_idx)); - return 0; -} - -static int sparx5_cmu_cfg(struct sparx5_serdes_private *priv, u32 cmu_idx) -{ - void __iomem *cmu_tgt, *cmu_cfg_tgt; - u32 spd10g = 1; - - if (cmu_idx == 1 || cmu_idx == 4 || cmu_idx == 7 || - cmu_idx == 10 || cmu_idx == 13) { - spd10g = 0; - } - - cmu_tgt = sdx5_inst_get(priv, TARGET_SD_CMU, cmu_idx); - cmu_cfg_tgt = sdx5_inst_get(priv, TARGET_SD_CMU_CFG, cmu_idx); - - return sparx5_cmu_apply_cfg(priv, cmu_idx, cmu_tgt, cmu_cfg_tgt, spd10g); -} - -static int sparx5_serdes_cmu_enable(struct sparx5_serdes_private *priv) -{ - int idx, err = 0; - - if (!priv->cmu_enabled) { - for (idx = 0; idx < SPX5_CMU_MAX; idx++) { - err = sparx5_cmu_cfg(priv, idx); - if (err) { - dev_err(priv->dev, "CMU %u, error: %d\n", idx, err); - goto leave; - } - } - priv->cmu_enabled = true; - } -leave: - return err; -} - static int sparx5_serdes_get_serdesmode(phy_interface_t portmode, int speed) { switch (portmode) { @@ -2120,10 +2223,6 @@ static int sparx5_serdes_config(struct sparx5_serdes_macro *macro) int serdesmode; int err; - err = sparx5_serdes_cmu_enable(macro->priv); - if (err) - return err; - serdesmode = sparx5_serdes_get_serdesmode(macro->portmode, macro->speed); if (serdesmode < 0) { dev_err(dev, "SerDes %u, interface not supported: %s\n", @@ -2215,9 +2314,6 @@ static int sparx5_serdes_reset(struct phy *phy) struct sparx5_serdes_macro *macro = phy_get_drvdata(phy); int err; - err = sparx5_serdes_cmu_enable(macro->priv); - if (err) - return err; if (macro->serdestype == SPX5_SDT_25G) err = sparx5_sd25g28_config(macro, true); else @@ -2308,6 +2404,9 @@ static int sparx5_phy_create(struct sparx5_serdes_private *priv, phy_set_drvdata(*phy, macro); + /* Power off serdes by default */ + sparx5_serdes_power_off(*phy); + return 0; } @@ -2491,6 +2590,9 @@ static int sparx5_serdes_probe(struct platform_device *pdev) return err; } + /* Power down all CMUs by default */ + sparx5_serdes_cmu_power_off(priv); + provider = devm_of_phy_provider_register(priv->dev, sparx5_serdes_xlate); return PTR_ERR_OR_ZERO(provider); diff --git a/drivers/phy/microchip/sparx5_serdes.h b/drivers/phy/microchip/sparx5_serdes.h index 0a3e496e6210..13f94a29225a 100644 --- a/drivers/phy/microchip/sparx5_serdes.h +++ b/drivers/phy/microchip/sparx5_serdes.h @@ -30,7 +30,6 @@ struct sparx5_serdes_private { struct device *dev; void __iomem *regs[NUM_TARGETS]; struct phy *phys[SPX5_SERDES_MAX]; - bool cmu_enabled; unsigned long coreclock; }; diff --git a/drivers/phy/microchip/sparx5_serdes_regs.h b/drivers/phy/microchip/sparx5_serdes_regs.h index b96386a4df5a..d0543fd3dc94 100644 --- a/drivers/phy/microchip/sparx5_serdes_regs.h +++ b/drivers/phy/microchip/sparx5_serdes_regs.h @@ -2149,6 +2149,92 @@ enum sparx5_serdes_target { #define SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0_GET(x)\ FIELD_GET(SD_CMU_CMU_05_CFG_BIAS_TP_SEL_1_0, x) +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_06 */ +#define SD_CMU_CMU_06(t) \ + __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 4, 0, 1, 4) + +#define SD_CMU_CMU_06_CFG_DISLOS BIT(0) +#define SD_CMU_CMU_06_CFG_DISLOS_SET(x)\ + FIELD_PREP(SD_CMU_CMU_06_CFG_DISLOS, x) +#define SD_CMU_CMU_06_CFG_DISLOS_GET(x)\ + FIELD_GET(SD_CMU_CMU_06_CFG_DISLOS, x) + +#define SD_CMU_CMU_06_CFG_DISLOL BIT(1) +#define SD_CMU_CMU_06_CFG_DISLOL_SET(x)\ + FIELD_PREP(SD_CMU_CMU_06_CFG_DISLOL, x) +#define SD_CMU_CMU_06_CFG_DISLOL_GET(x)\ + FIELD_GET(SD_CMU_CMU_06_CFG_DISLOL, x) + +#define SD_CMU_CMU_06_CFG_DCLOL BIT(2) +#define SD_CMU_CMU_06_CFG_DCLOL_SET(x)\ + FIELD_PREP(SD_CMU_CMU_06_CFG_DCLOL, x) +#define SD_CMU_CMU_06_CFG_DCLOL_GET(x)\ + FIELD_GET(SD_CMU_CMU_06_CFG_DCLOL, x) + +#define SD_CMU_CMU_06_CFG_FORCE_RX_FILT BIT(3) +#define SD_CMU_CMU_06_CFG_FORCE_RX_FILT_SET(x)\ + FIELD_PREP(SD_CMU_CMU_06_CFG_FORCE_RX_FILT, x) +#define SD_CMU_CMU_06_CFG_FORCE_RX_FILT_GET(x)\ + FIELD_GET(SD_CMU_CMU_06_CFG_FORCE_RX_FILT, x) + +#define SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD BIT(4) +#define SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD_SET(x)\ + FIELD_PREP(SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD, x) +#define SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD_GET(x)\ + FIELD_GET(SD_CMU_CMU_06_CFG_CTRL_LOGIC_PD, x) + +#define SD_CMU_CMU_06_CFG_VCO_PD BIT(5) +#define SD_CMU_CMU_06_CFG_VCO_PD_SET(x)\ + FIELD_PREP(SD_CMU_CMU_06_CFG_VCO_PD, x) +#define SD_CMU_CMU_06_CFG_VCO_PD_GET(x)\ + FIELD_GET(SD_CMU_CMU_06_CFG_VCO_PD, x) + +#define SD_CMU_CMU_06_CFG_VCO_CAL_RESETN BIT(6) +#define SD_CMU_CMU_06_CFG_VCO_CAL_RESETN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_06_CFG_VCO_CAL_RESETN, x) +#define SD_CMU_CMU_06_CFG_VCO_CAL_RESETN_GET(x)\ + FIELD_GET(SD_CMU_CMU_06_CFG_VCO_CAL_RESETN, x) + +#define SD_CMU_CMU_06_CFG_VCO_CAL_BYP BIT(7) +#define SD_CMU_CMU_06_CFG_VCO_CAL_BYP_SET(x)\ + FIELD_PREP(SD_CMU_CMU_06_CFG_VCO_CAL_BYP, x) +#define SD_CMU_CMU_06_CFG_VCO_CAL_BYP_GET(x)\ + FIELD_GET(SD_CMU_CMU_06_CFG_VCO_CAL_BYP, x) + +/* SD10G_CMU_TARGET:CMU_GRP_1:CMU_08 */ +#define SD_CMU_CMU_08(t) \ + __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 12, 0, 1, 4) + +#define SD_CMU_CMU_08_CFG_VFILT2PAD BIT(0) +#define SD_CMU_CMU_08_CFG_VFILT2PAD_SET(x)\ + FIELD_PREP(SD_CMU_CMU_08_CFG_VFILT2PAD, x) +#define SD_CMU_CMU_08_CFG_VFILT2PAD_GET(x)\ + FIELD_GET(SD_CMU_CMU_08_CFG_VFILT2PAD, x) + +#define SD_CMU_CMU_08_CFG_EN_DUMMY BIT(1) +#define SD_CMU_CMU_08_CFG_EN_DUMMY_SET(x)\ + FIELD_PREP(SD_CMU_CMU_08_CFG_EN_DUMMY, x) +#define SD_CMU_CMU_08_CFG_EN_DUMMY_GET(x)\ + FIELD_GET(SD_CMU_CMU_08_CFG_EN_DUMMY, x) + +#define SD_CMU_CMU_08_CFG_CK_TREE_PD BIT(2) +#define SD_CMU_CMU_08_CFG_CK_TREE_PD_SET(x)\ + FIELD_PREP(SD_CMU_CMU_08_CFG_CK_TREE_PD, x) +#define SD_CMU_CMU_08_CFG_CK_TREE_PD_GET(x)\ + FIELD_GET(SD_CMU_CMU_08_CFG_CK_TREE_PD, x) + +#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN BIT(3) +#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN, x) +#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_GET(x)\ + FIELD_GET(SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN, x) + +#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN BIT(4) +#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN_SET(x)\ + FIELD_PREP(SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN, x) +#define SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN_GET(x)\ + FIELD_GET(SD_CMU_CMU_08_CFG_RST_TREE_PD_MAN_EN, x) + /* SD10G_CMU_TARGET:CMU_GRP_1:CMU_09 */ #define SD_CMU_CMU_09(t) __REG(TARGET_SD_CMU, t, 14, 20, 0, 1, 72, 16, 0, 1, 4) @@ -2443,6 +2529,16 @@ enum sparx5_serdes_target { #define SD_LANE_SD_LANE_STAT_DBG_OBS_GET(x)\ FIELD_GET(SD_LANE_SD_LANE_STAT_DBG_OBS, x) +/* SD_LANE_TARGET:SD_PWR_CFG:QUIET_MODE_6G */ +#define SD_LANE_QUIET_MODE_6G(t) \ + __REG(TARGET_SD_LANE, t, 25, 24, 0, 1, 8, 4, 0, 1, 4) + +#define SD_LANE_QUIET_MODE_6G_QUIET_MODE GENMASK(24, 0) +#define SD_LANE_QUIET_MODE_6G_QUIET_MODE_SET(x)\ + FIELD_PREP(SD_LANE_QUIET_MODE_6G_QUIET_MODE, x) +#define SD_LANE_QUIET_MODE_6G_QUIET_MODE_GET(x)\ + FIELD_GET(SD_LANE_QUIET_MODE_6G_QUIET_MODE, x) + /* SD_LANE_TARGET:CFG_STAT_FX100:MISC */ #define SD_LANE_MISC(t) __REG(TARGET_SD_LANE, t, 25, 56, 0, 1, 56, 0, 0, 1, 4) @@ -2692,4 +2788,14 @@ enum sparx5_serdes_target { #define SD_LANE_25G_SD_LANE_STAT_DBG_OBS_GET(x)\ FIELD_GET(SD_LANE_25G_SD_LANE_STAT_DBG_OBS, x) +/* SD25G_CFG_TARGET:SD_PWR_CFG:QUIET_MODE_6G */ +#define SD_LANE_25G_QUIET_MODE_6G(t) \ + __REG(TARGET_SD_LANE_25G, t, 8, 28, 0, 1, 8, 4, 0, 1, 4) + +#define SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE GENMASK(24, 0) +#define SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE_SET(x)\ + FIELD_PREP(SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE, x) +#define SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE_GET(x)\ + FIELD_GET(SD_LANE_25G_QUIET_MODE_6G_QUIET_MODE, x) + #endif /* _SPARX5_SERDES_REGS_H_ */ diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 6464dcb56d56..96a0b1e111f3 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -11,6 +11,7 @@ #include <linux/export.h> #include <linux/module.h> #include <linux/err.h> +#include <linux/debugfs.h> #include <linux/device.h> #include <linux/slab.h> #include <linux/of.h> @@ -20,6 +21,7 @@ #include <linux/regulator/consumer.h> static struct class *phy_class; +static struct dentry *phy_debugfs_root; static DEFINE_MUTEX(phy_provider_mutex); static LIST_HEAD(phy_provider_list); static LIST_HEAD(phys); @@ -996,6 +998,8 @@ struct phy *phy_create(struct device *dev, struct device_node *node, pm_runtime_no_callbacks(&phy->dev); } + phy->debugfs = debugfs_create_dir(dev_name(&phy->dev), phy_debugfs_root); + return phy; put_dev: @@ -1226,6 +1230,7 @@ static void phy_release(struct device *dev) phy = to_phy(dev); dev_vdbg(dev, "releasing '%s'\n", dev_name(dev)); + debugfs_remove_recursive(phy->debugfs); regulator_put(phy->pwr); ida_simple_remove(&phy_ida, phy->id); kfree(phy); @@ -1242,6 +1247,15 @@ static int __init phy_core_init(void) phy_class->dev_release = phy_release; + phy_debugfs_root = debugfs_create_dir("phy", NULL); + return 0; } device_initcall(phy_core_init); + +static void __exit phy_core_exit(void) +{ + debugfs_remove_recursive(phy_debugfs_root); + class_destroy(phy_class); +} +module_exit(phy_core_exit); diff --git a/drivers/phy/qualcomm/Kconfig b/drivers/phy/qualcomm/Kconfig index 4850d48f31fa..97ca5952e34e 100644 --- a/drivers/phy/qualcomm/Kconfig +++ b/drivers/phy/qualcomm/Kconfig @@ -59,8 +59,11 @@ if PHY_QCOM_QMP config PHY_QCOM_QMP_COMBO tristate "Qualcomm QMP Combo PHY Driver" default PHY_QCOM_QMP + depends on TYPEC || TYPEC=n + depends on DRM || DRM=n select GENERIC_PHY select MFD_SYSCON + select DRM_PANEL_BRIDGE if DRM help Enable this to support the QMP Combo PHY transceiver that is used with USB3 and DisplayPort controllers on Qualcomm chips. @@ -185,3 +188,12 @@ config PHY_QCOM_IPQ806X_USB This option enables support for the Synopsis PHYs present inside the Qualcomm USB3.0 DWC3 controller on ipq806x SoC. This driver supports both HS and SS PHY controllers. + +config PHY_QCOM_SGMII_ETH + tristate "Qualcomm DWMAC SGMII SerDes/PHY driver" + depends on OF && (ARCH_QCOM || COMPILE_TEST) + depends on HAS_IOMEM + select GENERIC_PHY + help + Enable this to support the internal SerDes/SGMII PHY on various + Qualcomm chipsets. diff --git a/drivers/phy/qualcomm/Makefile b/drivers/phy/qualcomm/Makefile index de3dc9ccf067..b030858e0f8d 100644 --- a/drivers/phy/qualcomm/Makefile +++ b/drivers/phy/qualcomm/Makefile @@ -20,4 +20,5 @@ obj-$(CONFIG_PHY_QCOM_USB_HSIC) += phy-qcom-usb-hsic.o obj-$(CONFIG_PHY_QCOM_USB_HS_28NM) += phy-qcom-usb-hs-28nm.o obj-$(CONFIG_PHY_QCOM_USB_SS) += phy-qcom-usb-ss.o obj-$(CONFIG_PHY_QCOM_USB_SNPS_FEMTO_V2)+= phy-qcom-snps-femto-v2.o -obj-$(CONFIG_PHY_QCOM_IPQ806X_USB) += phy-qcom-ipq806x-usb.o +obj-$(CONFIG_PHY_QCOM_IPQ806X_USB) += phy-qcom-ipq806x-usb.o +obj-$(CONFIG_PHY_QCOM_SGMII_ETH) += phy-qcom-sgmii-eth.o diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c index 87b17e5877ab..bebce8c591a3 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-combo.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-combo.c @@ -19,6 +19,10 @@ #include <linux/regulator/consumer.h> #include <linux/reset.h> #include <linux/slab.h> +#include <linux/usb/typec.h> +#include <linux/usb/typec_mux.h> + +#include <drm/drm_bridge.h> #include <dt-bindings/phy/phy-qcom-qmp.h> @@ -63,6 +67,10 @@ /* QPHY_V3_PCS_MISC_CLAMP_ENABLE register bits */ #define CLAMP_EN BIT(0) /* enables i/o clamp_n */ +/* QPHY_V3_DP_COM_TYPEC_CTRL register bits */ +#define SW_PORTSELECT_VAL BIT(0) +#define SW_PORTSELECT_MUX BIT(1) + #define PHY_INIT_COMPLETE_TIMEOUT 10000 struct qmp_phy_init_tbl { @@ -1315,14 +1323,21 @@ struct qmp_combo { struct phy *usb_phy; enum phy_mode 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; struct clk_fixed_rate pipe_clk_fixed; struct clk_hw dp_link_hw; struct clk_hw dp_pixel_hw; + + struct drm_bridge bridge; + + struct typec_switch_dev *sw; + enum typec_orientation orientation; }; static void qmp_v3_dp_aux_init(struct qmp_combo *qmp); @@ -1954,30 +1969,24 @@ static void qmp_v3_configure_dp_tx(struct qmp_combo *qmp) static bool qmp_combo_configure_dp_mode(struct qmp_combo *qmp) { + bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); + const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; u32 val; - bool reverse = false; val = DP_PHY_PD_CTL_PWRDN | DP_PHY_PD_CTL_AUX_PWRDN | DP_PHY_PD_CTL_PLL_PWRDN | DP_PHY_PD_CTL_DP_CLAMP_EN; - /* - * TODO: Assume orientation is CC1 for now and two lanes, need to - * use type-c connector to understand orientation and lanes. - * - * Otherwise val changes to be like below if this code understood - * the orientation of the type-c cable. - * - * if (lane_cnt == 4 || orientation == ORIENTATION_CC2) - * val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN; - * if (lane_cnt == 4 || orientation == ORIENTATION_CC1) - * val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN; - * if (orientation == ORIENTATION_CC2) - * writel(0x4c, qmp->dp_dp_phy + QSERDES_V3_DP_PHY_MODE); - */ - val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN; + if (dp_opts->lanes == 4 || reverse) + val |= DP_PHY_PD_CTL_LANE_0_1_PWRDN; + if (dp_opts->lanes == 4 || !reverse) + val |= DP_PHY_PD_CTL_LANE_2_3_PWRDN; + writel(val, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); - writel(0x5c, qmp->dp_dp_phy + QSERDES_DP_PHY_MODE); + if (reverse) + writel(0x4c, qmp->pcs + QSERDES_DP_PHY_MODE); + else + writel(0x5c, qmp->pcs + QSERDES_DP_PHY_MODE); return reverse; } @@ -2142,6 +2151,7 @@ static void qmp_v4_configure_dp_tx(struct qmp_combo *qmp) static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, unsigned int com_resetm_ctrl_reg, unsigned int com_c_ready_status_reg, + unsigned int com_cmn_status_reg, unsigned int dp_phy_status_reg) { const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; @@ -2198,14 +2208,14 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, 10000)) return -ETIMEDOUT; - if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V4_COM_CMN_STATUS, + if (readl_poll_timeout(qmp->dp_serdes + com_cmn_status_reg, status, ((status & BIT(0)) > 0), 500, 10000)) return -ETIMEDOUT; - if (readl_poll_timeout(qmp->dp_serdes + QSERDES_V4_COM_CMN_STATUS, + if (readl_poll_timeout(qmp->dp_serdes + com_cmn_status_reg, status, ((status & BIT(1)) > 0), 500, @@ -2233,14 +2243,15 @@ static int qmp_v456_configure_dp_phy(struct qmp_combo *qmp, static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp) { + bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; u32 bias0_en, drvr0_en, bias1_en, drvr1_en; - bool reverse = false; u32 status; int ret; ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V4_COM_RESETSM_CNTRL, QSERDES_V4_COM_C_READY_STATUS, + QSERDES_V4_COM_CMN_STATUS, QSERDES_V4_DP_PHY_STATUS); if (ret < 0) return ret; @@ -2297,14 +2308,15 @@ static int qmp_v4_configure_dp_phy(struct qmp_combo *qmp) static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp) { + bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; u32 bias0_en, drvr0_en, bias1_en, drvr1_en; - bool reverse = false; u32 status; int ret; ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V4_COM_RESETSM_CNTRL, QSERDES_V4_COM_C_READY_STATUS, + QSERDES_V4_COM_CMN_STATUS, QSERDES_V4_DP_PHY_STATUS); if (ret < 0) return ret; @@ -2356,14 +2368,15 @@ static int qmp_v5_configure_dp_phy(struct qmp_combo *qmp) static int qmp_v6_configure_dp_phy(struct qmp_combo *qmp) { + bool reverse = (qmp->orientation == TYPEC_ORIENTATION_REVERSE); const struct phy_configure_opts_dp *dp_opts = &qmp->dp_opts; u32 bias0_en, drvr0_en, bias1_en, drvr1_en; - bool reverse = false; u32 status; int ret; ret = qmp_v456_configure_dp_phy(qmp, QSERDES_V6_COM_RESETSM_CNTRL, QSERDES_V6_COM_C_READY_STATUS, + QSERDES_V6_COM_CMN_STATUS, QSERDES_V6_DP_PHY_STATUS); if (ret < 0) return ret; @@ -2437,12 +2450,16 @@ static int qmp_combo_dp_configure(struct phy *phy, union phy_configure_opts *opt struct qmp_combo *qmp = phy_get_drvdata(phy); const struct qmp_phy_cfg *cfg = qmp->cfg; + mutex_lock(&qmp->phy_mutex); + memcpy(&qmp->dp_opts, dp_opts, sizeof(*dp_opts)); if (qmp->dp_opts.set_voltages) { cfg->configure_dp_tx(qmp); qmp->dp_opts.set_voltages = 0; } + mutex_unlock(&qmp->phy_mutex); + return 0; } @@ -2450,24 +2467,27 @@ static int qmp_combo_dp_calibrate(struct phy *phy) { struct qmp_combo *qmp = phy_get_drvdata(phy); const struct qmp_phy_cfg *cfg = qmp->cfg; + int ret = 0; + + mutex_lock(&qmp->phy_mutex); if (cfg->calibrate_dp_phy) - return cfg->calibrate_dp_phy(qmp); + ret = cfg->calibrate_dp_phy(qmp); - return 0; + mutex_unlock(&qmp->phy_mutex); + + return ret; } -static int qmp_combo_com_init(struct qmp_combo *qmp) +static int qmp_combo_com_init(struct qmp_combo *qmp, bool force) { const struct qmp_phy_cfg *cfg = qmp->cfg; void __iomem *com = qmp->com; int ret; + u32 val; - mutex_lock(&qmp->phy_mutex); - if (qmp->init_count++) { - mutex_unlock(&qmp->phy_mutex); + if (!force && qmp->init_count++) return 0; - } ret = regulator_bulk_enable(cfg->num_vregs, qmp->vregs); if (ret) { @@ -2498,10 +2518,12 @@ static int qmp_combo_com_init(struct qmp_combo *qmp) SW_DPPHY_RESET_MUX | SW_DPPHY_RESET | SW_USB3PHY_RESET_MUX | SW_USB3PHY_RESET); - /* Default type-c orientation, i.e CC1 */ - qphy_setbits(com, QPHY_V3_DP_COM_TYPEC_CTRL, 0x02); - - qphy_setbits(com, QPHY_V3_DP_COM_PHY_MODE_CTRL, USB3_MODE | DP_MODE); + /* Use software based port select and switch on typec orientation */ + val = SW_PORTSELECT_MUX; + 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, @@ -2514,8 +2536,6 @@ static int qmp_combo_com_init(struct qmp_combo *qmp) qphy_setbits(qmp->pcs, cfg->regs[QPHY_PCS_POWER_DOWN_CONTROL], SW_PWRDN); - mutex_unlock(&qmp->phy_mutex); - return 0; err_assert_reset: @@ -2524,20 +2544,16 @@ err_disable_regulators: regulator_bulk_disable(cfg->num_vregs, qmp->vregs); err_decrement_count: qmp->init_count--; - mutex_unlock(&qmp->phy_mutex); return ret; } -static int qmp_combo_com_exit(struct qmp_combo *qmp) +static int qmp_combo_com_exit(struct qmp_combo *qmp, bool force) { const struct qmp_phy_cfg *cfg = qmp->cfg; - mutex_lock(&qmp->phy_mutex); - if (--qmp->init_count) { - mutex_unlock(&qmp->phy_mutex); + if (!force && --qmp->init_count) return 0; - } reset_control_bulk_assert(cfg->num_resets, qmp->resets); @@ -2545,8 +2561,6 @@ static int qmp_combo_com_exit(struct qmp_combo *qmp) regulator_bulk_disable(cfg->num_vregs, qmp->vregs); - mutex_unlock(&qmp->phy_mutex); - return 0; } @@ -2556,20 +2570,32 @@ static int qmp_combo_dp_init(struct phy *phy) const struct qmp_phy_cfg *cfg = qmp->cfg; int ret; - ret = qmp_combo_com_init(qmp); + mutex_lock(&qmp->phy_mutex); + + ret = qmp_combo_com_init(qmp, false); if (ret) - return ret; + goto out_unlock; cfg->dp_aux_init(qmp); - return 0; + qmp->dp_init_count++; + +out_unlock: + mutex_unlock(&qmp->phy_mutex); + return ret; } static int qmp_combo_dp_exit(struct phy *phy) { struct qmp_combo *qmp = phy_get_drvdata(phy); - qmp_combo_com_exit(qmp); + mutex_lock(&qmp->phy_mutex); + + qmp_combo_com_exit(qmp, false); + + qmp->dp_init_count--; + + mutex_unlock(&qmp->phy_mutex); return 0; } @@ -2581,6 +2607,8 @@ static int qmp_combo_dp_power_on(struct phy *phy) void __iomem *tx = qmp->dp_tx; void __iomem *tx2 = qmp->dp_tx2; + mutex_lock(&qmp->phy_mutex); + qmp_combo_dp_serdes_init(qmp); qmp_combo_configure_lane(tx, cfg->dp_tx_tbl, cfg->dp_tx_tbl_num, 1); @@ -2592,6 +2620,8 @@ static int qmp_combo_dp_power_on(struct phy *phy) /* Configure link rate, swing, etc. */ cfg->configure_dp_phy(qmp); + mutex_unlock(&qmp->phy_mutex); + return 0; } @@ -2599,9 +2629,13 @@ static int qmp_combo_dp_power_off(struct phy *phy) { struct qmp_combo *qmp = phy_get_drvdata(phy); + mutex_lock(&qmp->phy_mutex); + /* Assert DP PHY power down */ writel(DP_PHY_PD_CTL_PSR_PWRDN, qmp->dp_dp_phy + QSERDES_DP_PHY_PD_CTL); + mutex_unlock(&qmp->phy_mutex); + return 0; } @@ -2687,14 +2721,21 @@ static int qmp_combo_usb_init(struct phy *phy) struct qmp_combo *qmp = phy_get_drvdata(phy); int ret; - ret = qmp_combo_com_init(qmp); + mutex_lock(&qmp->phy_mutex); + ret = qmp_combo_com_init(qmp, false); if (ret) - return ret; + goto out_unlock; ret = qmp_combo_usb_power_on(phy); - if (ret) - qmp_combo_com_exit(qmp); + if (ret) { + qmp_combo_com_exit(qmp, false); + goto out_unlock; + } + + qmp->usb_init_count++; +out_unlock: + mutex_unlock(&qmp->phy_mutex); return ret; } @@ -2703,11 +2744,20 @@ static int qmp_combo_usb_exit(struct phy *phy) struct qmp_combo *qmp = phy_get_drvdata(phy); int ret; + mutex_lock(&qmp->phy_mutex); ret = qmp_combo_usb_power_off(phy); if (ret) - return ret; + goto out_unlock; + + ret = qmp_combo_com_exit(qmp, false); + if (ret) + goto out_unlock; + + qmp->usb_init_count--; - return qmp_combo_com_exit(qmp); +out_unlock: + mutex_unlock(&qmp->phy_mutex); + return ret; } static int qmp_combo_usb_set_mode(struct phy *phy, enum phy_mode mode, int submode) @@ -3173,6 +3223,103 @@ static int qmp_combo_register_clocks(struct qmp_combo *qmp, struct device_node * return devm_add_action_or_reset(qmp->dev, phy_clk_release_provider, dp_np); } +#if IS_ENABLED(CONFIG_TYPEC) +static int qmp_combo_typec_switch_set(struct typec_switch_dev *sw, + enum typec_orientation orientation) +{ + struct qmp_combo *qmp = typec_switch_get_drvdata(sw); + const struct qmp_phy_cfg *cfg = qmp->cfg; + + if (orientation == qmp->orientation || orientation == TYPEC_ORIENTATION_NONE) + return 0; + + mutex_lock(&qmp->phy_mutex); + qmp->orientation = orientation; + + if (qmp->init_count) { + if (qmp->usb_init_count) + qmp_combo_usb_power_off(qmp->usb_phy); + qmp_combo_com_exit(qmp, true); + + qmp_combo_com_init(qmp, true); + if (qmp->usb_init_count) + qmp_combo_usb_power_on(qmp->usb_phy); + if (qmp->dp_init_count) + cfg->dp_aux_init(qmp); + } + mutex_unlock(&qmp->phy_mutex); + + return 0; +} + +static void qmp_combo_typec_unregister(void *data) +{ + struct qmp_combo *qmp = data; + + typec_switch_unregister(qmp->sw); +} + +static int qmp_combo_typec_switch_register(struct qmp_combo *qmp) +{ + struct typec_switch_desc sw_desc = {}; + struct device *dev = qmp->dev; + + sw_desc.drvdata = qmp; + sw_desc.fwnode = dev->fwnode; + sw_desc.set = qmp_combo_typec_switch_set; + qmp->sw = typec_switch_register(dev, &sw_desc); + if (IS_ERR(qmp->sw)) { + dev_err(dev, "Unable to register typec switch: %pe\n", qmp->sw); + return PTR_ERR(qmp->sw); + } + + return devm_add_action_or_reset(dev, qmp_combo_typec_unregister, qmp); +} +#else +static int qmp_combo_typec_switch_register(struct qmp_combo *qmp) +{ + return 0; +} +#endif + +#if IS_ENABLED(CONFIG_DRM) +static int qmp_combo_bridge_attach(struct drm_bridge *bridge, + enum drm_bridge_attach_flags flags) +{ + struct qmp_combo *qmp = container_of(bridge, struct qmp_combo, bridge); + struct drm_bridge *next_bridge; + + if (!(flags & DRM_BRIDGE_ATTACH_NO_CONNECTOR)) + return -EINVAL; + + next_bridge = devm_drm_of_get_bridge(qmp->dev, qmp->dev->of_node, 0, 0); + if (IS_ERR(next_bridge)) { + dev_err(qmp->dev, "failed to acquire drm_bridge: %pe\n", next_bridge); + return PTR_ERR(next_bridge); + } + + return drm_bridge_attach(bridge->encoder, next_bridge, bridge, + DRM_BRIDGE_ATTACH_NO_CONNECTOR); +} + +static const struct drm_bridge_funcs qmp_combo_bridge_funcs = { + .attach = qmp_combo_bridge_attach, +}; + +static int qmp_combo_dp_register_bridge(struct qmp_combo *qmp) +{ + qmp->bridge.funcs = &qmp_combo_bridge_funcs; + qmp->bridge.of_node = qmp->dev->of_node; + + return devm_drm_bridge_add(qmp->dev, &qmp->bridge); +} +#else +static int qmp_combo_dp_register_bridge(struct qmp_combo *qmp) +{ + return 0; +} +#endif + static int qmp_combo_parse_dt_lecacy_dp(struct qmp_combo *qmp, struct device_node *np) { struct device *dev = qmp->dev; @@ -3353,6 +3500,8 @@ static int qmp_combo_probe(struct platform_device *pdev) qmp->dev = dev; + qmp->orientation = TYPEC_ORIENTATION_NORMAL; + qmp->cfg = of_device_get_match_data(dev); if (!qmp->cfg) return -EINVAL; @@ -3371,6 +3520,14 @@ static int qmp_combo_probe(struct platform_device *pdev) if (ret) return ret; + ret = qmp_combo_typec_switch_register(qmp); + if (ret) + return ret; + + ret = qmp_combo_dp_register_bridge(qmp); + if (ret) + return ret; + /* Check for legacy binding with child nodes. */ usb_np = of_get_child_by_name(dev->of_node, "usb3-phy"); if (usb_np) { diff --git a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c index a49711c5a63d..466f0a56c82e 100644 --- a/drivers/phy/qualcomm/phy-qcom-qmp-usb.c +++ b/drivers/phy/qualcomm/phy-qcom-qmp-usb.c @@ -139,6 +139,88 @@ static const unsigned int qmp_v5_usb3phy_regs_layout[QPHY_LAYOUT_SIZE] = { [QPHY_PCS_LFPS_RXTERM_IRQ_CLEAR] = QPHY_V5_PCS_USB3_LFPS_RXTERM_IRQ_CLEAR, }; +static const struct qmp_phy_init_tbl ipq9574_usb3_serdes_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a), + QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), + QMP_PHY_INIT_CFG(QSERDES_COM_CLK_SELECT, 0x30), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TRIM, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0b), + QMP_PHY_INIT_CFG(QSERDES_COM_SVS_MODE_CLK_SEL, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_HSCLK_SEL, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CMN_CONFIG, 0x06), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_IVCO, 0x0f), + QMP_PHY_INIT_CFG(QSERDES_COM_SYS_CLK_CTRL, 0x06), + /* PLL and Loop filter settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_DEC_START_MODE0, 0x68), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START1_MODE0, 0xab), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START2_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_COM_DIV_FRAC_START3_MODE0, 0x02), + QMP_PHY_INIT_CFG(QSERDES_COM_CP_CTRL_MODE0, 0x09), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_RCTRL_MODE0, 0x16), + QMP_PHY_INIT_CFG(QSERDES_COM_PLL_CCTRL_MODE0, 0x28), + QMP_PHY_INIT_CFG(QSERDES_COM_INTEGLOOP_GAIN0_MODE0, 0xa0), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP1_MODE0, 0xaa), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP2_MODE0, 0x29), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP3_MODE0, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_CORE_CLK_EN, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_LOCK_CMP_CFG, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_VCO_TUNE_MAP, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_BG_TIMER, 0x0a), + /* SSC settings */ + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_EN_CENTER, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER1, 0x7d), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_PER2, 0x01), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER1, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_ADJ_PER2, 0x00), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE1, 0x0a), + QMP_PHY_INIT_CFG(QSERDES_COM_SSC_STEP_SIZE2, 0x05), +}; + +static const struct qmp_phy_init_tbl ipq9574_usb3_tx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_TX_HIGHZ_TRANSCEIVEREN_BIAS_DRVR_EN, 0x45), + QMP_PHY_INIT_CFG(QSERDES_TX_RCV_DETECT_LVL_2, 0x12), + QMP_PHY_INIT_CFG(QSERDES_TX_LANE_MODE, 0x06), +}; + +static const struct qmp_phy_init_tbl ipq9574_usb3_rx_tbl[] = { + QMP_PHY_INIT_CFG(QSERDES_RX_UCDR_SO_GAIN, 0x06), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x02), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x6c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4c), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0xb8), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x77), + QMP_PHY_INIT_CFG(QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x80), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_CNTRL, 0x03), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x16), + QMP_PHY_INIT_CFG(QSERDES_RX_SIGDET_ENABLES, 0x0c), +}; + +static const struct qmp_phy_init_tbl ipq9574_usb3_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x15), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0e), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL2, 0x83), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNTRL1, 0x02), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_L, 0x09), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_CNT_VAL_H_TOL, 0xa2), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_FLL_MAN_CODE, 0x85), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG1, 0xd1), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG2, 0x1f), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LOCK_DETECT_CONFIG3, 0x47), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_POWER_STATE_CONFIG2, 0x1b), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_WAIT_TIME, 0x75), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RXEQTRAINING_RUN_TIME, 0x13), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_LFPS_TX_ECSTART_EQTLOCK, 0x86), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_PWRUP_RESET_DLY_TIME_AUXCLK, 0x04), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TSYNC_RSYNC_TIME, 0x44), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_L, 0x40), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RCVR_DTCT_DLY_U3_H, 0x00), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_RX_SIGDET_LVL, 0x88), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M6DB_V0, 0x17), + QMP_PHY_INIT_CFG(QPHY_V3_PCS_TXDEEMPH_M3P5DB_V0, 0x0f), +}; + static const struct qmp_phy_init_tbl ipq8074_usb3_serdes_tbl[] = { QMP_PHY_INIT_CFG(QSERDES_COM_SYSCLK_EN_SEL, 0x1a), QMP_PHY_INIT_CFG(QSERDES_COM_BIAS_EN_CLKBUFLR_EN, 0x08), @@ -1408,12 +1490,36 @@ static const struct qmp_phy_init_tbl sc8280xp_usb3_uniphy_pcs_tbl[] = { QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21), }; +static const struct qmp_phy_init_tbl sa8775p_usb3_uniphy_pcs_tbl[] = { + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG1, 0xc4), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG2, 0x89), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG3, 0x20), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_LOCK_DETECT_CONFIG6, 0x13), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_L, 0xe7), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RCVR_DTCT_DLY_P1U2_H, 0x03), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_RX_SIGDET_LVL, 0xaa), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_PCS_TX_RX_CONFIG, 0x0c), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_RXEQTRAINING_DFE_TIME_S2, 0x07), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_LFPS_DET_HIGH_COUNT_VAL, 0xf8), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_USB3_POWER_STATE_CONFIG1, 0x6f), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_CDR_RESET_TIME, 0x0a), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG1, 0x88), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_ALIGN_DETECT_CONFIG2, 0x13), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG1, 0x4b), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_EQ_CONFIG5, 0x10), + QMP_PHY_INIT_CFG(QPHY_V5_PCS_REFGEN_REQ_CONFIG1, 0x21), +}; + struct qmp_usb_offsets { u16 serdes; u16 pcs; + u16 pcs_misc; u16 pcs_usb; u16 tx; u16 rx; + /* for PHYs with >= 2 lanes */ + u16 tx2; + u16 rx2; }; /* struct qmp_phy_cfg - per-PHY initialization config */ @@ -1558,6 +1664,24 @@ static const char * const qmp_phy_vreg_l[] = { "vdda-phy", "vdda-pll", }; +static const struct qmp_usb_offsets qmp_usb_offsets_ipq9574 = { + .serdes = 0, + .pcs = 0x800, + .pcs_usb = 0x800, + .tx = 0x200, + .rx = 0x400, +}; + +static const struct qmp_usb_offsets qmp_usb_offsets_v3 = { + .serdes = 0, + .pcs = 0xc00, + .pcs_misc = 0xa00, + .tx = 0x200, + .rx = 0x400, + .tx2 = 0x600, + .rx2 = 0x800, +}; + static const struct qmp_usb_offsets qmp_usb_offsets_v5 = { .serdes = 0, .pcs = 0x0200, @@ -1586,6 +1710,28 @@ static const struct qmp_phy_cfg ipq8074_usb3phy_cfg = { .regs = qmp_v3_usb3phy_regs_layout, }; +static const struct qmp_phy_cfg ipq9574_usb3phy_cfg = { + .lanes = 1, + + .offsets = &qmp_usb_offsets_ipq9574, + + .serdes_tbl = ipq9574_usb3_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(ipq9574_usb3_serdes_tbl), + .tx_tbl = ipq9574_usb3_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(ipq9574_usb3_tx_tbl), + .rx_tbl = ipq9574_usb3_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(ipq9574_usb3_rx_tbl), + .pcs_tbl = ipq9574_usb3_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(ipq9574_usb3_pcs_tbl), + .clk_list = msm8996_phy_clk_l, + .num_clks = ARRAY_SIZE(msm8996_phy_clk_l), + .reset_list = qcm2290_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v3_usb3phy_regs_layout, +}; + static const struct qmp_phy_cfg msm8996_usb3phy_cfg = { .lanes = 1, @@ -1629,6 +1775,28 @@ static const struct qmp_phy_cfg qmp_v3_usb3phy_cfg = { .has_phy_dp_com_ctrl = true, }; +static const struct qmp_phy_cfg sa8775p_usb3_uniphy_cfg = { + .lanes = 1, + + .offsets = &qmp_usb_offsets_v5, + + .serdes_tbl = sc8280xp_usb3_uniphy_serdes_tbl, + .serdes_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_serdes_tbl), + .tx_tbl = sc8280xp_usb3_uniphy_tx_tbl, + .tx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_tx_tbl), + .rx_tbl = sc8280xp_usb3_uniphy_rx_tbl, + .rx_tbl_num = ARRAY_SIZE(sc8280xp_usb3_uniphy_rx_tbl), + .pcs_tbl = sa8775p_usb3_uniphy_pcs_tbl, + .pcs_tbl_num = ARRAY_SIZE(sa8775p_usb3_uniphy_pcs_tbl), + .clk_list = qmp_v4_phy_clk_l, + .num_clks = ARRAY_SIZE(qmp_v4_phy_clk_l), + .reset_list = qcm2290_usb3phy_reset_l, + .num_resets = ARRAY_SIZE(qcm2290_usb3phy_reset_l), + .vreg_list = qmp_phy_vreg_l, + .num_vregs = ARRAY_SIZE(qmp_phy_vreg_l), + .regs = qmp_v5_usb3phy_regs_layout, +}; + static const struct qmp_phy_cfg sc7180_usb3phy_cfg = { .lanes = 2, @@ -1922,6 +2090,8 @@ static const struct qmp_phy_cfg sm8350_usb3_uniphy_cfg = { static const struct qmp_phy_cfg qcm2290_usb3phy_cfg = { .lanes = 2, + .offsets = &qmp_usb_offsets_v3, + .serdes_tbl = qcm2290_usb3_serdes_tbl, .serdes_tbl_num = ARRAY_SIZE(qcm2290_usb3_serdes_tbl), .tx_tbl = qcm2290_usb3_tx_tbl, @@ -2493,10 +2663,16 @@ static int qmp_usb_parse_dt(struct qmp_usb *qmp) qmp->serdes = base + offs->serdes; qmp->pcs = base + offs->pcs; + qmp->pcs_misc = base + offs->pcs_misc; qmp->pcs_usb = base + offs->pcs_usb; qmp->tx = base + offs->tx; qmp->rx = base + offs->rx; + if (cfg->lanes >= 2) { + qmp->tx2 = base + offs->tx2; + qmp->rx2 = base + offs->rx2; + } + qmp->pipe_clk = devm_clk_get(dev, "pipe"); if (IS_ERR(qmp->pipe_clk)) { return dev_err_probe(dev, PTR_ERR(qmp->pipe_clk), @@ -2589,6 +2765,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = { .compatible = "qcom,ipq8074-qmp-usb3-phy", .data = &ipq8074_usb3phy_cfg, }, { + .compatible = "qcom,ipq9574-qmp-usb3-phy", + .data = &ipq9574_usb3phy_cfg, + }, { .compatible = "qcom,msm8996-qmp-usb3-phy", .data = &msm8996_usb3phy_cfg, }, { @@ -2598,6 +2777,9 @@ static const struct of_device_id qmp_usb_of_match_table[] = { .compatible = "qcom,qcm2290-qmp-usb3-phy", .data = &qcm2290_usb3phy_cfg, }, { + .compatible = "qcom,sa8775p-qmp-usb3-uni-phy", + .data = &sa8775p_usb3_uniphy_cfg, + }, { .compatible = "qcom,sc7180-qmp-usb3-phy", .data = &sc7180_usb3phy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-qusb2.c b/drivers/phy/qualcomm/phy-qcom-qusb2.c index 2ef638b32e8f..bec6e40d5280 100644 --- a/drivers/phy/qualcomm/phy-qcom-qusb2.c +++ b/drivers/phy/qualcomm/phy-qcom-qusb2.c @@ -912,6 +912,9 @@ static const struct of_device_id qusb2_phy_of_match_table[] = { .compatible = "qcom,ipq8074-qusb2-phy", .data = &msm8996_phy_cfg, }, { + .compatible = "qcom,ipq9574-qusb2-phy", + .data = &ipq6018_phy_cfg, + }, { .compatible = "qcom,msm8953-qusb2-phy", .data = &msm8996_phy_cfg, }, { diff --git a/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c new file mode 100644 index 000000000000..03dc753f0de1 --- /dev/null +++ b/drivers/phy/qualcomm/phy-qcom-sgmii-eth.c @@ -0,0 +1,451 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright (c) 2023, Linaro Limited + */ + +#include <linux/clk.h> +#include <linux/ethtool.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/phy/phy.h> +#include <linux/platform_device.h> +#include <linux/regmap.h> + +#define QSERDES_QMP_PLL 0x0 +#define QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0 (QSERDES_QMP_PLL + 0x1ac) +#define QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0 (QSERDES_QMP_PLL + 0x1b0) +#define QSERDES_COM_BIN_VCOCAL_HSCLK_SEL (QSERDES_QMP_PLL + 0x1bc) +#define QSERDES_COM_CORE_CLK_EN (QSERDES_QMP_PLL + 0x174) +#define QSERDES_COM_CORECLK_DIV_MODE0 (QSERDES_QMP_PLL + 0x168) +#define QSERDES_COM_CP_CTRL_MODE0 (QSERDES_QMP_PLL + 0x74) +#define QSERDES_COM_DEC_START_MODE0 (QSERDES_QMP_PLL + 0xbc) +#define QSERDES_COM_DIV_FRAC_START1_MODE0 (QSERDES_QMP_PLL + 0xcc) +#define QSERDES_COM_DIV_FRAC_START2_MODE0 (QSERDES_QMP_PLL + 0xd0) +#define QSERDES_COM_DIV_FRAC_START3_MODE0 (QSERDES_QMP_PLL + 0xd4) +#define QSERDES_COM_HSCLK_HS_SWITCH_SEL (QSERDES_QMP_PLL + 0x15c) +#define QSERDES_COM_HSCLK_SEL (QSERDES_QMP_PLL + 0x158) +#define QSERDES_COM_LOCK_CMP1_MODE0 (QSERDES_QMP_PLL + 0xac) +#define QSERDES_COM_LOCK_CMP2_MODE0 (QSERDES_QMP_PLL + 0xb0) +#define QSERDES_COM_PLL_CCTRL_MODE0 (QSERDES_QMP_PLL + 0x84) +#define QSERDES_COM_PLL_IVCO (QSERDES_QMP_PLL + 0x58) +#define QSERDES_COM_PLL_RCTRL_MODE0 (QSERDES_QMP_PLL + 0x7c) +#define QSERDES_COM_SYSCLK_EN_SEL (QSERDES_QMP_PLL + 0x94) +#define QSERDES_COM_VCO_TUNE1_MODE0 (QSERDES_QMP_PLL + 0x110) +#define QSERDES_COM_VCO_TUNE2_MODE0 (QSERDES_QMP_PLL + 0x114) +#define QSERDES_COM_VCO_TUNE_INITVAL2 (QSERDES_QMP_PLL + 0x124) +#define QSERDES_COM_C_READY_STATUS (QSERDES_QMP_PLL + 0x178) +#define QSERDES_COM_CMN_STATUS (QSERDES_QMP_PLL + 0x140) + +#define QSERDES_RX 0x600 +#define QSERDES_RX_UCDR_FO_GAIN (QSERDES_RX + 0x8) +#define QSERDES_RX_UCDR_SO_GAIN (QSERDES_RX + 0x14) +#define QSERDES_RX_UCDR_FASTLOCK_FO_GAIN (QSERDES_RX + 0x30) +#define QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE (QSERDES_RX + 0x34) +#define QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW (QSERDES_RX + 0x3c) +#define QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH (QSERDES_RX + 0x40) +#define QSERDES_RX_UCDR_PI_CONTROLS (QSERDES_RX + 0x44) +#define QSERDES_RX_UCDR_PI_CTRL2 (QSERDES_RX + 0x48) +#define QSERDES_RX_RX_TERM_BW (QSERDES_RX + 0x80) +#define QSERDES_RX_VGA_CAL_CNTRL2 (QSERDES_RX + 0xd8) +#define QSERDES_RX_GM_CAL (QSERDES_RX + 0xdc) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1 (QSERDES_RX + 0xe8) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2 (QSERDES_RX + 0xec) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3 (QSERDES_RX + 0xf0) +#define QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4 (QSERDES_RX + 0xf4) +#define QSERDES_RX_RX_IDAC_TSETTLE_LOW (QSERDES_RX + 0xf8) +#define QSERDES_RX_RX_IDAC_TSETTLE_HIGH (QSERDES_RX + 0xfc) +#define QSERDES_RX_RX_IDAC_MEASURE_TIME (QSERDES_RX + 0x100) +#define QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1 (QSERDES_RX + 0x110) +#define QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2 (QSERDES_RX + 0x114) +#define QSERDES_RX_SIGDET_CNTRL (QSERDES_RX + 0x11c) +#define QSERDES_RX_SIGDET_DEGLITCH_CNTRL (QSERDES_RX + 0x124) +#define QSERDES_RX_RX_BAND (QSERDES_RX + 0x128) +#define QSERDES_RX_RX_MODE_00_LOW (QSERDES_RX + 0x15c) +#define QSERDES_RX_RX_MODE_00_HIGH (QSERDES_RX + 0x160) +#define QSERDES_RX_RX_MODE_00_HIGH2 (QSERDES_RX + 0x164) +#define QSERDES_RX_RX_MODE_00_HIGH3 (QSERDES_RX + 0x168) +#define QSERDES_RX_RX_MODE_00_HIGH4 (QSERDES_RX + 0x16c) +#define QSERDES_RX_RX_MODE_01_LOW (QSERDES_RX + 0x170) +#define QSERDES_RX_RX_MODE_01_HIGH (QSERDES_RX + 0x174) +#define QSERDES_RX_RX_MODE_01_HIGH2 (QSERDES_RX + 0x178) +#define QSERDES_RX_RX_MODE_01_HIGH3 (QSERDES_RX + 0x17c) +#define QSERDES_RX_RX_MODE_01_HIGH4 (QSERDES_RX + 0x180) +#define QSERDES_RX_RX_MODE_10_LOW (QSERDES_RX + 0x184) +#define QSERDES_RX_RX_MODE_10_HIGH (QSERDES_RX + 0x188) +#define QSERDES_RX_RX_MODE_10_HIGH2 (QSERDES_RX + 0x18c) +#define QSERDES_RX_RX_MODE_10_HIGH3 (QSERDES_RX + 0x190) +#define QSERDES_RX_RX_MODE_10_HIGH4 (QSERDES_RX + 0x194) +#define QSERDES_RX_DCC_CTRL1 (QSERDES_RX + 0x1a8) + +#define QSERDES_TX 0x400 +#define QSERDES_TX_TX_BAND (QSERDES_TX + 0x24) +#define QSERDES_TX_SLEW_CNTL (QSERDES_TX + 0x28) +#define QSERDES_TX_RES_CODE_LANE_OFFSET_TX (QSERDES_TX + 0x3c) +#define QSERDES_TX_RES_CODE_LANE_OFFSET_RX (QSERDES_TX + 0x40) +#define QSERDES_TX_LANE_MODE_1 (QSERDES_TX + 0x84) +#define QSERDES_TX_LANE_MODE_3 (QSERDES_TX + 0x8c) +#define QSERDES_TX_RCV_DETECT_LVL_2 (QSERDES_TX + 0xa4) +#define QSERDES_TX_TRAN_DRVR_EMP_EN (QSERDES_TX + 0xc0) + +#define QSERDES_PCS 0xC00 +#define QSERDES_PCS_PHY_START (QSERDES_PCS + 0x0) +#define QSERDES_PCS_POWER_DOWN_CONTROL (QSERDES_PCS + 0x4) +#define QSERDES_PCS_SW_RESET (QSERDES_PCS + 0x8) +#define QSERDES_PCS_LINE_RESET_TIME (QSERDES_PCS + 0xc) +#define QSERDES_PCS_TX_LARGE_AMP_DRV_LVL (QSERDES_PCS + 0x20) +#define QSERDES_PCS_TX_SMALL_AMP_DRV_LVL (QSERDES_PCS + 0x28) +#define QSERDES_PCS_TX_MID_TERM_CTRL1 (QSERDES_PCS + 0xd8) +#define QSERDES_PCS_TX_MID_TERM_CTRL2 (QSERDES_PCS + 0xdc) +#define QSERDES_PCS_SGMII_MISC_CTRL8 (QSERDES_PCS + 0x118) +#define QSERDES_PCS_PCS_READY_STATUS (QSERDES_PCS + 0x94) + +#define QSERDES_COM_C_READY BIT(0) +#define QSERDES_PCS_READY BIT(0) +#define QSERDES_PCS_SGMIIPHY_READY BIT(7) +#define QSERDES_COM_C_PLL_LOCKED BIT(1) + +struct qcom_dwmac_sgmii_phy_data { + struct regmap *regmap; + struct clk *refclk; + int speed; +}; + +static void qcom_dwmac_sgmii_phy_init_1g(struct regmap *regmap) +{ + regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x01); + regmap_write(regmap, QSERDES_PCS_POWER_DOWN_CONTROL, 0x01); + + regmap_write(regmap, QSERDES_COM_PLL_IVCO, 0x0F); + regmap_write(regmap, QSERDES_COM_CP_CTRL_MODE0, 0x06); + regmap_write(regmap, QSERDES_COM_PLL_RCTRL_MODE0, 0x16); + regmap_write(regmap, QSERDES_COM_PLL_CCTRL_MODE0, 0x36); + regmap_write(regmap, QSERDES_COM_SYSCLK_EN_SEL, 0x1A); + regmap_write(regmap, QSERDES_COM_LOCK_CMP1_MODE0, 0x0A); + regmap_write(regmap, QSERDES_COM_LOCK_CMP2_MODE0, 0x1A); + regmap_write(regmap, QSERDES_COM_DEC_START_MODE0, 0x82); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x55); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x55); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x03); + regmap_write(regmap, QSERDES_COM_VCO_TUNE1_MODE0, 0x24); + + regmap_write(regmap, QSERDES_COM_VCO_TUNE2_MODE0, 0x02); + regmap_write(regmap, QSERDES_COM_VCO_TUNE_INITVAL2, 0x00); + regmap_write(regmap, QSERDES_COM_HSCLK_SEL, 0x04); + regmap_write(regmap, QSERDES_COM_HSCLK_HS_SWITCH_SEL, 0x00); + regmap_write(regmap, QSERDES_COM_CORECLK_DIV_MODE0, 0x0A); + regmap_write(regmap, QSERDES_COM_CORE_CLK_EN, 0x00); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xB9); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1E); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_HSCLK_SEL, 0x11); + + regmap_write(regmap, QSERDES_TX_TX_BAND, 0x05); + regmap_write(regmap, QSERDES_TX_SLEW_CNTL, 0x0A); + regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_TX, 0x09); + regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_RX, 0x09); + regmap_write(regmap, QSERDES_TX_LANE_MODE_1, 0x05); + regmap_write(regmap, QSERDES_TX_LANE_MODE_3, 0x00); + regmap_write(regmap, QSERDES_TX_RCV_DETECT_LVL_2, 0x12); + regmap_write(regmap, QSERDES_TX_TRAN_DRVR_EMP_EN, 0x0C); + + regmap_write(regmap, QSERDES_RX_UCDR_FO_GAIN, 0x0A); + regmap_write(regmap, QSERDES_RX_UCDR_SO_GAIN, 0x06); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0A); + regmap_write(regmap, QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7F); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x01); + regmap_write(regmap, QSERDES_RX_UCDR_PI_CONTROLS, 0x81); + regmap_write(regmap, QSERDES_RX_UCDR_PI_CTRL2, 0x80); + regmap_write(regmap, QSERDES_RX_RX_TERM_BW, 0x04); + regmap_write(regmap, QSERDES_RX_VGA_CAL_CNTRL2, 0x08); + regmap_write(regmap, QSERDES_RX_GM_CAL, 0x0F); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x00); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4A); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0A); + regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_LOW, 0x80); + regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_HIGH, 0x01); + regmap_write(regmap, QSERDES_RX_RX_IDAC_MEASURE_TIME, 0x20); + regmap_write(regmap, QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17); + regmap_write(regmap, QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00); + regmap_write(regmap, QSERDES_RX_SIGDET_CNTRL, 0x0F); + regmap_write(regmap, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E); + regmap_write(regmap, QSERDES_RX_RX_BAND, 0x05); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH3, 0x09); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH4, 0xB1); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH3, 0x09); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH4, 0xB1); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH3, 0x3B); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH4, 0xB7); + regmap_write(regmap, QSERDES_RX_DCC_CTRL1, 0x0C); + + regmap_write(regmap, QSERDES_PCS_LINE_RESET_TIME, 0x0C); + regmap_write(regmap, QSERDES_PCS_TX_LARGE_AMP_DRV_LVL, 0x1F); + regmap_write(regmap, QSERDES_PCS_TX_SMALL_AMP_DRV_LVL, 0x03); + regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL1, 0x83); + regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL2, 0x08); + regmap_write(regmap, QSERDES_PCS_SGMII_MISC_CTRL8, 0x0C); + regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x00); + + regmap_write(regmap, QSERDES_PCS_PHY_START, 0x01); +} + +static void qcom_dwmac_sgmii_phy_init_2p5g(struct regmap *regmap) +{ + regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x01); + regmap_write(regmap, QSERDES_PCS_POWER_DOWN_CONTROL, 0x01); + + regmap_write(regmap, QSERDES_COM_PLL_IVCO, 0x0F); + regmap_write(regmap, QSERDES_COM_CP_CTRL_MODE0, 0x06); + regmap_write(regmap, QSERDES_COM_PLL_RCTRL_MODE0, 0x16); + regmap_write(regmap, QSERDES_COM_PLL_CCTRL_MODE0, 0x36); + regmap_write(regmap, QSERDES_COM_SYSCLK_EN_SEL, 0x1A); + regmap_write(regmap, QSERDES_COM_LOCK_CMP1_MODE0, 0x1A); + regmap_write(regmap, QSERDES_COM_LOCK_CMP2_MODE0, 0x41); + regmap_write(regmap, QSERDES_COM_DEC_START_MODE0, 0x7A); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START1_MODE0, 0x00); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START2_MODE0, 0x20); + regmap_write(regmap, QSERDES_COM_DIV_FRAC_START3_MODE0, 0x01); + regmap_write(regmap, QSERDES_COM_VCO_TUNE1_MODE0, 0xA1); + + regmap_write(regmap, QSERDES_COM_VCO_TUNE2_MODE0, 0x02); + regmap_write(regmap, QSERDES_COM_VCO_TUNE_INITVAL2, 0x00); + regmap_write(regmap, QSERDES_COM_HSCLK_SEL, 0x03); + regmap_write(regmap, QSERDES_COM_HSCLK_HS_SWITCH_SEL, 0x00); + regmap_write(regmap, QSERDES_COM_CORECLK_DIV_MODE0, 0x05); + regmap_write(regmap, QSERDES_COM_CORE_CLK_EN, 0x00); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE1_MODE0, 0xCD); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_CMP_CODE2_MODE0, 0x1C); + regmap_write(regmap, QSERDES_COM_BIN_VCOCAL_HSCLK_SEL, 0x11); + + regmap_write(regmap, QSERDES_TX_TX_BAND, 0x04); + regmap_write(regmap, QSERDES_TX_SLEW_CNTL, 0x0A); + regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_TX, 0x09); + regmap_write(regmap, QSERDES_TX_RES_CODE_LANE_OFFSET_RX, 0x02); + regmap_write(regmap, QSERDES_TX_LANE_MODE_1, 0x05); + regmap_write(regmap, QSERDES_TX_LANE_MODE_3, 0x00); + regmap_write(regmap, QSERDES_TX_RCV_DETECT_LVL_2, 0x12); + regmap_write(regmap, QSERDES_TX_TRAN_DRVR_EMP_EN, 0x0C); + + regmap_write(regmap, QSERDES_RX_UCDR_FO_GAIN, 0x0A); + regmap_write(regmap, QSERDES_RX_UCDR_SO_GAIN, 0x06); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_FO_GAIN, 0x0A); + regmap_write(regmap, QSERDES_RX_UCDR_SO_SATURATION_AND_ENABLE, 0x7F); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_LOW, 0x00); + regmap_write(regmap, QSERDES_RX_UCDR_FASTLOCK_COUNT_HIGH, 0x01); + regmap_write(regmap, QSERDES_RX_UCDR_PI_CONTROLS, 0x81); + regmap_write(regmap, QSERDES_RX_UCDR_PI_CTRL2, 0x80); + regmap_write(regmap, QSERDES_RX_RX_TERM_BW, 0x00); + regmap_write(regmap, QSERDES_RX_VGA_CAL_CNTRL2, 0x08); + regmap_write(regmap, QSERDES_RX_GM_CAL, 0x0F); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL1, 0x04); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL2, 0x00); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL3, 0x4A); + regmap_write(regmap, QSERDES_RX_RX_EQU_ADAPTOR_CNTRL4, 0x0A); + regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_LOW, 0x80); + regmap_write(regmap, QSERDES_RX_RX_IDAC_TSETTLE_HIGH, 0x01); + regmap_write(regmap, QSERDES_RX_RX_IDAC_MEASURE_TIME, 0x20); + regmap_write(regmap, QSERDES_RX_RX_EQ_OFFSET_ADAPTOR_CNTRL1, 0x17); + regmap_write(regmap, QSERDES_RX_RX_OFFSET_ADAPTOR_CNTRL2, 0x00); + regmap_write(regmap, QSERDES_RX_SIGDET_CNTRL, 0x0F); + regmap_write(regmap, QSERDES_RX_SIGDET_DEGLITCH_CNTRL, 0x1E); + regmap_write(regmap, QSERDES_RX_RX_BAND, 0x18); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_LOW, 0x18); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH3, 0x0C); + regmap_write(regmap, QSERDES_RX_RX_MODE_00_HIGH4, 0xB8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH3, 0x09); + regmap_write(regmap, QSERDES_RX_RX_MODE_01_HIGH4, 0xB1); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_LOW, 0xE0); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH2, 0xC8); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH3, 0x3B); + regmap_write(regmap, QSERDES_RX_RX_MODE_10_HIGH4, 0xB7); + regmap_write(regmap, QSERDES_RX_DCC_CTRL1, 0x0C); + + regmap_write(regmap, QSERDES_PCS_LINE_RESET_TIME, 0x0C); + regmap_write(regmap, QSERDES_PCS_TX_LARGE_AMP_DRV_LVL, 0x1F); + regmap_write(regmap, QSERDES_PCS_TX_SMALL_AMP_DRV_LVL, 0x03); + regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL1, 0x83); + regmap_write(regmap, QSERDES_PCS_TX_MID_TERM_CTRL2, 0x08); + regmap_write(regmap, QSERDES_PCS_SGMII_MISC_CTRL8, 0x8C); + regmap_write(regmap, QSERDES_PCS_SW_RESET, 0x00); + + regmap_write(regmap, QSERDES_PCS_PHY_START, 0x01); +} + +static inline int +qcom_dwmac_sgmii_phy_poll_status(struct regmap *regmap, unsigned int reg, + unsigned int bit) +{ + unsigned int val; + + return regmap_read_poll_timeout(regmap, reg, val, + val & bit, 1500, 750000); +} + +static int qcom_dwmac_sgmii_phy_calibrate(struct phy *phy) +{ + struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy); + struct device *dev = phy->dev.parent; + + switch (data->speed) { + case SPEED_10: + case SPEED_100: + case SPEED_1000: + qcom_dwmac_sgmii_phy_init_1g(data->regmap); + break; + case SPEED_2500: + qcom_dwmac_sgmii_phy_init_2p5g(data->regmap); + break; + } + + if (qcom_dwmac_sgmii_phy_poll_status(data->regmap, + QSERDES_COM_C_READY_STATUS, + QSERDES_COM_C_READY)) { + dev_err(dev, "QSERDES_COM_C_READY_STATUS timed-out"); + return -ETIMEDOUT; + } + + if (qcom_dwmac_sgmii_phy_poll_status(data->regmap, + QSERDES_PCS_PCS_READY_STATUS, + QSERDES_PCS_READY)) { + dev_err(dev, "PCS_READY timed-out"); + return -ETIMEDOUT; + } + + if (qcom_dwmac_sgmii_phy_poll_status(data->regmap, + QSERDES_PCS_PCS_READY_STATUS, + QSERDES_PCS_SGMIIPHY_READY)) { + dev_err(dev, "SGMIIPHY_READY timed-out"); + return -ETIMEDOUT; + } + + if (qcom_dwmac_sgmii_phy_poll_status(data->regmap, + QSERDES_COM_CMN_STATUS, + QSERDES_COM_C_PLL_LOCKED)) { + dev_err(dev, "PLL Lock Status timed-out"); + return -ETIMEDOUT; + } + + return 0; +} + +static int qcom_dwmac_sgmii_phy_power_on(struct phy *phy) +{ + struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy); + + return clk_prepare_enable(data->refclk); +} + +static int qcom_dwmac_sgmii_phy_power_off(struct phy *phy) +{ + struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy); + + regmap_write(data->regmap, QSERDES_PCS_TX_MID_TERM_CTRL2, 0x08); + regmap_write(data->regmap, QSERDES_PCS_SW_RESET, 0x01); + udelay(100); + regmap_write(data->regmap, QSERDES_PCS_SW_RESET, 0x00); + regmap_write(data->regmap, QSERDES_PCS_PHY_START, 0x01); + + clk_disable_unprepare(data->refclk); + + return 0; +} + +static int qcom_dwmac_sgmii_phy_set_speed(struct phy *phy, int speed) +{ + struct qcom_dwmac_sgmii_phy_data *data = phy_get_drvdata(phy); + + if (speed != data->speed) + data->speed = speed; + + return qcom_dwmac_sgmii_phy_calibrate(phy); +} + +static const struct phy_ops qcom_dwmac_sgmii_phy_ops = { + .power_on = qcom_dwmac_sgmii_phy_power_on, + .power_off = qcom_dwmac_sgmii_phy_power_off, + .set_speed = qcom_dwmac_sgmii_phy_set_speed, + .calibrate = qcom_dwmac_sgmii_phy_calibrate, + .owner = THIS_MODULE, +}; + +static const struct regmap_config qcom_dwmac_sgmii_phy_regmap_cfg = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .use_relaxed_mmio = true, + .disable_locking = true, +}; + +static int qcom_dwmac_sgmii_phy_probe(struct platform_device *pdev) +{ + struct qcom_dwmac_sgmii_phy_data *data; + struct device *dev = &pdev->dev; + struct phy_provider *provider; + void __iomem *base; + struct phy *phy; + + data = devm_kzalloc(dev, sizeof(*data), GFP_KERNEL); + if (!data) + return -ENOMEM; + + data->speed = SPEED_10; + + base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(base)) + return PTR_ERR(base); + + data->regmap = devm_regmap_init_mmio(dev, base, + &qcom_dwmac_sgmii_phy_regmap_cfg); + if (IS_ERR(data->regmap)) + return PTR_ERR(data->regmap); + + phy = devm_phy_create(dev, NULL, &qcom_dwmac_sgmii_phy_ops); + if (IS_ERR(phy)) + return PTR_ERR(phy); + + data->refclk = devm_clk_get(dev, "sgmi_ref"); + if (IS_ERR(data->refclk)) + return PTR_ERR(data->refclk); + + provider = devm_of_phy_provider_register(dev, of_phy_simple_xlate); + if (IS_ERR(provider)) + return PTR_ERR(provider); + + phy_set_drvdata(phy, data); + + return 0; +} + +static const struct of_device_id qcom_dwmac_sgmii_phy_of_match[] = { + { .compatible = "qcom,sa8775p-dwmac-sgmii-phy" }, + { }, +}; +MODULE_DEVICE_TABLE(of, qcom_dwmac_sgmii_phy_of_match); + +static struct platform_driver qcom_dwmac_sgmii_phy_driver = { + .probe = qcom_dwmac_sgmii_phy_probe, + .driver = { + .name = "qcom-dwmac-sgmii-phy", + .of_match_table = qcom_dwmac_sgmii_phy_of_match, + } +}; + +module_platform_driver(qcom_dwmac_sgmii_phy_driver); + +MODULE_DESCRIPTION("Qualcomm DWMAC SGMII PHY driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/phy/samsung/Kconfig b/drivers/phy/samsung/Kconfig index 3ccaabf2850a..f10afa3d7ff5 100644 --- a/drivers/phy/samsung/Kconfig +++ b/drivers/phy/samsung/Kconfig @@ -59,7 +59,7 @@ config PHY_EXYNOS4210_USB2 config PHY_EXYNOS4X12_USB2 bool depends on PHY_SAMSUNG_USB2 - default SOC_EXYNOS3250 || SOC_EXYNOS4412 + default SOC_EXYNOS3250 || SOC_EXYNOS4212 || SOC_EXYNOS4412 config PHY_EXYNOS5250_USB2 bool diff --git a/drivers/phy/tegra/xusb.c b/drivers/phy/tegra/xusb.c index b55d4e9f42b5..a296b87dced1 100644 --- a/drivers/phy/tegra/xusb.c +++ b/drivers/phy/tegra/xusb.c @@ -568,6 +568,7 @@ static void tegra_xusb_port_unregister(struct tegra_xusb_port *port) usb_role_switch_unregister(port->usb_role_sw); cancel_work_sync(&port->usb_phy_work); usb_remove_phy(&port->usb_phy); + port->usb_phy.dev->driver = NULL; } if (port->ops->remove) @@ -675,6 +676,9 @@ static int tegra_xusb_setup_usb_role_switch(struct tegra_xusb_port *port) port->dev.driver = devm_kzalloc(&port->dev, sizeof(struct device_driver), GFP_KERNEL); + if (!port->dev.driver) + return -ENOMEM; + port->dev.driver->owner = THIS_MODULE; port->usb_role_sw = usb_role_switch_register(&port->dev, diff --git a/drivers/phy/ti/phy-gmii-sel.c b/drivers/phy/ti/phy-gmii-sel.c index 8c667819c39a..6286cf25a426 100644 --- a/drivers/phy/ti/phy-gmii-sel.c +++ b/drivers/phy/ti/phy-gmii-sel.c @@ -23,7 +23,9 @@ #define AM33XX_GMII_SEL_MODE_RGMII 2 /* J72xx SoC specific definitions for the CONTROL port */ +#define J72XX_GMII_SEL_MODE_SGMII 3 #define J72XX_GMII_SEL_MODE_QSGMII 4 +#define J72XX_GMII_SEL_MODE_USXGMII 5 #define J72XX_GMII_SEL_MODE_QSGMII_SUB 6 #define PHY_GMII_PORT(n) BIT((n) - 1) @@ -106,6 +108,20 @@ static int phy_gmii_sel_mode(struct phy *phy, enum phy_mode mode, int submode) gmii_sel_mode = J72XX_GMII_SEL_MODE_QSGMII_SUB; break; + case PHY_INTERFACE_MODE_SGMII: + if (!(soc_data->extra_modes & BIT(PHY_INTERFACE_MODE_SGMII))) + goto unsupported; + else + gmii_sel_mode = J72XX_GMII_SEL_MODE_SGMII; + break; + + case PHY_INTERFACE_MODE_USXGMII: + if (!(soc_data->extra_modes & BIT(PHY_INTERFACE_MODE_USXGMII))) + goto unsupported; + else + gmii_sel_mode = J72XX_GMII_SEL_MODE_USXGMII; + break; + default: goto unsupported; } @@ -213,7 +229,7 @@ static const struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw5g_soc_j7200 = { .use_of_data = true, .regfields = phy_gmii_sel_fields_am654, - .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII), + .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) | BIT(PHY_INTERFACE_MODE_SGMII), .num_ports = 4, .num_qsgmii_main_ports = 1, }; @@ -222,7 +238,17 @@ static const struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw9g_soc_j721e = { .use_of_data = true, .regfields = phy_gmii_sel_fields_am654, - .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII), + .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) | BIT(PHY_INTERFACE_MODE_SGMII), + .num_ports = 8, + .num_qsgmii_main_ports = 2, +}; + +static const +struct phy_gmii_sel_soc_data phy_gmii_sel_cpsw9g_soc_j784s4 = { + .use_of_data = true, + .regfields = phy_gmii_sel_fields_am654, + .extra_modes = BIT(PHY_INTERFACE_MODE_QSGMII) | + BIT(PHY_INTERFACE_MODE_USXGMII), .num_ports = 8, .num_qsgmii_main_ports = 2, }; @@ -256,6 +282,10 @@ static const struct of_device_id phy_gmii_sel_id_table[] = { .compatible = "ti,j721e-cpsw9g-phy-gmii-sel", .data = &phy_gmii_sel_cpsw9g_soc_j721e, }, + { + .compatible = "ti,j784s4-cpsw9g-phy-gmii-sel", + .data = &phy_gmii_sel_cpsw9g_soc_j784s4, + }, {} }; MODULE_DEVICE_TABLE(of, phy_gmii_sel_id_table); diff --git a/drivers/platform/x86/amd/Makefile b/drivers/platform/x86/amd/Makefile index 2c229198e24c..65732f0a3913 100644 --- a/drivers/platform/x86/amd/Makefile +++ b/drivers/platform/x86/amd/Makefile @@ -4,7 +4,7 @@ # AMD x86 Platform-Specific Drivers # -amd-pmc-y := pmc.o +amd-pmc-y := pmc.o pmc-quirks.o obj-$(CONFIG_AMD_PMC) += amd-pmc.o amd_hsmp-y := hsmp.o obj-$(CONFIG_AMD_HSMP) += amd_hsmp.o diff --git a/drivers/platform/x86/amd/pmc-quirks.c b/drivers/platform/x86/amd/pmc-quirks.c new file mode 100644 index 000000000000..362e7c0097d7 --- /dev/null +++ b/drivers/platform/x86/amd/pmc-quirks.c @@ -0,0 +1,176 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * AMD SoC Power Management Controller Driver Quirks + * + * Copyright (c) 2023, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Mario Limonciello <mario.limonciello@amd.com> + */ + +#include <linux/dmi.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/slab.h> + +#include "pmc.h" + +struct quirk_entry { + u32 s2idle_bug_mmio; +}; + +static struct quirk_entry quirk_s2idle_bug = { + .s2idle_bug_mmio = 0xfed80380, +}; + +static const struct dmi_system_id fwbug_list[] = { + { + .ident = "L14 Gen2 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20X5"), + } + }, + { + .ident = "T14s Gen2 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20XF"), + } + }, + { + .ident = "X13 Gen2 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20XH"), + } + }, + { + .ident = "T14 Gen2 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20XK"), + } + }, + { + .ident = "T14 Gen1 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20UD"), + } + }, + { + .ident = "T14 Gen1 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20UE"), + } + }, + { + .ident = "T14s Gen1 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20UH"), + } + }, + { + .ident = "T14s Gen1 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20UJ"), + } + }, + { + .ident = "P14s Gen1 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "20Y1"), + } + }, + { + .ident = "P14s Gen2 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21A0"), + } + }, + { + .ident = "P14s Gen2 AMD", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_NAME, "21A1"), + } + }, + /* https://gitlab.freedesktop.org/drm/amd/-/issues/2684 */ + { + .ident = "HP Laptop 15s-eq2xxx", + .driver_data = &quirk_s2idle_bug, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "HP"), + DMI_MATCH(DMI_PRODUCT_NAME, "HP Laptop 15s-eq2xxx"), + } + }, + {} +}; + +/* + * Laptops that run a SMI handler during the D3->D0 transition that occurs + * specifically when exiting suspend to idle which can cause + * large delays during resume when the IOMMU translation layer is enabled (the default + * behavior) for NVME devices: + * + * To avoid this firmware problem, skip the SMI handler on these machines before the + * D0 transition occurs. + */ +static void amd_pmc_skip_nvme_smi_handler(u32 s2idle_bug_mmio) +{ + struct resource *res; + void __iomem *addr; + u8 val; + + res = request_mem_region_muxed(s2idle_bug_mmio, 1, "amd_pmc_pm80"); + if (!res) + return; + + addr = ioremap(s2idle_bug_mmio, 1); + if (!addr) + goto cleanup_resource; + + val = ioread8(addr); + iowrite8(val & ~BIT(0), addr); + + iounmap(addr); +cleanup_resource: + release_resource(res); + kfree(res); +} + +void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev) +{ + if (dev->quirks && dev->quirks->s2idle_bug_mmio) + amd_pmc_skip_nvme_smi_handler(dev->quirks->s2idle_bug_mmio); +} + +void amd_pmc_quirks_init(struct amd_pmc_dev *dev) +{ + const struct dmi_system_id *dmi_id; + + dmi_id = dmi_first_match(fwbug_list); + if (!dmi_id) + return; + dev->quirks = dmi_id->driver_data; + if (dev->quirks->s2idle_bug_mmio) + pr_info("Using s2idle quirk to avoid %s platform firmware bug\n", + dmi_id->ident); +} diff --git a/drivers/platform/x86/amd/pmc.c b/drivers/platform/x86/amd/pmc.c index 7d3d080ff174..c1e788b67a74 100644 --- a/drivers/platform/x86/amd/pmc.c +++ b/drivers/platform/x86/amd/pmc.c @@ -28,6 +28,8 @@ #include <linux/seq_file.h> #include <linux/uaccess.h> +#include "pmc.h" + /* SMU communication registers */ #define AMD_PMC_REGISTER_MESSAGE 0x538 #define AMD_PMC_REGISTER_RESPONSE 0x980 @@ -94,6 +96,7 @@ #define AMD_CPU_ID_CB 0x14D8 #define AMD_CPU_ID_PS 0x14E8 #define AMD_CPU_ID_SP 0x14A4 +#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 #define PMC_MSG_DELAY_MIN_US 50 #define RESPONSE_REGISTER_LOOP_MAX 20000 @@ -146,29 +149,6 @@ static const struct amd_pmc_bit_map soc15_ip_blk[] = { {} }; -struct amd_pmc_dev { - void __iomem *regbase; - void __iomem *smu_virt_addr; - void __iomem *stb_virt_addr; - void __iomem *fch_virt_addr; - bool msg_port; - u32 base_addr; - u32 cpu_id; - u32 active_ips; - u32 dram_size; - u32 num_ips; - u32 s2d_msg_id; -/* SMU version information */ - u8 smu_program; - u8 major; - u8 minor; - u8 rev; - struct device *dev; - struct pci_dev *rdev; - struct mutex lock; /* generic mutex lock */ - struct dentry *dbgfs_dir; -}; - static bool enable_stb; module_param(enable_stb, bool, 0644); MODULE_PARM_DESC(enable_stb, "Enable the STB debug mechanism"); @@ -891,6 +871,8 @@ static void amd_pmc_s2idle_restore(void) /* Notify on failed entry */ amd_pmc_validate_deepest(pdev); + + amd_pmc_process_restore_quirks(pdev); } static struct acpi_s2idle_dev_ops amd_pmc_s2idle_dev_ops = { @@ -926,6 +908,7 @@ static const struct pci_device_id pmc_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PCO) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RV) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_SP) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) }, { } }; @@ -1087,6 +1070,8 @@ static int amd_pmc_probe(struct platform_device *pdev) err = acpi_register_lps0_dev(&amd_pmc_s2idle_dev_ops); if (err) dev_warn(dev->dev, "failed to register LPS0 sleep handler, expect increased power consumption\n"); + if (!disable_workarounds) + amd_pmc_quirks_init(dev); } amd_pmc_dbgfs_register(dev); @@ -1115,6 +1100,7 @@ static const struct acpi_device_id amd_pmc_acpi_ids[] = { {"AMDI0007", 0}, {"AMDI0008", 0}, {"AMDI0009", 0}, + {"AMDI000A", 0}, {"AMD0004", 0}, {"AMD0005", 0}, { } diff --git a/drivers/platform/x86/amd/pmc.h b/drivers/platform/x86/amd/pmc.h new file mode 100644 index 000000000000..c27bd6a5642f --- /dev/null +++ b/drivers/platform/x86/amd/pmc.h @@ -0,0 +1,44 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * AMD SoC Power Management Controller Driver + * + * Copyright (c) 2023, Advanced Micro Devices, Inc. + * All Rights Reserved. + * + * Author: Mario Limonciello <mario.limonciello@amd.com> + */ + +#ifndef PMC_H +#define PMC_H + +#include <linux/types.h> +#include <linux/mutex.h> + +struct amd_pmc_dev { + void __iomem *regbase; + void __iomem *smu_virt_addr; + void __iomem *stb_virt_addr; + void __iomem *fch_virt_addr; + bool msg_port; + u32 base_addr; + u32 cpu_id; + u32 active_ips; + u32 dram_size; + u32 num_ips; + u32 s2d_msg_id; +/* SMU version information */ + u8 smu_program; + u8 major; + u8 minor; + u8 rev; + struct device *dev; + struct pci_dev *rdev; + struct mutex lock; /* generic mutex lock */ + struct dentry *dbgfs_dir; + struct quirk_entry *quirks; +}; + +void amd_pmc_process_restore_quirks(struct amd_pmc_dev *dev); +void amd_pmc_quirks_init(struct amd_pmc_dev *dev); + +#endif /* PMC_H */ diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 7780705917b7..d8732557f9db 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -40,6 +40,7 @@ /* List of supported CPU ids */ #define AMD_CPU_ID_RMB 0x14b5 #define AMD_CPU_ID_PS 0x14e8 +#define PCI_DEVICE_ID_AMD_1AH_M20H_ROOT 0x1507 #define PMF_MSG_DELAY_MIN_US 50 #define RESPONSE_REGISTER_LOOP_MAX 20000 @@ -242,6 +243,7 @@ out_unlock: static const struct pci_device_id pmf_pci_ids[] = { { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_RMB) }, { PCI_DEVICE(PCI_VENDOR_ID_AMD, AMD_CPU_ID_PS) }, + { PCI_DEVICE(PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_1AH_M20H_ROOT) }, { } }; @@ -333,6 +335,7 @@ static void amd_pmf_deinit_features(struct amd_pmf_dev *dev) static const struct acpi_device_id amd_pmf_acpi_ids[] = { {"AMDI0100", 0x100}, {"AMDI0102", 0}, + {"AMDI0103", 0}, { } }; MODULE_DEVICE_TABLE(acpi, amd_pmf_acpi_ids); diff --git a/drivers/platform/x86/dell/dell-wmi-ddv.c b/drivers/platform/x86/dell/dell-wmi-ddv.c index 2750dee99c3e..db1e9240dd02 100644 --- a/drivers/platform/x86/dell/dell-wmi-ddv.c +++ b/drivers/platform/x86/dell/dell-wmi-ddv.c @@ -616,7 +616,8 @@ static int dell_wmi_ddv_hwmon_add(struct dell_wmi_ddv_data *data) } if (index < 2) { - ret = -ENODEV; + /* Finding no available sensors is not an error */ + ret = 0; goto err_release; } @@ -841,13 +842,13 @@ static int dell_wmi_ddv_probe(struct wmi_device *wdev, const void *context) if (IS_REACHABLE(CONFIG_ACPI_BATTERY)) { ret = dell_wmi_ddv_battery_add(data); - if (ret < 0 && ret != -ENODEV) + if (ret < 0) dev_warn(&wdev->dev, "Unable to register ACPI battery hook: %d\n", ret); } if (IS_REACHABLE(CONFIG_HWMON)) { ret = dell_wmi_ddv_hwmon_add(data); - if (ret < 0 && ret != -ENODEV) + if (ret < 0) dev_warn(&wdev->dev, "Unable to register hwmon interface: %d\n", ret); } diff --git a/drivers/platform/x86/intel/int3472/clk_and_regulator.c b/drivers/platform/x86/intel/int3472/clk_and_regulator.c index 61aeca804ba2..ef4b3141efcd 100644 --- a/drivers/platform/x86/intel/int3472/clk_and_regulator.c +++ b/drivers/platform/x86/intel/int3472/clk_and_regulator.c @@ -260,7 +260,7 @@ static_assert(ARRAY_SIZE(skl_int3472_regulator_map_supplies) == * This DMI table contains the name of the second sensor. This is used to add * entries for the second sensor to the supply_map. */ -const struct dmi_system_id skl_int3472_regulator_second_sensor[] = { +static const struct dmi_system_id skl_int3472_regulator_second_sensor[] = { { /* Lenovo Miix 510-12IKB */ .matches = { diff --git a/drivers/platform/x86/intel/tpmi.c b/drivers/platform/x86/intel/tpmi.c index 9c606ee2030c..d1fd6e69401c 100644 --- a/drivers/platform/x86/intel/tpmi.c +++ b/drivers/platform/x86/intel/tpmi.c @@ -356,9 +356,7 @@ static int intel_vsec_tpmi_init(struct auxiliary_device *auxdev) if (!pfs_start) pfs_start = res_start; - pfs->pfs_header.cap_offset *= TPMI_CAP_OFFSET_UNIT; - - pfs->vsec_offset = pfs_start + pfs->pfs_header.cap_offset; + pfs->vsec_offset = pfs_start + pfs->pfs_header.cap_offset * TPMI_CAP_OFFSET_UNIT; /* * Process TPMI_INFO to get PCI device to CPU package ID. diff --git a/drivers/platform/x86/thinkpad_acpi.c b/drivers/platform/x86/thinkpad_acpi.c index 187018ffb068..ad460417f901 100644 --- a/drivers/platform/x86/thinkpad_acpi.c +++ b/drivers/platform/x86/thinkpad_acpi.c @@ -315,17 +315,12 @@ struct ibm_init_struct { /* DMI Quirks */ struct quirk_entry { bool btusb_bug; - u32 s2idle_bug_mmio; }; static struct quirk_entry quirk_btusb_bug = { .btusb_bug = true, }; -static struct quirk_entry quirk_s2idle_bug = { - .s2idle_bug_mmio = 0xfed80380, -}; - static struct { u32 bluetooth:1; u32 hotkey:1; @@ -4422,136 +4417,9 @@ static const struct dmi_system_id fwbug_list[] __initconst = { DMI_MATCH(DMI_BOARD_NAME, "20MV"), }, }, - { - .ident = "L14 Gen2 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20X5"), - } - }, - { - .ident = "T14s Gen2 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20XF"), - } - }, - { - .ident = "X13 Gen2 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20XH"), - } - }, - { - .ident = "T14 Gen2 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20XK"), - } - }, - { - .ident = "T14 Gen1 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20UD"), - } - }, - { - .ident = "T14 Gen1 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20UE"), - } - }, - { - .ident = "T14s Gen1 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20UH"), - } - }, - { - .ident = "T14s Gen1 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20UJ"), - } - }, - { - .ident = "P14s Gen1 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "20Y1"), - } - }, - { - .ident = "P14s Gen2 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "21A0"), - } - }, - { - .ident = "P14s Gen2 AMD", - .driver_data = &quirk_s2idle_bug, - .matches = { - DMI_MATCH(DMI_BOARD_VENDOR, "LENOVO"), - DMI_MATCH(DMI_PRODUCT_NAME, "21A1"), - } - }, {} }; -#ifdef CONFIG_SUSPEND -/* - * Lenovo laptops from a variety of generations run a SMI handler during the D3->D0 - * transition that occurs specifically when exiting suspend to idle which can cause - * large delays during resume when the IOMMU translation layer is enabled (the default - * behavior) for NVME devices: - * - * To avoid this firmware problem, skip the SMI handler on these machines before the - * D0 transition occurs. - */ -static void thinkpad_acpi_amd_s2idle_restore(void) -{ - struct resource *res; - void __iomem *addr; - u8 val; - - res = request_mem_region_muxed(tp_features.quirks->s2idle_bug_mmio, 1, - "thinkpad_acpi_pm80"); - if (!res) - return; - - addr = ioremap(tp_features.quirks->s2idle_bug_mmio, 1); - if (!addr) - goto cleanup_resource; - - val = ioread8(addr); - iowrite8(val & ~BIT(0), addr); - - iounmap(addr); -cleanup_resource: - release_resource(res); - kfree(res); -} - -static struct acpi_s2idle_dev_ops thinkpad_acpi_s2idle_dev_ops = { - .restore = thinkpad_acpi_amd_s2idle_restore, -}; -#endif - static const struct pci_device_id fwbug_cards_ids[] __initconst = { { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x24F3) }, { PCI_DEVICE(PCI_VENDOR_ID_INTEL, 0x24FD) }, @@ -11668,10 +11536,6 @@ static void thinkpad_acpi_module_exit(void) tpacpi_lifecycle = TPACPI_LIFE_EXITING; -#ifdef CONFIG_SUSPEND - if (tp_features.quirks && tp_features.quirks->s2idle_bug_mmio) - acpi_unregister_lps0_dev(&thinkpad_acpi_s2idle_dev_ops); -#endif if (tpacpi_hwmon) hwmon_device_unregister(tpacpi_hwmon); if (tp_features.sensors_pdrv_registered) @@ -11861,13 +11725,6 @@ static int __init thinkpad_acpi_module_init(void) tp_features.input_device_registered = 1; } -#ifdef CONFIG_SUSPEND - if (tp_features.quirks && tp_features.quirks->s2idle_bug_mmio) { - if (!acpi_register_lps0_dev(&thinkpad_acpi_s2idle_dev_ops)) - pr_info("Using s2idle quirk to avoid %s platform firmware bug\n", - (dmi_id && dmi_id->ident) ? dmi_id->ident : ""); - } -#endif return 0; } diff --git a/drivers/platform/x86/touchscreen_dmi.c b/drivers/platform/x86/touchscreen_dmi.c index 68e66b60445c..a5b687eed8f3 100644 --- a/drivers/platform/x86/touchscreen_dmi.c +++ b/drivers/platform/x86/touchscreen_dmi.c @@ -26,6 +26,21 @@ struct ts_dmi_data { /* NOTE: Please keep all entries sorted alphabetically */ +static const struct property_entry archos_101_cesium_educ_props[] = { + PROPERTY_ENTRY_U32("touchscreen-size-x", 1280), + PROPERTY_ENTRY_U32("touchscreen-size-y", 1850), + PROPERTY_ENTRY_BOOL("touchscreen-inverted-x"), + PROPERTY_ENTRY_BOOL("touchscreen-swapped-x-y"), + PROPERTY_ENTRY_U32("silead,max-fingers", 10), + PROPERTY_ENTRY_STRING("firmware-name", "gsl1680-archos-101-cesium-educ.fw"), + { } +}; + +static const struct ts_dmi_data archos_101_cesium_educ_data = { + .acpi_name = "MSSL1680:00", + .properties = archos_101_cesium_educ_props, +}; + static const struct property_entry chuwi_hi8_props[] = { PROPERTY_ENTRY_U32("touchscreen-size-x", 1665), PROPERTY_ENTRY_U32("touchscreen-size-y", 1140), @@ -1048,6 +1063,13 @@ static const struct ts_dmi_data vinga_twizzle_j116_data = { /* NOTE: Please keep this table sorted alphabetically */ const struct dmi_system_id touchscreen_dmi_table[] = { { + /* Archos 101 Cesium Educ */ + .driver_data = (void *)&archos_101_cesium_educ_data, + .matches = { + DMI_MATCH(DMI_PRODUCT_NAME, "ARCHOS 101 Cesium Educ"), + }, + }, + { /* Chuwi Hi8 */ .driver_data = (void *)&chuwi_hi8_data, .matches = { diff --git a/drivers/platform/x86/wmi.c b/drivers/platform/x86/wmi.c index 5b95d7aa5c2f..a78ddd83cda0 100644 --- a/drivers/platform/x86/wmi.c +++ b/drivers/platform/x86/wmi.c @@ -136,6 +136,16 @@ static acpi_status find_guid(const char *guid_string, struct wmi_block **out) return AE_NOT_FOUND; } +static bool guid_parse_and_compare(const char *string, const guid_t *guid) +{ + guid_t guid_input; + + if (guid_parse(string, &guid_input)) + return false; + + return guid_equal(&guid_input, guid); +} + static const void *find_guid_context(struct wmi_block *wblock, struct wmi_driver *wdriver) { @@ -146,11 +156,7 @@ static const void *find_guid_context(struct wmi_block *wblock, return NULL; while (*id->guid_string) { - guid_t guid_input; - - if (guid_parse(id->guid_string, &guid_input)) - continue; - if (guid_equal(&wblock->gblock.guid, &guid_input)) + if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) return id->context; id++; } @@ -895,11 +901,7 @@ static int wmi_dev_match(struct device *dev, struct device_driver *driver) return 0; while (*id->guid_string) { - guid_t driver_guid; - - if (WARN_ON(guid_parse(id->guid_string, &driver_guid))) - continue; - if (guid_equal(&driver_guid, &wblock->gblock.guid)) + if (guid_parse_and_compare(id->guid_string, &wblock->gblock.guid)) return 1; id++; @@ -1239,11 +1241,7 @@ static bool guid_already_parsed_for_legacy(struct acpi_device *device, const gui list_for_each_entry(wblock, &wmi_block_list, list) { /* skip warning and register if we know the driver will use struct wmi_driver */ for (int i = 0; allow_duplicates[i] != NULL; i++) { - guid_t tmp; - - if (guid_parse(allow_duplicates[i], &tmp)) - continue; - if (guid_equal(&tmp, guid)) + if (guid_parse_and_compare(allow_duplicates[i], guid)) return false; } if (guid_equal(&wblock->gblock.guid, guid)) { diff --git a/drivers/regulator/Kconfig b/drivers/regulator/Kconfig index 08d33290296b..823f8e6e4801 100644 --- a/drivers/regulator/Kconfig +++ b/drivers/regulator/Kconfig @@ -1047,6 +1047,7 @@ config REGULATOR_QCOM_USB_VBUS config REGULATOR_RAA215300 tristate "Renesas RAA215300 driver" select REGMAP_I2C + depends on COMMON_CLK depends on I2C help Support for the Renesas RAA215300 PMIC. diff --git a/drivers/s390/block/dasd_diag.c b/drivers/s390/block/dasd_diag.c index f956a4ac9881..2e4e555b37c3 100644 --- a/drivers/s390/block/dasd_diag.c +++ b/drivers/s390/block/dasd_diag.c @@ -24,7 +24,7 @@ #include <asm/debug.h> #include <asm/diag.h> #include <asm/ebcdic.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/irq.h> #include <asm/vtoc.h> diff --git a/drivers/s390/block/dasd_eckd.c b/drivers/s390/block/dasd_eckd.c index 113c509bf6d0..8587e423169e 100644 --- a/drivers/s390/block/dasd_eckd.c +++ b/drivers/s390/block/dasd_eckd.c @@ -21,13 +21,13 @@ #include <linux/compat.h> #include <linux/init.h> #include <linux/seq_file.h> +#include <linux/uaccess.h> +#include <linux/io.h> #include <asm/css_chars.h> #include <asm/debug.h> #include <asm/idals.h> #include <asm/ebcdic.h> -#include <asm/io.h> -#include <linux/uaccess.h> #include <asm/cio.h> #include <asm/ccwdev.h> #include <asm/itcw.h> diff --git a/drivers/s390/block/dasd_fba.c b/drivers/s390/block/dasd_fba.c index bcb67fa747a7..c06fa2b27120 100644 --- a/drivers/s390/block/dasd_fba.c +++ b/drivers/s390/block/dasd_fba.c @@ -16,10 +16,10 @@ #include <linux/bio.h> #include <linux/module.h> #include <linux/init.h> +#include <linux/io.h> #include <asm/idals.h> #include <asm/ebcdic.h> -#include <asm/io.h> #include <asm/ccwdev.h> #include "dasd_int.h" diff --git a/drivers/s390/block/dcssblk.c b/drivers/s390/block/dcssblk.c index 405d76df9427..09acf3853a77 100644 --- a/drivers/s390/block/dcssblk.c +++ b/drivers/s390/block/dcssblk.c @@ -20,8 +20,8 @@ #include <linux/pfn_t.h> #include <linux/uio.h> #include <linux/dax.h> +#include <linux/io.h> #include <asm/extmem.h> -#include <asm/io.h> #define DCSSBLK_NAME "dcssblk" #define DCSSBLK_MINORS_PER_DISK 1 diff --git a/drivers/s390/char/con3215.c b/drivers/s390/char/con3215.c index 0b05cd76b7d0..a1fef666c9b0 100644 --- a/drivers/s390/char/con3215.c +++ b/drivers/s390/char/con3215.c @@ -25,7 +25,7 @@ #include <linux/slab.h> #include <asm/ccwdev.h> #include <asm/cio.h> -#include <asm/io.h> +#include <linux/io.h> #include <asm/ebcdic.h> #include <linux/uaccess.h> #include <asm/delay.h> diff --git a/drivers/s390/char/monwriter.c b/drivers/s390/char/monwriter.c index 9cd1ea92d619..bc5193d81f9c 100644 --- a/drivers/s390/char/monwriter.c +++ b/drivers/s390/char/monwriter.c @@ -22,8 +22,8 @@ #include <linux/mutex.h> #include <linux/slab.h> #include <linux/uaccess.h> +#include <linux/io.h> #include <asm/ebcdic.h> -#include <asm/io.h> #include <asm/appldata.h> #include <asm/monwriter.h> diff --git a/drivers/s390/cio/ccwgroup.c b/drivers/s390/cio/ccwgroup.c index f0538609dfe4..aa3292e57e38 100644 --- a/drivers/s390/cio/ccwgroup.c +++ b/drivers/s390/cio/ccwgroup.c @@ -152,7 +152,7 @@ static ssize_t ccwgroup_online_show(struct device *dev, /* * Provide an 'ungroup' attribute so the user can remove group devices no - * longer needed or accidentially created. Saves memory :) + * longer needed or accidentally created. Saves memory :) */ static void ccwgroup_ungroup(struct ccwgroup_device *gdev) { diff --git a/drivers/s390/cio/device.c b/drivers/s390/cio/device.c index c0d620ffea61..4ca5adce9107 100644 --- a/drivers/s390/cio/device.c +++ b/drivers/s390/cio/device.c @@ -943,7 +943,7 @@ static int ccw_device_move_to_sch(struct ccw_device *cdev, cdev->private->dev_id.devno, sch->schid.ssid, sch->schib.pmcw.dev, rc); if (old_enabled) { - /* Try to reenable the old subchannel. */ + /* Try to re-enable the old subchannel. */ spin_lock_irq(old_sch->lock); cio_enable_subchannel(old_sch, (u32)virt_to_phys(old_sch)); spin_unlock_irq(old_sch->lock); diff --git a/drivers/s390/cio/device_fsm.c b/drivers/s390/cio/device_fsm.c index 2b2058427a2b..c396ac3e3a32 100644 --- a/drivers/s390/cio/device_fsm.c +++ b/drivers/s390/cio/device_fsm.c @@ -310,7 +310,7 @@ static void ccw_device_oper_notify(struct ccw_device *cdev) struct subchannel *sch = to_subchannel(cdev->dev.parent); if (ccw_device_notify(cdev, CIO_OPER) == NOTIFY_OK) { - /* Reenable channel measurements, if needed. */ + /* Re-enable channel measurements, if needed. */ ccw_device_sched_todo(cdev, CDEV_TODO_ENABLE_CMF); /* Save indication for new paths. */ cdev->private->path_new_mask = sch->vpm; @@ -947,7 +947,7 @@ void ccw_device_trigger_reprobe(struct ccw_device *cdev) */ sch->lpm = sch->schib.pmcw.pam & sch->opm; /* - * Use the initial configuration since we can't be shure that the old + * Use the initial configuration since we can't be sure that the old * paths are valid. */ io_subchannel_init_config(sch); diff --git a/drivers/s390/cio/vfio_ccw_cp.c b/drivers/s390/cio/vfio_ccw_cp.c index 1c31e81ca8de..aafd66305ead 100644 --- a/drivers/s390/cio/vfio_ccw_cp.c +++ b/drivers/s390/cio/vfio_ccw_cp.c @@ -672,7 +672,7 @@ out_init: /* * Fetch one ccw. * To reduce memory copy, we'll pin the cda page in memory, - * and to get rid of the cda 2G limitiaion of ccw1, we'll translate + * and to get rid of the cda 2G limitation of ccw1, we'll translate * direct ccws to idal ccws. */ static int ccwchain_fetch_one(struct ccw1 *ccw, @@ -787,7 +787,7 @@ void cp_free(struct channel_program *cp) * program. * * These APIs will copy the ccws into kernel-space buffers, and update - * the guest phsical addresses with their corresponding host physical + * the guest physical addresses with their corresponding host physical * addresses. Then channel I/O device drivers could issue the * translated channel program to real devices to perform an I/O * operation. diff --git a/drivers/s390/crypto/ap_bus.c b/drivers/s390/crypto/ap_bus.c index 8d6b9a52bf3c..420120be300f 100644 --- a/drivers/s390/crypto/ap_bus.c +++ b/drivers/s390/crypto/ap_bus.c @@ -497,7 +497,7 @@ static void ap_tasklet_fn(unsigned long dummy) enum ap_sm_wait wait = AP_SM_WAIT_NONE; /* Reset the indicator if interrupts are used. Thus new interrupts can - * be received. Doing it in the beginning of the tasklet is therefor + * be received. Doing it in the beginning of the tasklet is therefore * important that no requests on any AP get lost. */ if (ap_irq_flag) @@ -2289,7 +2289,7 @@ static int __init ap_module_init(void) timer_setup(&ap_config_timer, ap_config_timeout, 0); /* - * Setup the high resultion poll timer. + * Setup the high resolution poll timer. * If we are running under z/VM adjust polling to z/VM polling rate. */ if (MACHINE_IS_VM) diff --git a/drivers/s390/crypto/ap_bus.h b/drivers/s390/crypto/ap_bus.h index 101fb324476f..0d7b7eb374ad 100644 --- a/drivers/s390/crypto/ap_bus.h +++ b/drivers/s390/crypto/ap_bus.h @@ -233,30 +233,6 @@ struct ap_queue { typedef enum ap_sm_wait (ap_func_t)(struct ap_queue *queue); -/* failure injection cmd struct */ -struct ap_fi { - union { - u16 cmd; /* fi flags + action */ - struct { - u8 flags; /* fi flags only */ - u8 action; /* fi action only */ - }; - }; -}; - -/* all currently known fi actions */ -enum ap_fi_actions { - AP_FI_ACTION_CCA_AGENT_FF = 0x01, - AP_FI_ACTION_CCA_DOM_INVAL = 0x02, - AP_FI_ACTION_NQAP_QID_INVAL = 0x03, -}; - -/* all currently known fi flags */ -enum ap_fi_flags { - AP_FI_FLAG_NO_RETRY = 0x01, - AP_FI_FLAG_TOGGLE_SPECIAL = 0x02, -}; - struct ap_message { struct list_head list; /* Request queueing. */ unsigned long psmid; /* Message id. */ @@ -264,7 +240,6 @@ struct ap_message { size_t len; /* actual msg len in msg buffer */ size_t bufsize; /* allocated msg buffer size */ u16 flags; /* Flags, see AP_MSG_FLAG_xxx */ - struct ap_fi fi; /* Failure Injection cmd */ int rc; /* Return code for this message */ void *private; /* ap driver private pointer. */ /* receive is called from tasklet context */ @@ -384,7 +359,7 @@ int ap_apqn_in_matrix_owned_by_def_drv(unsigned long *apm, * like "+1-16,-32,-0x40,+128" where only single bits or ranges of * bits are cleared or set. Distinction is done based on the very * first character which may be '+' or '-' for the relative string - * and othewise assume to be an absolute value string. If parsing fails + * and otherwise assume to be an absolute value string. If parsing fails * a negative errno value is returned. All arguments and bitmaps are * big endian order. */ diff --git a/drivers/s390/crypto/ap_queue.c b/drivers/s390/crypto/ap_queue.c index ed8f813653fe..30df83735adf 100644 --- a/drivers/s390/crypto/ap_queue.c +++ b/drivers/s390/crypto/ap_queue.c @@ -274,13 +274,6 @@ static enum ap_sm_wait ap_sm_write(struct ap_queue *aq) /* Start the next request on the queue. */ ap_msg = list_entry(aq->requestq.next, struct ap_message, list); -#ifdef CONFIG_ZCRYPT_DEBUG - if (ap_msg->fi.action == AP_FI_ACTION_NQAP_QID_INVAL) { - AP_DBF_WARN("%s fi cmd 0x%04x: forcing invalid qid 0xFF00\n", - __func__, ap_msg->fi.cmd); - qid = 0xFF00; - } -#endif status = __ap_send(qid, ap_msg->psmid, ap_msg->msg, ap_msg->len, ap_msg->flags & AP_MSG_FLAG_SPECIAL); diff --git a/drivers/s390/crypto/vfio_ap_ops.c b/drivers/s390/crypto/vfio_ap_ops.c index a8f58e133e6e..b441745b0418 100644 --- a/drivers/s390/crypto/vfio_ap_ops.c +++ b/drivers/s390/crypto/vfio_ap_ops.c @@ -445,7 +445,7 @@ static struct ap_queue_status vfio_ap_irq_enable(struct vfio_ap_queue *q, q->saved_isc = isc; break; case AP_RESPONSE_OTHERWISE_CHANGED: - /* We could not modify IRQ setings: clear new configuration */ + /* We could not modify IRQ settings: clear new configuration */ vfio_unpin_pages(&q->matrix_mdev->vdev, nib, 1); kvm_s390_gisc_unregister(kvm, isc); break; @@ -524,7 +524,7 @@ static void vfio_ap_le_guid_to_be_uuid(guid_t *guid, unsigned long *uuid) * Response.status may be set to following Response Code: * - AP_RESPONSE_Q_NOT_AVAIL: if the queue is not available * - AP_RESPONSE_DECONFIGURED: if the queue is not configured - * - AP_RESPONSE_NORMAL (0) : in case of successs + * - AP_RESPONSE_NORMAL (0) : in case of success * Check vfio_ap_setirq() and vfio_ap_clrirq() for other possible RC. * We take the matrix_dev lock to ensure serialization on queues and * mediated device access. diff --git a/drivers/s390/crypto/zcrypt_api.c b/drivers/s390/crypto/zcrypt_api.c index 444ef95d3f59..4b23c9f7f3e5 100644 --- a/drivers/s390/crypto/zcrypt_api.c +++ b/drivers/s390/crypto/zcrypt_api.c @@ -111,8 +111,6 @@ EXPORT_SYMBOL(zcrypt_msgtype); * Multi device nodes extension functions. */ -#ifdef CONFIG_ZCRYPT_MULTIDEVNODES - struct zcdn_device; static struct class *zcrypt_class; @@ -477,8 +475,6 @@ static void zcdn_destroy_all(void) mutex_unlock(&ap_perms_mutex); } -#endif - /* * zcrypt_read (): Not supported beyond zcrypt 1.3.1. * @@ -510,7 +506,6 @@ static int zcrypt_open(struct inode *inode, struct file *filp) { struct ap_perms *perms = &ap_perms; -#ifdef CONFIG_ZCRYPT_MULTIDEVNODES if (filp->f_inode->i_cdev == &zcrypt_cdev) { struct zcdn_device *zcdndev; @@ -522,7 +517,6 @@ static int zcrypt_open(struct inode *inode, struct file *filp) if (zcdndev) perms = &zcdndev->perms; } -#endif filp->private_data = (void *)perms; atomic_inc(&zcrypt_open_count); @@ -536,7 +530,6 @@ static int zcrypt_open(struct inode *inode, struct file *filp) */ static int zcrypt_release(struct inode *inode, struct file *filp) { -#ifdef CONFIG_ZCRYPT_MULTIDEVNODES if (filp->f_inode->i_cdev == &zcrypt_cdev) { struct zcdn_device *zcdndev; @@ -549,7 +542,6 @@ static int zcrypt_release(struct inode *inode, struct file *filp) put_device(&zcdndev->device); } } -#endif atomic_dec(&zcrypt_open_count); return 0; @@ -661,11 +653,6 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms, ap_init_message(&ap_msg); -#ifdef CONFIG_ZCRYPT_DEBUG - if (tr && tr->fi.cmd) - ap_msg.fi.cmd = tr->fi.cmd; -#endif - if (mex->outputdatalength < mex->inputdatalength) { func_code = 0; rc = -EINVAL; @@ -687,7 +674,7 @@ static long zcrypt_rsa_modexpo(struct ap_perms *perms, pref_zq = NULL; spin_lock(&zcrypt_list_lock); for_each_zcrypt_card(zc) { - /* Check for usable accelarator or CCA card */ + /* Check for usable accelerator or CCA card */ if (!zc->online || !zc->card->config || zc->card->chkstop || !(zc->card->functions & 0x18000000)) continue; @@ -771,11 +758,6 @@ static long zcrypt_rsa_crt(struct ap_perms *perms, ap_init_message(&ap_msg); -#ifdef CONFIG_ZCRYPT_DEBUG - if (tr && tr->fi.cmd) - ap_msg.fi.cmd = tr->fi.cmd; -#endif - if (crt->outputdatalength < crt->inputdatalength) { func_code = 0; rc = -EINVAL; @@ -797,7 +779,7 @@ static long zcrypt_rsa_crt(struct ap_perms *perms, pref_zq = NULL; spin_lock(&zcrypt_list_lock); for_each_zcrypt_card(zc) { - /* Check for usable accelarator or CCA card */ + /* Check for usable accelerator or CCA card */ if (!zc->online || !zc->card->config || zc->card->chkstop || !(zc->card->functions & 0x18000000)) continue; @@ -883,16 +865,6 @@ static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms, xcrb->status = 0; ap_init_message(&ap_msg); -#ifdef CONFIG_ZCRYPT_DEBUG - if (tr && tr->fi.cmd) - ap_msg.fi.cmd = tr->fi.cmd; - if (tr && tr->fi.action == AP_FI_ACTION_CCA_AGENT_FF) { - ZCRYPT_DBF_WARN("%s fi cmd 0x%04x: forcing invalid agent_ID 'FF'\n", - __func__, tr->fi.cmd); - xcrb->agent_ID = 0x4646; - } -#endif - rc = prep_cca_ap_msg(userspace, xcrb, &ap_msg, &func_code, &domain); if (rc) goto out; @@ -982,14 +954,6 @@ static long _zcrypt_send_cprb(bool userspace, struct ap_perms *perms, if (*domain == AUTOSEL_DOM) *domain = AP_QID_QUEUE(qid); -#ifdef CONFIG_ZCRYPT_DEBUG - if (tr && tr->fi.action == AP_FI_ACTION_CCA_DOM_INVAL) { - ZCRYPT_DBF_WARN("%s fi cmd 0x%04x: forcing invalid domain\n", - __func__, tr->fi.cmd); - *domain = 99; - } -#endif - rc = pref_zq->ops->send_cprb(userspace, pref_zq, xcrb, &ap_msg); spin_lock(&zcrypt_list_lock); @@ -1058,11 +1022,6 @@ static long _zcrypt_send_ep11_cprb(bool userspace, struct ap_perms *perms, ap_init_message(&ap_msg); -#ifdef CONFIG_ZCRYPT_DEBUG - if (tr && tr->fi.cmd) - ap_msg.fi.cmd = tr->fi.cmd; -#endif - target_num = (unsigned short)xcrb->targets_num; /* empty list indicates autoselect (all available targets) */ @@ -1473,23 +1432,10 @@ static int icarsamodexpo_ioctl(struct ap_perms *perms, unsigned long arg) if (copy_from_user(&mex, umex, sizeof(mex))) return -EFAULT; -#ifdef CONFIG_ZCRYPT_DEBUG - if (mex.inputdatalength & (1U << 31)) { - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - tr.fi.cmd = (u16)(mex.inputdatalength >> 16); - } - mex.inputdatalength &= 0x0000FFFF; -#endif - do { rc = zcrypt_rsa_modexpo(perms, &tr, &mex); if (rc == -EAGAIN) tr.again_counter++; -#ifdef CONFIG_ZCRYPT_DEBUG - if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY)) - break; -#endif } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); /* on failure: retry once again after a requested rescan */ if ((rc == -ENODEV) && (zcrypt_process_rescan())) @@ -1518,23 +1464,10 @@ static int icarsacrt_ioctl(struct ap_perms *perms, unsigned long arg) if (copy_from_user(&crt, ucrt, sizeof(crt))) return -EFAULT; -#ifdef CONFIG_ZCRYPT_DEBUG - if (crt.inputdatalength & (1U << 31)) { - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - tr.fi.cmd = (u16)(crt.inputdatalength >> 16); - } - crt.inputdatalength &= 0x0000FFFF; -#endif - do { rc = zcrypt_rsa_crt(perms, &tr, &crt); if (rc == -EAGAIN) tr.again_counter++; -#ifdef CONFIG_ZCRYPT_DEBUG - if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY)) - break; -#endif } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); /* on failure: retry once again after a requested rescan */ if ((rc == -ENODEV) && (zcrypt_process_rescan())) @@ -1563,23 +1496,10 @@ static int zsecsendcprb_ioctl(struct ap_perms *perms, unsigned long arg) if (copy_from_user(&xcrb, uxcrb, sizeof(xcrb))) return -EFAULT; -#ifdef CONFIG_ZCRYPT_DEBUG - if ((xcrb.status & 0x8000FFFF) == 0x80004649 /* 'FI' */) { - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - tr.fi.cmd = (u16)(xcrb.status >> 16); - } - xcrb.status = 0; -#endif - do { rc = _zcrypt_send_cprb(true, perms, &tr, &xcrb); if (rc == -EAGAIN) tr.again_counter++; -#ifdef CONFIG_ZCRYPT_DEBUG - if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY)) - break; -#endif } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); /* on failure: retry once again after a requested rescan */ if ((rc == -ENODEV) && (zcrypt_process_rescan())) @@ -1609,23 +1529,10 @@ static int zsendep11cprb_ioctl(struct ap_perms *perms, unsigned long arg) if (copy_from_user(&xcrb, uxcrb, sizeof(xcrb))) return -EFAULT; -#ifdef CONFIG_ZCRYPT_DEBUG - if (xcrb.req_len & (1ULL << 63)) { - if (!capable(CAP_SYS_ADMIN)) - return -EPERM; - tr.fi.cmd = (u16)(xcrb.req_len >> 48); - } - xcrb.req_len &= 0x0000FFFFFFFFFFFFULL; -#endif - do { rc = _zcrypt_send_ep11_cprb(true, perms, &tr, &xcrb); if (rc == -EAGAIN) tr.again_counter++; -#ifdef CONFIG_ZCRYPT_DEBUG - if (rc == -EAGAIN && (tr.fi.flags & AP_FI_FLAG_NO_RETRY)) - break; -#endif } while (rc == -EAGAIN && tr.again_counter < TRACK_AGAIN_MAX); /* on failure: retry once again after a requested rescan */ if ((rc == -ENODEV) && (zcrypt_process_rescan())) @@ -1668,14 +1575,16 @@ static long zcrypt_unlocked_ioctl(struct file *filp, unsigned int cmd, size_t total_size = MAX_ZDEV_ENTRIES_EXT * sizeof(struct zcrypt_device_status_ext); - device_status = kzalloc(total_size, GFP_KERNEL); + device_status = kvmalloc_array(MAX_ZDEV_ENTRIES_EXT, + sizeof(struct zcrypt_device_status_ext), + GFP_KERNEL); if (!device_status) return -ENOMEM; zcrypt_device_status_mask_ext(device_status); if (copy_to_user((char __user *)arg, device_status, total_size)) rc = -EFAULT; - kfree(device_status); + kvfree(device_status); return rc; } case ZCRYPT_STATUS_MASK: { @@ -2144,8 +2053,6 @@ void zcrypt_debug_exit(void) debug_unregister(zcrypt_dbf_info); } -#ifdef CONFIG_ZCRYPT_MULTIDEVNODES - static int __init zcdn_init(void) { int rc; @@ -2203,8 +2110,6 @@ static void zcdn_exit(void) class_destroy(zcrypt_class); } -#endif - /* * zcrypt_api_init(): Module initialization. * @@ -2218,11 +2123,9 @@ int __init zcrypt_api_init(void) if (rc) goto out; -#ifdef CONFIG_ZCRYPT_MULTIDEVNODES rc = zcdn_init(); if (rc) goto out; -#endif /* Register the request sprayer. */ rc = misc_register(&zcrypt_misc_device); @@ -2235,9 +2138,7 @@ int __init zcrypt_api_init(void) return 0; out_misc_register_failed: -#ifdef CONFIG_ZCRYPT_MULTIDEVNODES zcdn_exit(); -#endif zcrypt_debug_exit(); out: return rc; @@ -2250,9 +2151,7 @@ out: */ void __exit zcrypt_api_exit(void) { -#ifdef CONFIG_ZCRYPT_MULTIDEVNODES zcdn_exit(); -#endif misc_deregister(&zcrypt_misc_device); zcrypt_msgtype6_exit(); zcrypt_msgtype50_exit(); diff --git a/drivers/s390/crypto/zcrypt_api.h b/drivers/s390/crypto/zcrypt_api.h index f299deb8b8c7..de659954c8f7 100644 --- a/drivers/s390/crypto/zcrypt_api.h +++ b/drivers/s390/crypto/zcrypt_api.h @@ -60,9 +60,6 @@ struct zcrypt_track { int again_counter; /* retry attempts counter */ int last_qid; /* last qid used */ int last_rc; /* last return code */ -#ifdef CONFIG_ZCRYPT_DEBUG - struct ap_fi fi; /* failure injection cmd */ -#endif }; /* defines related to message tracking */ diff --git a/drivers/s390/crypto/zcrypt_ccamisc.c b/drivers/s390/crypto/zcrypt_ccamisc.c index 8c8808cc68a4..263fe182648b 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.c +++ b/drivers/s390/crypto/zcrypt_ccamisc.c @@ -689,7 +689,7 @@ int cca_sec2protkey(u16 cardnr, u16 domain, goto out; } - /* copy the tanslated protected key */ + /* copy the translated protected key */ switch (prepparm->lv3.ckb.len) { case 16 + 32: /* AES 128 protected key */ diff --git a/drivers/s390/crypto/zcrypt_ccamisc.h b/drivers/s390/crypto/zcrypt_ccamisc.h index 78bf5631848e..5ddf02f965f9 100644 --- a/drivers/s390/crypto/zcrypt_ccamisc.h +++ b/drivers/s390/crypto/zcrypt_ccamisc.h @@ -115,7 +115,7 @@ struct eccprivkeytoken { u64 mkvp; /* master key verification pattern */ u8 opk[48]; /* encrypted object protection key data */ u16 adatalen; /* associated data length in bytes */ - u16 fseclen; /* formated section length in bytes */ + u16 fseclen; /* formatted section length in bytes */ u8 more_data[]; /* more data follows */ } __packed; @@ -232,7 +232,7 @@ int cca_findcard(const u8 *key, u16 *pcardnr, u16 *pdomain, int verify); * the number of apqns stored into the list is returned in *nr_apqns. One apqn * entry is simple a 32 bit value with 16 bit cardnr and 16 bit domain nr and * may be casted to struct pkey_apqn. The return value is either 0 for success - * or a negative errno value. If no apqn meeting the criterias is found, + * or a negative errno value. If no apqn meeting the criteria is found, * -ENODEV is returned. */ int cca_findcard2(u32 **apqns, u32 *nr_apqns, u16 cardnr, u16 domain, diff --git a/drivers/s390/crypto/zcrypt_ep11misc.c b/drivers/s390/crypto/zcrypt_ep11misc.c index f67d19d08571..958f5ee47f1b 100644 --- a/drivers/s390/crypto/zcrypt_ep11misc.c +++ b/drivers/s390/crypto/zcrypt_ep11misc.c @@ -1368,7 +1368,7 @@ int ep11_kblob2protkey(u16 card, u16 dom, const u8 *keyblob, size_t keybloblen, goto out; } - /* copy the tanslated protected key */ + /* copy the translated protected key */ if (wki->pkeysize > *protkeylen) { DEBUG_ERR("%s wk info pkeysize %llu > protkeysize %u\n", __func__, wki->pkeysize, *protkeylen); diff --git a/drivers/s390/crypto/zcrypt_ep11misc.h b/drivers/s390/crypto/zcrypt_ep11misc.h index 07445041869f..a3eddf51242d 100644 --- a/drivers/s390/crypto/zcrypt_ep11misc.h +++ b/drivers/s390/crypto/zcrypt_ep11misc.h @@ -131,14 +131,14 @@ int ep11_clr2keyblob(u16 cardnr, u16 domain, u32 keybitsize, u32 keygenflags, * - if minapi > 0 only apqns with API_ord_nr >= minapi * - if wkvp != NULL only apqns where the wkvp (EP11_WKVPLEN bytes) matches * to the first EP11_WKVPLEN bytes of the wkvp of the current wrapping - * key for this domain. When a wkvp is given there will aways be a re-fetch + * key for this domain. When a wkvp is given there will always be a re-fetch * of the domain info for the potential apqn - so this triggers an request * reply to each apqn eligible. * The array of apqn entries is allocated with kmalloc and returned in *apqns; * the number of apqns stored into the list is returned in *nr_apqns. One apqn * entry is simple a 32 bit value with 16 bit cardnr and 16 bit domain nr and * may be casted to struct pkey_apqn. The return value is either 0 for success - * or a negative errno value. If no apqn meeting the criterias is found, + * or a negative errno value. If no apqn meeting the criteria is found, * -ENODEV is returned. */ int ep11_findcard2(u32 **apqns, u32 *nr_apqns, u16 cardnr, u16 domain, diff --git a/drivers/s390/crypto/zcrypt_msgtype50.c b/drivers/s390/crypto/zcrypt_msgtype50.c index 05ace18c12b0..51f8f7a463f7 100644 --- a/drivers/s390/crypto/zcrypt_msgtype50.c +++ b/drivers/s390/crypto/zcrypt_msgtype50.c @@ -246,11 +246,6 @@ static int ICAMEX_msg_to_type50MEX_msg(struct zcrypt_queue *zq, copy_from_user(inp, mex->inputdata, mod_len)) return -EFAULT; -#ifdef CONFIG_ZCRYPT_DEBUG - if (ap_msg->fi.flags & AP_FI_FLAG_TOGGLE_SPECIAL) - ap_msg->flags ^= AP_MSG_FLAG_SPECIAL; -#endif - return 0; } @@ -338,11 +333,6 @@ static int ICACRT_msg_to_type50CRT_msg(struct zcrypt_queue *zq, copy_from_user(inp, crt->inputdata, mod_len)) return -EFAULT; -#ifdef CONFIG_ZCRYPT_DEBUG - if (ap_msg->fi.flags & AP_FI_FLAG_TOGGLE_SPECIAL) - ap_msg->flags ^= AP_MSG_FLAG_SPECIAL; -#endif - return 0; } diff --git a/drivers/s390/crypto/zcrypt_msgtype6.c b/drivers/s390/crypto/zcrypt_msgtype6.c index 2f9bf23fbb44..67fd2ec9c5a1 100644 --- a/drivers/s390/crypto/zcrypt_msgtype6.c +++ b/drivers/s390/crypto/zcrypt_msgtype6.c @@ -425,11 +425,6 @@ static int xcrb_msg_to_type6cprb_msgx(bool userspace, struct ap_message *ap_msg, memcmp(function_code, "AU", 2) == 0) ap_msg->flags |= AP_MSG_FLAG_SPECIAL; -#ifdef CONFIG_ZCRYPT_DEBUG - if (ap_msg->fi.flags & AP_FI_FLAG_TOGGLE_SPECIAL) - ap_msg->flags ^= AP_MSG_FLAG_SPECIAL; -#endif - /* check CPRB minor version, set info bits in ap_message flag field */ switch (*(unsigned short *)(&msg->cprbx.func_id[0])) { case 0x5432: /* "T2" */ @@ -535,11 +530,6 @@ static int xcrb_msg_to_type6_ep11cprb_msgx(bool userspace, struct ap_message *ap if (msg->cprbx.flags & 0x20) ap_msg->flags |= AP_MSG_FLAG_SPECIAL; -#ifdef CONFIG_ZCRYPT_DEBUG - if (ap_msg->fi.flags & AP_FI_FLAG_TOGGLE_SPECIAL) - ap_msg->flags ^= AP_MSG_FLAG_SPECIAL; -#endif - /* set info bits in ap_message flag field */ if (msg->cprbx.flags & 0x80) ap_msg->flags |= AP_MSG_FLAG_ADMIN; @@ -1143,6 +1133,9 @@ static long zcrypt_msgtype6_send_cprb(bool userspace, struct zcrypt_queue *zq, ap_cancel_message(zq->queue, ap_msg); } + if (rc == -EAGAIN && ap_msg->flags & AP_MSG_FLAG_ADMIN) + rc = -EIO; /* do not retry administrative requests */ + out: if (rc) ZCRYPT_DBF_DBG("%s send cprb at dev=%02x.%04x rc=%d\n", @@ -1263,6 +1256,9 @@ static long zcrypt_msgtype6_send_ep11_cprb(bool userspace, struct zcrypt_queue * ap_cancel_message(zq->queue, ap_msg); } + if (rc == -EAGAIN && ap_msg->flags & AP_MSG_FLAG_ADMIN) + rc = -EIO; /* do not retry administrative requests */ + out: if (rc) ZCRYPT_DBF_DBG("%s send cprb at dev=%02x.%04x rc=%d\n", diff --git a/drivers/s390/net/ctcm_mpc.c b/drivers/s390/net/ctcm_mpc.c index c44ba88f9f47..7a2f34a5e0e0 100644 --- a/drivers/s390/net/ctcm_mpc.c +++ b/drivers/s390/net/ctcm_mpc.c @@ -43,13 +43,13 @@ #include <linux/netdevice.h> #include <net/dst.h> -#include <linux/io.h> /* instead of <asm/io.h> ok ? */ -#include <asm/ccwdev.h> -#include <asm/ccwgroup.h> -#include <linux/bitops.h> /* instead of <asm/bitops.h> ok ? */ -#include <linux/uaccess.h> /* instead of <asm/uaccess.h> ok ? */ +#include <linux/io.h> +#include <linux/bitops.h> +#include <linux/uaccess.h> #include <linux/wait.h> #include <linux/moduleparam.h> +#include <asm/ccwdev.h> +#include <asm/ccwgroup.h> #include <asm/idals.h> #include "ctcm_main.h" diff --git a/drivers/s390/net/ism_drv.c b/drivers/s390/net/ism_drv.c index 9b5fccdbc7d6..6df7f377d2f9 100644 --- a/drivers/s390/net/ism_drv.c +++ b/drivers/s390/net/ism_drv.c @@ -36,7 +36,7 @@ static const struct smcd_ops ism_ops; static struct ism_client *clients[MAX_CLIENTS]; /* use an array rather than */ /* a list for fast mapping */ static u8 max_client; -static DEFINE_SPINLOCK(clients_lock); +static DEFINE_MUTEX(clients_lock); struct ism_dev_list { struct list_head list; struct mutex mutex; /* protects ism device list */ @@ -47,14 +47,22 @@ static struct ism_dev_list ism_dev_list = { .mutex = __MUTEX_INITIALIZER(ism_dev_list.mutex), }; +static void ism_setup_forwarding(struct ism_client *client, struct ism_dev *ism) +{ + unsigned long flags; + + spin_lock_irqsave(&ism->lock, flags); + ism->subs[client->id] = client; + spin_unlock_irqrestore(&ism->lock, flags); +} + int ism_register_client(struct ism_client *client) { struct ism_dev *ism; - unsigned long flags; int i, rc = -ENOSPC; mutex_lock(&ism_dev_list.mutex); - spin_lock_irqsave(&clients_lock, flags); + mutex_lock(&clients_lock); for (i = 0; i < MAX_CLIENTS; ++i) { if (!clients[i]) { clients[i] = client; @@ -65,12 +73,14 @@ int ism_register_client(struct ism_client *client) break; } } - spin_unlock_irqrestore(&clients_lock, flags); + mutex_unlock(&clients_lock); + if (i < MAX_CLIENTS) { /* initialize with all devices that we got so far */ list_for_each_entry(ism, &ism_dev_list.list, list) { ism->priv[i] = NULL; client->add(ism); + ism_setup_forwarding(client, ism); } } mutex_unlock(&ism_dev_list.mutex); @@ -86,25 +96,32 @@ int ism_unregister_client(struct ism_client *client) int rc = 0; mutex_lock(&ism_dev_list.mutex); - spin_lock_irqsave(&clients_lock, flags); - clients[client->id] = NULL; - if (client->id + 1 == max_client) - max_client--; - spin_unlock_irqrestore(&clients_lock, flags); list_for_each_entry(ism, &ism_dev_list.list, list) { + spin_lock_irqsave(&ism->lock, flags); + /* Stop forwarding IRQs and events */ + ism->subs[client->id] = NULL; for (int i = 0; i < ISM_NR_DMBS; ++i) { if (ism->sba_client_arr[i] == client->id) { - pr_err("%s: attempt to unregister client '%s'" - "with registered dmb(s)\n", __func__, - client->name); + WARN(1, "%s: attempt to unregister '%s' with registered dmb(s)\n", + __func__, client->name); rc = -EBUSY; - goto out; + goto err_reg_dmb; } } + spin_unlock_irqrestore(&ism->lock, flags); } -out: mutex_unlock(&ism_dev_list.mutex); + mutex_lock(&clients_lock); + clients[client->id] = NULL; + if (client->id + 1 == max_client) + max_client--; + mutex_unlock(&clients_lock); + return rc; + +err_reg_dmb: + spin_unlock_irqrestore(&ism->lock, flags); + mutex_unlock(&ism_dev_list.mutex); return rc; } EXPORT_SYMBOL_GPL(ism_unregister_client); @@ -328,6 +345,7 @@ int ism_register_dmb(struct ism_dev *ism, struct ism_dmb *dmb, struct ism_client *client) { union ism_reg_dmb cmd; + unsigned long flags; int ret; ret = ism_alloc_dmb(ism, dmb); @@ -351,7 +369,9 @@ int ism_register_dmb(struct ism_dev *ism, struct ism_dmb *dmb, goto out; } dmb->dmb_tok = cmd.response.dmb_tok; + spin_lock_irqsave(&ism->lock, flags); ism->sba_client_arr[dmb->sba_idx - ISM_DMB_BIT_OFFSET] = client->id; + spin_unlock_irqrestore(&ism->lock, flags); out: return ret; } @@ -360,6 +380,7 @@ EXPORT_SYMBOL_GPL(ism_register_dmb); int ism_unregister_dmb(struct ism_dev *ism, struct ism_dmb *dmb) { union ism_unreg_dmb cmd; + unsigned long flags; int ret; memset(&cmd, 0, sizeof(cmd)); @@ -368,7 +389,9 @@ int ism_unregister_dmb(struct ism_dev *ism, struct ism_dmb *dmb) cmd.request.dmb_tok = dmb->dmb_tok; + spin_lock_irqsave(&ism->lock, flags); ism->sba_client_arr[dmb->sba_idx - ISM_DMB_BIT_OFFSET] = NO_CLIENT; + spin_unlock_irqrestore(&ism->lock, flags); ret = ism_cmd(ism, &cmd); if (ret && ret != ISM_ERROR) @@ -491,6 +514,7 @@ static u16 ism_get_chid(struct ism_dev *ism) static void ism_handle_event(struct ism_dev *ism) { struct ism_event *entry; + struct ism_client *clt; int i; while ((ism->ieq_idx + 1) != READ_ONCE(ism->ieq->header.idx)) { @@ -499,21 +523,21 @@ static void ism_handle_event(struct ism_dev *ism) entry = &ism->ieq->entry[ism->ieq_idx]; debug_event(ism_debug_info, 2, entry, sizeof(*entry)); - spin_lock(&clients_lock); - for (i = 0; i < max_client; ++i) - if (clients[i]) - clients[i]->handle_event(ism, entry); - spin_unlock(&clients_lock); + for (i = 0; i < max_client; ++i) { + clt = ism->subs[i]; + if (clt) + clt->handle_event(ism, entry); + } } } static irqreturn_t ism_handle_irq(int irq, void *data) { struct ism_dev *ism = data; - struct ism_client *clt; unsigned long bit, end; unsigned long *bv; u16 dmbemask; + u8 client_id; bv = (void *) &ism->sba->dmb_bits[ISM_DMB_WORD_OFFSET]; end = sizeof(ism->sba->dmb_bits) * BITS_PER_BYTE - ISM_DMB_BIT_OFFSET; @@ -530,8 +554,10 @@ static irqreturn_t ism_handle_irq(int irq, void *data) dmbemask = ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET]; ism->sba->dmbe_mask[bit + ISM_DMB_BIT_OFFSET] = 0; barrier(); - clt = clients[ism->sba_client_arr[bit]]; - clt->handle_irq(ism, bit + ISM_DMB_BIT_OFFSET, dmbemask); + client_id = ism->sba_client_arr[bit]; + if (unlikely(client_id == NO_CLIENT || !ism->subs[client_id])) + continue; + ism->subs[client_id]->handle_irq(ism, bit + ISM_DMB_BIT_OFFSET, dmbemask); } if (ism->sba->e) { @@ -548,20 +574,9 @@ static u64 ism_get_local_gid(struct ism_dev *ism) return ism->local_gid; } -static void ism_dev_add_work_func(struct work_struct *work) -{ - struct ism_client *client = container_of(work, struct ism_client, - add_work); - - client->add(client->tgt_ism); - atomic_dec(&client->tgt_ism->add_dev_cnt); - wake_up(&client->tgt_ism->waitq); -} - static int ism_dev_init(struct ism_dev *ism) { struct pci_dev *pdev = ism->pdev; - unsigned long flags; int i, ret; ret = pci_alloc_irq_vectors(pdev, 1, 1, PCI_IRQ_MSI); @@ -594,25 +609,16 @@ static int ism_dev_init(struct ism_dev *ism) /* hardware is V2 capable */ ism_create_system_eid(); - init_waitqueue_head(&ism->waitq); - atomic_set(&ism->free_clients_cnt, 0); - atomic_set(&ism->add_dev_cnt, 0); - - wait_event(ism->waitq, !atomic_read(&ism->add_dev_cnt)); - spin_lock_irqsave(&clients_lock, flags); - for (i = 0; i < max_client; ++i) + mutex_lock(&ism_dev_list.mutex); + mutex_lock(&clients_lock); + for (i = 0; i < max_client; ++i) { if (clients[i]) { - INIT_WORK(&clients[i]->add_work, - ism_dev_add_work_func); - clients[i]->tgt_ism = ism; - atomic_inc(&ism->add_dev_cnt); - schedule_work(&clients[i]->add_work); + clients[i]->add(ism); + ism_setup_forwarding(clients[i], ism); } - spin_unlock_irqrestore(&clients_lock, flags); - - wait_event(ism->waitq, !atomic_read(&ism->add_dev_cnt)); + } + mutex_unlock(&clients_lock); - mutex_lock(&ism_dev_list.mutex); list_add(&ism->list, &ism_dev_list.list); mutex_unlock(&ism_dev_list.mutex); @@ -687,36 +693,24 @@ err_dev: return ret; } -static void ism_dev_remove_work_func(struct work_struct *work) -{ - struct ism_client *client = container_of(work, struct ism_client, - remove_work); - - client->remove(client->tgt_ism); - atomic_dec(&client->tgt_ism->free_clients_cnt); - wake_up(&client->tgt_ism->waitq); -} - -/* Callers must hold ism_dev_list.mutex */ static void ism_dev_exit(struct ism_dev *ism) { struct pci_dev *pdev = ism->pdev; unsigned long flags; int i; - wait_event(ism->waitq, !atomic_read(&ism->free_clients_cnt)); - spin_lock_irqsave(&clients_lock, flags); + spin_lock_irqsave(&ism->lock, flags); for (i = 0; i < max_client; ++i) - if (clients[i]) { - INIT_WORK(&clients[i]->remove_work, - ism_dev_remove_work_func); - clients[i]->tgt_ism = ism; - atomic_inc(&ism->free_clients_cnt); - schedule_work(&clients[i]->remove_work); - } - spin_unlock_irqrestore(&clients_lock, flags); + ism->subs[i] = NULL; + spin_unlock_irqrestore(&ism->lock, flags); - wait_event(ism->waitq, !atomic_read(&ism->free_clients_cnt)); + mutex_lock(&ism_dev_list.mutex); + mutex_lock(&clients_lock); + for (i = 0; i < max_client; ++i) { + if (clients[i]) + clients[i]->remove(ism); + } + mutex_unlock(&clients_lock); if (SYSTEM_EID.serial_number[0] != '0' || SYSTEM_EID.type[0] != '0') @@ -727,15 +721,14 @@ static void ism_dev_exit(struct ism_dev *ism) kfree(ism->sba_client_arr); pci_free_irq_vectors(pdev); list_del_init(&ism->list); + mutex_unlock(&ism_dev_list.mutex); } static void ism_remove(struct pci_dev *pdev) { struct ism_dev *ism = dev_get_drvdata(&pdev->dev); - mutex_lock(&ism_dev_list.mutex); ism_dev_exit(ism); - mutex_unlock(&ism_dev_list.mutex); pci_release_mem_regions(pdev); pci_disable_device(pdev); diff --git a/drivers/s390/net/netiucv.c b/drivers/s390/net/netiucv.c index 66076cada8ae..8852b03f943b 100644 --- a/drivers/s390/net/netiucv.c +++ b/drivers/s390/net/netiucv.c @@ -47,7 +47,7 @@ #include <linux/ctype.h> #include <net/dst.h> -#include <asm/io.h> +#include <linux/io.h> #include <linux/uaccess.h> #include <asm/ebcdic.h> diff --git a/drivers/scsi/isci/scu_task_context.h b/drivers/scsi/isci/scu_task_context.h index 869a979eb5b2..582d22d54689 100644 --- a/drivers/scsi/isci/scu_task_context.h +++ b/drivers/scsi/isci/scu_task_context.h @@ -845,7 +845,7 @@ struct scu_task_context { /** * This field is used by the SCU TL to determine when to take a snapshot when - * tranmitting read data frames. + * transmitting read data frames. * - 0x00 The entire IO * - 0x01 32k * - 0x02 64k diff --git a/drivers/scsi/lpfc/lpfc_hw.h b/drivers/scsi/lpfc/lpfc_hw.h index 663755842e4a..aaea3e31944d 100644 --- a/drivers/scsi/lpfc/lpfc_hw.h +++ b/drivers/scsi/lpfc/lpfc_hw.h @@ -365,7 +365,7 @@ struct lpfc_name { uint8_t IEEE[6]; /* FC IEEE address */ } s; uint8_t wwn[8]; - uint64_t name; + uint64_t name __packed __aligned(4); } u; }; @@ -850,7 +850,7 @@ typedef struct _ADISC { /* Structure is in Big Endian format */ struct lpfc_name portName; struct lpfc_name nodeName; uint32_t DID; -} __packed ADISC; +} ADISC; typedef struct _FARP { /* Structure is in Big Endian format */ uint32_t Mflags:8; @@ -880,7 +880,7 @@ typedef struct _FAN { /* Structure is in Big Endian format */ uint32_t Fdid; struct lpfc_name FportName; struct lpfc_name FnodeName; -} __packed FAN; +} FAN; typedef struct _SCR { /* Structure is in Big Endian format */ uint8_t resvd1; @@ -924,7 +924,7 @@ typedef struct _RNID { /* Structure is in Big Endian format */ union { RNID_TOP_DISC topologyDisc; /* topology disc (0xdf) */ } un; -} __packed RNID; +} RNID; struct RLS { /* Structure is in Big Endian format */ uint32_t rls; @@ -1514,7 +1514,7 @@ struct lpfc_fdmi_hba_ident { struct lpfc_fdmi_reg_port_list { __be32 EntryCnt; struct lpfc_fdmi_port_entry pe; -} __packed; +}; /* * Register HBA(RHBA) diff --git a/drivers/scsi/ncr53c8xx.c b/drivers/scsi/ncr53c8xx.c index 4458449c960b..35869b4f9329 100644 --- a/drivers/scsi/ncr53c8xx.c +++ b/drivers/scsi/ncr53c8xx.c @@ -4555,7 +4555,7 @@ static void ncr_detach(struct ncb *np) char inst_name[16]; /* Local copy so we don't access np after freeing it! */ - strlcpy(inst_name, ncr_name(np), sizeof(inst_name)); + strscpy(inst_name, ncr_name(np), sizeof(inst_name)); printk("%s: releasing host resources\n", ncr_name(np)); diff --git a/drivers/scsi/scsi.c b/drivers/scsi/scsi.c index c4bf99a842f3..d0911bc28663 100644 --- a/drivers/scsi/scsi.c +++ b/drivers/scsi/scsi.c @@ -586,31 +586,22 @@ static bool scsi_cdl_check_cmd(struct scsi_device *sdev, u8 opcode, u16 sa, if ((buf[1] & 0x03) != 0x03) return false; - /* See SPC-6, one command format of REPORT SUPPORTED OPERATION CODES */ + /* + * See SPC-6, One_command parameter data format for + * REPORT SUPPORTED OPERATION CODES. We have the following cases + * depending on rwcdlp (buf[0] & 0x01) value: + * - rwcdlp == 0: then cdlp indicates support for the A mode page when + * it is equal to 1 and for the B mode page when it is + * equal to 2. + * - rwcdlp == 1: then cdlp indicates support for the T2A mode page + * when it is equal to 1 and for the T2B mode page when + * it is equal to 2. + * Overall, to detect support for command duration limits, we only need + * to check that cdlp is 1 or 2. + */ cdlp = (buf[1] & 0x18) >> 3; - if (buf[0] & 0x01) { - /* rwcdlp == 1 */ - switch (cdlp) { - case 0x01: - /* T2A page */ - return true; - case 0x02: - /* T2B page */ - return true; - } - } else { - /* rwcdlp == 0 */ - switch (cdlp) { - case 0x01: - /* A page */ - return true; - case 0x02: - /* B page */ - return true; - } - } - return false; + return cdlp == 0x01 || cdlp == 0x02; } /** diff --git a/drivers/scsi/smartpqi/smartpqi.h b/drivers/scsi/smartpqi/smartpqi.h index f960b5095d09..e392eaf5b2bf 100644 --- a/drivers/scsi/smartpqi/smartpqi.h +++ b/drivers/scsi/smartpqi/smartpqi.h @@ -982,12 +982,12 @@ struct report_phys_lun_16byte_wwid { struct report_phys_lun_8byte_wwid_list { struct report_lun_header header; - struct report_phys_lun_8byte_wwid lun_entries[1]; + struct report_phys_lun_8byte_wwid lun_entries[]; }; struct report_phys_lun_16byte_wwid_list { struct report_lun_header header; - struct report_phys_lun_16byte_wwid lun_entries[1]; + struct report_phys_lun_16byte_wwid lun_entries[]; }; struct raid_map_disk_data { diff --git a/drivers/scsi/smartpqi/smartpqi_init.c b/drivers/scsi/smartpqi/smartpqi_init.c index 19af36e9a16d..6aaaa7ebca37 100644 --- a/drivers/scsi/smartpqi/smartpqi_init.c +++ b/drivers/scsi/smartpqi/smartpqi_init.c @@ -1203,7 +1203,6 @@ static inline int pqi_report_phys_luns(struct pqi_ctrl_info *ctrl_info, void **b unsigned int i; u8 rpl_response_format; u32 num_physicals; - size_t rpl_16byte_wwid_list_length; void *rpl_list; struct report_lun_header *rpl_header; struct report_phys_lun_8byte_wwid_list *rpl_8byte_wwid_list; @@ -1232,9 +1231,9 @@ static inline int pqi_report_phys_luns(struct pqi_ctrl_info *ctrl_info, void **b rpl_8byte_wwid_list = rpl_list; num_physicals = get_unaligned_be32(&rpl_8byte_wwid_list->header.list_length) / sizeof(rpl_8byte_wwid_list->lun_entries[0]); - rpl_16byte_wwid_list_length = sizeof(struct report_lun_header) + (num_physicals * sizeof(struct report_phys_lun_16byte_wwid)); - rpl_16byte_wwid_list = kmalloc(rpl_16byte_wwid_list_length, GFP_KERNEL); + rpl_16byte_wwid_list = kmalloc(struct_size(rpl_16byte_wwid_list, lun_entries, + num_physicals), GFP_KERNEL); if (!rpl_16byte_wwid_list) return -ENOMEM; diff --git a/drivers/spi/Kconfig b/drivers/spi/Kconfig index abbd1fb5fbc0..8962b2557615 100644 --- a/drivers/spi/Kconfig +++ b/drivers/spi/Kconfig @@ -826,7 +826,7 @@ config SPI_RSPI SPI driver for Renesas RSPI and QSPI blocks. config SPI_RZV2M_CSI - tristate "Renesas RZV2M CSI controller" + tristate "Renesas RZ/V2M CSI controller" depends on ARCH_RENESAS || COMPILE_TEST help SPI driver for Renesas RZ/V2M Clocked Serial Interface (CSI) diff --git a/drivers/spi/spi-bcm-qspi.c b/drivers/spi/spi-bcm-qspi.c index 6b46a3b67c41..d91dfbe47aa5 100644 --- a/drivers/spi/spi-bcm-qspi.c +++ b/drivers/spi/spi-bcm-qspi.c @@ -1543,13 +1543,9 @@ int bcm_qspi_probe(struct platform_device *pdev, res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mspi"); - if (res) { - qspi->base[MSPI] = devm_ioremap_resource(dev, res); - if (IS_ERR(qspi->base[MSPI])) - return PTR_ERR(qspi->base[MSPI]); - } else { - return 0; - } + qspi->base[MSPI] = devm_ioremap_resource(dev, res); + if (IS_ERR(qspi->base[MSPI])) + return PTR_ERR(qspi->base[MSPI]); res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "bspi"); if (res) { diff --git a/drivers/spi/spi-bcm63xx-hsspi.c b/drivers/spi/spi-bcm63xx-hsspi.c index ee2528dad02d..9e218e143263 100644 --- a/drivers/spi/spi-bcm63xx-hsspi.c +++ b/drivers/spi/spi-bcm63xx-hsspi.c @@ -2,7 +2,7 @@ * Broadcom BCM63XX High Speed SPI Controller driver * * Copyright 2000-2010 Broadcom Corporation - * Copyright 2012-2013 Jonas Gorski <jogo@openwrt.org> + * Copyright 2012-2013 Jonas Gorski <jonas.gorski@gmail.com> * * Licensed under the GNU/GPL. See COPYING for details. */ diff --git a/drivers/spi/spi-bcmbca-hsspi.c b/drivers/spi/spi-bcmbca-hsspi.c index 8cbd01619789..ca1b4741e9f4 100644 --- a/drivers/spi/spi-bcmbca-hsspi.c +++ b/drivers/spi/spi-bcmbca-hsspi.c @@ -3,7 +3,7 @@ * Broadcom BCMBCA High Speed SPI Controller driver * * Copyright 2000-2010 Broadcom Corporation - * Copyright 2012-2013 Jonas Gorski <jogo@openwrt.org> + * Copyright 2012-2013 Jonas Gorski <jonas.gorski@gmail.com> * Copyright 2019-2022 Broadcom Ltd */ diff --git a/drivers/spi/spi-geni-qcom.c b/drivers/spi/spi-geni-qcom.c index 26ce959d98df..1df9d4844a68 100644 --- a/drivers/spi/spi-geni-qcom.c +++ b/drivers/spi/spi-geni-qcom.c @@ -1097,6 +1097,12 @@ static int spi_geni_probe(struct platform_device *pdev) if (mas->cur_xfer_mode == GENI_SE_FIFO) spi->set_cs = spi_geni_set_cs; + /* + * TX is required per GSI spec, see setup_gsi_xfer(). + */ + if (mas->cur_xfer_mode == GENI_GPI_DMA) + spi->flags = SPI_CONTROLLER_MUST_TX; + ret = request_irq(mas->irq, geni_spi_isr, 0, dev_name(dev), spi); if (ret) goto spi_geni_release_dma; diff --git a/drivers/target/target_core_iblock.c b/drivers/target/target_core_iblock.c index a3c5f3558a33..3d1b511ea284 100644 --- a/drivers/target/target_core_iblock.c +++ b/drivers/target/target_core_iblock.c @@ -888,7 +888,7 @@ static sense_reason_t iblock_execute_pr_out(struct se_cmd *cmd, u8 sa, u64 key, ret = ops->pr_preempt(bdev, key, sa_key, scsi_pr_type_to_block(type), - sa == PRO_PREEMPT ? false : true); + sa == PRO_PREEMPT_AND_ABORT); break; case PRO_RELEASE: if (!ops->pr_clear) { diff --git a/drivers/target/target_core_user.c b/drivers/target/target_core_user.c index 15ffc8d2ac7b..22cc6cac0ba2 100644 --- a/drivers/target/target_core_user.c +++ b/drivers/target/target_core_user.c @@ -2820,14 +2820,14 @@ static ssize_t tcmu_dev_config_store(struct config_item *item, const char *page, pr_err("Unable to reconfigure device\n"); return ret; } - strlcpy(udev->dev_config, page, TCMU_CONFIG_LEN); + strscpy(udev->dev_config, page, TCMU_CONFIG_LEN); ret = tcmu_update_uio_info(udev); if (ret) return ret; return count; } - strlcpy(udev->dev_config, page, TCMU_CONFIG_LEN); + strscpy(udev->dev_config, page, TCMU_CONFIG_LEN); return count; } diff --git a/drivers/ufs/core/ufshcd-priv.h b/drivers/ufs/core/ufshcd-priv.h index 9566a95aeed9..0f3bd943b58b 100644 --- a/drivers/ufs/core/ufshcd-priv.h +++ b/drivers/ufs/core/ufshcd-priv.h @@ -68,7 +68,6 @@ int ufshcd_mcq_decide_queue_depth(struct ufs_hba *hba); int ufshcd_mcq_memory_alloc(struct ufs_hba *hba); void ufshcd_mcq_make_queues_operational(struct ufs_hba *hba); void ufshcd_mcq_config_mac(struct ufs_hba *hba, u32 max_active_cmds); -void ufshcd_mcq_select_mcq_mode(struct ufs_hba *hba); u32 ufshcd_mcq_read_cqis(struct ufs_hba *hba, int i); void ufshcd_mcq_write_cqis(struct ufs_hba *hba, u32 val, int i); struct ufs_hw_queue *ufshcd_mcq_req_to_hwq(struct ufs_hba *hba, diff --git a/drivers/watchdog/Kconfig b/drivers/watchdog/Kconfig index f22138709bf5..ee97d89dfc11 100644 --- a/drivers/watchdog/Kconfig +++ b/drivers/watchdog/Kconfig @@ -304,6 +304,24 @@ config XILINX_WATCHDOG To compile this driver as a module, choose M here: the module will be called of_xilinx_wdt. +config XILINX_WINDOW_WATCHDOG + tristate "Xilinx window watchdog timer" + depends on HAS_IOMEM + depends on ARM64 + select WATCHDOG_CORE + help + Window watchdog driver for the versal_wwdt IP core. + Window watchdog timer(WWDT) contains closed(first) and + open(second) window with 32 bit width. Write to the watchdog + timer within predefined window periods of time. This means + a period that is not too soon and a period that is not too + late. The WWDT has to be restarted within the open window time. + If software tries to restart WWDT outside of the open window + time period, it generates a reset. + + To compile this driver as a module, choose M here: the + module will be called xilinx_wwdt. + config ZIIRAVE_WATCHDOG tristate "Zodiac RAVE Watchdog Timer" depends on I2C diff --git a/drivers/watchdog/Makefile b/drivers/watchdog/Makefile index b4c4ccf2d703..3633f5b98236 100644 --- a/drivers/watchdog/Makefile +++ b/drivers/watchdog/Makefile @@ -157,6 +157,7 @@ obj-$(CONFIG_M54xx_WATCHDOG) += m54xx_wdt.o # MicroBlaze Architecture obj-$(CONFIG_XILINX_WATCHDOG) += of_xilinx_wdt.o +obj-$(CONFIG_XILINX_WINDOW_WATCHDOG) += xilinx_wwdt.o # MIPS Architecture obj-$(CONFIG_ATH79_WDT) += ath79_wdt.o diff --git a/drivers/watchdog/ep93xx_wdt.c b/drivers/watchdog/ep93xx_wdt.c index 38e26f160b9a..59dfd7f6bf0b 100644 --- a/drivers/watchdog/ep93xx_wdt.c +++ b/drivers/watchdog/ep93xx_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Watchdog driver for Cirrus Logic EP93xx family of devices. * @@ -11,10 +12,6 @@ * Copyright (c) 2012 H Hartley Sweeten <hsweeten@visionengravers.com> * Convert to a platform device and use the watchdog framework API * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * This watchdog fires after 250msec, which is a too short interval * for us to rely on the user space daemon alone. So we ping the * wdt each ~200msec and eventually stop doing it if the user space diff --git a/drivers/watchdog/ibmasr.c b/drivers/watchdog/ibmasr.c index 4a22fe152086..6955c693b5fd 100644 --- a/drivers/watchdog/ibmasr.c +++ b/drivers/watchdog/ibmasr.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-1.0+ /* * IBM Automatic Server Restart driver. * @@ -6,8 +7,6 @@ * Based on driver written by Pete Reynolds. * Copyright (c) IBM Corporation, 1998-2004. * - * This software may be used and distributed according to the terms - * of the GNU Public License, incorporated herein by reference. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/watchdog/loongson1_wdt.c b/drivers/watchdog/loongson1_wdt.c index 3c651c50a98c..4ac7810a314d 100644 --- a/drivers/watchdog/loongson1_wdt.c +++ b/drivers/watchdog/loongson1_wdt.c @@ -5,6 +5,7 @@ #include <linux/clk.h> #include <linux/module.h> +#include <linux/of.h> #include <linux/platform_device.h> #include <linux/watchdog.h> @@ -112,7 +113,7 @@ static int ls1x_wdt_probe(struct platform_device *pdev) if (IS_ERR(drvdata->base)) return PTR_ERR(drvdata->base); - drvdata->clk = devm_clk_get_enabled(dev, pdev->name); + drvdata->clk = devm_clk_get_enabled(dev, NULL); if (IS_ERR(drvdata->clk)) return PTR_ERR(drvdata->clk); @@ -144,10 +145,20 @@ static int ls1x_wdt_probe(struct platform_device *pdev) return 0; } +#ifdef CONFIG_OF +static const struct of_device_id ls1x_wdt_dt_ids[] = { + { .compatible = "loongson,ls1b-wdt", }, + { .compatible = "loongson,ls1c-wdt", }, + { /* sentinel */ } +}; +MODULE_DEVICE_TABLE(of, ls1x_wdt_dt_ids); +#endif + static struct platform_driver ls1x_wdt_driver = { .probe = ls1x_wdt_probe, .driver = { .name = "ls1x-wdt", + .of_match_table = of_match_ptr(ls1x_wdt_dt_ids), }, }; diff --git a/drivers/watchdog/m54xx_wdt.c b/drivers/watchdog/m54xx_wdt.c index f388a769dbd3..062ea3e6497e 100644 --- a/drivers/watchdog/m54xx_wdt.c +++ b/drivers/watchdog/m54xx_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/watchdog/m54xx_wdt.c * @@ -11,9 +12,6 @@ * Copyright 2004 (c) MontaVista, Software, Inc. * Based on sa1100 driver, Copyright (C) 2000 Oleg Drokin <green@crimea.edu> * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/watchdog/max63xx_wdt.c b/drivers/watchdog/max63xx_wdt.c index 9e1541cfae0d..21935f9620e4 100644 --- a/drivers/watchdog/max63xx_wdt.c +++ b/drivers/watchdog/max63xx_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/char/watchdog/max63xx_wdt.c * @@ -5,10 +6,6 @@ * * Copyright (C) 2009 Marc Zyngier <maz@misterjones.org> * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. - * * This driver assumes the watchdog pins are memory mapped (as it is * the case for the Arcom Zeus). Should it be connected over GPIOs or * another interface, some abstraction will have to be introduced. diff --git a/drivers/watchdog/moxart_wdt.c b/drivers/watchdog/moxart_wdt.c index 6340a1f5f471..b7b1da3c932d 100644 --- a/drivers/watchdog/moxart_wdt.c +++ b/drivers/watchdog/moxart_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * MOXA ART SoCs watchdog driver. * @@ -5,9 +6,6 @@ * * Jonas Jensen <jonas.jensen@gmail.com> * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/clk.h> diff --git a/drivers/watchdog/octeon-wdt-nmi.S b/drivers/watchdog/octeon-wdt-nmi.S index 97f6eb7b5a8e..e308cc743920 100644 --- a/drivers/watchdog/octeon-wdt-nmi.S +++ b/drivers/watchdog/octeon-wdt-nmi.S @@ -1,8 +1,5 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ /* - * This file is subject to the terms and conditions of the GNU General Public - * License. See the file "COPYING" in the main directory of this archive - * for more details. - * * Copyright (C) 2007-2017 Cavium, Inc. */ #include <asm/asm.h> diff --git a/drivers/watchdog/orion_wdt.c b/drivers/watchdog/orion_wdt.c index 5ec2dd8fd5fa..1fe583e8a95b 100644 --- a/drivers/watchdog/orion_wdt.c +++ b/drivers/watchdog/orion_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * drivers/watchdog/orion_wdt.c * @@ -5,9 +6,6 @@ * * Author: Sylver Bruneau <sylver.bruneau@googlemail.com> * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt diff --git a/drivers/watchdog/rtd119x_wdt.c b/drivers/watchdog/rtd119x_wdt.c index 95c8d7abce42..984905695dde 100644 --- a/drivers/watchdog/rtd119x_wdt.c +++ b/drivers/watchdog/rtd119x_wdt.c @@ -1,9 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0+ /* * Realtek RTD129x watchdog * * Copyright (c) 2017 Andreas Färber * - * SPDX-License-Identifier: GPL-2.0+ */ #include <linux/bitops.h> diff --git a/drivers/watchdog/sbc_fitpc2_wdt.c b/drivers/watchdog/sbc_fitpc2_wdt.c index 13db71e16583..b8eb8d5ca1af 100644 --- a/drivers/watchdog/sbc_fitpc2_wdt.c +++ b/drivers/watchdog/sbc_fitpc2_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Watchdog driver for SBC-FITPC2 board * @@ -5,9 +6,6 @@ * * Adapted from the IXP2000 watchdog driver by Deepak Saxena. * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #define pr_fmt(fmt) KBUILD_MODNAME " WATCHDOG: " fmt diff --git a/drivers/watchdog/sp5100_tco.c b/drivers/watchdog/sp5100_tco.c index 14f8d8d90920..2bd3dc25cb03 100644 --- a/drivers/watchdog/sp5100_tco.c +++ b/drivers/watchdog/sp5100_tco.c @@ -96,7 +96,7 @@ static enum tco_reg_layout tco_reg_layout(struct pci_dev *dev) sp5100_tco_pci->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && sp5100_tco_pci->revision >= AMD_ZEN_SMBUS_PCI_REV) { return efch_mmio; - } else if (dev->vendor == PCI_VENDOR_ID_AMD && + } else if ((dev->vendor == PCI_VENDOR_ID_AMD || dev->vendor == PCI_VENDOR_ID_HYGON) && ((dev->device == PCI_DEVICE_ID_AMD_HUDSON2_SMBUS && dev->revision >= 0x41) || (dev->device == PCI_DEVICE_ID_AMD_KERNCZ_SMBUS && @@ -579,6 +579,8 @@ static const struct pci_device_id sp5100_tco_pci_tbl[] = { PCI_ANY_ID, }, { PCI_VENDOR_ID_AMD, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID, PCI_ANY_ID, }, + { PCI_VENDOR_ID_HYGON, PCI_DEVICE_ID_AMD_KERNCZ_SMBUS, PCI_ANY_ID, + PCI_ANY_ID, }, { 0, }, /* End of list */ }; MODULE_DEVICE_TABLE(pci, sp5100_tco_pci_tbl); diff --git a/drivers/watchdog/ts4800_wdt.c b/drivers/watchdog/ts4800_wdt.c index 0ea554c7cda5..0099403f4992 100644 --- a/drivers/watchdog/ts4800_wdt.c +++ b/drivers/watchdog/ts4800_wdt.c @@ -1,11 +1,9 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Watchdog driver for TS-4800 based boards * * Copyright (c) 2015 - Savoir-faire Linux * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/kernel.h> diff --git a/drivers/watchdog/ts72xx_wdt.c b/drivers/watchdog/ts72xx_wdt.c index bf918f5fa131..3d57670befe1 100644 --- a/drivers/watchdog/ts72xx_wdt.c +++ b/drivers/watchdog/ts72xx_wdt.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0-only /* * Watchdog driver for Technologic Systems TS-72xx based SBCs * (TS-7200, TS-7250 and TS-7260). These boards have external @@ -8,9 +9,6 @@ * * This driver is based on ep93xx_wdt and wm831x_wdt drivers. * - * This file is licensed under the terms of the GNU General Public - * License version 2. This program is licensed "as is" without any - * warranty of any kind, whether express or implied. */ #include <linux/platform_device.h> diff --git a/drivers/watchdog/xilinx_wwdt.c b/drivers/watchdog/xilinx_wwdt.c new file mode 100644 index 000000000000..2585038d5575 --- /dev/null +++ b/drivers/watchdog/xilinx_wwdt.c @@ -0,0 +1,201 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Window watchdog device driver for Xilinx Versal WWDT + * + * Copyright (C) 2022 - 2023, Advanced Micro Devices, Inc. + */ + +#include <linux/clk.h> +#include <linux/interrupt.h> +#include <linux/io.h> +#include <linux/ioport.h> +#include <linux/module.h> +#include <linux/of_device.h> +#include <linux/of_address.h> +#include <linux/watchdog.h> + +/* Max timeout is calculated at 100MHz source clock */ +#define XWWDT_DEFAULT_TIMEOUT 42 +#define XWWDT_MIN_TIMEOUT 1 + +/* Register offsets for the WWDT device */ +#define XWWDT_MWR_OFFSET 0x00 +#define XWWDT_ESR_OFFSET 0x04 +#define XWWDT_FCR_OFFSET 0x08 +#define XWWDT_FWR_OFFSET 0x0c +#define XWWDT_SWR_OFFSET 0x10 + +/* Master Write Control Register Masks */ +#define XWWDT_MWR_MASK BIT(0) + +/* Enable and Status Register Masks */ +#define XWWDT_ESR_WINT_MASK BIT(16) +#define XWWDT_ESR_WSW_MASK BIT(8) +#define XWWDT_ESR_WEN_MASK BIT(0) + +#define XWWDT_CLOSE_WINDOW_PERCENT 50 + +static int wwdt_timeout; +static int closed_window_percent; + +module_param(wwdt_timeout, int, 0); +MODULE_PARM_DESC(wwdt_timeout, + "Watchdog time in seconds. (default=" + __MODULE_STRING(XWWDT_DEFAULT_TIMEOUT) ")"); +module_param(closed_window_percent, int, 0); +MODULE_PARM_DESC(closed_window_percent, + "Watchdog closed window percentage. (default=" + __MODULE_STRING(XWWDT_CLOSE_WINDOW_PERCENT) ")"); +/** + * struct xwwdt_device - Watchdog device structure + * @base: base io address of WDT device + * @spinlock: spinlock for IO register access + * @xilinx_wwdt_wdd: watchdog device structure + * @freq: source clock frequency of WWDT + * @close_percent: Closed window percent + */ +struct xwwdt_device { + void __iomem *base; + spinlock_t spinlock; /* spinlock for register handling */ + struct watchdog_device xilinx_wwdt_wdd; + unsigned long freq; + u32 close_percent; +}; + +static int xilinx_wwdt_start(struct watchdog_device *wdd) +{ + struct xwwdt_device *xdev = watchdog_get_drvdata(wdd); + struct watchdog_device *xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd; + u64 time_out, closed_timeout, open_timeout; + u32 control_status_reg; + + /* Calculate timeout count */ + time_out = xdev->freq * wdd->timeout; + closed_timeout = (time_out * xdev->close_percent) / 100; + open_timeout = time_out - closed_timeout; + wdd->min_hw_heartbeat_ms = xdev->close_percent * 10 * wdd->timeout; + + spin_lock(&xdev->spinlock); + + iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET); + iowrite32(~(u32)XWWDT_ESR_WEN_MASK, xdev->base + XWWDT_ESR_OFFSET); + iowrite32((u32)closed_timeout, xdev->base + XWWDT_FWR_OFFSET); + iowrite32((u32)open_timeout, xdev->base + XWWDT_SWR_OFFSET); + + /* Enable the window watchdog timer */ + control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET); + control_status_reg |= XWWDT_ESR_WEN_MASK; + iowrite32(control_status_reg, xdev->base + XWWDT_ESR_OFFSET); + + spin_unlock(&xdev->spinlock); + + dev_dbg(xilinx_wwdt_wdd->parent, "Watchdog Started!\n"); + + return 0; +} + +static int xilinx_wwdt_keepalive(struct watchdog_device *wdd) +{ + struct xwwdt_device *xdev = watchdog_get_drvdata(wdd); + u32 control_status_reg; + + spin_lock(&xdev->spinlock); + + /* Enable write access control bit for the window watchdog */ + iowrite32(XWWDT_MWR_MASK, xdev->base + XWWDT_MWR_OFFSET); + + /* Trigger restart kick to watchdog */ + control_status_reg = ioread32(xdev->base + XWWDT_ESR_OFFSET); + control_status_reg |= XWWDT_ESR_WSW_MASK; + iowrite32(control_status_reg, xdev->base + XWWDT_ESR_OFFSET); + + spin_unlock(&xdev->spinlock); + + return 0; +} + +static const struct watchdog_info xilinx_wwdt_ident = { + .options = WDIOF_KEEPALIVEPING | + WDIOF_SETTIMEOUT, + .firmware_version = 1, + .identity = "xlnx_window watchdog", +}; + +static const struct watchdog_ops xilinx_wwdt_ops = { + .owner = THIS_MODULE, + .start = xilinx_wwdt_start, + .ping = xilinx_wwdt_keepalive, +}; + +static int xwwdt_probe(struct platform_device *pdev) +{ + struct watchdog_device *xilinx_wwdt_wdd; + struct device *dev = &pdev->dev; + struct xwwdt_device *xdev; + struct clk *clk; + int ret; + + xdev = devm_kzalloc(dev, sizeof(*xdev), GFP_KERNEL); + if (!xdev) + return -ENOMEM; + + xilinx_wwdt_wdd = &xdev->xilinx_wwdt_wdd; + xilinx_wwdt_wdd->info = &xilinx_wwdt_ident; + xilinx_wwdt_wdd->ops = &xilinx_wwdt_ops; + xilinx_wwdt_wdd->parent = dev; + + xdev->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(xdev->base)) + return PTR_ERR(xdev->base); + + clk = devm_clk_get_enabled(dev, NULL); + if (IS_ERR(clk)) + return PTR_ERR(clk); + + xdev->freq = clk_get_rate(clk); + if (!xdev->freq) + return -EINVAL; + + xilinx_wwdt_wdd->min_timeout = XWWDT_MIN_TIMEOUT; + xilinx_wwdt_wdd->timeout = XWWDT_DEFAULT_TIMEOUT; + xilinx_wwdt_wdd->max_hw_heartbeat_ms = 1000 * xilinx_wwdt_wdd->timeout; + + if (closed_window_percent == 0 || closed_window_percent >= 100) + xdev->close_percent = XWWDT_CLOSE_WINDOW_PERCENT; + else + xdev->close_percent = closed_window_percent; + + watchdog_init_timeout(xilinx_wwdt_wdd, wwdt_timeout, &pdev->dev); + spin_lock_init(&xdev->spinlock); + watchdog_set_drvdata(xilinx_wwdt_wdd, xdev); + watchdog_set_nowayout(xilinx_wwdt_wdd, 1); + + ret = devm_watchdog_register_device(dev, xilinx_wwdt_wdd); + if (ret) + return ret; + + dev_info(dev, "Xilinx window watchdog Timer with timeout %ds\n", + xilinx_wwdt_wdd->timeout); + + return 0; +} + +static const struct of_device_id xwwdt_of_match[] = { + { .compatible = "xlnx,versal-wwdt", }, + {}, +}; +MODULE_DEVICE_TABLE(of, xwwdt_of_match); + +static struct platform_driver xwwdt_driver = { + .probe = xwwdt_probe, + .driver = { + .name = "Xilinx window watchdog", + .of_match_table = xwwdt_of_match, + }, +}; + +module_platform_driver(xwwdt_driver); + +MODULE_AUTHOR("Neeli Srinivas <srinivas.neeli@amd.com>"); +MODULE_DESCRIPTION("Xilinx window watchdog driver"); +MODULE_LICENSE("GPL"); diff --git a/drivers/watchdog/ziirave_wdt.c b/drivers/watchdog/ziirave_wdt.c index 21ca08a694ee..5ed33df68e9a 100644 --- a/drivers/watchdog/ziirave_wdt.c +++ b/drivers/watchdog/ziirave_wdt.c @@ -731,7 +731,7 @@ static struct i2c_driver ziirave_wdt_driver = { .name = "ziirave_wdt", .of_match_table = zrv_wdt_of_match, }, - .probe_new = ziirave_wdt_probe, + .probe = ziirave_wdt_probe, .remove = ziirave_wdt_remove, .id_table = ziirave_wdt_id, }; diff --git a/drivers/xen/grant-dma-ops.c b/drivers/xen/grant-dma-ops.c index 9784a77fa3c9..76f6f26265a3 100644 --- a/drivers/xen/grant-dma-ops.c +++ b/drivers/xen/grant-dma-ops.c @@ -303,6 +303,8 @@ static struct device_node *xen_dt_get_node(struct device *dev) while (!pci_is_root_bus(bus)) bus = bus->parent; + if (!bus->bridge->parent) + return NULL; return of_node_get(bus->bridge->parent->of_node); } |