From a75a1dec037ff3de863375fa3a74569619667184 Mon Sep 17 00:00:00 2001 From: Ahmed Naseef Date: Tue, 9 Dec 2025 11:16:02 +0400 Subject: mtd: spinand: add support for Dosilicon DS35Q1GA/DS35M1GA Add support for Dosilicon DS35Q1GA (3.3V) and DS35M1GA (1.8V) SPI NAND. These are 1Gbit (128MB) devices with: - 2048 byte pages + 64 byte OOB - 64 pages per block, 1024 blocks - On-die 4-bit ECC per 512 byte sector The 64-byte OOB area is divided into 4 segments of 16 bytes, with each segment containing 8 bytes of user data (M2+M1) and 8 bytes of ECC parity (R1). This provides 30 bytes of usable OOB space after reserving 2 bytes for the bad block marker. Tested on Genexis Platinum 4410 (EcoNet EN751221) by writing known patterns to OOB and verifying ECC parity placement in R1 regions. Datasheet: https://www.dosilicon.com/resources/SPI%20NAND/DS35X1GAXXX_rev08.pdf Signed-off-by: Ahmed Naseef Signed-off-by: Miquel Raynal --- include/linux/mtd/spinand.h | 1 + 1 file changed, 1 insertion(+) (limited to 'include/linux') diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index ce76f5c632e1..c50a43b447d2 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -354,6 +354,7 @@ struct spinand_manufacturer { /* SPI NAND manufacturers */ extern const struct spinand_manufacturer alliancememory_spinand_manufacturer; extern const struct spinand_manufacturer ato_spinand_manufacturer; +extern const struct spinand_manufacturer dosilicon_spinand_manufacturer; extern const struct spinand_manufacturer esmt_8c_spinand_manufacturer; extern const struct spinand_manufacturer esmt_c8_spinand_manufacturer; extern const struct spinand_manufacturer fmsh_spinand_manufacturer; -- cgit v1.2.3 From f5a4aa643ee968137eea902aa321c58c14c256c7 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sat, 1 Nov 2025 12:15:24 -0700 Subject: dmaengine: dw_edma: correct kernel-doc warnings in Use the correct enum name in its kernel-doc heading. Add ending ':' to struct member names. Drop the @id: kernel-doc entry since there is no struct member named 'id'. edma.h:46: warning: expecting prototype for struct dw_edma_core_ops. Prototype was for struct dw_edma_plat_ops instead Warning: edma.h:101 struct member 'ops' not described in 'dw_edma_chip' Warning: edma.h:101 struct member 'flags' not described in 'dw_edma_chip' Warning: edma.h:101 struct member 'reg_base' not described in 'dw_edma_chip' Warning: edma.h:101 struct member 'll_wr_cnt' not described in 'dw_edma_chip' Warning: edma.h:101 struct member 'll_rd_cnt' not described in 'dw_edma_chip' Warning: edma.h:101 struct member 'll_region_wr' not described in 'dw_edma_chip' Warning: edma.h:101 struct member 'll_region_rd' not described in 'dw_edma_chip' Warning: edma.h:101 struct member 'dt_region_wr' not described in 'dw_edma_chip' Warning: edma.h:101 struct member 'dt_region_rd' not described in 'dw_edma_chip' Warning: edma.h:101 struct member 'mf' not described in 'dw_edma_chip' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251101191524.1991135-1-rdunlap@infradead.org Signed-off-by: Vinod Koul --- include/linux/dma/edma.h | 24 +++++++++++------------- 1 file changed, 11 insertions(+), 13 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dma/edma.h b/include/linux/dma/edma.h index 3080747689f6..270b5458aecf 100644 --- a/include/linux/dma/edma.h +++ b/include/linux/dma/edma.h @@ -27,7 +27,7 @@ struct dw_edma_region { }; /** - * struct dw_edma_core_ops - platform-specific eDMA methods + * struct dw_edma_plat_ops - platform-specific eDMA methods * @irq_vector: Get IRQ number of the passed eDMA channel. Note the * method accepts the channel id in the end-to-end * numbering with the eDMA write channels being placed @@ -63,19 +63,17 @@ enum dw_edma_chip_flags { /** * struct dw_edma_chip - representation of DesignWare eDMA controller hardware * @dev: struct device of the eDMA controller - * @id: instance ID * @nr_irqs: total number of DMA IRQs - * @ops DMA channel to IRQ number mapping - * @flags dw_edma_chip_flags - * @reg_base DMA register base address - * @ll_wr_cnt DMA write link list count - * @ll_rd_cnt DMA read link list count - * @rg_region DMA register region - * @ll_region_wr DMA descriptor link list memory for write channel - * @ll_region_rd DMA descriptor link list memory for read channel - * @dt_region_wr DMA data memory for write channel - * @dt_region_rd DMA data memory for read channel - * @mf DMA register map format + * @ops: DMA channel to IRQ number mapping + * @flags: dw_edma_chip_flags + * @reg_base: DMA register base address + * @ll_wr_cnt: DMA write link list count + * @ll_rd_cnt: DMA read link list count + * @ll_region_wr: DMA descriptor link list memory for write channel + * @ll_region_rd: DMA descriptor link list memory for read channel + * @dt_region_wr: DMA data memory for write channel + * @dt_region_rd: DMA data memory for read channel + * @mf: DMA register map format * @dw: struct dw_edma that is filled by dw_edma_probe() */ struct dw_edma_chip { -- cgit v1.2.3 From de4761fb57f6a71eeb5a4c1167ae3606b08d8f59 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 3 Nov 2025 16:20:01 -0800 Subject: dmaengine: shdma: correct most kernel-doc issues in shdma-base.h Fix kernel-doc comments in include/linux/shdma-base.h to avoid most warnings: - prefix an enum name with "enum" - prefix enum values with '@' - prefix struct member names with '@' shdma-base.h:28: warning: cannot understand function prototype: 'enum shdma_pm_state ' Warning: shdma-base.h:103 struct member 'desc_completed' not described in 'shdma_ops' Warning: shdma-base.h:103 struct member 'halt_channel' not described in 'shdma_ops' Warning: shdma-base.h:103 struct member 'channel_busy' not described in 'shdma_ops' Warning: shdma-base.h:103 struct member 'slave_addr' not described in 'shdma_ops' Warning: shdma-base.h:103 struct member 'desc_setup' not described in 'shdma_ops' Warning: shdma-base.h:103 struct member 'set_slave' not described in 'shdma_ops' Warning: shdma-base.h:103 struct member 'setup_xfer' not described in 'shdma_ops' Warning: shdma-base.h:103 struct member 'start_xfer' not described in 'shdma_ops' Warning: shdma-base.h:103 struct member 'embedded_desc' not described in 'shdma_ops' Warning: shdma-base.h:103 struct member 'chan_irq' not described in 'shdma_ops' This one is not fixed: from 4f46f8ac80416: Warning: shdma-base.h:103 struct member 'get_partial' not described in 'shdma_ops' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251104002001.445297-1-rdunlap@infradead.org Signed-off-by: Vinod Koul --- include/linux/shdma-base.h | 28 ++++++++++++++-------------- 1 file changed, 14 insertions(+), 14 deletions(-) (limited to 'include/linux') diff --git a/include/linux/shdma-base.h b/include/linux/shdma-base.h index 03ba4dab2ef7..b6827c06d332 100644 --- a/include/linux/shdma-base.h +++ b/include/linux/shdma-base.h @@ -19,11 +19,11 @@ #include /** - * shdma_pm_state - DMA channel PM state - * SHDMA_PM_ESTABLISHED: either idle or during data transfer - * SHDMA_PM_BUSY: during the transfer preparation, when we have to + * enum shdma_pm_state - DMA channel PM state + * @SHDMA_PM_ESTABLISHED: either idle or during data transfer + * @SHDMA_PM_BUSY: during the transfer preparation, when we have to * drop the lock temporarily - * SHDMA_PM_PENDING: transfers pending + * @SHDMA_PM_PENDING: transfers pending */ enum shdma_pm_state { SHDMA_PM_ESTABLISHED, @@ -74,18 +74,18 @@ struct shdma_chan { /** * struct shdma_ops - simple DMA driver operations - * desc_completed: return true, if this is the descriptor, that just has + * @desc_completed: return true, if this is the descriptor, that just has * completed (atomic) - * halt_channel: stop DMA channel operation (atomic) - * channel_busy: return true, if the channel is busy (atomic) - * slave_addr: return slave DMA address - * desc_setup: set up the hardware specific descriptor portion (atomic) - * set_slave: bind channel to a slave - * setup_xfer: configure channel hardware for operation (atomic) - * start_xfer: start the DMA transfer (atomic) - * embedded_desc: return Nth struct shdma_desc pointer from the + * @halt_channel: stop DMA channel operation (atomic) + * @channel_busy: return true, if the channel is busy (atomic) + * @slave_addr: return slave DMA address + * @desc_setup: set up the hardware specific descriptor portion (atomic) + * @set_slave: bind channel to a slave + * @setup_xfer: configure channel hardware for operation (atomic) + * @start_xfer: start the DMA transfer (atomic) + * @embedded_desc: return Nth struct shdma_desc pointer from the * descriptor array - * chan_irq: process channel IRQ, return true if a transfer has + * @chan_irq: process channel IRQ, return true if a transfer has * completed (atomic) */ struct shdma_ops { -- cgit v1.2.3 From 4dee13db29de6dd869af9b3827e1ff569644e838 Mon Sep 17 00:00:00 2001 From: Chaoyi Chen Date: Mon, 8 Dec 2025 09:54:51 +0800 Subject: usb: typec: Export typec bus and typec altmode device type The DRM may want to register a notifier on the typec bus to know when a typec altmode device is added, and distinguish between different types of typec altmode device. Export these things. Signed-off-by: Chaoyi Chen Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20251208015500.94-3-kernel@airkyi.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/bus.c | 1 + drivers/usb/typec/bus.h | 10 ---------- drivers/usb/typec/class.c | 3 +++ include/linux/usb/typec.h | 3 +++ include/linux/usb/typec_altmode.h | 8 ++++++++ 5 files changed, 15 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/typec/bus.c b/drivers/usb/typec/bus.c index a33da7f458a5..e84b134a3381 100644 --- a/drivers/usb/typec/bus.c +++ b/drivers/usb/typec/bus.c @@ -569,3 +569,4 @@ const struct bus_type typec_bus = { .probe = typec_probe, .remove = typec_remove, }; +EXPORT_SYMBOL_GPL(typec_bus); diff --git a/drivers/usb/typec/bus.h b/drivers/usb/typec/bus.h index b58e131450d1..7df5deb1dd3a 100644 --- a/drivers/usb/typec/bus.h +++ b/drivers/usb/typec/bus.h @@ -5,7 +5,6 @@ #include -struct bus_type; struct typec_mux; struct typec_retimer; @@ -28,13 +27,4 @@ struct altmode { #define to_altmode(d) container_of(d, struct altmode, adev) -extern const struct bus_type typec_bus; -extern const struct device_type typec_port_altmode_dev_type; -extern const struct device_type typec_plug_altmode_dev_type; -extern const struct device_type typec_partner_altmode_dev_type; - -#define is_typec_port_altmode(dev) ((dev)->type == &typec_port_altmode_dev_type) -#define is_typec_plug_altmode(dev) ((dev)->type == &typec_plug_altmode_dev_type) -#define is_typec_partner_altmode(dev) ((dev)->type == &typec_partner_altmode_dev_type) - #endif /* __USB_TYPEC_ALTMODE_H__ */ diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index d6b88317f8a4..c4ff4310ff58 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -537,18 +537,21 @@ const struct device_type typec_port_altmode_dev_type = { .groups = typec_altmode_groups, .release = typec_altmode_release, }; +EXPORT_SYMBOL_GPL(typec_port_altmode_dev_type); const struct device_type typec_plug_altmode_dev_type = { .name = "typec_plug_alternate_mode", .groups = typec_altmode_groups, .release = typec_altmode_release, }; +EXPORT_SYMBOL_GPL(typec_plug_altmode_dev_type); const struct device_type typec_partner_altmode_dev_type = { .name = "typec_partner_alternate_mode", .groups = typec_altmode_groups, .release = typec_altmode_release, }; +EXPORT_SYMBOL_GPL(typec_partner_altmode_dev_type); static struct typec_altmode * typec_register_altmode(struct device *parent, diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index 309251572e2e..c6fd46902fce 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -20,12 +20,15 @@ struct typec_port; struct typec_altmode_ops; struct typec_cable_ops; +struct bus_type; struct fwnode_handle; struct device; struct usb_power_delivery; struct usb_power_delivery_desc; +extern const struct bus_type typec_bus; + enum typec_port_type { TYPEC_PORT_SRC, TYPEC_PORT_SNK, diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index f7db3bd4c90e..9197a4637a93 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -9,6 +9,14 @@ #define MODE_DISCOVERY_MAX 6 +extern const struct device_type typec_port_altmode_dev_type; +extern const struct device_type typec_plug_altmode_dev_type; +extern const struct device_type typec_partner_altmode_dev_type; + +#define is_typec_port_altmode(dev) ((dev)->type == &typec_port_altmode_dev_type) +#define is_typec_plug_altmode(dev) ((dev)->type == &typec_plug_altmode_dev_type) +#define is_typec_partner_altmode(dev) ((dev)->type == &typec_partner_altmode_dev_type) + struct typec_altmode_ops; /** -- cgit v1.2.3 From dd1fbe324a548e8057d5f3c72ce1a64a80f1753e Mon Sep 17 00:00:00 2001 From: Yi Cong Date: Tue, 2 Dec 2025 17:09:48 +0800 Subject: usb: linux/usb.h: Correct the description of the usb_device_driver member In the current kernel USB device driver code, only the name field is required to be provided; all other fields are optional. Correct this part of the description. Signed-off-by: Yi Cong Acked-by: Alan Stern Link: https://patch.msgid.link/20251202090948.334809-1-cong.yi@linux.dev Signed-off-by: Greg Kroah-Hartman --- include/linux/usb.h | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/usb.h b/include/linux/usb.h index e85105939af8..fbfcc70b07fb 100644 --- a/include/linux/usb.h +++ b/include/linux/usb.h @@ -1295,8 +1295,7 @@ struct usb_driver { * resume and suspend functions will be called in addition to the driver's * own, so this part of the setup does not need to be replicated. * - * USB drivers must provide all the fields listed above except driver, - * match, and id_table. + * USB device drivers must provide a name, other driver fields are optional. */ struct usb_device_driver { const char *name; -- cgit v1.2.3 From 6d71c62b13c33ea858ab298fe20beaec5736edc7 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 12 Dec 2025 09:09:06 +0100 Subject: serdev: Provide a bustype shutdown function MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit To prepare serdev driver to migrate away from struct device_driver::shutdown (and then eventually remove that callback) create a serdev driver shutdown callback and migration code to keep the existing behaviour. Note this introduces a warning for each driver at register time that isn't converted yet to that callback. Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/ab518883e3ed0976a19cb5b5b5faf42bd3a655b7.1765526117.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/serdev/core.c | 21 +++++++++++++++++++++ include/linux/serdev.h | 1 + 2 files changed, 22 insertions(+) (limited to 'include/linux') diff --git a/drivers/tty/serdev/core.c b/drivers/tty/serdev/core.c index b33e708cb245..40eedc15277c 100644 --- a/drivers/tty/serdev/core.c +++ b/drivers/tty/serdev/core.c @@ -414,11 +414,21 @@ static void serdev_drv_remove(struct device *dev) sdrv->remove(to_serdev_device(dev)); } +static void serdev_drv_shutdown(struct device *dev) +{ + const struct serdev_device_driver *sdrv = + to_serdev_device_driver(dev->driver); + + if (dev->driver && sdrv->shutdown) + sdrv->shutdown(to_serdev_device(dev)); +} + static const struct bus_type serdev_bus_type = { .name = "serial", .match = serdev_device_match, .probe = serdev_drv_probe, .remove = serdev_drv_remove, + .shutdown = serdev_drv_shutdown, }; /** @@ -814,6 +824,14 @@ void serdev_controller_remove(struct serdev_controller *ctrl) } EXPORT_SYMBOL_GPL(serdev_controller_remove); +static void serdev_legacy_shutdown(struct serdev_device *serdev) +{ + struct device *dev = &serdev->dev; + struct device_driver *driver = dev->driver; + + driver->shutdown(dev); +} + /** * __serdev_device_driver_register() - Register client driver with serdev core * @sdrv: client driver to be associated with client-device. @@ -830,6 +848,9 @@ int __serdev_device_driver_register(struct serdev_device_driver *sdrv, struct mo /* force drivers to async probe so I/O is possible in probe */ sdrv->driver.probe_type = PROBE_PREFER_ASYNCHRONOUS; + if (!sdrv->shutdown && sdrv->driver.shutdown) + sdrv->shutdown = serdev_legacy_shutdown; + return driver_register(&sdrv->driver); } EXPORT_SYMBOL_GPL(__serdev_device_driver_register); diff --git a/include/linux/serdev.h b/include/linux/serdev.h index 34562eb99931..5654c58eb73c 100644 --- a/include/linux/serdev.h +++ b/include/linux/serdev.h @@ -65,6 +65,7 @@ struct serdev_device_driver { struct device_driver driver; int (*probe)(struct serdev_device *); void (*remove)(struct serdev_device *); + void (*shutdown)(struct serdev_device *); }; static inline struct serdev_device_driver *to_serdev_device_driver(struct device_driver *d) -- cgit v1.2.3 From 247a357a9101b76d832b7e19d85ba8dcfb3e3e8d Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 19 Dec 2025 15:28:14 +0000 Subject: iio: buffer-dma: Turn iio_dma_buffer_init() void MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit iio_dma_buffer_init() always return 0. Therefore there's no point in returning int. While at it, fix a mismatch between the function declaration and definition regarding the struct device (dma_dev != dev). Signed-off-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dma.c | 6 ++---- include/linux/iio/buffer-dma.h | 4 ++-- 2 files changed, 4 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index 49d1bff5242a..ed187bb23708 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -826,8 +826,8 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_set_length, "IIO_DMA_BUFFER"); * should refer to the device that will perform the DMA to ensure that * allocations are done from a memory region that can be accessed by the device. */ -int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, - struct device *dev, const struct iio_dma_buffer_ops *ops) +void iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, struct device *dev, + const struct iio_dma_buffer_ops *ops) { iio_buffer_init(&queue->buffer); queue->buffer.length = PAGE_SIZE; @@ -839,8 +839,6 @@ int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, mutex_init(&queue->lock); spin_lock_init(&queue->list_lock); - - return 0; } EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_init, "IIO_DMA_BUFFER"); diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h index 4f33e6a39797..cd2ba4bb7501 100644 --- a/include/linux/iio/buffer-dma.h +++ b/include/linux/iio/buffer-dma.h @@ -157,8 +157,8 @@ int iio_dma_buffer_set_bytes_per_datum(struct iio_buffer *buffer, size_t bpd); int iio_dma_buffer_set_length(struct iio_buffer *buffer, unsigned int length); int iio_dma_buffer_request_update(struct iio_buffer *buffer); -int iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, - struct device *dma_dev, const struct iio_dma_buffer_ops *ops); +void iio_dma_buffer_init(struct iio_dma_buffer_queue *queue, struct device *dev, + const struct iio_dma_buffer_ops *ops); void iio_dma_buffer_exit(struct iio_dma_buffer_queue *queue); void iio_dma_buffer_release(struct iio_dma_buffer_queue *queue); -- cgit v1.2.3 From 0c1316b9521af0801e2502f390a89df5459094ed Mon Sep 17 00:00:00 2001 From: Nuno Sá Date: Fri, 19 Dec 2025 15:28:15 +0000 Subject: iio: buffer-dma: Fix coding style complains MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Just making sure checkpatch is happy. No functional change intended. Signed-off-by: Nuno Sá Signed-off-by: Jonathan Cameron --- drivers/iio/buffer/industrialio-buffer-dma.c | 24 +++++++++++------------- include/linux/iio/buffer-dma.h | 16 ++++++++++------ 2 files changed, 21 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/buffer/industrialio-buffer-dma.c b/drivers/iio/buffer/industrialio-buffer-dma.c index ed187bb23708..1c94b334f987 100644 --- a/drivers/iio/buffer/industrialio-buffer-dma.c +++ b/drivers/iio/buffer/industrialio-buffer-dma.c @@ -169,8 +169,9 @@ static struct iio_dma_buffer_queue *iio_buffer_to_queue(struct iio_buffer *buf) return container_of(buf, struct iio_dma_buffer_queue, buffer); } -static struct iio_dma_buffer_block *iio_dma_buffer_alloc_block( - struct iio_dma_buffer_queue *queue, size_t size, bool fileio) +static struct iio_dma_buffer_block * +iio_dma_buffer_alloc_block(struct iio_dma_buffer_queue *queue, size_t size, + bool fileio) { struct iio_dma_buffer_block *block __free(kfree) = kzalloc(sizeof(*block), GFP_KERNEL); @@ -254,7 +255,7 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_block_done, "IIO_DMA_BUFFER"); * hand the blocks back to the queue. */ void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue, - struct list_head *list) + struct list_head *list) { struct iio_dma_buffer_block *block, *_block; bool cookie; @@ -434,7 +435,7 @@ static void iio_dma_buffer_fileio_free(struct iio_dma_buffer_queue *queue) } static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue, - struct iio_dma_buffer_block *block) + struct iio_dma_buffer_block *block) { int ret; @@ -478,8 +479,7 @@ static void iio_dma_buffer_submit_block(struct iio_dma_buffer_queue *queue, * * This will allocate the DMA buffers and start the DMA transfers. */ -int iio_dma_buffer_enable(struct iio_buffer *buffer, - struct iio_dev *indio_dev) +int iio_dma_buffer_enable(struct iio_buffer *buffer, struct iio_dev *indio_dev) { struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer); struct iio_dma_buffer_block *block, *_block; @@ -503,8 +503,7 @@ EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_enable, "IIO_DMA_BUFFER"); * Needs to be called when the device that the buffer is attached to stops * sampling. Typically should be the iio_buffer_access_ops disable callback. */ -int iio_dma_buffer_disable(struct iio_buffer *buffer, - struct iio_dev *indio_dev) +int iio_dma_buffer_disable(struct iio_buffer *buffer, struct iio_dev *indio_dev) { struct iio_dma_buffer_queue *queue = iio_buffer_to_queue(buffer); @@ -519,7 +518,7 @@ int iio_dma_buffer_disable(struct iio_buffer *buffer, EXPORT_SYMBOL_NS_GPL(iio_dma_buffer_disable, "IIO_DMA_BUFFER"); static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue, - struct iio_dma_buffer_block *block) + struct iio_dma_buffer_block *block) { if (block->state == IIO_BLOCK_STATE_DEAD) { iio_buffer_block_put(block); @@ -531,8 +530,8 @@ static void iio_dma_buffer_enqueue(struct iio_dma_buffer_queue *queue, } } -static struct iio_dma_buffer_block *iio_dma_buffer_dequeue( - struct iio_dma_buffer_queue *queue) +static struct iio_dma_buffer_block * +iio_dma_buffer_dequeue(struct iio_dma_buffer_queue *queue) { struct iio_dma_buffer_block *block; unsigned int idx; @@ -661,8 +660,7 @@ size_t iio_dma_buffer_usage(struct iio_buffer *buf) for (i = 0; i < ARRAY_SIZE(queue->fileio.blocks); i++) { block = queue->fileio.blocks[i]; - if (block != queue->fileio.active_block - && block->state == IIO_BLOCK_STATE_DONE) + if (block != queue->fileio.active_block && block->state == IIO_BLOCK_STATE_DONE) data_available += block->size; } diff --git a/include/linux/iio/buffer-dma.h b/include/linux/iio/buffer-dma.h index cd2ba4bb7501..ef8687a88b73 100644 --- a/include/linux/iio/buffer-dma.h +++ b/include/linux/iio/buffer-dma.h @@ -119,7 +119,12 @@ struct iio_dma_buffer_queue { struct device *dev; const struct iio_dma_buffer_ops *ops; + /* + * A mutex to protect accessing, configuring (eg: enqueuing DMA blocks) + * and do file IO on struct iio_dma_buffer_queue objects. + */ struct mutex lock; + /* A spin lock to protect adding/removing blocks to the queue list */ spinlock_t list_lock; struct list_head incoming; @@ -136,20 +141,19 @@ struct iio_dma_buffer_queue { */ struct iio_dma_buffer_ops { int (*submit)(struct iio_dma_buffer_queue *queue, - struct iio_dma_buffer_block *block); + struct iio_dma_buffer_block *block); void (*abort)(struct iio_dma_buffer_queue *queue); }; void iio_dma_buffer_block_done(struct iio_dma_buffer_block *block); void iio_dma_buffer_block_list_abort(struct iio_dma_buffer_queue *queue, - struct list_head *list); + struct list_head *list); -int iio_dma_buffer_enable(struct iio_buffer *buffer, - struct iio_dev *indio_dev); +int iio_dma_buffer_enable(struct iio_buffer *buffer, struct iio_dev *indio_dev); int iio_dma_buffer_disable(struct iio_buffer *buffer, - struct iio_dev *indio_dev); + struct iio_dev *indio_dev); int iio_dma_buffer_read(struct iio_buffer *buffer, size_t n, - char __user *user_buffer); + char __user *user_buffer); int iio_dma_buffer_write(struct iio_buffer *buffer, size_t n, const char __user *user_buffer); size_t iio_dma_buffer_usage(struct iio_buffer *buffer); -- cgit v1.2.3 From 3285c471d0c0b991e5efc96c1a8bcc9ace17b9b8 Mon Sep 17 00:00:00 2001 From: James Clark Date: Fri, 28 Nov 2025 11:55:22 +0000 Subject: coresight: Remove misleading definitions ETM_OPT_* definitions duplicate the PMU format attributes that have always been published in sysfs. Hardcoding them here makes it misleading as to what the 'real' PMU API is and prevents attributes from being rearranged in the future. ETM4_CFG_BIT_* definitions just define what the Arm Architecture is which is not the responsibility of the kernel to do and doesn't scale to other registers or versions of ETM. It's not an actual software ABI/API and these definitions here mislead that it is. Any tools using the first ones would be broken anyway as they won't work when attributes are moved, so removing them is the right thing to do and will prompt a fix. Tools using the second ones can trivially redefine them locally. Perf also has its own copy of the headers so both of these things can be fixed up at a later date. Reviewed-by: Leo Yan Reviewed-by: Mike Leach Tested-by: Leo Yan Signed-off-by: James Clark Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20251128-james-cs-syncfreq-v8-10-4d319764cc58@linaro.org --- include/linux/coresight-pmu.h | 24 ------------------------ 1 file changed, 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/coresight-pmu.h b/include/linux/coresight-pmu.h index 89b0ac0014b0..2e179abe472a 100644 --- a/include/linux/coresight-pmu.h +++ b/include/linux/coresight-pmu.h @@ -21,30 +21,6 @@ */ #define CORESIGHT_LEGACY_CPU_TRACE_ID(cpu) (0x10 + (cpu * 2)) -/* - * Below are the definition of bit offsets for perf option, and works as - * arbitrary values for all ETM versions. - * - * Most of them are orignally from ETMv3.5/PTM's ETMCR config, therefore, - * ETMv3.5/PTM doesn't define ETMCR config bits with prefix "ETM3_" and - * directly use below macros as config bits. - */ -#define ETM_OPT_BRANCH_BROADCAST 8 -#define ETM_OPT_CYCACC 12 -#define ETM_OPT_CTXTID 14 -#define ETM_OPT_CTXTID2 15 -#define ETM_OPT_TS 28 -#define ETM_OPT_RETSTK 29 - -/* ETMv4 CONFIGR programming bits for the ETM OPTs */ -#define ETM4_CFG_BIT_BB 3 -#define ETM4_CFG_BIT_CYCACC 4 -#define ETM4_CFG_BIT_CTXTID 6 -#define ETM4_CFG_BIT_VMID 7 -#define ETM4_CFG_BIT_TS 11 -#define ETM4_CFG_BIT_RETSTK 12 -#define ETM4_CFG_BIT_VMID_OPT 15 - /* * Interpretation of the PERF_RECORD_AUX_OUTPUT_HW_ID payload. * Used to associate a CPU with the CoreSight Trace ID. -- cgit v1.2.3 From 478f3890709a092a97a0218f61af19ac9b725573 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Mon, 15 Dec 2025 18:49:25 +0100 Subject: soundwire: Make remove function return no value MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit All remove functions return zero and the driver core ignores any other returned value (just emits a warning about it being ignored). So make all remove callbacks return void instead of an ignored int. This is in line with most other subsystems. Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/20251215174925.1327021-5-u.kleine-koenig@baylibre.com Signed-off-by: Vinod Koul --- drivers/soundwire/bus_type.c | 5 ++--- include/linux/soundwire/sdw.h | 2 +- sound/soc/codecs/cs35l56-sdw.c | 4 +--- sound/soc/codecs/cs42l42-sdw.c | 4 +--- sound/soc/codecs/max98373-sdw.c | 4 +--- sound/soc/codecs/pm4125-sdw.c | 4 +--- sound/soc/codecs/rt1017-sdca-sdw.c | 4 +--- sound/soc/codecs/rt1308-sdw.c | 4 +--- sound/soc/codecs/rt1316-sdw.c | 4 +--- sound/soc/codecs/rt1318-sdw.c | 4 +--- sound/soc/codecs/rt1320-sdw.c | 4 +--- sound/soc/codecs/rt5682-sdw.c | 4 +--- sound/soc/codecs/rt700-sdw.c | 4 +--- sound/soc/codecs/rt711-sdca-sdw.c | 4 +--- sound/soc/codecs/rt711-sdw.c | 4 +--- sound/soc/codecs/rt712-sdca-dmic.c | 4 +--- sound/soc/codecs/rt712-sdca-sdw.c | 4 +--- sound/soc/codecs/rt715-sdca-sdw.c | 4 +--- sound/soc/codecs/rt715-sdw.c | 4 +--- sound/soc/codecs/rt721-sdca-sdw.c | 4 +--- sound/soc/codecs/rt722-sdca-sdw.c | 4 +--- sound/soc/codecs/tas2783-sdw.c | 4 +--- sound/soc/codecs/wcd937x-sdw.c | 4 +--- sound/soc/codecs/wcd938x-sdw.c | 4 +--- sound/soc/codecs/wcd939x-sdw.c | 4 +--- 25 files changed, 26 insertions(+), 73 deletions(-) (limited to 'include/linux') diff --git a/drivers/soundwire/bus_type.c b/drivers/soundwire/bus_type.c index 91e70cb46fb5..c40de98f1485 100644 --- a/drivers/soundwire/bus_type.c +++ b/drivers/soundwire/bus_type.c @@ -168,7 +168,6 @@ static int sdw_drv_remove(struct device *dev) { struct sdw_slave *slave = dev_to_sdw_dev(dev); struct sdw_driver *drv = drv_to_sdw_driver(dev->driver); - int ret = 0; mutex_lock(&slave->sdw_dev_lock); @@ -177,11 +176,11 @@ static int sdw_drv_remove(struct device *dev) mutex_unlock(&slave->sdw_dev_lock); if (drv->remove) - ret = drv->remove(slave); + drv->remove(slave); ida_free(&slave->bus->slave_ida, slave->index); - return ret; + return 0; } static void sdw_drv_shutdown(struct device *dev) diff --git a/include/linux/soundwire/sdw.h b/include/linux/soundwire/sdw.h index e6a3476bcef1..f462717acf20 100644 --- a/include/linux/soundwire/sdw.h +++ b/include/linux/soundwire/sdw.h @@ -705,7 +705,7 @@ struct sdw_master_device { struct sdw_driver { int (*probe)(struct sdw_slave *sdw, const struct sdw_device_id *id); - int (*remove)(struct sdw_slave *sdw); + void (*remove)(struct sdw_slave *sdw); void (*shutdown)(struct sdw_slave *sdw); const struct sdw_device_id *id_table; diff --git a/sound/soc/codecs/cs35l56-sdw.c b/sound/soc/codecs/cs35l56-sdw.c index 42d24ac2977f..30b3192d6ce9 100644 --- a/sound/soc/codecs/cs35l56-sdw.c +++ b/sound/soc/codecs/cs35l56-sdw.c @@ -554,7 +554,7 @@ static int cs35l56_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi return 0; } -static int cs35l56_sdw_remove(struct sdw_slave *peripheral) +static void cs35l56_sdw_remove(struct sdw_slave *peripheral) { struct cs35l56_private *cs35l56 = dev_get_drvdata(&peripheral->dev); @@ -566,8 +566,6 @@ static int cs35l56_sdw_remove(struct sdw_slave *peripheral) sdw_write_no_pm(peripheral, CS35L56_SDW_GEN_INT_STAT_1, 0xFF); cs35l56_remove(cs35l56); - - return 0; } static const struct dev_pm_ops cs35l56_sdw_pm = { diff --git a/sound/soc/codecs/cs42l42-sdw.c b/sound/soc/codecs/cs42l42-sdw.c index f837c7eff10b..d5999ad9ff9b 100644 --- a/sound/soc/codecs/cs42l42-sdw.c +++ b/sound/soc/codecs/cs42l42-sdw.c @@ -585,14 +585,12 @@ static int cs42l42_sdw_probe(struct sdw_slave *peripheral, const struct sdw_devi return 0; } -static int cs42l42_sdw_remove(struct sdw_slave *peripheral) +static void cs42l42_sdw_remove(struct sdw_slave *peripheral) { struct cs42l42_private *cs42l42 = dev_get_drvdata(&peripheral->dev); cs42l42_common_remove(cs42l42); pm_runtime_disable(cs42l42->dev); - - return 0; } static const struct dev_pm_ops cs42l42_sdw_pm = { diff --git a/sound/soc/codecs/max98373-sdw.c b/sound/soc/codecs/max98373-sdw.c index 88ff215f52b3..16673440218c 100644 --- a/sound/soc/codecs/max98373-sdw.c +++ b/sound/soc/codecs/max98373-sdw.c @@ -839,11 +839,9 @@ static int max98373_sdw_probe(struct sdw_slave *slave, return max98373_init(slave, regmap); } -static int max98373_sdw_remove(struct sdw_slave *slave) +static void max98373_sdw_remove(struct sdw_slave *slave) { pm_runtime_disable(&slave->dev); - - return 0; } #if defined(CONFIG_OF) diff --git a/sound/soc/codecs/pm4125-sdw.c b/sound/soc/codecs/pm4125-sdw.c index 3167b38e2876..1c612ae4a4b2 100644 --- a/sound/soc/codecs/pm4125-sdw.c +++ b/sound/soc/codecs/pm4125-sdw.c @@ -436,13 +436,11 @@ static int pm4125_probe(struct sdw_slave *pdev, const struct sdw_device_id *id) return 0; } -static int pm4125_remove(struct sdw_slave *pdev) +static void pm4125_remove(struct sdw_slave *pdev) { struct device *dev = &pdev->dev; component_del(dev, &wcd_sdw_component_ops); - - return 0; } static const struct sdw_device_id pm4125_slave_id[] = { diff --git a/sound/soc/codecs/rt1017-sdca-sdw.c b/sound/soc/codecs/rt1017-sdca-sdw.c index a9c000876be8..148b36173a25 100644 --- a/sound/soc/codecs/rt1017-sdca-sdw.c +++ b/sound/soc/codecs/rt1017-sdca-sdw.c @@ -741,14 +741,12 @@ static int rt1017_sdca_sdw_probe(struct sdw_slave *slave, return rt1017_sdca_init(&slave->dev, regmap, slave); } -static int rt1017_sdca_sdw_remove(struct sdw_slave *slave) +static void rt1017_sdca_sdw_remove(struct sdw_slave *slave) { struct rt1017_sdca_priv *rt1017 = dev_get_drvdata(&slave->dev); if (rt1017->first_hw_init) pm_runtime_disable(&slave->dev); - - return 0; } static const struct sdw_device_id rt1017_sdca_id[] = { diff --git a/sound/soc/codecs/rt1308-sdw.c b/sound/soc/codecs/rt1308-sdw.c index b6c224832a43..e077d096bc23 100644 --- a/sound/soc/codecs/rt1308-sdw.c +++ b/sound/soc/codecs/rt1308-sdw.c @@ -739,11 +739,9 @@ static int rt1308_sdw_probe(struct sdw_slave *slave, return rt1308_sdw_init(&slave->dev, regmap, slave); } -static int rt1308_sdw_remove(struct sdw_slave *slave) +static void rt1308_sdw_remove(struct sdw_slave *slave) { pm_runtime_disable(&slave->dev); - - return 0; } static const struct sdw_device_id rt1308_id[] = { diff --git a/sound/soc/codecs/rt1316-sdw.c b/sound/soc/codecs/rt1316-sdw.c index 01a977398864..20fc1579eb9c 100644 --- a/sound/soc/codecs/rt1316-sdw.c +++ b/sound/soc/codecs/rt1316-sdw.c @@ -716,11 +716,9 @@ static int rt1316_sdw_probe(struct sdw_slave *slave, return rt1316_sdw_init(&slave->dev, regmap, slave); } -static int rt1316_sdw_remove(struct sdw_slave *slave) +static void rt1316_sdw_remove(struct sdw_slave *slave) { pm_runtime_disable(&slave->dev); - - return 0; } static const struct sdw_device_id rt1316_id[] = { diff --git a/sound/soc/codecs/rt1318-sdw.c b/sound/soc/codecs/rt1318-sdw.c index 70db5450d6d2..d28f1afe68f1 100644 --- a/sound/soc/codecs/rt1318-sdw.c +++ b/sound/soc/codecs/rt1318-sdw.c @@ -793,11 +793,9 @@ static int rt1318_sdw_probe(struct sdw_slave *slave, return rt1318_sdw_init(&slave->dev, regmap, slave); } -static int rt1318_sdw_remove(struct sdw_slave *slave) +static void rt1318_sdw_remove(struct sdw_slave *slave) { pm_runtime_disable(&slave->dev); - - return 0; } static const struct sdw_device_id rt1318_id[] = { diff --git a/sound/soc/codecs/rt1320-sdw.c b/sound/soc/codecs/rt1320-sdw.c index e3f9b03df3aa..d03e8dbd0cda 100644 --- a/sound/soc/codecs/rt1320-sdw.c +++ b/sound/soc/codecs/rt1320-sdw.c @@ -1740,11 +1740,9 @@ static int rt1320_sdw_probe(struct sdw_slave *slave, return rt1320_sdw_init(&slave->dev, regmap, mbq_regmap, slave); } -static int rt1320_sdw_remove(struct sdw_slave *slave) +static void rt1320_sdw_remove(struct sdw_slave *slave) { pm_runtime_disable(&slave->dev); - - return 0; } /* diff --git a/sound/soc/codecs/rt5682-sdw.c b/sound/soc/codecs/rt5682-sdw.c index 055bea0a4a3b..fc464538ceff 100644 --- a/sound/soc/codecs/rt5682-sdw.c +++ b/sound/soc/codecs/rt5682-sdw.c @@ -690,7 +690,7 @@ static int rt5682_sdw_probe(struct sdw_slave *slave, return rt5682_sdw_init(&slave->dev, regmap, slave); } -static int rt5682_sdw_remove(struct sdw_slave *slave) +static void rt5682_sdw_remove(struct sdw_slave *slave) { struct rt5682_priv *rt5682 = dev_get_drvdata(&slave->dev); @@ -698,8 +698,6 @@ static int rt5682_sdw_remove(struct sdw_slave *slave) cancel_delayed_work_sync(&rt5682->jack_detect_work); pm_runtime_disable(&slave->dev); - - return 0; } static const struct sdw_device_id rt5682_id[] = { diff --git a/sound/soc/codecs/rt700-sdw.c b/sound/soc/codecs/rt700-sdw.c index 44543c0da177..9ce36a66fae1 100644 --- a/sound/soc/codecs/rt700-sdw.c +++ b/sound/soc/codecs/rt700-sdw.c @@ -455,7 +455,7 @@ static int rt700_sdw_probe(struct sdw_slave *slave, return rt700_init(&slave->dev, sdw_regmap, regmap, slave); } -static int rt700_sdw_remove(struct sdw_slave *slave) +static void rt700_sdw_remove(struct sdw_slave *slave) { struct rt700_priv *rt700 = dev_get_drvdata(&slave->dev); @@ -465,8 +465,6 @@ static int rt700_sdw_remove(struct sdw_slave *slave) } pm_runtime_disable(&slave->dev); - - return 0; } static const struct sdw_device_id rt700_id[] = { diff --git a/sound/soc/codecs/rt711-sdca-sdw.c b/sound/soc/codecs/rt711-sdca-sdw.c index 6eb05871db37..49dacceddf81 100644 --- a/sound/soc/codecs/rt711-sdca-sdw.c +++ b/sound/soc/codecs/rt711-sdca-sdw.c @@ -365,7 +365,7 @@ static int rt711_sdca_sdw_probe(struct sdw_slave *slave, return rt711_sdca_init(&slave->dev, regmap, mbq_regmap, slave); } -static int rt711_sdca_sdw_remove(struct sdw_slave *slave) +static void rt711_sdca_sdw_remove(struct sdw_slave *slave) { struct rt711_sdca_priv *rt711 = dev_get_drvdata(&slave->dev); @@ -378,8 +378,6 @@ static int rt711_sdca_sdw_remove(struct sdw_slave *slave) mutex_destroy(&rt711->calibrate_mutex); mutex_destroy(&rt711->disable_irq_lock); - - return 0; } static const struct sdw_device_id rt711_sdca_id[] = { diff --git a/sound/soc/codecs/rt711-sdw.c b/sound/soc/codecs/rt711-sdw.c index 93a5a89a96b1..72ddf4cebdf3 100644 --- a/sound/soc/codecs/rt711-sdw.c +++ b/sound/soc/codecs/rt711-sdw.c @@ -458,7 +458,7 @@ static int rt711_sdw_probe(struct sdw_slave *slave, return rt711_init(&slave->dev, sdw_regmap, regmap, slave); } -static int rt711_sdw_remove(struct sdw_slave *slave) +static void rt711_sdw_remove(struct sdw_slave *slave) { struct rt711_priv *rt711 = dev_get_drvdata(&slave->dev); @@ -472,8 +472,6 @@ static int rt711_sdw_remove(struct sdw_slave *slave) mutex_destroy(&rt711->calibrate_mutex); mutex_destroy(&rt711->disable_irq_lock); - - return 0; } static const struct sdw_device_id rt711_id[] = { diff --git a/sound/soc/codecs/rt712-sdca-dmic.c b/sound/soc/codecs/rt712-sdca-dmic.c index 2928649e80e4..4d83544ef204 100644 --- a/sound/soc/codecs/rt712-sdca-dmic.c +++ b/sound/soc/codecs/rt712-sdca-dmic.c @@ -960,11 +960,9 @@ static int rt712_sdca_dmic_sdw_probe(struct sdw_slave *slave, return rt712_sdca_dmic_init(&slave->dev, regmap, mbq_regmap, slave); } -static int rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave) +static void rt712_sdca_dmic_sdw_remove(struct sdw_slave *slave) { pm_runtime_disable(&slave->dev); - - return 0; } static struct sdw_driver rt712_sdca_dmic_sdw_driver = { diff --git a/sound/soc/codecs/rt712-sdca-sdw.c b/sound/soc/codecs/rt712-sdca-sdw.c index ea07131edfa2..8c82887174db 100644 --- a/sound/soc/codecs/rt712-sdca-sdw.c +++ b/sound/soc/codecs/rt712-sdca-sdw.c @@ -374,7 +374,7 @@ static int rt712_sdca_sdw_probe(struct sdw_slave *slave, return rt712_sdca_init(&slave->dev, regmap, mbq_regmap, slave); } -static int rt712_sdca_sdw_remove(struct sdw_slave *slave) +static void rt712_sdca_sdw_remove(struct sdw_slave *slave) { struct rt712_sdca_priv *rt712 = dev_get_drvdata(&slave->dev); @@ -387,8 +387,6 @@ static int rt712_sdca_sdw_remove(struct sdw_slave *slave) mutex_destroy(&rt712->calibrate_mutex); mutex_destroy(&rt712->disable_irq_lock); - - return 0; } static const struct sdw_device_id rt712_sdca_id[] = { diff --git a/sound/soc/codecs/rt715-sdca-sdw.c b/sound/soc/codecs/rt715-sdca-sdw.c index ce7d8955efc3..968bc183b8d8 100644 --- a/sound/soc/codecs/rt715-sdca-sdw.c +++ b/sound/soc/codecs/rt715-sdca-sdw.c @@ -191,11 +191,9 @@ static int rt715_sdca_sdw_probe(struct sdw_slave *slave, return rt715_sdca_init(&slave->dev, mbq_regmap, regmap, slave); } -static int rt715_sdca_sdw_remove(struct sdw_slave *slave) +static void rt715_sdca_sdw_remove(struct sdw_slave *slave) { pm_runtime_disable(&slave->dev); - - return 0; } static const struct sdw_device_id rt715_sdca_id[] = { diff --git a/sound/soc/codecs/rt715-sdw.c b/sound/soc/codecs/rt715-sdw.c index a3df4bbedf86..49c91d015be4 100644 --- a/sound/soc/codecs/rt715-sdw.c +++ b/sound/soc/codecs/rt715-sdw.c @@ -471,11 +471,9 @@ static int rt715_sdw_probe(struct sdw_slave *slave, return rt715_init(&slave->dev, sdw_regmap, regmap, slave); } -static int rt715_sdw_remove(struct sdw_slave *slave) +static void rt715_sdw_remove(struct sdw_slave *slave) { pm_runtime_disable(&slave->dev); - - return 0; } static const struct sdw_device_id rt715_id[] = { diff --git a/sound/soc/codecs/rt721-sdca-sdw.c b/sound/soc/codecs/rt721-sdca-sdw.c index 4d8a12b13015..6eb8512975b8 100644 --- a/sound/soc/codecs/rt721-sdca-sdw.c +++ b/sound/soc/codecs/rt721-sdca-sdw.c @@ -415,7 +415,7 @@ static int rt721_sdca_sdw_probe(struct sdw_slave *slave, return rt721_sdca_init(&slave->dev, regmap, mbq_regmap, slave); } -static int rt721_sdca_sdw_remove(struct sdw_slave *slave) +static void rt721_sdca_sdw_remove(struct sdw_slave *slave) { struct rt721_sdca_priv *rt721 = dev_get_drvdata(&slave->dev); @@ -429,8 +429,6 @@ static int rt721_sdca_sdw_remove(struct sdw_slave *slave) mutex_destroy(&rt721->calibrate_mutex); mutex_destroy(&rt721->disable_irq_lock); - - return 0; } static const struct sdw_device_id rt721_sdca_id[] = { diff --git a/sound/soc/codecs/rt722-sdca-sdw.c b/sound/soc/codecs/rt722-sdca-sdw.c index a0f5601a262a..0a5b3ffa90da 100644 --- a/sound/soc/codecs/rt722-sdca-sdw.c +++ b/sound/soc/codecs/rt722-sdca-sdw.c @@ -428,7 +428,7 @@ static int rt722_sdca_sdw_probe(struct sdw_slave *slave, return rt722_sdca_init(&slave->dev, regmap, slave); } -static int rt722_sdca_sdw_remove(struct sdw_slave *slave) +static void rt722_sdca_sdw_remove(struct sdw_slave *slave) { struct rt722_sdca_priv *rt722 = dev_get_drvdata(&slave->dev); @@ -442,8 +442,6 @@ static int rt722_sdca_sdw_remove(struct sdw_slave *slave) mutex_destroy(&rt722->calibrate_mutex); mutex_destroy(&rt722->disable_irq_lock); - - return 0; } static const struct sdw_device_id rt722_sdca_id[] = { diff --git a/sound/soc/codecs/tas2783-sdw.c b/sound/soc/codecs/tas2783-sdw.c index 43b779873b93..0b07981c3f2c 100644 --- a/sound/soc/codecs/tas2783-sdw.c +++ b/sound/soc/codecs/tas2783-sdw.c @@ -1310,7 +1310,7 @@ static s32 tas_sdw_probe(struct sdw_slave *peripheral, return tas_init(tas_dev); } -static s32 tas_sdw_remove(struct sdw_slave *peripheral) +static void tas_sdw_remove(struct sdw_slave *peripheral) { struct tas2783_prv *tas_dev = dev_get_drvdata(&peripheral->dev); @@ -1319,8 +1319,6 @@ static s32 tas_sdw_remove(struct sdw_slave *peripheral) mutex_destroy(&tas_dev->calib_lock); mutex_destroy(&tas_dev->pde_lock); dev_set_drvdata(&peripheral->dev, NULL); - - return 0; } static const struct sdw_device_id tas_sdw_id[] = { diff --git a/sound/soc/codecs/wcd937x-sdw.c b/sound/soc/codecs/wcd937x-sdw.c index 1878d67e3fa1..7a18bed7f347 100644 --- a/sound/soc/codecs/wcd937x-sdw.c +++ b/sound/soc/codecs/wcd937x-sdw.c @@ -1056,13 +1056,11 @@ static int wcd9370_probe(struct sdw_slave *pdev, return 0; } -static int wcd9370_remove(struct sdw_slave *pdev) +static void wcd9370_remove(struct sdw_slave *pdev) { struct device *dev = &pdev->dev; component_del(dev, &wcd_sdw_component_ops); - - return 0; } static const struct sdw_device_id wcd9370_slave_id[] = { diff --git a/sound/soc/codecs/wcd938x-sdw.c b/sound/soc/codecs/wcd938x-sdw.c index 8c8f39d04972..0f0cc0ac3056 100644 --- a/sound/soc/codecs/wcd938x-sdw.c +++ b/sound/soc/codecs/wcd938x-sdw.c @@ -1217,13 +1217,11 @@ static int wcd9380_probe(struct sdw_slave *pdev, return 0; } -static int wcd9380_remove(struct sdw_slave *pdev) +static void wcd9380_remove(struct sdw_slave *pdev) { struct device *dev = &pdev->dev; component_del(dev, &wcd_sdw_component_ops); - - return 0; } static const struct sdw_device_id wcd9380_slave_id[] = { diff --git a/sound/soc/codecs/wcd939x-sdw.c b/sound/soc/codecs/wcd939x-sdw.c index da342a0c95a5..965c768e7995 100644 --- a/sound/soc/codecs/wcd939x-sdw.c +++ b/sound/soc/codecs/wcd939x-sdw.c @@ -1414,7 +1414,7 @@ err_free_regmap: return ret; } -static int wcd9390_remove(struct sdw_slave *pdev) +static void wcd9390_remove(struct sdw_slave *pdev) { struct device *dev = &pdev->dev; struct wcd939x_sdw_priv *wcd = dev_get_drvdata(dev); @@ -1423,8 +1423,6 @@ static int wcd9390_remove(struct sdw_slave *pdev) if (wcd->regmap) regmap_exit(wcd->regmap); - - return 0; } static const struct sdw_device_id wcd9390_slave_id[] = { -- cgit v1.2.3 From e715bc42e337b6f54ada7262e1bbc0b7860525c2 Mon Sep 17 00:00:00 2001 From: Christophe JAILLET Date: Fri, 19 Dec 2025 18:16:15 +0100 Subject: usb: gadget: Constify struct configfs_item_operations and configfs_group_operations 'struct configfs_item_operations' and 'configfs_group_operations' are not modified in these drivers. Constifying these structures moves some data to a read-only section, so increases overall security, especially when the structure holds some function pointers. On a x86_64, with allmodconfig, as an example: Before: ====== text data bss dec hex filename 65061 20968 256 86285 1510d drivers/usb/gadget/configfs.o After: ===== text data bss dec hex filename 66181 19848 256 86285 1510d drivers/usb/gadget/configfs.o Signed-off-by: Christophe JAILLET Link: https://patch.msgid.link/49cec1cb84425f854de80b6d69b53a5a3cda8189.1766164523.git.christophe.jaillet@wanadoo.fr Signed-off-by: Greg Kroah-Hartman --- drivers/usb/gadget/configfs.c | 24 ++++++++--------- drivers/usb/gadget/function/f_acm.c | 2 +- drivers/usb/gadget/function/f_fs.c | 2 +- drivers/usb/gadget/function/f_hid.c | 2 +- drivers/usb/gadget/function/f_loopback.c | 2 +- drivers/usb/gadget/function/f_mass_storage.c | 6 ++--- drivers/usb/gadget/function/f_midi.c | 2 +- drivers/usb/gadget/function/f_midi2.c | 10 +++---- drivers/usb/gadget/function/f_obex.c | 2 +- drivers/usb/gadget/function/f_phonet.c | 2 +- drivers/usb/gadget/function/f_printer.c | 2 +- drivers/usb/gadget/function/f_serial.c | 2 +- drivers/usb/gadget/function/f_sourcesink.c | 2 +- drivers/usb/gadget/function/f_tcm.c | 2 +- drivers/usb/gadget/function/f_uac1.c | 2 +- drivers/usb/gadget/function/f_uac1_legacy.c | 2 +- drivers/usb/gadget/function/f_uac2.c | 2 +- drivers/usb/gadget/function/u_ether_configfs.h | 2 +- drivers/usb/gadget/function/uvc_configfs.c | 36 +++++++++++++------------- include/linux/usb/gadget_configfs.h | 4 +-- 20 files changed, 55 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/gadget/configfs.c b/drivers/usb/gadget/configfs.c index 6bcac85c5550..acef1c6f199c 100644 --- a/drivers/usb/gadget/configfs.c +++ b/drivers/usb/gadget/configfs.c @@ -409,7 +409,7 @@ static void gadget_info_attr_release(struct config_item *item) kfree(gi); } -static struct configfs_item_operations gadget_root_item_ops = { +static const struct configfs_item_operations gadget_root_item_ops = { .release = gadget_info_attr_release, }; @@ -514,7 +514,7 @@ static void config_usb_cfg_unlink( WARN(1, "Unable to locate function to unbind\n"); } -static struct configfs_item_operations gadget_config_item_ops = { +static const struct configfs_item_operations gadget_config_item_ops = { .release = gadget_config_attr_release, .allow_link = config_usb_cfg_link, .drop_link = config_usb_cfg_unlink, @@ -663,7 +663,7 @@ static void function_drop( config_item_put(item); } -static struct configfs_group_operations functions_ops = { +static const struct configfs_group_operations functions_ops = { .make_group = &function_make, .drop_item = &function_drop, }; @@ -766,7 +766,7 @@ static void config_desc_drop( config_item_put(item); } -static struct configfs_group_operations config_desc_ops = { +static const struct configfs_group_operations config_desc_ops = { .make_group = &config_desc_make, .drop_item = &config_desc_drop, }; @@ -799,7 +799,7 @@ static void gadget_language_attr_release(struct config_item *item) kfree(gs); } -static struct configfs_item_operations gadget_language_langid_item_ops = { +static const struct configfs_item_operations gadget_language_langid_item_ops = { .release = gadget_language_attr_release, }; @@ -852,7 +852,7 @@ static void gadget_string_release(struct config_item *item) kfree(string); } -static struct configfs_item_operations gadget_string_item_ops = { +static const struct configfs_item_operations gadget_string_item_ops = { .release = gadget_string_release, }; @@ -901,7 +901,7 @@ static void gadget_language_string_drop(struct config_group *group, string->usb_string.id = i++; } -static struct configfs_group_operations gadget_language_langid_group_ops = { +static const struct configfs_group_operations gadget_language_langid_group_ops = { .make_item = gadget_language_string_make, .drop_item = gadget_language_string_drop, }; @@ -960,7 +960,7 @@ static void gadget_language_drop(struct config_group *group, config_item_put(item); } -static struct configfs_group_operations gadget_language_group_ops = { +static const struct configfs_group_operations gadget_language_group_ops = { .make_group = &gadget_language_make, .drop_item = &gadget_language_drop, }; @@ -1266,7 +1266,7 @@ static void os_desc_unlink(struct config_item *os_desc_ci, mutex_unlock(&gi->lock); } -static struct configfs_item_operations os_desc_ops = { +static const struct configfs_item_operations os_desc_ops = { .allow_link = os_desc_link, .drop_link = os_desc_unlink, }; @@ -1391,7 +1391,7 @@ static void usb_os_desc_ext_prop_release(struct config_item *item) kfree(ext_prop); /* frees a whole chunk */ } -static struct configfs_item_operations ext_prop_ops = { +static const struct configfs_item_operations ext_prop_ops = { .release = usb_os_desc_ext_prop_release, }; @@ -1456,7 +1456,7 @@ static void ext_prop_drop(struct config_group *group, struct config_item *item) config_item_put(item); } -static struct configfs_group_operations interf_grp_ops = { +static const struct configfs_group_operations interf_grp_ops = { .make_item = &ext_prop_make, .drop_item = &ext_prop_drop, }; @@ -2061,7 +2061,7 @@ static void gadgets_drop(struct config_group *group, struct config_item *item) config_item_put(item); } -static struct configfs_group_operations gadgets_ops = { +static const struct configfs_group_operations gadgets_ops = { .make_group = &gadgets_make, .drop_item = &gadgets_drop, }; diff --git a/drivers/usb/gadget/function/f_acm.c b/drivers/usb/gadget/function/f_acm.c index 106046e17c4e..0ad857f1f325 100644 --- a/drivers/usb/gadget/function/f_acm.c +++ b/drivers/usb/gadget/function/f_acm.c @@ -793,7 +793,7 @@ static void acm_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations acm_item_ops = { +static const struct configfs_item_operations acm_item_ops = { .release = acm_attr_release, }; diff --git a/drivers/usb/gadget/function/f_fs.c b/drivers/usb/gadget/function/f_fs.c index 05c6750702b6..6a0a4d870865 100644 --- a/drivers/usb/gadget/function/f_fs.c +++ b/drivers/usb/gadget/function/f_fs.c @@ -4004,7 +4004,7 @@ static void ffs_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations ffs_item_ops = { +static const struct configfs_item_operations ffs_item_ops = { .release = ffs_attr_release, }; diff --git a/drivers/usb/gadget/function/f_hid.c b/drivers/usb/gadget/function/f_hid.c index 3ddfd4f66f0b..bee0d0458ff7 100644 --- a/drivers/usb/gadget/function/f_hid.c +++ b/drivers/usb/gadget/function/f_hid.c @@ -1328,7 +1328,7 @@ static void hid_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations hidg_item_ops = { +static const struct configfs_item_operations hidg_item_ops = { .release = hid_attr_release, }; diff --git a/drivers/usb/gadget/function/f_loopback.c b/drivers/usb/gadget/function/f_loopback.c index 49b009a7d5d7..39862e236837 100644 --- a/drivers/usb/gadget/function/f_loopback.c +++ b/drivers/usb/gadget/function/f_loopback.c @@ -464,7 +464,7 @@ static void lb_attr_release(struct config_item *item) usb_put_function_instance(&lb_opts->func_inst); } -static struct configfs_item_operations lb_item_ops = { +static const struct configfs_item_operations lb_item_ops = { .release = lb_attr_release, }; diff --git a/drivers/usb/gadget/function/f_mass_storage.c b/drivers/usb/gadget/function/f_mass_storage.c index 94d478b6bcd3..5c3f34a2f35c 100644 --- a/drivers/usb/gadget/function/f_mass_storage.c +++ b/drivers/usb/gadget/function/f_mass_storage.c @@ -3153,7 +3153,7 @@ static void fsg_lun_attr_release(struct config_item *item) kfree(lun_opts); } -static struct configfs_item_operations fsg_lun_item_ops = { +static const struct configfs_item_operations fsg_lun_item_ops = { .release = fsg_lun_attr_release, }; @@ -3369,7 +3369,7 @@ static void fsg_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations fsg_item_ops = { +static const struct configfs_item_operations fsg_item_ops = { .release = fsg_attr_release, }; @@ -3462,7 +3462,7 @@ static struct configfs_attribute *fsg_attrs[] = { NULL, }; -static struct configfs_group_operations fsg_group_ops = { +static const struct configfs_group_operations fsg_group_ops = { .make_group = fsg_lun_make, .drop_item = fsg_lun_drop, }; diff --git a/drivers/usb/gadget/function/f_midi.c b/drivers/usb/gadget/function/f_midi.c index ad679a6ecac1..f592f9eb85d4 100644 --- a/drivers/usb/gadget/function/f_midi.c +++ b/drivers/usb/gadget/function/f_midi.c @@ -1091,7 +1091,7 @@ static void midi_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations midi_item_ops = { +static const struct configfs_item_operations midi_item_ops = { .release = midi_attr_release, }; diff --git a/drivers/usb/gadget/function/f_midi2.c b/drivers/usb/gadget/function/f_midi2.c index de16b02d857e..95ec87bed3ee 100644 --- a/drivers/usb/gadget/function/f_midi2.c +++ b/drivers/usb/gadget/function/f_midi2.c @@ -2316,7 +2316,7 @@ static void f_midi2_block_opts_release(struct config_item *item) kfree(opts); } -static struct configfs_item_operations f_midi2_block_item_ops = { +static const struct configfs_item_operations f_midi2_block_item_ops = { .release = f_midi2_block_opts_release, }; @@ -2479,11 +2479,11 @@ static void f_midi2_ep_opts_release(struct config_item *item) kfree(opts); } -static struct configfs_item_operations f_midi2_ep_item_ops = { +static const struct configfs_item_operations f_midi2_ep_item_ops = { .release = f_midi2_ep_opts_release, }; -static struct configfs_group_operations f_midi2_ep_group_ops = { +static const struct configfs_group_operations f_midi2_ep_group_ops = { .make_group = f_midi2_opts_block_make, .drop_item = f_midi2_opts_block_drop, }; @@ -2618,11 +2618,11 @@ static void f_midi2_opts_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations f_midi2_item_ops = { +static const struct configfs_item_operations f_midi2_item_ops = { .release = f_midi2_opts_release, }; -static struct configfs_group_operations f_midi2_group_ops = { +static const struct configfs_group_operations f_midi2_group_ops = { .make_group = f_midi2_opts_ep_make, .drop_item = f_midi2_opts_ep_drop, }; diff --git a/drivers/usb/gadget/function/f_obex.c b/drivers/usb/gadget/function/f_obex.c index 1305e2326cdf..6d498f63183e 100644 --- a/drivers/usb/gadget/function/f_obex.c +++ b/drivers/usb/gadget/function/f_obex.c @@ -390,7 +390,7 @@ static void obex_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations obex_item_ops = { +static const struct configfs_item_operations obex_item_ops = { .release = obex_attr_release, }; diff --git a/drivers/usb/gadget/function/f_phonet.c b/drivers/usb/gadget/function/f_phonet.c index 0aa9e8224cae..d644d5495fdc 100644 --- a/drivers/usb/gadget/function/f_phonet.c +++ b/drivers/usb/gadget/function/f_phonet.c @@ -585,7 +585,7 @@ static void phonet_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations phonet_item_ops = { +static const struct configfs_item_operations phonet_item_ops = { .release = phonet_attr_release, }; diff --git a/drivers/usb/gadget/function/f_printer.c b/drivers/usb/gadget/function/f_printer.c index d295ade8fa67..b2fa2b56c37e 100644 --- a/drivers/usb/gadget/function/f_printer.c +++ b/drivers/usb/gadget/function/f_printer.c @@ -1220,7 +1220,7 @@ static void printer_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations printer_item_ops = { +static const struct configfs_item_operations printer_item_ops = { .release = printer_attr_release, }; diff --git a/drivers/usb/gadget/function/f_serial.c b/drivers/usb/gadget/function/f_serial.c index 0f266bc067f5..e6b412e0e045 100644 --- a/drivers/usb/gadget/function/f_serial.c +++ b/drivers/usb/gadget/function/f_serial.c @@ -260,7 +260,7 @@ static void serial_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations serial_item_ops = { +static const struct configfs_item_operations serial_item_ops = { .release = serial_attr_release, }; diff --git a/drivers/usb/gadget/function/f_sourcesink.c b/drivers/usb/gadget/function/f_sourcesink.c index ec5fd25020fd..44c644877d3d 100644 --- a/drivers/usb/gadget/function/f_sourcesink.c +++ b/drivers/usb/gadget/function/f_sourcesink.c @@ -882,7 +882,7 @@ static void ss_attr_release(struct config_item *item) usb_put_function_instance(&ss_opts->func_inst); } -static struct configfs_item_operations ss_item_ops = { +static const struct configfs_item_operations ss_item_ops = { .release = ss_attr_release, }; diff --git a/drivers/usb/gadget/function/f_tcm.c b/drivers/usb/gadget/function/f_tcm.c index 6e8804f04baa..92f90592004e 100644 --- a/drivers/usb/gadget/function/f_tcm.c +++ b/drivers/usb/gadget/function/f_tcm.c @@ -2446,7 +2446,7 @@ static void tcm_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations tcm_item_ops = { +static const struct configfs_item_operations tcm_item_ops = { .release = tcm_attr_release, }; diff --git a/drivers/usb/gadget/function/f_uac1.c b/drivers/usb/gadget/function/f_uac1.c index 9da9fb4e1239..efe9f270b02d 100644 --- a/drivers/usb/gadget/function/f_uac1.c +++ b/drivers/usb/gadget/function/f_uac1.c @@ -1512,7 +1512,7 @@ static void f_uac1_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations f_uac1_item_ops = { +static const struct configfs_item_operations f_uac1_item_ops = { .release = f_uac1_attr_release, }; diff --git a/drivers/usb/gadget/function/f_uac1_legacy.c b/drivers/usb/gadget/function/f_uac1_legacy.c index 49cf5aae90ca..8fc452b4b39a 100644 --- a/drivers/usb/gadget/function/f_uac1_legacy.c +++ b/drivers/usb/gadget/function/f_uac1_legacy.c @@ -812,7 +812,7 @@ static void f_uac1_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations f_uac1_item_ops = { +static const struct configfs_item_operations f_uac1_item_ops = { .release = f_uac1_attr_release, }; diff --git a/drivers/usb/gadget/function/f_uac2.c b/drivers/usb/gadget/function/f_uac2.c index dd252ff2fb4e..98f0f50dc7a8 100644 --- a/drivers/usb/gadget/function/f_uac2.c +++ b/drivers/usb/gadget/function/f_uac2.c @@ -1874,7 +1874,7 @@ static void f_uac2_attr_release(struct config_item *item) usb_put_function_instance(&opts->func_inst); } -static struct configfs_item_operations f_uac2_item_ops = { +static const struct configfs_item_operations f_uac2_item_ops = { .release = f_uac2_attr_release, }; diff --git a/drivers/usb/gadget/function/u_ether_configfs.h b/drivers/usb/gadget/function/u_ether_configfs.h index f558c3139ebe..51f0d79e5eca 100644 --- a/drivers/usb/gadget/function/u_ether_configfs.h +++ b/drivers/usb/gadget/function/u_ether_configfs.h @@ -21,7 +21,7 @@ usb_put_function_instance(&opts->func_inst); \ } \ \ - static struct configfs_item_operations _f_##_item_ops = { \ + static const struct configfs_item_operations _f_##_item_ops = { \ .release = _f_##_attr_release, \ } diff --git a/drivers/usb/gadget/function/uvc_configfs.c b/drivers/usb/gadget/function/uvc_configfs.c index a4a2d3dcb0d6..2e74816b9faf 100644 --- a/drivers/usb/gadget/function/uvc_configfs.c +++ b/drivers/usb/gadget/function/uvc_configfs.c @@ -127,7 +127,7 @@ static void uvcg_config_item_release(struct config_item *item) kfree(group); } -static struct configfs_item_operations uvcg_config_item_ops = { +static const struct configfs_item_operations uvcg_config_item_ops = { .release = uvcg_config_item_release, }; @@ -284,7 +284,7 @@ static struct config_item *uvcg_control_header_make(struct config_group *group, return &h->item; } -static struct configfs_group_operations uvcg_control_header_grp_ops = { +static const struct configfs_group_operations uvcg_control_header_grp_ops = { .make_item = uvcg_control_header_make, }; @@ -1232,7 +1232,7 @@ static void uvcg_extension_drop_link(struct config_item *src, struct config_item mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_extension_item_ops = { +static const struct configfs_item_operations uvcg_extension_item_ops = { .release = uvcg_extension_release, .allow_link = uvcg_extension_allow_link, .drop_link = uvcg_extension_drop_link, @@ -1297,7 +1297,7 @@ static struct config_item *uvcg_extension_make(struct config_group *group, const return &xu->item; } -static struct configfs_group_operations uvcg_extensions_grp_ops = { +static const struct configfs_group_operations uvcg_extensions_grp_ops = { .make_item = uvcg_extension_make, .drop_item = uvcg_extension_drop, }; @@ -1413,7 +1413,7 @@ out: mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_control_class_item_ops = { +static const struct configfs_item_operations uvcg_control_class_item_ops = { .release = uvcg_config_item_release, .allow_link = uvcg_control_class_allow_link, .drop_link = uvcg_control_class_drop_link, @@ -1663,7 +1663,7 @@ static void uvcg_format_drop_link(struct config_item *src, struct config_item *t mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_format_item_operations = { +static const struct configfs_item_operations uvcg_format_item_operations = { .release = uvcg_config_item_release, .allow_link = uvcg_format_allow_link, .drop_link = uvcg_format_drop_link, @@ -1839,7 +1839,7 @@ out: mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_streaming_header_item_ops = { +static const struct configfs_item_operations uvcg_streaming_header_item_ops = { .release = uvcg_config_item_release, .allow_link = uvcg_streaming_header_allow_link, .drop_link = uvcg_streaming_header_drop_link, @@ -1913,7 +1913,7 @@ static struct config_item return &h->item; } -static struct configfs_group_operations uvcg_streaming_header_grp_ops = { +static const struct configfs_group_operations uvcg_streaming_header_grp_ops = { .make_item = uvcg_streaming_header_make, }; @@ -2260,7 +2260,7 @@ static void uvcg_format_set_indices(struct config_group *fmt) * streaming/uncompressed/ */ -static struct configfs_group_operations uvcg_uncompressed_group_ops = { +static const struct configfs_group_operations uvcg_uncompressed_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, }; @@ -2507,7 +2507,7 @@ static struct config_group *uvcg_uncompressed_make(struct config_group *group, return &h->fmt.group; } -static struct configfs_group_operations uvcg_uncompressed_grp_ops = { +static const struct configfs_group_operations uvcg_uncompressed_grp_ops = { .make_group = uvcg_uncompressed_make, }; @@ -2524,7 +2524,7 @@ static const struct uvcg_config_group_type uvcg_uncompressed_grp_type = { * streaming/mjpeg/ */ -static struct configfs_group_operations uvcg_mjpeg_group_ops = { +static const struct configfs_group_operations uvcg_mjpeg_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, }; @@ -2697,7 +2697,7 @@ static struct config_group *uvcg_mjpeg_make(struct config_group *group, return &h->fmt.group; } -static struct configfs_group_operations uvcg_mjpeg_grp_ops = { +static const struct configfs_group_operations uvcg_mjpeg_grp_ops = { .make_group = uvcg_mjpeg_make, }; @@ -2714,7 +2714,7 @@ static const struct uvcg_config_group_type uvcg_mjpeg_grp_type = { * streaming/framebased/ */ -static struct configfs_group_operations uvcg_framebased_group_ops = { +static const struct configfs_group_operations uvcg_framebased_group_ops = { .make_item = uvcg_frame_make, .drop_item = uvcg_frame_drop, }; @@ -2952,7 +2952,7 @@ static struct config_group *uvcg_framebased_make(struct config_group *group, return &h->fmt.group; } -static struct configfs_group_operations uvcg_framebased_grp_ops = { +static const struct configfs_group_operations uvcg_framebased_grp_ops = { .make_group = uvcg_framebased_make, }; @@ -3055,7 +3055,7 @@ static void uvcg_color_matching_release(struct config_item *item) kfree(color_match); } -static struct configfs_item_operations uvcg_color_matching_item_ops = { +static const struct configfs_item_operations uvcg_color_matching_item_ops = { .release = uvcg_color_matching_release, }; @@ -3088,7 +3088,7 @@ static struct config_group *uvcg_color_matching_make(struct config_group *group, return &color_match->group; } -static struct configfs_group_operations uvcg_color_matching_grp_group_ops = { +static const struct configfs_group_operations uvcg_color_matching_grp_group_ops = { .make_group = uvcg_color_matching_make, }; @@ -3529,7 +3529,7 @@ out: mutex_unlock(su_mutex); } -static struct configfs_item_operations uvcg_streaming_class_item_ops = { +static const struct configfs_item_operations uvcg_streaming_class_item_ops = { .release = uvcg_config_item_release, .allow_link = uvcg_streaming_class_allow_link, .drop_link = uvcg_streaming_class_drop_link, @@ -3697,7 +3697,7 @@ static void uvc_func_drop_link(struct config_item *src, struct config_item *tgt) mutex_unlock(&opts->lock); } -static struct configfs_item_operations uvc_func_item_ops = { +static const struct configfs_item_operations uvc_func_item_ops = { .release = uvc_func_item_release, .allow_link = uvc_func_allow_link, .drop_link = uvc_func_drop_link, diff --git a/include/linux/usb/gadget_configfs.h b/include/linux/usb/gadget_configfs.h index 6b5d6838f865..23c1091e88c0 100644 --- a/include/linux/usb/gadget_configfs.h +++ b/include/linux/usb/gadget_configfs.h @@ -30,7 +30,7 @@ static ssize_t __struct##_##__name##_show(struct config_item *item, char *page) CONFIGFS_ATTR(struct_name##_, _name) #define USB_CONFIG_STRING_RW_OPS(struct_in) \ -static struct configfs_item_operations struct_in##_langid_item_ops = { \ +static const struct configfs_item_operations struct_in##_langid_item_ops = { \ .release = struct_in##_attr_release, \ }; \ \ @@ -86,7 +86,7 @@ static void struct_in##_strings_drop( \ config_item_put(item); \ } \ \ -static struct configfs_group_operations struct_in##_strings_ops = { \ +static const struct configfs_group_operations struct_in##_strings_ops = { \ .make_group = &struct_in##_strings_make, \ .drop_item = &struct_in##_strings_drop, \ }; \ -- cgit v1.2.3 From caad07ae07e3fb173e804abdd53fb96aa7186830 Mon Sep 17 00:00:00 2001 From: "Rafael J. Wysocki" Date: Mon, 22 Dec 2025 21:22:48 +0100 Subject: phy: core: Discard pm_runtime_put() return values The PHY core defines phy_pm_runtime_put() to return an int, but that return value is never used. It also passes the return value of pm_runtime_put() to the caller which is not very useful. Returning an error code from pm_runtime_put() merely means that it has not queued up a work item to check whether or not the device can be suspended and there are many perfectly valid situations in which that can happen, like after writing "on" to the devices' runtime PM "control" attribute in sysfs for one example. Modify phy_pm_runtime_put() to discard the pm_runtime_put() return value and change its return type to void. Also drop the redundant pm_runtime_enabled() call from there. No intentional functional impact. This will facilitate a planned change of the pm_runtime_put() return type to void in the future. Signed-off-by: Rafael J. Wysocki Link: https://patch.msgid.link/2556645.jE0xQCEvom@rafael.j.wysocki Signed-off-by: Vinod Koul --- drivers/phy/phy-core.c | 9 +++------ include/linux/phy/phy.h | 7 ++----- 2 files changed, 5 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/phy/phy-core.c b/drivers/phy/phy-core.c index 8d227890a345..160ecb757d1d 100644 --- a/drivers/phy/phy-core.c +++ b/drivers/phy/phy-core.c @@ -190,15 +190,12 @@ int phy_pm_runtime_get_sync(struct phy *phy) } EXPORT_SYMBOL_GPL(phy_pm_runtime_get_sync); -int phy_pm_runtime_put(struct phy *phy) +void phy_pm_runtime_put(struct phy *phy) { if (!phy) - return 0; - - if (!pm_runtime_enabled(&phy->dev)) - return -ENOTSUPP; + return; - return pm_runtime_put(&phy->dev); + pm_runtime_put(&phy->dev); } EXPORT_SYMBOL_GPL(phy_pm_runtime_put); diff --git a/include/linux/phy/phy.h b/include/linux/phy/phy.h index 2af0d01ebb39..ea47975e288a 100644 --- a/include/linux/phy/phy.h +++ b/include/linux/phy/phy.h @@ -243,7 +243,7 @@ static inline void *phy_get_drvdata(struct phy *phy) #if IS_ENABLED(CONFIG_GENERIC_PHY) int phy_pm_runtime_get(struct phy *phy); int phy_pm_runtime_get_sync(struct phy *phy); -int phy_pm_runtime_put(struct phy *phy); +void phy_pm_runtime_put(struct phy *phy); int phy_pm_runtime_put_sync(struct phy *phy); int phy_init(struct phy *phy); int phy_exit(struct phy *phy); @@ -324,11 +324,8 @@ static inline int phy_pm_runtime_get_sync(struct phy *phy) return -ENOSYS; } -static inline int phy_pm_runtime_put(struct phy *phy) +static inline void phy_pm_runtime_put(struct phy *phy) { - if (!phy) - return 0; - return -ENOSYS; } static inline int phy_pm_runtime_put_sync(struct phy *phy) -- cgit v1.2.3 From a722de305eacb382a5d306f9f8e502f81bab682d Mon Sep 17 00:00:00 2001 From: Sven Peter Date: Sun, 14 Dec 2025 11:51:34 +0000 Subject: soc: apple: Add hardware tunable support Various hardware, like the Type-C PHY or the Thunderbolt/USB4 NHI, present on Apple SoCs need machine-specific tunables passed from our bootloader m1n1 to the device tree. Add generic helpers so that we don't have to duplicate this across multiple drivers. Reviewed-by: Alyssa Rosenzweig Reviewed-by: Neal Gompa Reviewed-by: Janne Grunau Signed-off-by: Sven Peter Link: https://patch.msgid.link/20251214-b4-atcphy-v3-1-ba82b20e9459@kernel.org Signed-off-by: Vinod Koul --- drivers/soc/apple/Kconfig | 4 ++ drivers/soc/apple/Makefile | 3 ++ drivers/soc/apple/tunable.c | 80 +++++++++++++++++++++++++++++++++++++++ include/linux/soc/apple/tunable.h | 62 ++++++++++++++++++++++++++++++ 4 files changed, 149 insertions(+) create mode 100644 drivers/soc/apple/tunable.c create mode 100644 include/linux/soc/apple/tunable.h (limited to 'include/linux') diff --git a/drivers/soc/apple/Kconfig b/drivers/soc/apple/Kconfig index ad6736889231..d0ff32182a2b 100644 --- a/drivers/soc/apple/Kconfig +++ b/drivers/soc/apple/Kconfig @@ -38,6 +38,10 @@ config APPLE_SART Say 'y' here if you have an Apple SoC. +config APPLE_TUNABLE + tristate + depends on ARCH_APPLE || COMPILE_TEST + endmenu endif diff --git a/drivers/soc/apple/Makefile b/drivers/soc/apple/Makefile index 4d9ab8f3037b..0b85ab61aefe 100644 --- a/drivers/soc/apple/Makefile +++ b/drivers/soc/apple/Makefile @@ -8,3 +8,6 @@ apple-rtkit-y = rtkit.o rtkit-crashlog.o obj-$(CONFIG_APPLE_SART) += apple-sart.o apple-sart-y = sart.o + +obj-$(CONFIG_APPLE_TUNABLE) += apple-tunable.o +apple-tunable-y = tunable.o diff --git a/drivers/soc/apple/tunable.c b/drivers/soc/apple/tunable.c new file mode 100644 index 000000000000..659323839171 --- /dev/null +++ b/drivers/soc/apple/tunable.c @@ -0,0 +1,80 @@ +// SPDX-License-Identifier: GPL-2.0-only OR MIT +/* + * Apple Silicon hardware tunable support + * + * Each tunable is a list with each entry containing a offset into the MMIO + * region, a mask of bits to be cleared and a set of bits to be set. These + * tunables are passed along by the previous boot stages and vary from device + * to device such that they cannot be hardcoded in the individual drivers. + * + * Copyright (C) The Asahi Linux Contributors + */ + +#include +#include +#include +#include +#include + +struct apple_tunable *devm_apple_tunable_parse(struct device *dev, + struct device_node *np, + const char *name, + struct resource *res) +{ + struct apple_tunable *tunable; + struct property *prop; + const __be32 *p; + size_t sz; + int i; + + if (resource_size(res) < 4) + return ERR_PTR(-EINVAL); + + prop = of_find_property(np, name, NULL); + if (!prop) + return ERR_PTR(-ENOENT); + + if (prop->length % (3 * sizeof(u32))) + return ERR_PTR(-EINVAL); + sz = prop->length / (3 * sizeof(u32)); + + tunable = devm_kzalloc(dev, struct_size(tunable, values, sz), GFP_KERNEL); + if (!tunable) + return ERR_PTR(-ENOMEM); + tunable->sz = sz; + + for (i = 0, p = NULL; i < tunable->sz; ++i) { + p = of_prop_next_u32(prop, p, &tunable->values[i].offset); + p = of_prop_next_u32(prop, p, &tunable->values[i].mask); + p = of_prop_next_u32(prop, p, &tunable->values[i].value); + + /* Sanity checks to catch bugs in our bootloader */ + if (tunable->values[i].offset % 4) + return ERR_PTR(-EINVAL); + if (tunable->values[i].offset > (resource_size(res) - 4)) + return ERR_PTR(-EINVAL); + } + + return tunable; +} +EXPORT_SYMBOL(devm_apple_tunable_parse); + +void apple_tunable_apply(void __iomem *regs, struct apple_tunable *tunable) +{ + size_t i; + + for (i = 0; i < tunable->sz; ++i) { + u32 val, old_val; + + old_val = readl(regs + tunable->values[i].offset); + val = old_val & ~tunable->values[i].mask; + val |= tunable->values[i].value; + if (val != old_val) + writel(val, regs + tunable->values[i].offset); + } +} +EXPORT_SYMBOL(apple_tunable_apply); + +MODULE_LICENSE("Dual MIT/GPL"); +MODULE_AUTHOR("Sven Peter "); +MODULE_DESCRIPTION("Apple Silicon hardware tunable support"); diff --git a/include/linux/soc/apple/tunable.h b/include/linux/soc/apple/tunable.h new file mode 100644 index 000000000000..531ca814cd02 --- /dev/null +++ b/include/linux/soc/apple/tunable.h @@ -0,0 +1,62 @@ +/* SPDX-License-Identifier: GPL-2.0-only OR MIT */ +/* + * Apple Silicon hardware tunable support + * + * Each tunable is a list with each entry containing a offset into the MMIO + * region, a mask of bits to be cleared and a set of bits to be set. These + * tunables are passed along by the previous boot stages and vary from device + * to device such that they cannot be hardcoded in the individual drivers. + * + * Copyright (C) The Asahi Linux Contributors + */ + +#ifndef _LINUX_SOC_APPLE_TUNABLE_H_ +#define _LINUX_SOC_APPLE_TUNABLE_H_ + +#include +#include + +/** + * Struct to store an Apple Silicon hardware tunable. + * + * Each tunable is a list with each entry containing a offset into the MMIO + * region, a mask of bits to be cleared and a set of bits to be set. These + * tunables are passed along by the previous boot stages and vary from device + * to device such that they cannot be hardcoded in the individual drivers. + * + * @param sz Number of [offset, mask, value] tuples stored in values. + * @param values [offset, mask, value] array. + */ +struct apple_tunable { + size_t sz; + struct { + u32 offset; + u32 mask; + u32 value; + } values[] __counted_by(sz); +}; + +/** + * Parse an array of hardware tunables from the device tree. + * + * @dev: Device node used for devm_kzalloc internally. + * @np: Device node which contains the tunable array. + * @name: Name of the device tree property which contains the tunables. + * @res: Resource to which the tunables will be applied, used for bound checking + * + * @return: devres allocated struct on success or PTR_ERR on failure. + */ +struct apple_tunable *devm_apple_tunable_parse(struct device *dev, + struct device_node *np, + const char *name, + struct resource *res); + +/** + * Apply a previously loaded hardware tunable. + * + * @param regs: MMIO to which the tunable will be applied. + * @param tunable: Pointer to the tunable. + */ +void apple_tunable_apply(void __iomem *regs, struct apple_tunable *tunable); + +#endif -- cgit v1.2.3 From 031314bd37cb6ce352b1300ffd4e07a7bebce1ef Mon Sep 17 00:00:00 2001 From: Pritam Manohar Sutar Date: Mon, 24 Nov 2025 16:34:49 +0530 Subject: phy: exynos5-usbdrd: support HS phy for ExynosAutov920 Enable UTMI+ phy support for this SoC which is very similar to what the existing Exynos850 supports. Add required change in phy driver to support HS phy for this SoC. Reviewed-by: Alim Akhtar Signed-off-by: Pritam Manohar Sutar Link: https://patch.msgid.link/20251124110453.2887437-3-pritam.sutar@samsung.com Signed-off-by: Vinod Koul --- drivers/phy/samsung/phy-exynos5-usbdrd.c | 123 ++++++++++++++++++++++++++++ include/linux/soc/samsung/exynos-regs-pmu.h | 2 + 2 files changed, 125 insertions(+) (limited to 'include/linux') diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index 1c8bf80119f1..7416d2e1e358 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -2054,6 +2054,126 @@ static const struct exynos5_usbdrd_phy_drvdata exynos990_usbdrd_phy = { .n_regulators = ARRAY_SIZE(exynos5_regulator_names), }; +static int exynosautov920_usbdrd_phy_init(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + int ret; + + ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks); + if (ret) + return ret; + + /* Bypass PHY isol */ + inst->phy_cfg->phy_isol(inst, false); + + /* UTMI or PIPE3 specific init */ + inst->phy_cfg->phy_init(phy_drd); + + clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks); + + return 0; +} + +static int exynosautov920_usbdrd_phy_exit(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + int ret; + + ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_clks, phy_drd->clks); + if (ret) + return ret; + + exynos850_usbdrd_phy_exit(phy); + + /* enable PHY isol */ + inst->phy_cfg->phy_isol(inst, true); + + clk_bulk_disable_unprepare(phy_drd->drv_data->n_clks, phy_drd->clks); + + return 0; +} + +static int exynosautov920_usbdrd_phy_power_on(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + int ret; + + dev_dbg(phy_drd->dev, "Request to power_on usbdrd_phy phy\n"); + + ret = clk_bulk_prepare_enable(phy_drd->drv_data->n_core_clks, + phy_drd->core_clks); + if (ret) + return ret; + + /* Enable supply */ + ret = regulator_bulk_enable(phy_drd->drv_data->n_regulators, + phy_drd->regulators); + if (ret) { + dev_err(phy_drd->dev, "Failed to enable PHY regulator(s)\n"); + goto fail_supply; + } + + return 0; + +fail_supply: + clk_bulk_disable_unprepare(phy_drd->drv_data->n_core_clks, + phy_drd->core_clks); + + return ret; +} + +static int exynosautov920_usbdrd_phy_power_off(struct phy *phy) +{ + struct phy_usb_instance *inst = phy_get_drvdata(phy); + struct exynos5_usbdrd_phy *phy_drd = to_usbdrd_phy(inst); + + dev_dbg(phy_drd->dev, "Request to power_off usbdrd_phy phy\n"); + + /* Disable supply */ + regulator_bulk_disable(phy_drd->drv_data->n_regulators, + phy_drd->regulators); + + clk_bulk_disable_unprepare(phy_drd->drv_data->n_core_clks, + phy_drd->core_clks); + + return 0; +} + +static const char * const exynosautov920_usb20_regulators[] = { + "dvdd", "vdd18", "vdd33", +}; + +static const struct phy_ops exynosautov920_usbdrd_phy_ops = { + .init = exynosautov920_usbdrd_phy_init, + .exit = exynosautov920_usbdrd_phy_exit, + .power_on = exynosautov920_usbdrd_phy_power_on, + .power_off = exynosautov920_usbdrd_phy_power_off, + .owner = THIS_MODULE, +}; + +static const struct exynos5_usbdrd_phy_config phy_cfg_exynosautov920[] = { + { + .id = EXYNOS5_DRDPHY_UTMI, + .phy_isol = exynos5_usbdrd_phy_isol, + .phy_init = exynos850_usbdrd_utmi_init, + }, +}; + +static const struct exynos5_usbdrd_phy_drvdata exynosautov920_usbdrd_phy = { + .phy_cfg = phy_cfg_exynosautov920, + .phy_ops = &exynosautov920_usbdrd_phy_ops, + .pmu_offset_usbdrd0_phy = EXYNOSAUTOV920_PHY_CTRL_USB20, + .clk_names = exynos5_clk_names, + .n_clks = ARRAY_SIZE(exynos5_clk_names), + .core_clk_names = exynos5_core_clk_names, + .n_core_clks = ARRAY_SIZE(exynos5_core_clk_names), + .regulator_names = exynosautov920_usb20_regulators, + .n_regulators = ARRAY_SIZE(exynosautov920_usb20_regulators), +}; + static const struct exynos5_usbdrd_phy_config phy_cfg_gs101[] = { { .id = EXYNOS5_DRDPHY_UTMI, @@ -2260,6 +2380,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { }, { .compatible = "samsung,exynos990-usbdrd-phy", .data = &exynos990_usbdrd_phy + }, { + .compatible = "samsung,exynosautov920-usbdrd-phy", + .data = &exynosautov920_usbdrd_phy }, { }, }; diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h index 532c6c2d1195..ab4d8be0e073 100644 --- a/include/linux/soc/samsung/exynos-regs-pmu.h +++ b/include/linux/soc/samsung/exynos-regs-pmu.h @@ -1015,4 +1015,6 @@ #define GS101_GRP2_INTR_BID_UPEND (0x0208) #define GS101_GRP2_INTR_BID_CLEAR (0x020c) +/* exynosautov920 */ +#define EXYNOSAUTOV920_PHY_CTRL_USB20 (0x0710) #endif /* __LINUX_SOC_EXYNOS_REGS_PMU_H */ -- cgit v1.2.3 From 2fdfc1bb752e393561cf532f5d54607d70e464bc Mon Sep 17 00:00:00 2001 From: Pritam Manohar Sutar Date: Mon, 24 Nov 2025 16:34:53 +0530 Subject: phy: exynos5-usbdrd: support SS combo phy for ExynosAutov920 Update phy driver to enable SS combo phy for this SoC. New registers' definitions, phy ops (init/exit), and dedicated phy driver data structure are added for SS combo phy. Add these changes in the driver to support SS combo phy for this SoC. Reviewed-by: Alim Akhtar Signed-off-by: Pritam Manohar Sutar Link: https://patch.msgid.link/20251124110453.2887437-7-pritam.sutar@samsung.com Signed-off-by: Vinod Koul --- drivers/phy/samsung/phy-exynos5-usbdrd.c | 325 +++++++++++++++++++++++++++- include/linux/soc/samsung/exynos-regs-pmu.h | 1 + 2 files changed, 322 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/phy/samsung/phy-exynos5-usbdrd.c b/drivers/phy/samsung/phy-exynos5-usbdrd.c index 5bbf78d44a74..5a181cb4597e 100644 --- a/drivers/phy/samsung/phy-exynos5-usbdrd.c +++ b/drivers/phy/samsung/phy-exynos5-usbdrd.c @@ -273,6 +273,36 @@ #define EXYNOSAUTOV920_DRD_HSPPLLTUNE 0x110 #define HSPPLLTUNE_FSEL GENMASK(18, 16) +/* ExynosAutov920 phy usb31drd port reg */ +#define EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL 0x000 +#define PHY_RST_CTRL_PIPE_LANE0_RESET_N_OVRD_EN BIT(5) +#define PHY_RST_CTRL_PIPE_LANE0_RESET_N BIT(4) +#define PHY_RST_CTRL_PHY_RESET_OVRD_EN BIT(1) +#define PHY_RST_CTRL_PHY_RESET BIT(0) + +#define EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0 0x0004 +#define PHY_CR_PARA_CON0_PHY0_CR_PARA_ADDR GENMASK(31, 16) +#define PHY_CR_PARA_CON0_PHY0_CR_PARA_CLK BIT(8) +#define PHY_CR_PARA_CON0_PHY0_CR_PARA_ACK BIT(4) +#define PHY_CR_PARA_CON0_PHY0_CR_PARA_SEL BIT(0) + +#define EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON1 0x0008 + +#define EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON2 0x000c +#define PHY_CR_PARA_CON2_PHY0_CR_PARA_WR_EN BIT(0) +#define PHY_CR_PARA_CON2_PHY0_CR_PARA_WR_DATA GENMASK(31, 16) + +#define EXYNOSAUTOV920_USB31DRD_PHY_CONFIG0 0x100 +#define PHY_CONFIG0_PHY0_PMA_PWR_STABLE BIT(14) +#define PHY_CONFIG0_PHY0_PCS_PWR_STABLE BIT(13) +#define PHY_CONFIG0_PHY0_ANA_PWR_EN BIT(1) + +#define EXYNOSAUTOV920_USB31DRD_PHY_CONFIG7 0x11c +#define PHY_CONFIG7_PHY_TEST_POWERDOWN BIT(24) + +#define EXYNOSAUTOV920_USB31DRD_PHY_CONFIG4 0x110 +#define PHY_CONFIG4_PIPE_RX0_SRIS_MODE_EN BIT(2) + /* Exynos9 - GS101 */ #define EXYNOS850_DRD_SECPMACTL 0x48 #define SECPMACTL_PMA_ROPLL_REF_CLK_SEL GENMASK(13, 12) @@ -2077,6 +2107,251 @@ static const struct exynos5_usbdrd_phy_drvdata exynos990_usbdrd_phy = { .n_regulators = ARRAY_SIZE(exynos5_regulator_names), }; +static void +exynosautov920_usb31drd_cr_clk(struct exynos5_usbdrd_phy *phy_drd, bool high) +{ + void __iomem *reg_phy = phy_drd->reg_phy; + u32 reg; + + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + if (high) + reg |= PHY_CR_PARA_CON0_PHY0_CR_PARA_CLK; + else + reg &= ~PHY_CR_PARA_CON0_PHY0_CR_PARA_CLK; + + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + fsleep(1); +} + +static void +exynosautov920_usb31drd_port_phy_ready(struct exynos5_usbdrd_phy *phy_drd) +{ + struct device *dev = phy_drd->dev; + void __iomem *reg_phy = phy_drd->reg_phy; + static const unsigned int timeout_us = 20000; + static const unsigned int sleep_us = 40; + u32 reg; + int err; + + /* Clear cr_para_con */ + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + reg &= ~(PHY_CR_PARA_CON0_PHY0_CR_PARA_CLK | + PHY_CR_PARA_CON0_PHY0_CR_PARA_ADDR); + reg |= PHY_CR_PARA_CON0_PHY0_CR_PARA_SEL; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + writel(0x0, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON1); + writel(0x0, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON2); + + exynosautov920_usb31drd_cr_clk(phy_drd, true); + exynosautov920_usb31drd_cr_clk(phy_drd, false); + + /* + * The maximum time from phy reset de-assertion to de-assertion of + * tx/rx_ack can be as high as 5ms in fast simulation mode. + * Time to phy ready is < 20ms + */ + err = readl_poll_timeout(reg_phy + + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0, + reg, !(reg & PHY_CR_PARA_CON0_PHY0_CR_PARA_ACK), + sleep_us, timeout_us); + if (err) + dev_err(dev, "timed out waiting for rx/tx_ack: %#.8x\n", reg); + + reg &= ~PHY_CR_PARA_CON0_PHY0_CR_PARA_CLK; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); +} + +static void +exynosautov920_usb31drd_cr_write(struct exynos5_usbdrd_phy *phy_drd, + u16 addr, u16 data) +{ + void __iomem *reg_phy = phy_drd->reg_phy; + u32 cnt = 0; + u32 reg; + + /* Pre Clocking */ + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + reg |= PHY_CR_PARA_CON0_PHY0_CR_PARA_SEL; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + + /* + * tx clks must be available prior to assertion of tx req. + * tx pstate p2 to p0 transition directly is not permitted. + * tx clk ready must be asserted synchronously on tx clk prior + * to internal transmit clk alignment sequence in the phy + * when entering from p2 to p1 to p0. + */ + do { + exynosautov920_usb31drd_cr_clk(phy_drd, true); + exynosautov920_usb31drd_cr_clk(phy_drd, false); + cnt++; + } while (cnt < 15); + + reg &= ~PHY_CR_PARA_CON0_PHY0_CR_PARA_SEL; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + + /* + * tx data path is active when tx lane is in p0 state + * and tx data en asserted. enable cr_para_wr_en. + */ + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON2); + reg &= ~PHY_CR_PARA_CON2_PHY0_CR_PARA_WR_DATA; + reg |= FIELD_PREP(PHY_CR_PARA_CON2_PHY0_CR_PARA_WR_DATA, data) | + PHY_CR_PARA_CON2_PHY0_CR_PARA_WR_EN; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON2); + + /* write addr */ + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + reg &= ~PHY_CR_PARA_CON0_PHY0_CR_PARA_ADDR; + reg |= FIELD_PREP(PHY_CR_PARA_CON0_PHY0_CR_PARA_ADDR, addr) | + PHY_CR_PARA_CON0_PHY0_CR_PARA_CLK | + PHY_CR_PARA_CON0_PHY0_CR_PARA_SEL; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + + /* check cr_para_ack*/ + cnt = 0; + do { + /* + * data symbols are captured by phy on rising edge of the + * tx_clk when tx data enabled. + * completion of the write cycle is acknowledged by assertion + * of the cr_para_ack. + */ + exynosautov920_usb31drd_cr_clk(phy_drd, true); + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CR_PARA_CON0); + if ((reg & PHY_CR_PARA_CON0_PHY0_CR_PARA_ACK)) + break; + + exynosautov920_usb31drd_cr_clk(phy_drd, false); + + /* + * wait for minimum of 10 cr_para_clk cycles after phy reset + * is negated, before accessing control regs to allow for + * internal resets. + */ + cnt++; + } while (cnt < 10); + + if (cnt < 10) + exynosautov920_usb31drd_cr_clk(phy_drd, false); +} + +static void +exynosautov920_usb31drd_phy_reset(struct exynos5_usbdrd_phy *phy_drd, int val) +{ + void __iomem *reg_phy = phy_drd->reg_phy; + u32 reg; + + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL); + reg &= ~PHY_RST_CTRL_PHY_RESET_OVRD_EN; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL); + + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL); + if (val) + reg |= PHY_RST_CTRL_PHY_RESET; + else + reg &= ~PHY_RST_CTRL_PHY_RESET; + + reg |= PHY_RST_CTRL_PHY_RESET_OVRD_EN; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL); +} + +static void +exynosautov920_usb31drd_lane0_reset(struct exynos5_usbdrd_phy *phy_drd, int val) +{ + void __iomem *reg_phy = phy_drd->reg_phy; + u32 reg; + + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL); + reg |= PHY_RST_CTRL_PIPE_LANE0_RESET_N_OVRD_EN; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL); + + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL); + if (val) + reg &= ~PHY_RST_CTRL_PIPE_LANE0_RESET_N; + else + reg |= PHY_RST_CTRL_PIPE_LANE0_RESET_N; + + reg &= ~PHY_RST_CTRL_PIPE_LANE0_RESET_N_OVRD_EN; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_RST_CTRL); +} + +static void +exynosautov920_usb31drd_pipe3_init(struct exynos5_usbdrd_phy *phy_drd) +{ + void __iomem *reg_phy = phy_drd->reg_phy; + u32 reg; + + /* + * Phy and Pipe Lane reset assert. + * assert reset (phy_reset = 1). + * The lane-ack outputs are asserted during reset (tx_ack = rx_ack = 1) + */ + exynosautov920_usb31drd_phy_reset(phy_drd, 1); + exynosautov920_usb31drd_lane0_reset(phy_drd, 1); + + /* + * ANA Power En, PCS & PMA PWR Stable Set + * ramp-up power suppiles + */ + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG0); + reg |= PHY_CONFIG0_PHY0_ANA_PWR_EN | PHY_CONFIG0_PHY0_PCS_PWR_STABLE | + PHY_CONFIG0_PHY0_PMA_PWR_STABLE; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG0); + + fsleep(10); + + /* + * phy is not functional in test_powerdown mode, test_powerdown to be + * de-asserted for normal operation + */ + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG7); + reg &= ~PHY_CONFIG7_PHY_TEST_POWERDOWN; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG7); + + /* + * phy reset signal be asserted for minimum 10us after power + * supplies are ramped-up + */ + fsleep(10); + + /* + * Phy and Pipe Lane reset assert de-assert + */ + exynosautov920_usb31drd_phy_reset(phy_drd, 0); + exynosautov920_usb31drd_lane0_reset(phy_drd, 0); + + /* Pipe_rx0_sris_mode_en = 1 */ + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG4); + reg |= PHY_CONFIG4_PIPE_RX0_SRIS_MODE_EN; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG4); + + /* + * wait for lane ack outputs to de-assert (tx_ack = rx_ack = 0) + * Exit from the reset state is indicated by de-assertion of *_ack + */ + exynosautov920_usb31drd_port_phy_ready(phy_drd); + + /* override values for level settings */ + exynosautov920_usb31drd_cr_write(phy_drd, 0x22, 0x00F5); +} + +static void +exynosautov920_usb31drd_ssphy_disable(struct exynos5_usbdrd_phy *phy_drd) +{ + void __iomem *reg_phy = phy_drd->reg_phy; + u32 reg; + + /* 1. Assert reset (phy_reset = 1) */ + exynosautov920_usb31drd_lane0_reset(phy_drd, 1); + exynosautov920_usb31drd_phy_reset(phy_drd, 1); + + /* phy test power down */ + reg = readl(reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG7); + reg |= PHY_CONFIG7_PHY_TEST_POWERDOWN; + writel(reg, reg_phy + EXYNOSAUTOV920_USB31DRD_PHY_CONFIG7); +} + static void exynosautov920_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) { @@ -2172,12 +2447,15 @@ exynosautov920_usbdrd_utmi_init(struct exynos5_usbdrd_phy *phy_drd) /* after POR low and delay 75us, PHYCLOCK is guaranteed. */ fsleep(75); - /* force pipe3 signal for link */ + /* Disable forcing pipe interface */ reg = readl(reg_phy + EXYNOS850_DRD_LINKCTRL); - reg |= LINKCTRL_FORCE_PIPE_EN; - reg &= ~LINKCTRL_FORCE_PHYSTATUS; - reg |= LINKCTRL_FORCE_RXELECIDLE; + reg &= ~LINKCTRL_FORCE_PIPE_EN; writel(reg, reg_phy + EXYNOS850_DRD_LINKCTRL); + + /* Pclk to pipe_clk */ + reg = readl(reg_phy + EXYNOS2200_DRD_CLKRST); + reg |= EXYNOS2200_CLKRST_LINK_PCLK_SEL; + writel(reg, reg_phy + EXYNOS2200_DRD_CLKRST); } static void @@ -2264,6 +2542,8 @@ static int exynosautov920_usbdrd_combo_phy_exit(struct phy *phy) if (inst->phy_cfg->id == EXYNOS5_DRDPHY_UTMI) exynosautov920_usbdrd_hsphy_disable(phy_drd); + else if (inst->phy_cfg->id == EXYNOS5_DRDPHY_PIPE3) + exynosautov920_usb31drd_ssphy_disable(phy_drd); /* enable PHY isol */ inst->phy_cfg->phy_isol(inst, true); @@ -2320,10 +2600,44 @@ static int exynosautov920_usbdrd_phy_power_off(struct phy *phy) return 0; } +static const char * const exynosautov920_usb30_regulators[] = { + "dvdd", "vdd18", +}; + static const char * const exynosautov920_usb20_regulators[] = { "dvdd", "vdd18", "vdd33", }; +static const struct +exynos5_usbdrd_phy_config usb31drd_phy_cfg_exynosautov920[] = { + { + .id = EXYNOS5_DRDPHY_PIPE3, + .phy_isol = exynos5_usbdrd_phy_isol, + .phy_init = exynosautov920_usb31drd_pipe3_init, + }, +}; + +static const struct phy_ops exynosautov920_usb31drd_combo_ssphy_ops = { + .init = exynosautov920_usbdrd_phy_init, + .exit = exynosautov920_usbdrd_combo_phy_exit, + .power_on = exynosautov920_usbdrd_phy_power_on, + .power_off = exynosautov920_usbdrd_phy_power_off, + .owner = THIS_MODULE, +}; + +static const +struct exynos5_usbdrd_phy_drvdata exynosautov920_usb31drd_combo_ssphy = { + .phy_cfg = usb31drd_phy_cfg_exynosautov920, + .phy_ops = &exynosautov920_usb31drd_combo_ssphy_ops, + .pmu_offset_usbdrd0_phy = EXYNOSAUTOV920_PHY_CTRL_USB31, + .clk_names = exynos5_clk_names, + .n_clks = ARRAY_SIZE(exynos5_clk_names), + .core_clk_names = exynos5_core_clk_names, + .n_core_clks = ARRAY_SIZE(exynos5_core_clk_names), + .regulator_names = exynosautov920_usb30_regulators, + .n_regulators = ARRAY_SIZE(exynosautov920_usb30_regulators), +}; + static const struct phy_ops exynosautov920_usbdrd_combo_hsphy_ops = { .init = exynosautov920_usbdrd_phy_init, .exit = exynosautov920_usbdrd_combo_phy_exit, @@ -2588,6 +2902,9 @@ static const struct of_device_id exynos5_usbdrd_phy_of_match[] = { }, { .compatible = "samsung,exynos990-usbdrd-phy", .data = &exynos990_usbdrd_phy + }, { + .compatible = "samsung,exynosautov920-usb31drd-combo-ssphy", + .data = &exynosautov920_usb31drd_combo_ssphy }, { .compatible = "samsung,exynosautov920-usbdrd-combo-hsphy", .data = &exynosautov920_usbdrd_combo_hsphy diff --git a/include/linux/soc/samsung/exynos-regs-pmu.h b/include/linux/soc/samsung/exynos-regs-pmu.h index ab4d8be0e073..db8a7ca81080 100644 --- a/include/linux/soc/samsung/exynos-regs-pmu.h +++ b/include/linux/soc/samsung/exynos-regs-pmu.h @@ -1017,4 +1017,5 @@ /* exynosautov920 */ #define EXYNOSAUTOV920_PHY_CTRL_USB20 (0x0710) +#define EXYNOSAUTOV920_PHY_CTRL_USB31 (0x0714) #endif /* __LINUX_SOC_EXYNOS_REGS_PMU_H */ -- cgit v1.2.3 From c0fef45dbab06238e96e221f7c0a8fd2d569f7dd Mon Sep 17 00:00:00 2001 From: "Jiri Slaby (SUSE)" Date: Mon, 24 Nov 2025 11:39:52 +0100 Subject: char/mwave: drop it When I tried to clean up the driver a bit, Arnd noted: > According to thinkwiki.de, the 3780i modem was only used in a > couple of Thinkpad models that are now over 25 years old, using > Pentium II processors, and they all have a physical RS232 port > that can be used to connect an external modem instead. > > Maybe we can just retire this driver? So instead of the clean up, drop the driver altogether. Signed-off-by: Jiri Slaby (SUSE) Suggested-by: Arnd Bergmann Link: https://lore.kernel.org/all/b8834e5d-fdde-4b1a-8757-288dddc507a9@app.fastmail.com/ Link: https://patch.msgid.link/20251124103952.995229-1-jirislaby@kernel.org Signed-off-by: Greg Kroah-Hartman --- Documentation/admin-guide/devices.txt | 2 +- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 - drivers/char/Kconfig | 26 - drivers/char/Makefile | 1 - drivers/char/mwave/3780i.c | 536 --------------------- drivers/char/mwave/3780i.h | 358 -------------- drivers/char/mwave/Makefile | 10 - drivers/char/mwave/README | 37 -- drivers/char/mwave/mwavedd.c | 432 ----------------- drivers/char/mwave/mwavedd.h | 90 ---- drivers/char/mwave/mwavepub.h | 89 ---- drivers/char/mwave/smapi.c | 404 ---------------- drivers/char/mwave/smapi.h | 76 --- drivers/char/mwave/tp3780i.c | 477 ------------------ drivers/char/mwave/tp3780i.h | 103 ---- include/linux/miscdevice.h | 1 - 16 files changed, 1 insertion(+), 2642 deletions(-) delete mode 100644 drivers/char/mwave/3780i.c delete mode 100644 drivers/char/mwave/3780i.h delete mode 100644 drivers/char/mwave/Makefile delete mode 100644 drivers/char/mwave/README delete mode 100644 drivers/char/mwave/mwavedd.c delete mode 100644 drivers/char/mwave/mwavedd.h delete mode 100644 drivers/char/mwave/mwavepub.h delete mode 100644 drivers/char/mwave/smapi.c delete mode 100644 drivers/char/mwave/smapi.h delete mode 100644 drivers/char/mwave/tp3780i.c delete mode 100644 drivers/char/mwave/tp3780i.h (limited to 'include/linux') diff --git a/Documentation/admin-guide/devices.txt b/Documentation/admin-guide/devices.txt index 94c98be1329a..0fbde7d8dc78 100644 --- a/Documentation/admin-guide/devices.txt +++ b/Documentation/admin-guide/devices.txt @@ -352,7 +352,7 @@ 216 = /dev/fujitsu/apanel Fujitsu/Siemens application panel 217 = /dev/ni/natmotn National Instruments Motion 218 = /dev/kchuid Inter-process chuid control - 219 = /dev/modems/mwave MWave modem firmware upload + 219 = 220 = /dev/mptctl Message passing technology (MPT) control 221 = /dev/mvista/hssdsi Montavista PICMG hot swap system driver 222 = /dev/mvista/hasi Montavista PICMG high availability diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 7232b3544cec..6fbd9fbcc43f 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -397,7 +397,6 @@ Code Seq# Include File Comments 0xCD 01 linux/reiserfs_fs.h Dead since 6.13 0xCE 01-02 uapi/linux/cxl_mem.h Compute Express Link Memory Devices 0xCF 02 fs/smb/client/cifs_ioctl.h -0xDB 00-0F drivers/char/mwave/mwavepub.h 0xDD 00-3F ZFCP device driver see drivers/s390/scsi/ 0xE5 00-3F linux/fuse.h diff --git a/drivers/char/Kconfig b/drivers/char/Kconfig index d2cfc584e202..2a3a37b2cf3c 100644 --- a/drivers/char/Kconfig +++ b/drivers/char/Kconfig @@ -249,32 +249,6 @@ config SONYPI To compile this driver as a module, choose M here: the module will be called sonypi. -config MWAVE - tristate "ACP Modem (Mwave) support" - depends on X86 && TTY - select SERIAL_8250 - help - The ACP modem (Mwave) for Linux is a WinModem. It is composed of a - kernel driver and a user level application. Together these components - support direct attachment to public switched telephone networks (PSTNs) - and support selected world wide countries. - - This version of the ACP Modem driver supports the IBM Thinkpad 600E, - 600, and 770 that include on board ACP modem hardware. - - The modem also supports the standard communications port interface - (ttySx) and is compatible with the Hayes AT Command Set. - - The user level application needed to use this driver can be found at - the IBM Linux Technology Center (LTC) web site: - . - - If you own one of the above IBM Thinkpads which has the Mwave chipset - in it, say Y. - - To compile this driver as a module, choose M here: the - module will be called mwave. - config SCx200_GPIO tristate "NatSemi SCx200 GPIO Support" depends on SCx200 diff --git a/drivers/char/Makefile b/drivers/char/Makefile index 1291369b9126..47bdc882797a 100644 --- a/drivers/char/Makefile +++ b/drivers/char/Makefile @@ -33,7 +33,6 @@ obj-$(CONFIG_PC8736x_GPIO) += pc8736x_gpio.o obj-$(CONFIG_NSC_GPIO) += nsc_gpio.o obj-$(CONFIG_TELCLOCK) += tlclk.o -obj-$(CONFIG_MWAVE) += mwave/ obj-y += agp/ obj-$(CONFIG_HANGCHECK_TIMER) += hangcheck-timer.o diff --git a/drivers/char/mwave/3780i.c b/drivers/char/mwave/3780i.c deleted file mode 100644 index 90f93cefb21c..000000000000 --- a/drivers/char/mwave/3780i.c +++ /dev/null @@ -1,536 +0,0 @@ -/* -* -* 3780i.c -- helper routines for the 3780i DSP -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#define pr_fmt(fmt) "3780i: " fmt - -#include -#include -#include -#include -#include -#include /* cond_resched() */ - -#include -#include -#include -#include "smapi.h" -#include "mwavedd.h" -#include "3780i.h" - -static DEFINE_SPINLOCK(dsp_lock); - -static void PaceMsaAccess(unsigned short usDspBaseIO) -{ - cond_resched(); - udelay(100); - cond_resched(); -} - -unsigned short dsp3780I_ReadMsaCfg(unsigned short usDspBaseIO, - unsigned long ulMsaAddr) -{ - unsigned long flags; - unsigned short val; - - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulMsaAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulMsaAddr >> 16)); - val = InWordDsp(DSP_MsaDataDSISHigh); - spin_unlock_irqrestore(&dsp_lock, flags); - - return val; -} - -void dsp3780I_WriteMsaCfg(unsigned short usDspBaseIO, - unsigned long ulMsaAddr, unsigned short usValue) -{ - unsigned long flags; - - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulMsaAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulMsaAddr >> 16)); - OutWordDsp(DSP_MsaDataDSISHigh, usValue); - spin_unlock_irqrestore(&dsp_lock, flags); -} - -static void dsp3780I_WriteGenCfg(unsigned short usDspBaseIO, unsigned uIndex, - unsigned char ucValue) -{ - DSP_ISA_SLAVE_CONTROL rSlaveControl; - DSP_ISA_SLAVE_CONTROL rSlaveControl_Save; - - MKBYTE(rSlaveControl) = InByteDsp(DSP_IsaSlaveControl); - - rSlaveControl_Save = rSlaveControl; - rSlaveControl.ConfigMode = true; - - OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl)); - OutByteDsp(DSP_ConfigAddress, (unsigned char) uIndex); - OutByteDsp(DSP_ConfigData, ucValue); - OutByteDsp(DSP_IsaSlaveControl, MKBYTE(rSlaveControl_Save)); -} - -int dsp3780I_EnableDSP(struct dsp_3780i_config_settings *pSettings, - unsigned short *pIrqMap, - unsigned short *pDmaMap) -{ - unsigned long flags; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - int i; - DSP_UART_CFG_1 rUartCfg1; - DSP_UART_CFG_2 rUartCfg2; - DSP_HBRIDGE_CFG_1 rHBridgeCfg1; - DSP_HBRIDGE_CFG_2 rHBridgeCfg2; - DSP_BUSMASTER_CFG_1 rBusmasterCfg1; - DSP_BUSMASTER_CFG_2 rBusmasterCfg2; - DSP_ISA_PROT_CFG rIsaProtCfg; - DSP_POWER_MGMT_CFG rPowerMgmtCfg; - DSP_HBUS_TIMER_CFG rHBusTimerCfg; - DSP_LBUS_TIMEOUT_DISABLE rLBusTimeoutDisable; - DSP_CHIP_RESET rChipReset; - DSP_CLOCK_CONTROL_1 rClockControl1; - DSP_CLOCK_CONTROL_2 rClockControl2; - DSP_ISA_SLAVE_CONTROL rSlaveControl; - DSP_HBRIDGE_CONTROL rHBridgeControl; - unsigned short tval; - - if (!pSettings->bDSPEnabled) { - pr_err("%s: Error: DSP not enabled. Aborting.\n", __func__); - return -EIO; - } - - if (pSettings->bModemEnabled) { - rUartCfg1.Reserved = rUartCfg2.Reserved = 0; - rUartCfg1.IrqActiveLow = pSettings->bUartIrqActiveLow; - rUartCfg1.IrqPulse = pSettings->bUartIrqPulse; - rUartCfg1.Irq = - (unsigned char) pIrqMap[pSettings->usUartIrq]; - switch (pSettings->usUartBaseIO) { - case 0x03F8: - rUartCfg1.BaseIO = 0; - break; - case 0x02F8: - rUartCfg1.BaseIO = 1; - break; - case 0x03E8: - rUartCfg1.BaseIO = 2; - break; - case 0x02E8: - rUartCfg1.BaseIO = 3; - break; - } - rUartCfg2.Enable = true; - } - - rHBridgeCfg1.Reserved = rHBridgeCfg2.Reserved = 0; - rHBridgeCfg1.IrqActiveLow = pSettings->bDspIrqActiveLow; - rHBridgeCfg1.IrqPulse = pSettings->bDspIrqPulse; - rHBridgeCfg1.Irq = (unsigned char) pIrqMap[pSettings->usDspIrq]; - rHBridgeCfg1.AccessMode = 1; - rHBridgeCfg2.Enable = true; - - - rBusmasterCfg2.Reserved = 0; - rBusmasterCfg1.Dma = (unsigned char) pDmaMap[pSettings->usDspDma]; - rBusmasterCfg1.NumTransfers = - (unsigned char) pSettings->usNumTransfers; - rBusmasterCfg1.ReRequest = (unsigned char) pSettings->usReRequest; - rBusmasterCfg1.MEMCS16 = pSettings->bEnableMEMCS16; - rBusmasterCfg2.IsaMemCmdWidth = - (unsigned char) pSettings->usIsaMemCmdWidth; - - - rIsaProtCfg.Reserved = 0; - rIsaProtCfg.GateIOCHRDY = pSettings->bGateIOCHRDY; - - rPowerMgmtCfg.Reserved = 0; - rPowerMgmtCfg.Enable = pSettings->bEnablePwrMgmt; - - rHBusTimerCfg.LoadValue = - (unsigned char) pSettings->usHBusTimerLoadValue; - - rLBusTimeoutDisable.Reserved = 0; - rLBusTimeoutDisable.DisableTimeout = - pSettings->bDisableLBusTimeout; - - MKWORD(rChipReset) = ~pSettings->usChipletEnable; - - rClockControl1.Reserved1 = rClockControl1.Reserved2 = 0; - rClockControl1.N_Divisor = pSettings->usN_Divisor; - rClockControl1.M_Multiplier = pSettings->usM_Multiplier; - - rClockControl2.Reserved = 0; - rClockControl2.PllBypass = pSettings->bPllBypass; - - /* Issue a soft reset to the chip */ - /* Note: Since we may be coming in with 3780i clocks suspended, we must keep - * soft-reset active for 10ms. - */ - rSlaveControl.ClockControl = 0; - rSlaveControl.SoftReset = true; - rSlaveControl.ConfigMode = false; - rSlaveControl.Reserved = 0; - - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); - MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl); - - for (i = 0; i < 11; i++) - udelay(2000); - - rSlaveControl.SoftReset = false; - OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); - - MKWORD(tval) = InWordDsp(DSP_IsaSlaveControl); - - /* Program our general configuration registers */ - WriteGenCfg(DSP_HBridgeCfg1Index, MKBYTE(rHBridgeCfg1)); - WriteGenCfg(DSP_HBridgeCfg2Index, MKBYTE(rHBridgeCfg2)); - WriteGenCfg(DSP_BusMasterCfg1Index, MKBYTE(rBusmasterCfg1)); - WriteGenCfg(DSP_BusMasterCfg2Index, MKBYTE(rBusmasterCfg2)); - WriteGenCfg(DSP_IsaProtCfgIndex, MKBYTE(rIsaProtCfg)); - WriteGenCfg(DSP_PowerMgCfgIndex, MKBYTE(rPowerMgmtCfg)); - WriteGenCfg(DSP_HBusTimerCfgIndex, MKBYTE(rHBusTimerCfg)); - - if (pSettings->bModemEnabled) { - WriteGenCfg(DSP_UartCfg1Index, MKBYTE(rUartCfg1)); - WriteGenCfg(DSP_UartCfg2Index, MKBYTE(rUartCfg2)); - } - - - rHBridgeControl.EnableDspInt = false; - rHBridgeControl.MemAutoInc = true; - rHBridgeControl.IoAutoInc = false; - rHBridgeControl.DiagnosticMode = false; - - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - WriteMsaCfg(DSP_LBusTimeoutDisable, MKWORD(rLBusTimeoutDisable)); - WriteMsaCfg(DSP_ClockControl_1, MKWORD(rClockControl1)); - WriteMsaCfg(DSP_ClockControl_2, MKWORD(rClockControl2)); - WriteMsaCfg(DSP_ChipReset, MKWORD(rChipReset)); - - ReadMsaCfg(DSP_ChipID); - - return 0; -} - -int dsp3780I_DisableDSP(struct dsp_3780i_config_settings *pSettings) -{ - unsigned long flags; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - DSP_ISA_SLAVE_CONTROL rSlaveControl; - - rSlaveControl.ClockControl = 0; - rSlaveControl.SoftReset = true; - rSlaveControl.ConfigMode = false; - rSlaveControl.Reserved = 0; - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); - - udelay(5); - - rSlaveControl.ClockControl = 1; - OutWordDsp(DSP_IsaSlaveControl, MKWORD(rSlaveControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - - udelay(5); - - return 0; -} - -int dsp3780I_Reset(struct dsp_3780i_config_settings *pSettings) -{ - unsigned long flags; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - DSP_BOOT_DOMAIN rBootDomain; - DSP_HBRIDGE_CONTROL rHBridgeControl; - - spin_lock_irqsave(&dsp_lock, flags); - /* Mask DSP to PC interrupt */ - MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); - - rHBridgeControl.EnableDspInt = false; - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Reset the core via the boot domain register */ - rBootDomain.ResetCore = true; - rBootDomain.Halt = true; - rBootDomain.NMI = true; - rBootDomain.Reserved = 0; - - WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); - - /* Reset all the chiplets and then reactivate them */ - WriteMsaCfg(DSP_ChipReset, 0xFFFF); - udelay(5); - WriteMsaCfg(DSP_ChipReset, - (unsigned short) (~pSettings->usChipletEnable)); - - return 0; -} - - -int dsp3780I_Run(struct dsp_3780i_config_settings *pSettings) -{ - unsigned long flags; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - DSP_BOOT_DOMAIN rBootDomain; - DSP_HBRIDGE_CONTROL rHBridgeControl; - - /* Transition the core to a running state */ - rBootDomain.ResetCore = true; - rBootDomain.Halt = false; - rBootDomain.NMI = true; - rBootDomain.Reserved = 0; - WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); - - udelay(5); - - rBootDomain.ResetCore = false; - WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); - udelay(5); - - rBootDomain.NMI = false; - WriteMsaCfg(DSP_MspBootDomain, MKWORD(rBootDomain)); - udelay(5); - - /* Enable DSP to PC interrupt */ - spin_lock_irqsave(&dsp_lock, flags); - MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); - rHBridgeControl.EnableDspInt = true; - - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - - return 0; -} - - -int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - unsigned short val; - - /* Set the initial MSA address. No adjustments need to be made to data store addresses */ - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - spin_lock_irqsave(&dsp_lock, flags); - val = InWordDsp(DSP_MsaDataDSISHigh); - spin_unlock_irqrestore(&dsp_lock, flags); - if(put_user(val, pusBuffer++)) - return -EFAULT; - - PaceMsaAccess(usDspBaseIO); - } - - return 0; -} - -int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO, - void __user *pvBuffer, unsigned uCount, - unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - unsigned short val; - - /* Set the initial MSA address. No adjustments need to be made to data store addresses */ - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - spin_lock_irqsave(&dsp_lock, flags); - val = InWordDsp(DSP_ReadAndClear); - spin_unlock_irqrestore(&dsp_lock, flags); - if(put_user(val, pusBuffer++)) - return -EFAULT; - - PaceMsaAccess(usDspBaseIO); - } - - return 0; -} - - -int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - - /* Set the initial MSA address. No adjustments need to be made to data store addresses */ - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - unsigned short val; - if(get_user(val, pusBuffer++)) - return -EFAULT; - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaDataDSISHigh, val); - spin_unlock_irqrestore(&dsp_lock, flags); - - PaceMsaAccess(usDspBaseIO); - } - - return 0; -} - - -int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - - /* - * Set the initial MSA address. To convert from an instruction store - * address to an MSA address - * shift the address two bits to the left and set bit 22 - */ - ulDSPAddr = (ulDSPAddr << 2) | (1 << 22); - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - unsigned short val_lo, val_hi; - spin_lock_irqsave(&dsp_lock, flags); - val_lo = InWordDsp(DSP_MsaDataISLow); - val_hi = InWordDsp(DSP_MsaDataDSISHigh); - spin_unlock_irqrestore(&dsp_lock, flags); - if(put_user(val_lo, pusBuffer++)) - return -EFAULT; - if(put_user(val_hi, pusBuffer++)) - return -EFAULT; - - PaceMsaAccess(usDspBaseIO); - - } - - return 0; -} - - -int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr) -{ - unsigned long flags; - unsigned short __user *pusBuffer = pvBuffer; - - /* - * Set the initial MSA address. To convert from an instruction store - * address to an MSA address - * shift the address two bits to the left and set bit 22 - */ - ulDSPAddr = (ulDSPAddr << 2) | (1 << 22); - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaAddrLow, (unsigned short) ulDSPAddr); - OutWordDsp(DSP_MsaAddrHigh, (unsigned short) (ulDSPAddr >> 16)); - spin_unlock_irqrestore(&dsp_lock, flags); - - /* Transfer the memory block */ - while (uCount-- != 0) { - unsigned short val_lo, val_hi; - if(get_user(val_lo, pusBuffer++)) - return -EFAULT; - if(get_user(val_hi, pusBuffer++)) - return -EFAULT; - spin_lock_irqsave(&dsp_lock, flags); - OutWordDsp(DSP_MsaDataISLow, val_lo); - OutWordDsp(DSP_MsaDataDSISHigh, val_hi); - spin_unlock_irqrestore(&dsp_lock, flags); - - PaceMsaAccess(usDspBaseIO); - } - - return 0; -} - - -int dsp3780I_GetIPCSource(unsigned short usDspBaseIO, - unsigned short *pusIPCSource) -{ - unsigned long flags; - DSP_HBRIDGE_CONTROL rHBridgeControl; - - /* - * Disable DSP to PC interrupts, read the interrupt register, - * clear the pending IPC bits, and reenable DSP to PC interrupts - */ - spin_lock_irqsave(&dsp_lock, flags); - MKWORD(rHBridgeControl) = InWordDsp(DSP_HBridgeControl); - rHBridgeControl.EnableDspInt = false; - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - - *pusIPCSource = InWordDsp(DSP_Interrupt); - OutWordDsp(DSP_Interrupt, (unsigned short) ~(*pusIPCSource)); - - rHBridgeControl.EnableDspInt = true; - OutWordDsp(DSP_HBridgeControl, MKWORD(rHBridgeControl)); - spin_unlock_irqrestore(&dsp_lock, flags); - - return 0; -} diff --git a/drivers/char/mwave/3780i.h b/drivers/char/mwave/3780i.h deleted file mode 100644 index 53dafceb20e0..000000000000 --- a/drivers/char/mwave/3780i.h +++ /dev/null @@ -1,358 +0,0 @@ -/* -* -* 3780i.h -- declarations for 3780i.c -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_3780I_H -#define _LINUX_3780I_H - -#include - -/* DSP I/O port offsets and definitions */ -#define DSP_IsaSlaveControl 0x0000 /* ISA slave control register */ -#define DSP_IsaSlaveStatus 0x0001 /* ISA slave status register */ -#define DSP_ConfigAddress 0x0002 /* General config address register */ -#define DSP_ConfigData 0x0003 /* General config data register */ -#define DSP_HBridgeControl 0x0002 /* HBridge control register */ -#define DSP_MsaAddrLow 0x0004 /* MSP System Address, low word */ -#define DSP_MsaAddrHigh 0x0006 /* MSP System Address, high word */ -#define DSP_MsaDataDSISHigh 0x0008 /* MSA data register: d-store word or high byte of i-store */ -#define DSP_MsaDataISLow 0x000A /* MSA data register: low word of i-store */ -#define DSP_ReadAndClear 0x000C /* MSA read and clear data register */ -#define DSP_Interrupt 0x000E /* Interrupt register (IPC source) */ - -typedef struct { - unsigned char ClockControl:1; /* RW: Clock control: 0=normal, 1=stop 3780i clocks */ - unsigned char SoftReset:1; /* RW: Soft reset 0=normal, 1=soft reset active */ - unsigned char ConfigMode:1; /* RW: Configuration mode, 0=normal, 1=config mode */ - unsigned short Reserved:13; /* 0: Reserved */ -} DSP_ISA_SLAVE_CONTROL; - - -typedef struct { - unsigned short EnableDspInt:1; /* RW: Enable DSP to X86 ISA interrupt 0=mask it, 1=enable it */ - unsigned short MemAutoInc:1; /* RW: Memory address auto increment, 0=disable, 1=enable */ - unsigned short IoAutoInc:1; /* RW: I/O address auto increment, 0=disable, 1=enable */ - unsigned short DiagnosticMode:1; /* RW: Disgnostic mode 0=nromal, 1=diagnostic mode */ - unsigned short IsaPacingTimer:12; /* R: ISA access pacing timer: count of core cycles stolen */ -} DSP_HBRIDGE_CONTROL; - - -/* DSP register indexes used with the configuration register address (index) register */ -#define DSP_UartCfg1Index 0x0003 /* UART config register 1 */ -#define DSP_UartCfg2Index 0x0004 /* UART config register 2 */ -#define DSP_HBridgeCfg1Index 0x0007 /* HBridge config register 1 */ -#define DSP_HBridgeCfg2Index 0x0008 /* HBridge config register 2 */ -#define DSP_BusMasterCfg1Index 0x0009 /* ISA bus master config register 1 */ -#define DSP_BusMasterCfg2Index 0x000A /* ISA bus master config register 2 */ -#define DSP_IsaProtCfgIndex 0x000F /* ISA protocol control register */ -#define DSP_PowerMgCfgIndex 0x0010 /* Low poser suspend/resume enable */ -#define DSP_HBusTimerCfgIndex 0x0011 /* HBUS timer load value */ - -typedef struct { - unsigned char IrqActiveLow:1; /* RW: IRQ active high or low: 0=high, 1=low */ - unsigned char IrqPulse:1; /* RW: IRQ pulse or level: 0=level, 1=pulse */ - unsigned char Irq:3; /* RW: IRQ selection */ - unsigned char BaseIO:2; /* RW: Base I/O selection */ - unsigned char Reserved:1; /* 0: Reserved */ -} DSP_UART_CFG_1; - -typedef struct { - unsigned char Enable:1; /* RW: Enable I/O and IRQ: 0=false, 1=true */ - unsigned char Reserved:7; /* 0: Reserved */ -} DSP_UART_CFG_2; - -typedef struct { - unsigned char IrqActiveLow:1; /* RW: IRQ active high=0 or low=1 */ - unsigned char IrqPulse:1; /* RW: IRQ pulse=1 or level=0 */ - unsigned char Irq:3; /* RW: IRQ selection */ - unsigned char AccessMode:1; /* RW: 16-bit register access method 0=byte, 1=word */ - unsigned char Reserved:2; /* 0: Reserved */ -} DSP_HBRIDGE_CFG_1; - -typedef struct { - unsigned char Enable:1; /* RW: enable I/O and IRQ: 0=false, 1=true */ - unsigned char Reserved:7; /* 0: Reserved */ -} DSP_HBRIDGE_CFG_2; - - -typedef struct { - unsigned char Dma:3; /* RW: DMA channel selection */ - unsigned char NumTransfers:2; /* RW: Maximum # of transfers once being granted the ISA bus */ - unsigned char ReRequest:2; /* RW: Minimum delay between releasing the ISA bus and requesting it again */ - unsigned char MEMCS16:1; /* RW: ISA signal MEMCS16: 0=disabled, 1=enabled */ -} DSP_BUSMASTER_CFG_1; - -typedef struct { - unsigned char IsaMemCmdWidth:2; /* RW: ISA memory command width */ - unsigned char Reserved:6; /* 0: Reserved */ -} DSP_BUSMASTER_CFG_2; - - -typedef struct { - unsigned char GateIOCHRDY:1; /* RW: Enable IOCHRDY gating: 0=false, 1=true */ - unsigned char Reserved:7; /* 0: Reserved */ -} DSP_ISA_PROT_CFG; - -typedef struct { - unsigned char Enable:1; /* RW: Enable low power suspend/resume 0=false, 1=true */ - unsigned char Reserved:7; /* 0: Reserved */ -} DSP_POWER_MGMT_CFG; - -typedef struct { - unsigned char LoadValue:8; /* RW: HBUS timer load value */ -} DSP_HBUS_TIMER_CFG; - - - -/* DSP registers that exist in MSA I/O space */ -#define DSP_ChipID 0x80000000 -#define DSP_MspBootDomain 0x80000580 -#define DSP_LBusTimeoutDisable 0x80000580 -#define DSP_ClockControl_1 0x8000058A -#define DSP_ClockControl_2 0x8000058C -#define DSP_ChipReset 0x80000588 -#define DSP_GpioModeControl_15_8 0x80000082 -#define DSP_GpioDriverEnable_15_8 0x80000076 -#define DSP_GpioOutputData_15_8 0x80000072 - -typedef struct { - unsigned short NMI:1; /* RW: non maskable interrupt */ - unsigned short Halt:1; /* RW: Halt MSP clock */ - unsigned short ResetCore:1; /* RW: Reset MSP core interface */ - unsigned short Reserved:13; /* 0: Reserved */ -} DSP_BOOT_DOMAIN; - -typedef struct { - unsigned short DisableTimeout:1; /* RW: Disable LBus timeout */ - unsigned short Reserved:15; /* 0: Reserved */ -} DSP_LBUS_TIMEOUT_DISABLE; - -typedef struct { - unsigned short Memory:1; /* RW: Reset memory interface */ - unsigned short SerialPort1:1; /* RW: Reset serial port 1 interface */ - unsigned short SerialPort2:1; /* RW: Reset serial port 2 interface */ - unsigned short SerialPort3:1; /* RW: Reset serial port 3 interface */ - unsigned short Gpio:1; /* RW: Reset GPIO interface */ - unsigned short Dma:1; /* RW: Reset DMA interface */ - unsigned short SoundBlaster:1; /* RW: Reset soundblaster interface */ - unsigned short Uart:1; /* RW: Reset UART interface */ - unsigned short Midi:1; /* RW: Reset MIDI interface */ - unsigned short IsaMaster:1; /* RW: Reset ISA master interface */ - unsigned short Reserved:6; /* 0: Reserved */ -} DSP_CHIP_RESET; - -typedef struct { - unsigned short N_Divisor:6; /* RW: (N) PLL output clock divisor */ - unsigned short Reserved1:2; /* 0: reserved */ - unsigned short M_Multiplier:6; /* RW: (M) PLL feedback clock multiplier */ - unsigned short Reserved2:2; /* 0: reserved */ -} DSP_CLOCK_CONTROL_1; - -typedef struct { - unsigned short PllBypass:1; /* RW: PLL Bypass */ - unsigned short Reserved:15; /* 0: Reserved */ -} DSP_CLOCK_CONTROL_2; - -typedef struct { - unsigned short Latch8:1; - unsigned short Latch9:1; - unsigned short Latch10:1; - unsigned short Latch11:1; - unsigned short Latch12:1; - unsigned short Latch13:1; - unsigned short Latch14:1; - unsigned short Latch15:1; - unsigned short Mask8:1; - unsigned short Mask9:1; - unsigned short Mask10:1; - unsigned short Mask11:1; - unsigned short Mask12:1; - unsigned short Mask13:1; - unsigned short Mask14:1; - unsigned short Mask15:1; -} DSP_GPIO_OUTPUT_DATA_15_8; - -typedef struct { - unsigned short Enable8:1; - unsigned short Enable9:1; - unsigned short Enable10:1; - unsigned short Enable11:1; - unsigned short Enable12:1; - unsigned short Enable13:1; - unsigned short Enable14:1; - unsigned short Enable15:1; - unsigned short Mask8:1; - unsigned short Mask9:1; - unsigned short Mask10:1; - unsigned short Mask11:1; - unsigned short Mask12:1; - unsigned short Mask13:1; - unsigned short Mask14:1; - unsigned short Mask15:1; -} DSP_GPIO_DRIVER_ENABLE_15_8; - -typedef struct { - unsigned short GpioMode8:2; - unsigned short GpioMode9:2; - unsigned short GpioMode10:2; - unsigned short GpioMode11:2; - unsigned short GpioMode12:2; - unsigned short GpioMode13:2; - unsigned short GpioMode14:2; - unsigned short GpioMode15:2; -} DSP_GPIO_MODE_15_8; - -/* Component masks that are defined in dspmgr.h */ -#define MW_ADC_MASK 0x0001 -#define MW_AIC2_MASK 0x0006 -#define MW_MIDI_MASK 0x0008 -#define MW_CDDAC_MASK 0x8001 -#define MW_AIC1_MASK 0xE006 -#define MW_UART_MASK 0xE00A -#define MW_ACI_MASK 0xE00B - -/* -* Definition of 3780i configuration structure. Unless otherwise stated, -* these values are provided as input to the 3780i support layer. At present, -* the only values maintained by the 3780i support layer are the saved UART -* registers. -*/ -struct dsp_3780i_config_settings { - - /* Location of base configuration register */ - unsigned short usBaseConfigIO; - - /* Enables for various DSP components */ - int bDSPEnabled; - int bModemEnabled; - int bInterruptClaimed; - - /* IRQ, DMA, and Base I/O addresses for various DSP components */ - unsigned short usDspIrq; - unsigned short usDspDma; - unsigned short usDspBaseIO; - unsigned short usUartIrq; - unsigned short usUartBaseIO; - - /* IRQ modes for various DSP components */ - int bDspIrqActiveLow; - int bUartIrqActiveLow; - int bDspIrqPulse; - int bUartIrqPulse; - - /* Card abilities */ - unsigned uIps; - unsigned uDStoreSize; - unsigned uIStoreSize; - unsigned uDmaBandwidth; - - /* Adapter specific 3780i settings */ - unsigned short usNumTransfers; - unsigned short usReRequest; - int bEnableMEMCS16; - unsigned short usIsaMemCmdWidth; - int bGateIOCHRDY; - int bEnablePwrMgmt; - unsigned short usHBusTimerLoadValue; - int bDisableLBusTimeout; - unsigned short usN_Divisor; - unsigned short usM_Multiplier; - int bPllBypass; - unsigned short usChipletEnable; /* Used with the chip reset register to enable specific chiplets */ - - /* Saved UART registers. These are maintained by the 3780i support layer. */ - int bUartSaved; /* True after a successful save of the UART registers */ - unsigned char ucIER; /* Interrupt enable register */ - unsigned char ucFCR; /* FIFO control register */ - unsigned char ucLCR; /* Line control register */ - unsigned char ucMCR; /* Modem control register */ - unsigned char ucSCR; /* Scratch register */ - unsigned char ucDLL; /* Divisor latch, low byte */ - unsigned char ucDLM; /* Divisor latch, high byte */ -}; - - -/* 3780i support functions */ -int dsp3780I_EnableDSP(struct dsp_3780i_config_settings *pSettings, - unsigned short *pIrqMap, - unsigned short *pDmaMap); -int dsp3780I_DisableDSP(struct dsp_3780i_config_settings *pSettings); -int dsp3780I_Reset(struct dsp_3780i_config_settings *pSettings); -int dsp3780I_Run(struct dsp_3780i_config_settings *pSettings); -int dsp3780I_ReadDStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr); -int dsp3780I_ReadAndClearDStore(unsigned short usDspBaseIO, - void __user *pvBuffer, unsigned uCount, - unsigned long ulDSPAddr); -int dsp3780I_WriteDStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr); -int dsp3780I_ReadIStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr); -int dsp3780I_WriteIStore(unsigned short usDspBaseIO, void __user *pvBuffer, - unsigned uCount, unsigned long ulDSPAddr); -unsigned short dsp3780I_ReadMsaCfg(unsigned short usDspBaseIO, - unsigned long ulMsaAddr); -void dsp3780I_WriteMsaCfg(unsigned short usDspBaseIO, - unsigned long ulMsaAddr, unsigned short usValue); -int dsp3780I_GetIPCSource(unsigned short usDspBaseIO, - unsigned short *pusIPCSource); - -/* I/O port access macros */ -#define MKWORD(var) (*((unsigned short *)(&var))) -#define MKBYTE(var) (*((unsigned char *)(&var))) - -#define WriteMsaCfg(addr,value) dsp3780I_WriteMsaCfg(usDspBaseIO,addr,value) -#define ReadMsaCfg(addr) dsp3780I_ReadMsaCfg(usDspBaseIO,addr) -#define WriteGenCfg(index,value) dsp3780I_WriteGenCfg(usDspBaseIO,index,value) -#define ReadGenCfg(index) dsp3780I_ReadGenCfg(usDspBaseIO,index) - -#define InWordDsp(index) inw(usDspBaseIO+index) -#define InByteDsp(index) inb(usDspBaseIO+index) -#define OutWordDsp(index,value) outw(value,usDspBaseIO+index) -#define OutByteDsp(index,value) outb(value,usDspBaseIO+index) - -#endif diff --git a/drivers/char/mwave/Makefile b/drivers/char/mwave/Makefile deleted file mode 100644 index e56c1a375535..000000000000 --- a/drivers/char/mwave/Makefile +++ /dev/null @@ -1,10 +0,0 @@ -# SPDX-License-Identifier: GPL-2.0-only -# -# Makefile for ACP Modem (Mwave). -# -# See the README file in this directory for more info. -# - -obj-$(CONFIG_MWAVE) += mwave.o - -mwave-y := mwavedd.o smapi.o tp3780i.o 3780i.o diff --git a/drivers/char/mwave/README b/drivers/char/mwave/README deleted file mode 100644 index 6224aa814c62..000000000000 --- a/drivers/char/mwave/README +++ /dev/null @@ -1,37 +0,0 @@ -Module options --------------- - -The mwave module takes the following options. Note that these options -are not saved by the BIOS and so do not persist after unload and reload. - - mwave_3780i_irq=5/7/10/11/15 - If the dsp irq has not been setup and stored in bios by the - thinkpad configuration utility then this parameter allows the - irq used by the dsp to be configured. - - mwave_3780i_io=0x130/0x350/0x0070/0xDB0 - If the dsp io range has not been setup and stored in bios by the - thinkpad configuration utility then this parameter allows the - io range used by the dsp to be configured. - - mwave_uart_irq=3/4 - If the mwave's uart irq has not been setup and stored in bios by the - thinkpad configuration utility then this parameter allows the - irq used by the mwave uart to be configured. - - mwave_uart_io=0x3f8/0x2f8/0x3E8/0x2E8 - If the uart io range has not been setup and stored in bios by the - thinkpad configuration utility then this parameter allows the - io range used by the mwave uart to be configured. - -Example to enable the 3780i DSP using ttyS1 resources: - - insmod mwave mwave_3780i_irq=10 mwave_3780i_io=0x0130 mwave_uart_irq=3 mwave_uart_io=0x2f8 - -Accessing the driver --------------------- - -You must also create a node for the driver: - mkdir -p /dev/modems - mknod --mode=660 /dev/modems/mwave c 10 219 - diff --git a/drivers/char/mwave/mwavedd.c b/drivers/char/mwave/mwavedd.c deleted file mode 100644 index 640a9cb0dd8d..000000000000 --- a/drivers/char/mwave/mwavedd.c +++ /dev/null @@ -1,432 +0,0 @@ -/* -* -* mwavedd.c -- mwave device driver -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#define pr_fmt(fmt) "mwavedd: " fmt - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include "smapi.h" -#include "mwavedd.h" -#include "3780i.h" -#include "tp3780i.h" - -MODULE_DESCRIPTION("3780i Advanced Communications Processor (Mwave) driver"); -MODULE_AUTHOR("Mike Sullivan and Paul Schroeder"); -MODULE_LICENSE("GPL"); - -/* -* These parameters support the setting of MWave resources. Note that no -* checks are made against other devices (ie. superio) for conflicts. -* We'll depend on users using the tpctl utility to do that for now -*/ -static DEFINE_MUTEX(mwave_mutex); -int mwave_3780i_irq = 0; -int mwave_3780i_io = 0; -int mwave_uart_irq = 0; -int mwave_uart_io = 0; -module_param_hw(mwave_3780i_irq, int, irq, 0); -module_param_hw(mwave_3780i_io, int, ioport, 0); -module_param_hw(mwave_uart_irq, int, irq, 0); -module_param_hw(mwave_uart_io, int, ioport, 0); - -struct mwave_device_data mwave_s_mdd; - -static long mwave_ioctl(struct file *file, unsigned int iocmd, - unsigned long ioarg) -{ - unsigned int retval = 0; - struct mwave_device_data *pDrvData = &mwave_s_mdd; - void __user *arg = (void __user *)ioarg; - - switch (iocmd) { - - case IOCTL_MW_RESET: - mutex_lock(&mwave_mutex); - retval = tp3780I_ResetDSP(&pDrvData->rBDData); - mutex_unlock(&mwave_mutex); - break; - - case IOCTL_MW_RUN: - mutex_lock(&mwave_mutex); - retval = tp3780I_StartDSP(&pDrvData->rBDData); - mutex_unlock(&mwave_mutex); - break; - - case IOCTL_MW_DSP_ABILITIES: { - struct mw_abilities rAbilities; - - mutex_lock(&mwave_mutex); - retval = tp3780I_QueryAbilities(&pDrvData->rBDData, - &rAbilities); - mutex_unlock(&mwave_mutex); - if (retval == 0) { - if (copy_to_user(arg, &rAbilities, sizeof(rAbilities))) - return -EFAULT; - } - } - break; - - case IOCTL_MW_READ_DATA: - case IOCTL_MW_READCLEAR_DATA: { - struct mw_readwrite rReadData; - unsigned short __user *pusBuffer = NULL; - - if( copy_from_user(&rReadData, arg, - sizeof(struct mw_readwrite)) ) - return -EFAULT; - pusBuffer = (unsigned short __user *) (rReadData.pBuf); - - mutex_lock(&mwave_mutex); - retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData, - iocmd, - pusBuffer, - rReadData.ulDataLength, - rReadData.usDspAddress); - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_READ_INST: { - struct mw_readwrite rReadData; - unsigned short __user *pusBuffer = NULL; - - if (copy_from_user(&rReadData, arg, sizeof(rReadData))) - return -EFAULT; - pusBuffer = (unsigned short __user *) (rReadData.pBuf); - - mutex_lock(&mwave_mutex); - retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData, - iocmd, pusBuffer, - rReadData.ulDataLength / 2, - rReadData.usDspAddress); - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_WRITE_DATA: { - struct mw_readwrite rWriteData; - unsigned short __user *pusBuffer = NULL; - - if (copy_from_user(&rWriteData, arg, sizeof(rWriteData))) - return -EFAULT; - pusBuffer = (unsigned short __user *) (rWriteData.pBuf); - - mutex_lock(&mwave_mutex); - retval = tp3780I_ReadWriteDspDStore(&pDrvData->rBDData, - iocmd, pusBuffer, - rWriteData.ulDataLength, - rWriteData.usDspAddress); - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_WRITE_INST: { - struct mw_readwrite rWriteData; - unsigned short __user *pusBuffer = NULL; - - if (copy_from_user(&rWriteData, arg, sizeof(rWriteData))) - return -EFAULT; - pusBuffer = (unsigned short __user *)(rWriteData.pBuf); - - mutex_lock(&mwave_mutex); - retval = tp3780I_ReadWriteDspIStore(&pDrvData->rBDData, - iocmd, pusBuffer, - rWriteData.ulDataLength, - rWriteData.usDspAddress); - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_REGISTER_IPC: { - unsigned int ipcnum = (unsigned int) ioarg; - - if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { - pr_err("%s: IOCTL_MW_REGISTER_IPC: Error: Invalid ipcnum %x\n", - __func__, ipcnum); - return -EINVAL; - } - ipcnum = array_index_nospec(ipcnum, - ARRAY_SIZE(pDrvData->IPCs)); - - mutex_lock(&mwave_mutex); - pDrvData->IPCs[ipcnum].bIsHere = false; - pDrvData->IPCs[ipcnum].bIsEnabled = true; - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_GET_IPC: { - unsigned int ipcnum = (unsigned int) ioarg; - - if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { - pr_err("%s: IOCTL_MW_GET_IPC: Error: Invalid ipcnum %x\n", __func__, - ipcnum); - return -EINVAL; - } - ipcnum = array_index_nospec(ipcnum, - ARRAY_SIZE(pDrvData->IPCs)); - - mutex_lock(&mwave_mutex); - if (pDrvData->IPCs[ipcnum].bIsEnabled == true) { - DECLARE_WAITQUEUE(wait, current); - - add_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait); - pDrvData->IPCs[ipcnum].bIsHere = true; - set_current_state(TASK_INTERRUPTIBLE); - /* check whether an event was signalled by */ - /* the interrupt handler while we were gone */ - if (pDrvData->IPCs[ipcnum].usIntCount == 1) { /* first int has occurred (race condition) */ - pDrvData->IPCs[ipcnum].usIntCount = 2; /* first int has been handled */ - } else { /* either 1st int has not yet occurred, or we have already handled the first int */ - schedule(); - if (pDrvData->IPCs[ipcnum].usIntCount == 1) { - pDrvData->IPCs[ipcnum].usIntCount = 2; - } - } - pDrvData->IPCs[ipcnum].bIsHere = false; - remove_wait_queue(&pDrvData->IPCs[ipcnum].ipc_wait_queue, &wait); - set_current_state(TASK_RUNNING); - } - mutex_unlock(&mwave_mutex); - } - break; - - case IOCTL_MW_UNREGISTER_IPC: { - unsigned int ipcnum = (unsigned int) ioarg; - - if (ipcnum >= ARRAY_SIZE(pDrvData->IPCs)) { - pr_err("%s: IOCTL_MW_UNREGISTER_IPC: Error: Invalid ipcnum %x\n", - __func__, ipcnum); - return -EINVAL; - } - ipcnum = array_index_nospec(ipcnum, - ARRAY_SIZE(pDrvData->IPCs)); - mutex_lock(&mwave_mutex); - if (pDrvData->IPCs[ipcnum].bIsEnabled == true) { - pDrvData->IPCs[ipcnum].bIsEnabled = false; - if (pDrvData->IPCs[ipcnum].bIsHere == true) { - wake_up_interruptible(&pDrvData->IPCs[ipcnum].ipc_wait_queue); - } - } - mutex_unlock(&mwave_mutex); - } - break; - - default: - return -ENOTTY; - } /* switch */ - - return retval; -} - -static int register_serial_portandirq(unsigned int port, int irq) -{ - struct uart_8250_port uart; - - switch ( port ) { - case 0x3f8: - case 0x2f8: - case 0x3e8: - case 0x2e8: - /* OK */ - break; - default: - pr_err("%s: Error: Illegal port %x\n", __func__, port); - return -1; - } /* switch */ - /* port is okay */ - - switch ( irq ) { - case 3: - case 4: - case 5: - case 7: - /* OK */ - break; - default: - pr_err("%s: Error: Illegal irq %x\n", __func__, irq); - return -1; - } /* switch */ - /* irq is okay */ - - memset(&uart, 0, sizeof(uart)); - - uart.port.uartclk = 1843200; - uart.port.iobase = port; - uart.port.irq = irq; - uart.port.iotype = UPIO_PORT; - uart.port.flags = UPF_SHARE_IRQ; - return serial8250_register_8250_port(&uart); -} - -static const struct file_operations mwave_fops = { - .owner = THIS_MODULE, - .unlocked_ioctl = mwave_ioctl, - .llseek = default_llseek, -}; - -static struct miscdevice mwave_misc_dev = { MWAVE_MINOR, "mwave", &mwave_fops }; - -/* -* mwave_init is called on module load -* -* mwave_exit is called on module unload -* mwave_exit is also used to clean up after an aborted mwave_init -*/ -static void mwave_exit(void) -{ - struct mwave_device_data *pDrvData = &mwave_s_mdd; - - if ( pDrvData->sLine >= 0 ) { - serial8250_unregister_port(pDrvData->sLine); - } - if (pDrvData->bMwaveDevRegistered) { - misc_deregister(&mwave_misc_dev); - } - if (pDrvData->bDSPEnabled) { - tp3780I_DisableDSP(&pDrvData->rBDData); - } - if (pDrvData->bResourcesClaimed) { - tp3780I_ReleaseResources(&pDrvData->rBDData); - } - if (pDrvData->bBDInitialized) { - tp3780I_Cleanup(&pDrvData->rBDData); - } -} - -module_exit(mwave_exit); - -static int __init mwave_init(void) -{ - int i; - int retval = 0; - struct mwave_device_data *pDrvData = &mwave_s_mdd; - - memset(&mwave_s_mdd, 0, sizeof(mwave_s_mdd)); - - pDrvData->bBDInitialized = false; - pDrvData->bResourcesClaimed = false; - pDrvData->bDSPEnabled = false; - pDrvData->bDSPReset = false; - pDrvData->bMwaveDevRegistered = false; - pDrvData->sLine = -1; - - for (i = 0; i < ARRAY_SIZE(pDrvData->IPCs); i++) { - pDrvData->IPCs[i].bIsEnabled = false; - pDrvData->IPCs[i].bIsHere = false; - pDrvData->IPCs[i].usIntCount = 0; /* no ints received yet */ - init_waitqueue_head(&pDrvData->IPCs[i].ipc_wait_queue); - } - - retval = tp3780I_InitializeBoardData(&pDrvData->rBDData); - if (retval) { - pr_err("%s: Error: Failed to initialize board data\n", __func__); - goto cleanup_error; - } - pDrvData->bBDInitialized = true; - - retval = tp3780I_CalcResources(&pDrvData->rBDData); - if (retval) { - pr_err("%s: Error: Failed to calculate resources\n", __func__); - goto cleanup_error; - } - - retval = tp3780I_ClaimResources(&pDrvData->rBDData); - if (retval) { - pr_err("%s: Error: Failed to claim resources\n", __func__); - goto cleanup_error; - } - pDrvData->bResourcesClaimed = true; - - retval = tp3780I_EnableDSP(&pDrvData->rBDData); - if (retval) { - pr_err("%s: Error: Failed to enable DSP\n", __func__); - goto cleanup_error; - } - pDrvData->bDSPEnabled = true; - - if (misc_register(&mwave_misc_dev) < 0) { - pr_err("%s: Error: Failed to register misc device\n", __func__); - goto cleanup_error; - } - pDrvData->bMwaveDevRegistered = true; - - pDrvData->sLine = register_serial_portandirq( - pDrvData->rBDData.rDspSettings.usUartBaseIO, - pDrvData->rBDData.rDspSettings.usUartIrq - ); - if (pDrvData->sLine < 0) { - pr_err("%s: Error: Failed to register serial driver\n", __func__); - goto cleanup_error; - } - /* uart is registered */ - - /* SUCCESS! */ - return 0; - -cleanup_error: - pr_err("%s: Error: Failed to initialize\n", __func__); - mwave_exit(); /* clean up */ - - return -EIO; -} - -module_init(mwave_init); - diff --git a/drivers/char/mwave/mwavedd.h b/drivers/char/mwave/mwavedd.h deleted file mode 100644 index e1da1493eec5..000000000000 --- a/drivers/char/mwave/mwavedd.h +++ /dev/null @@ -1,90 +0,0 @@ -/* -* -* mwavedd.h -- declarations for mwave device driver -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_MWAVEDD_H -#define _LINUX_MWAVEDD_H -#include "3780i.h" -#include "tp3780i.h" -#include "smapi.h" -#include "mwavepub.h" -#include -#include -#include - -extern int mwave_3780i_irq; -extern int mwave_3780i_io; -extern int mwave_uart_irq; -extern int mwave_uart_io; - -struct mwave_ipc { - unsigned short usIntCount; /* 0=none, 1=first, 2=greater than 1st */ - bool bIsEnabled; - bool bIsHere; - /* entry spin lock */ - wait_queue_head_t ipc_wait_queue; -}; - -struct mwave_device_data { - struct thinkpad_bd_data rBDData; /* board driver's data area */ - unsigned long ulIPCSource_ISR; /* IPC source bits for recently processed intr, set during ISR processing */ - unsigned long ulIPCSource_DPC; /* IPC source bits for recently processed intr, set during DPC processing */ - bool bBDInitialized; - bool bResourcesClaimed; - bool bDSPEnabled; - bool bDSPReset; - struct mwave_ipc IPCs[16]; - bool bMwaveDevRegistered; - short sLine; - int nr_registered_attrs; - int device_registered; - -}; - -extern struct mwave_device_data mwave_s_mdd; - -#endif diff --git a/drivers/char/mwave/mwavepub.h b/drivers/char/mwave/mwavepub.h deleted file mode 100644 index 280327bdaa38..000000000000 --- a/drivers/char/mwave/mwavepub.h +++ /dev/null @@ -1,89 +0,0 @@ -/* -* -* mwavepub.h -- PUBLIC declarations for the mwave driver -* and applications using it -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_MWAVEPUB_H -#define _LINUX_MWAVEPUB_H - -#include - - -struct mw_abilities { - unsigned long instr_per_sec; - unsigned long data_size; - unsigned long inst_size; - unsigned long bus_dma_bw; - unsigned short uart_enable; - short component_count; - unsigned long component_list[7]; - char mwave_os_name[16]; - char bios_task_name[16]; -}; - - -struct mw_readwrite { - unsigned short usDspAddress; /* The dsp address */ - unsigned long ulDataLength; /* The size in bytes of the data or user buffer */ - void __user *pBuf; /* Input:variable sized buffer */ -}; - -#define IOCTL_MW_RESET _IO(MWAVE_MINOR,1) -#define IOCTL_MW_RUN _IO(MWAVE_MINOR,2) -#define IOCTL_MW_DSP_ABILITIES _IOR(MWAVE_MINOR,3,struct mw_abilities) -#define IOCTL_MW_READ_DATA _IOR(MWAVE_MINOR,4,struct mw_readwrite) -#define IOCTL_MW_READCLEAR_DATA _IOR(MWAVE_MINOR,5,struct mw_readwrite) -#define IOCTL_MW_READ_INST _IOR(MWAVE_MINOR,6,struct mw_readwrite) -#define IOCTL_MW_WRITE_DATA _IOW(MWAVE_MINOR,7,struct mw_readwrite) -#define IOCTL_MW_WRITE_INST _IOW(MWAVE_MINOR,8,struct mw_readwrite) -#define IOCTL_MW_REGISTER_IPC _IOW(MWAVE_MINOR,9,int) -#define IOCTL_MW_UNREGISTER_IPC _IOW(MWAVE_MINOR,10,int) -#define IOCTL_MW_GET_IPC _IOW(MWAVE_MINOR,11,int) -#define IOCTL_MW_TRACE _IOR(MWAVE_MINOR,12,struct mw_readwrite) - - -#endif diff --git a/drivers/char/mwave/smapi.c b/drivers/char/mwave/smapi.c deleted file mode 100644 index df6354b24339..000000000000 --- a/drivers/char/mwave/smapi.c +++ /dev/null @@ -1,404 +0,0 @@ -/* -* -* smapi.c -- SMAPI interface routines -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#define pr_fmt(fmt) "smapi: " fmt - -#include -#include /* CMOS defines */ -#include "smapi.h" -#include "mwavedd.h" - -static unsigned short g_usSmapiPort = 0; - - -static int smapi_request(unsigned short inBX, unsigned short inCX, - unsigned short inDI, unsigned short inSI, - unsigned short *outAX, unsigned short *outBX, - unsigned short *outCX, unsigned short *outDX, - unsigned short *outDI, unsigned short *outSI) -{ - unsigned short myoutAX = 2, *pmyoutAX = &myoutAX; - unsigned short myoutBX = 3, *pmyoutBX = &myoutBX; - unsigned short myoutCX = 4, *pmyoutCX = &myoutCX; - unsigned short myoutDX = 5, *pmyoutDX = &myoutDX; - unsigned short myoutDI = 6, *pmyoutDI = &myoutDI; - unsigned short myoutSI = 7, *pmyoutSI = &myoutSI; - unsigned short usSmapiOK = -EIO, *pusSmapiOK = &usSmapiOK; - unsigned int inBXCX = (inBX << 16) | inCX; - unsigned int inDISI = (inDI << 16) | inSI; - - __asm__ __volatile__("movw $0x5380,%%ax\n\t" - "movl %7,%%ebx\n\t" - "shrl $16, %%ebx\n\t" - "movw %7,%%cx\n\t" - "movl %8,%%edi\n\t" - "shrl $16,%%edi\n\t" - "movw %8,%%si\n\t" - "movw %9,%%dx\n\t" - "out %%al,%%dx\n\t" - "out %%al,$0x4F\n\t" - "cmpb $0x53,%%ah\n\t" - "je 2f\n\t" - "1:\n\t" - "orb %%ah,%%ah\n\t" - "jnz 2f\n\t" - "movw %%ax,%0\n\t" - "movw %%bx,%1\n\t" - "movw %%cx,%2\n\t" - "movw %%dx,%3\n\t" - "movw %%di,%4\n\t" - "movw %%si,%5\n\t" - "movw $1,%6\n\t" - "2:\n\t":"=m"(*(unsigned short *) pmyoutAX), - "=m"(*(unsigned short *) pmyoutBX), - "=m"(*(unsigned short *) pmyoutCX), - "=m"(*(unsigned short *) pmyoutDX), - "=m"(*(unsigned short *) pmyoutDI), - "=m"(*(unsigned short *) pmyoutSI), - "=m"(*(unsigned short *) pusSmapiOK) - :"m"(inBXCX), "m"(inDISI), "m"(g_usSmapiPort) - :"%eax", "%ebx", "%ecx", "%edx", "%edi", - "%esi"); - - *outAX = myoutAX; - *outBX = myoutBX; - *outCX = myoutCX; - *outDX = myoutDX; - *outDI = myoutDI; - *outSI = myoutSI; - - return usSmapiOK == 1 ? 0 : -EIO; -} - - -int smapi_query_DSP_cfg(struct smapi_dsp_settings *pSettings) -{ - int bRC; - unsigned short usAX, usBX, usCX, usDX, usDI, usSI; - static const unsigned short ausDspBases[] = { - 0x0030, 0x4E30, 0x8E30, 0xCE30, - 0x0130, 0x0350, 0x0070, 0x0DB0 }; - static const unsigned short ausUartBases[] = { - 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; - - bRC = smapi_request(0x1802, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) { - pr_err("%s: Error: Could not get DSP Settings. Aborting.\n", __func__); - return bRC; - } - - pSettings->bDSPPresent = ((usBX & 0x0100) != 0); - pSettings->bDSPEnabled = ((usCX & 0x0001) != 0); - pSettings->usDspIRQ = usSI & 0x00FF; - pSettings->usDspDMA = (usSI & 0xFF00) >> 8; - if ((usDI & 0x00FF) < ARRAY_SIZE(ausDspBases)) { - pSettings->usDspBaseIO = ausDspBases[usDI & 0x00FF]; - } else { - pSettings->usDspBaseIO = 0; - } - - /* check for illegal values */ - if ( pSettings->usDspBaseIO == 0 ) - pr_err("%s: Worry: DSP base I/O address is 0\n", __func__); - if ( pSettings->usDspIRQ == 0 ) - pr_err("%s: Worry: DSP IRQ line is 0\n", __func__); - - bRC = smapi_request(0x1804, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) { - pr_err("%s: Error: Could not get DSP modem settings. Aborting.\n", __func__); - return bRC; - } - - pSettings->bModemEnabled = ((usCX & 0x0001) != 0); - pSettings->usUartIRQ = usSI & 0x000F; - if (((usSI & 0xFF00) >> 8) < ARRAY_SIZE(ausUartBases)) { - pSettings->usUartBaseIO = ausUartBases[(usSI & 0xFF00) >> 8]; - } else { - pSettings->usUartBaseIO = 0; - } - - /* check for illegal values */ - if ( pSettings->usUartBaseIO == 0 ) - pr_err("%s: Worry: UART base I/O address is 0\n", __func__); - if ( pSettings->usUartIRQ == 0 ) - pr_err("%s: Worry: UART IRQ line is 0\n", __func__); - - return bRC; -} - - -int smapi_set_DSP_cfg(void) -{ - int bRC = -EIO; - int i; - unsigned short usAX, usBX, usCX, usDX, usDI, usSI; - static const unsigned short ausDspBases[] = { - 0x0030, 0x4E30, 0x8E30, 0xCE30, - 0x0130, 0x0350, 0x0070, 0x0DB0 }; - static const unsigned short ausUartBases[] = { - 0x03F8, 0x02F8, 0x03E8, 0x02E8 }; - static const unsigned short ausDspIrqs[] = { - 5, 7, 10, 11, 15 }; - static const unsigned short ausUartIrqs[] = { - 3, 4 }; - - unsigned short dspio_index = 0, uartio_index = 0; - - if (mwave_3780i_io) { - for (i = 0; i < ARRAY_SIZE(ausDspBases); i++) { - if (mwave_3780i_io == ausDspBases[i]) - break; - } - if (i == ARRAY_SIZE(ausDspBases)) { - pr_err("%s: Error: Invalid mwave_3780i_io address %x. Aborting.\n", - __func__, mwave_3780i_io); - return bRC; - } - dspio_index = i; - } - - if (mwave_3780i_irq) { - for (i = 0; i < ARRAY_SIZE(ausDspIrqs); i++) { - if (mwave_3780i_irq == ausDspIrqs[i]) - break; - } - if (i == ARRAY_SIZE(ausDspIrqs)) { - pr_err("%s: Error: Invalid mwave_3780i_irq %x. Aborting.\n", __func__, - mwave_3780i_irq); - return bRC; - } - } - - if (mwave_uart_io) { - for (i = 0; i < ARRAY_SIZE(ausUartBases); i++) { - if (mwave_uart_io == ausUartBases[i]) - break; - } - if (i == ARRAY_SIZE(ausUartBases)) { - pr_err("%s: Error: Invalid mwave_uart_io address %x. Aborting.\n", __func__, - mwave_uart_io); - return bRC; - } - uartio_index = i; - } - - - if (mwave_uart_irq) { - for (i = 0; i < ARRAY_SIZE(ausUartIrqs); i++) { - if (mwave_uart_irq == ausUartIrqs[i]) - break; - } - if (i == ARRAY_SIZE(ausUartIrqs)) { - pr_err("%s: Error: Invalid mwave_uart_irq %x. Aborting.\n", __func__, - mwave_uart_irq); - return bRC; - } - } - - if (mwave_uart_irq || mwave_uart_io) { - - /* Check serial port A */ - bRC = smapi_request(0x1402, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - /* bRC == 0 */ - if (usBX & 0x0100) { /* serial port A is present */ - if (usCX & 1) { /* serial port is enabled */ - if ((usSI & 0xFF) == mwave_uart_irq) { - pr_err("%s: Serial port A irq %x conflicts with mwave_uart_irq %x\n", - __func__, usSI & 0xFF, mwave_uart_irq); - goto exit_conflict; - } else { - if ((usSI >> 8) == uartio_index) { - pr_err("%s: Serial port A base I/O address %x conflicts with mwave uart I/O %x\n", - __func__, ausUartBases[usSI >> 8], - ausUartBases[uartio_index]); - goto exit_conflict; - } - } - } - } - - /* Check serial port B */ - bRC = smapi_request(0x1404, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - /* bRC == 0 */ - if (usBX & 0x0100) { /* serial port B is present */ - if (usCX & 1) { /* serial port is enabled */ - if ((usSI & 0xFF) == mwave_uart_irq) { - pr_err("%s: Serial port B irq %x conflicts with mwave_uart_irq %x\n", - __func__, usSI & 0xFF, mwave_uart_irq); - goto exit_conflict; - } else { - if ((usSI >> 8) == uartio_index) { - pr_err("%s: Serial port B base I/O address %x conflicts with mwave uart I/O %x\n", - __func__, ausUartBases[usSI >> 8], - ausUartBases[uartio_index]); - goto exit_conflict; - } - } - } - } - - /* Check IR port */ - bRC = smapi_request(0x1700, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - bRC = smapi_request(0x1704, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - /* bRC == 0 */ - if ((usCX & 0xff) != 0xff) { /* IR port not disabled */ - if ((usCX & 0xff) == mwave_uart_irq) { - pr_err("%s: IR port irq %x conflicts with mwave_uart_irq %x\n", - __func__, usCX & 0xff, mwave_uart_irq); - goto exit_conflict; - } else { - if ((usSI & 0xff) == uartio_index) { - pr_err("%s: IR port base I/O address %x conflicts with mwave uart I/O %x\n", - __func__, ausUartBases[usSI & 0xff], - ausUartBases[uartio_index]); - goto exit_conflict; - } - } - } - } - - bRC = smapi_request(0x1802, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - if (mwave_3780i_io) { - usDI = dspio_index; - } - if (mwave_3780i_irq) { - usSI = (usSI & 0xff00) | mwave_3780i_irq; - } - - bRC = smapi_request(0x1803, 0x0101, usDI, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - bRC = smapi_request(0x1804, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - if (mwave_uart_io) { - usSI = (usSI & 0x00ff) | (uartio_index << 8); - } - if (mwave_uart_irq) { - usSI = (usSI & 0xff00) | mwave_uart_irq; - } - bRC = smapi_request(0x1805, 0x0101, 0, usSI, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - bRC = smapi_request(0x1802, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - - bRC = smapi_request(0x1804, 0x0000, 0, 0, - &usAX, &usBX, &usCX, &usDX, &usDI, &usSI); - if (bRC) goto exit_smapi_request_error; - -/* normal exit: */ - return 0; - -exit_conflict: - /* Message has already been printed */ - return -EIO; - -exit_smapi_request_error: - pr_err("%s: exit on smapi_request error bRC %x\n", __func__, bRC); - return bRC; -} - - -int smapi_set_DSP_power_state(bool bOn) -{ - unsigned short usAX, usBX, usCX, usDX, usDI, usSI; - unsigned short usPowerFunction; - - usPowerFunction = (bOn) ? 1 : 0; - - return smapi_request(0x4901, 0x0000, 0, usPowerFunction, &usAX, &usBX, &usCX, &usDX, &usDI, - &usSI); -} - -int smapi_init(void) -{ - int retval = -EIO; - unsigned short usSmapiID = 0; - unsigned long flags; - - spin_lock_irqsave(&rtc_lock, flags); - usSmapiID = CMOS_READ(0x7C); - usSmapiID |= (CMOS_READ(0x7D) << 8); - spin_unlock_irqrestore(&rtc_lock, flags); - - if (usSmapiID == 0x5349) { - spin_lock_irqsave(&rtc_lock, flags); - g_usSmapiPort = CMOS_READ(0x7E); - g_usSmapiPort |= (CMOS_READ(0x7F) << 8); - spin_unlock_irqrestore(&rtc_lock, flags); - if (g_usSmapiPort == 0) { - pr_err("%s: ERROR unable to read from SMAPI port\n", __func__); - } else { - retval = 0; - //SmapiQuerySystemID(); - } - } else { - pr_err("%s: ERROR invalid usSmapiID\n", __func__); - retval = -ENXIO; - } - - return retval; -} diff --git a/drivers/char/mwave/smapi.h b/drivers/char/mwave/smapi.h deleted file mode 100644 index e605b16ed23c..000000000000 --- a/drivers/char/mwave/smapi.h +++ /dev/null @@ -1,76 +0,0 @@ -/* -* -* smapi.h -- declarations for SMAPI interface routines -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_SMAPI_H -#define _LINUX_SMAPI_H - -struct smapi_dsp_settings { - int bDSPPresent; - int bDSPEnabled; - int bModemEnabled; - int bMIDIEnabled; - int bSblstEnabled; - unsigned short usDspIRQ; - unsigned short usDspDMA; - unsigned short usDspBaseIO; - unsigned short usUartIRQ; - unsigned short usUartBaseIO; - unsigned short usMidiIRQ; - unsigned short usMidiBaseIO; - unsigned short usSndblstIRQ; - unsigned short usSndblstDMA; - unsigned short usSndblstBaseIO; -}; - -int smapi_init(void); -int smapi_query_DSP_cfg(struct smapi_dsp_settings *pSettings); -int smapi_set_DSP_cfg(void); -int smapi_set_DSP_power_state(bool bOn); - - -#endif diff --git a/drivers/char/mwave/tp3780i.c b/drivers/char/mwave/tp3780i.c deleted file mode 100644 index 7363b0f764e0..000000000000 --- a/drivers/char/mwave/tp3780i.c +++ /dev/null @@ -1,477 +0,0 @@ -/* -* -* tp3780i.c -- board driver for 3780i on ThinkPads -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#define pr_fmt(fmt) "tp3780i: " fmt - -#include -#include -#include -#include -#include -#include "smapi.h" -#include "mwavedd.h" -#include "tp3780i.h" -#include "3780i.h" -#include "mwavepub.h" - -static unsigned short s_ausThinkpadIrqToField[16] = - { 0xFFFF, 0xFFFF, 0xFFFF, 0x0001, 0x0002, 0x0003, 0xFFFF, 0x0004, - 0xFFFF, 0xFFFF, 0x0005, 0x0006, 0xFFFF, 0xFFFF, 0xFFFF, 0x0007 }; -static unsigned short s_ausThinkpadDmaToField[8] = - { 0x0001, 0x0002, 0xFFFF, 0xFFFF, 0xFFFF, 0xFFFF, 0x0003, 0x0004 }; -static unsigned short s_numIrqs = 16, s_numDmas = 8; - - -static void EnableSRAM(struct thinkpad_bd_data *pBDData) -{ - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - DSP_GPIO_OUTPUT_DATA_15_8 rGpioOutputData; - DSP_GPIO_DRIVER_ENABLE_15_8 rGpioDriverEnable; - DSP_GPIO_MODE_15_8 rGpioMode; - - MKWORD(rGpioMode) = ReadMsaCfg(DSP_GpioModeControl_15_8); - rGpioMode.GpioMode10 = 0; - WriteMsaCfg(DSP_GpioModeControl_15_8, MKWORD(rGpioMode)); - - MKWORD(rGpioDriverEnable) = 0; - rGpioDriverEnable.Enable10 = true; - rGpioDriverEnable.Mask10 = true; - WriteMsaCfg(DSP_GpioDriverEnable_15_8, MKWORD(rGpioDriverEnable)); - - MKWORD(rGpioOutputData) = 0; - rGpioOutputData.Latch10 = 0; - rGpioOutputData.Mask10 = true; - WriteMsaCfg(DSP_GpioOutputData_15_8, MKWORD(rGpioOutputData)); -} - - -static irqreturn_t UartInterrupt(int irq, void *dev_id) -{ - return IRQ_HANDLED; -} - -static irqreturn_t DspInterrupt(int irq, void *dev_id) -{ - struct mwave_device_data *pDrvData = &mwave_s_mdd; - struct dsp_3780i_config_settings *pSettings = &pDrvData->rBDData.rDspSettings; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - unsigned short usIPCSource = 0, usIsolationMask, usPCNum; - - if (dsp3780I_GetIPCSource(usDspBaseIO, &usIPCSource) == 0) { - usIsolationMask = 1; - for (usPCNum = 1; usPCNum <= 16; usPCNum++) { - if (usIPCSource & usIsolationMask) { - usIPCSource &= ~usIsolationMask; - if (pDrvData->IPCs[usPCNum - 1].usIntCount == 0) { - pDrvData->IPCs[usPCNum - 1].usIntCount = 1; - } - if (pDrvData->IPCs[usPCNum - 1].bIsEnabled == true) { - wake_up_interruptible(&pDrvData->IPCs[usPCNum - 1].ipc_wait_queue); - } - } - if (usIPCSource == 0) - break; - /* try next IPC */ - usIsolationMask = usIsolationMask << 1; - } - } - return IRQ_HANDLED; -} - - -int tp3780I_InitializeBoardData(struct thinkpad_bd_data *pBDData) -{ - int retval = 0; - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - - pBDData->bDSPEnabled = false; - pSettings->bInterruptClaimed = false; - - retval = smapi_init(); - if (retval) { - pr_err("%s: Error: SMAPI is not available on this machine\n", __func__); - } else { - if (mwave_3780i_irq || mwave_3780i_io || mwave_uart_irq || mwave_uart_io) { - retval = smapi_set_DSP_cfg(); - } - } - - return retval; -} - -void tp3780I_Cleanup(struct thinkpad_bd_data *pBDData) -{ -} - -int tp3780I_CalcResources(struct thinkpad_bd_data *pBDData) -{ - struct smapi_dsp_settings rSmapiInfo; - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - - if (smapi_query_DSP_cfg(&rSmapiInfo)) { - pr_err("%s: Error: Could not query DSP config. Aborting.\n", __func__); - return -EIO; - } - - /* Sanity check */ - if ( - ( rSmapiInfo.usDspIRQ == 0 ) - || ( rSmapiInfo.usDspBaseIO == 0 ) - || ( rSmapiInfo.usUartIRQ == 0 ) - || ( rSmapiInfo.usUartBaseIO == 0 ) - ) { - pr_err("%s: Error: Illegal resource setting. Aborting.\n", __func__); - return -EIO; - } - - pSettings->bDSPEnabled = (rSmapiInfo.bDSPEnabled && rSmapiInfo.bDSPPresent); - pSettings->bModemEnabled = rSmapiInfo.bModemEnabled; - pSettings->usDspIrq = rSmapiInfo.usDspIRQ; - pSettings->usDspDma = rSmapiInfo.usDspDMA; - pSettings->usDspBaseIO = rSmapiInfo.usDspBaseIO; - pSettings->usUartIrq = rSmapiInfo.usUartIRQ; - pSettings->usUartBaseIO = rSmapiInfo.usUartBaseIO; - - pSettings->uDStoreSize = TP_ABILITIES_DATA_SIZE; - pSettings->uIStoreSize = TP_ABILITIES_INST_SIZE; - pSettings->uIps = TP_ABILITIES_INTS_PER_SEC; - - if (pSettings->bDSPEnabled && pSettings->bModemEnabled && pSettings->usDspIrq == pSettings->usUartIrq) { - pBDData->bShareDspIrq = pBDData->bShareUartIrq = 1; - } else { - pBDData->bShareDspIrq = pBDData->bShareUartIrq = 0; - } - - return 0; -} - - -int tp3780I_ClaimResources(struct thinkpad_bd_data *pBDData) -{ - int retval = 0; - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - struct resource *pres; - - pres = request_region(pSettings->usDspBaseIO, 16, "mwave_3780i"); - if ( pres == NULL ) retval = -EIO; - - if (retval) { - pr_err("%s: Error: Could not claim I/O region starting at %x\n", __func__, - pSettings->usDspBaseIO); - return -EIO; - } - - return retval; -} - -int tp3780I_ReleaseResources(struct thinkpad_bd_data *pBDData) -{ - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - - release_region(pSettings->usDspBaseIO & (~3), 16); - - if (pSettings->bInterruptClaimed) { - free_irq(pSettings->usDspIrq, NULL); - pSettings->bInterruptClaimed = false; - } - - return 0; -} - - - -int tp3780I_EnableDSP(struct thinkpad_bd_data *pBDData) -{ - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - bool bDSPPoweredUp = false, bInterruptAllocated = false; - - if (pBDData->bDSPEnabled) { - pr_err("%s: Error: DSP already enabled!\n", __func__); - goto exit_cleanup; - } - - if (!pSettings->bDSPEnabled) { - pr_err("%s: Error: pSettings->bDSPEnabled not set\n", __func__); - goto exit_cleanup; - } - - if ( - (pSettings->usDspIrq >= s_numIrqs) - || (pSettings->usDspDma >= s_numDmas) - || (s_ausThinkpadIrqToField[pSettings->usDspIrq] == 0xFFFF) - || (s_ausThinkpadDmaToField[pSettings->usDspDma] == 0xFFFF) - ) { - pr_err("%s: Error: invalid irq %x\n", __func__, pSettings->usDspIrq); - goto exit_cleanup; - } - - if ( - ((pSettings->usDspBaseIO & 0xF00F) != 0) - || (pSettings->usDspBaseIO & 0x0FF0) == 0 - ) { - pr_err("%s: Error: Invalid DSP base I/O address %x\n", __func__, - pSettings->usDspBaseIO); - goto exit_cleanup; - } - - if (pSettings->bModemEnabled) { - if ( - pSettings->usUartIrq >= s_numIrqs - || s_ausThinkpadIrqToField[pSettings->usUartIrq] == 0xFFFF - ) { - pr_err("%s: Error: Invalid UART IRQ %x\n", __func__, pSettings->usUartIrq); - goto exit_cleanup; - } - switch (pSettings->usUartBaseIO) { - case 0x03F8: - case 0x02F8: - case 0x03E8: - case 0x02E8: - break; - - default: - pr_err("%s: Error: Invalid UART base I/O address %x\n", __func__, - pSettings->usUartBaseIO); - goto exit_cleanup; - } - } - - pSettings->bDspIrqActiveLow = pSettings->bDspIrqPulse = true; - pSettings->bUartIrqActiveLow = pSettings->bUartIrqPulse = true; - - if (pBDData->bShareDspIrq) { - pSettings->bDspIrqActiveLow = false; - } - if (pBDData->bShareUartIrq) { - pSettings->bUartIrqActiveLow = false; - } - - pSettings->usNumTransfers = TP_CFG_NumTransfers; - pSettings->usReRequest = TP_CFG_RerequestTimer; - pSettings->bEnableMEMCS16 = TP_CFG_MEMCS16; - pSettings->usIsaMemCmdWidth = TP_CFG_IsaMemCmdWidth; - pSettings->bGateIOCHRDY = TP_CFG_GateIOCHRDY; - pSettings->bEnablePwrMgmt = TP_CFG_EnablePwrMgmt; - pSettings->usHBusTimerLoadValue = TP_CFG_HBusTimerValue; - pSettings->bDisableLBusTimeout = TP_CFG_DisableLBusTimeout; - pSettings->usN_Divisor = TP_CFG_N_Divisor; - pSettings->usM_Multiplier = TP_CFG_M_Multiplier; - pSettings->bPllBypass = TP_CFG_PllBypass; - pSettings->usChipletEnable = TP_CFG_ChipletEnable; - - if (request_irq(pSettings->usUartIrq, &UartInterrupt, 0, "mwave_uart", NULL)) { - pr_err("%s: Error: Could not get UART IRQ %x\n", __func__, pSettings->usUartIrq); - goto exit_cleanup; - } else { /* no conflict just release */ - free_irq(pSettings->usUartIrq, NULL); - } - - if (request_irq(pSettings->usDspIrq, &DspInterrupt, 0, "mwave_3780i", NULL)) { - pr_err("%s: Error: Could not get 3780i IRQ %x\n", __func__, pSettings->usDspIrq); - goto exit_cleanup; - } else { - bInterruptAllocated = true; - pSettings->bInterruptClaimed = true; - } - - smapi_set_DSP_power_state(false); - if (smapi_set_DSP_power_state(true)) { - pr_err("%s: Error: smapi_set_DSP_power_state(true) failed\n", __func__); - goto exit_cleanup; - } else { - bDSPPoweredUp = true; - } - - if (dsp3780I_EnableDSP(pSettings, s_ausThinkpadIrqToField, s_ausThinkpadDmaToField)) { - pr_err("%s: Error: dsp7880I_EnableDSP() failed\n", __func__); - goto exit_cleanup; - } - - EnableSRAM(pBDData); - - pBDData->bDSPEnabled = true; - - return 0; - -exit_cleanup: - pr_err("%s: Cleaning up\n", __func__); - if (bDSPPoweredUp) - smapi_set_DSP_power_state(false); - if (bInterruptAllocated) { - free_irq(pSettings->usDspIrq, NULL); - pSettings->bInterruptClaimed = false; - } - return -EIO; -} - - -int tp3780I_DisableDSP(struct thinkpad_bd_data *pBDData) -{ - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - - if (pBDData->bDSPEnabled) { - dsp3780I_DisableDSP(&pBDData->rDspSettings); - if (pSettings->bInterruptClaimed) { - free_irq(pSettings->usDspIrq, NULL); - pSettings->bInterruptClaimed = false; - } - smapi_set_DSP_power_state(false); - pBDData->bDSPEnabled = false; - } - - return 0; -} - - -int tp3780I_ResetDSP(struct thinkpad_bd_data *pBDData) -{ - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - - if (dsp3780I_Reset(pSettings) == 0) { - EnableSRAM(pBDData); - return 0; - } - return -EIO; -} - - -int tp3780I_StartDSP(struct thinkpad_bd_data *pBDData) -{ - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - - if (dsp3780I_Run(pSettings) == 0) { - // @BUG @TBD EnableSRAM(pBDData); - } else { - return -EIO; - } - - return 0; -} - - -int tp3780I_QueryAbilities(struct thinkpad_bd_data *pBDData, struct mw_abilities *pAbilities) -{ - memset(pAbilities, 0, sizeof(*pAbilities)); - /* fill out standard constant fields */ - pAbilities->instr_per_sec = pBDData->rDspSettings.uIps; - pAbilities->data_size = pBDData->rDspSettings.uDStoreSize; - pAbilities->inst_size = pBDData->rDspSettings.uIStoreSize; - pAbilities->bus_dma_bw = pBDData->rDspSettings.uDmaBandwidth; - - /* fill out dynamically determined fields */ - pAbilities->component_list[0] = 0x00010000 | MW_ADC_MASK; - pAbilities->component_list[1] = 0x00010000 | MW_ACI_MASK; - pAbilities->component_list[2] = 0x00010000 | MW_AIC1_MASK; - pAbilities->component_list[3] = 0x00010000 | MW_AIC2_MASK; - pAbilities->component_list[4] = 0x00010000 | MW_CDDAC_MASK; - pAbilities->component_list[5] = 0x00010000 | MW_MIDI_MASK; - pAbilities->component_list[6] = 0x00010000 | MW_UART_MASK; - pAbilities->component_count = 7; - - /* Fill out Mwave OS and BIOS task names */ - - memcpy(pAbilities->mwave_os_name, TP_ABILITIES_MWAVEOS_NAME, - sizeof(TP_ABILITIES_MWAVEOS_NAME)); - memcpy(pAbilities->bios_task_name, TP_ABILITIES_BIOSTASK_NAME, - sizeof(TP_ABILITIES_BIOSTASK_NAME)); - - return 0; -} - -int tp3780I_ReadWriteDspDStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode, - void __user *pvBuffer, unsigned int uCount, - unsigned long ulDSPAddr) -{ - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - bool bRC = 0; - - if (pBDData->bDSPEnabled) { - switch (uOpcode) { - case IOCTL_MW_READ_DATA: - bRC = dsp3780I_ReadDStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - - case IOCTL_MW_READCLEAR_DATA: - bRC = dsp3780I_ReadAndClearDStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - - case IOCTL_MW_WRITE_DATA: - bRC = dsp3780I_WriteDStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - } - } - - return bRC ? -EIO : 0; -} - - -int tp3780I_ReadWriteDspIStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode, - void __user *pvBuffer, unsigned int uCount, - unsigned long ulDSPAddr) -{ - struct dsp_3780i_config_settings *pSettings = &pBDData->rDspSettings; - unsigned short usDspBaseIO = pSettings->usDspBaseIO; - bool bRC = 0; - - if (pBDData->bDSPEnabled) { - switch (uOpcode) { - case IOCTL_MW_READ_INST: - bRC = dsp3780I_ReadIStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - - case IOCTL_MW_WRITE_INST: - bRC = dsp3780I_WriteIStore(usDspBaseIO, pvBuffer, uCount, ulDSPAddr); - break; - } - } - - return bRC ? -EIO : 0; -} - diff --git a/drivers/char/mwave/tp3780i.h b/drivers/char/mwave/tp3780i.h deleted file mode 100644 index c0001a344741..000000000000 --- a/drivers/char/mwave/tp3780i.h +++ /dev/null @@ -1,103 +0,0 @@ -/* -* -* tp3780i.h -- declarations for tp3780i.c -* -* -* Written By: Mike Sullivan IBM Corporation -* -* Copyright (C) 1999 IBM Corporation -* -* This program is free software; you can redistribute it and/or modify -* it under the terms of the GNU General Public License as published by -* the Free Software Foundation; either version 2 of the License, or -* (at your option) any later version. -* -* This program is distributed in the hope that it will be useful, -* but WITHOUT ANY WARRANTY; without even the implied warranty of -* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -* GNU General Public License for more details. -* -* NO WARRANTY -* THE PROGRAM IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OR -* CONDITIONS OF ANY KIND, EITHER EXPRESS OR IMPLIED INCLUDING, WITHOUT -* LIMITATION, ANY WARRANTIES OR CONDITIONS OF TITLE, NON-INFRINGEMENT, -* MERCHANTABILITY OR FITNESS FOR A PARTICULAR PURPOSE. Each Recipient is -* solely responsible for determining the appropriateness of using and -* distributing the Program and assumes all risks associated with its -* exercise of rights under this Agreement, including but not limited to -* the risks and costs of program errors, damage to or loss of data, -* programs or equipment, and unavailability or interruption of operations. -* -* DISCLAIMER OF LIABILITY -* NEITHER RECIPIENT NOR ANY CONTRIBUTORS SHALL HAVE ANY LIABILITY FOR ANY -* DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL -* DAMAGES (INCLUDING WITHOUT LIMITATION LOST PROFITS), HOWEVER CAUSED AND -* ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR -* TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE -* USE OR DISTRIBUTION OF THE PROGRAM OR THE EXERCISE OF ANY RIGHTS GRANTED -* HEREUNDER, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGES -* -* You should have received a copy of the GNU General Public License -* along with this program; if not, write to the Free Software -* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA -* -* -* 10/23/2000 - Alpha Release -* First release to the public -*/ - -#ifndef _LINUX_TP3780I_H -#define _LINUX_TP3780I_H - -#include -#include "mwavepub.h" - - -/* DSP abilities constants for 3780i based Thinkpads */ -#define TP_ABILITIES_INTS_PER_SEC 39160800 -#define TP_ABILITIES_DATA_SIZE 32768 -#define TP_ABILITIES_INST_SIZE 32768 -#define TP_ABILITIES_MWAVEOS_NAME "mwaveos0700.dsp" -#define TP_ABILITIES_BIOSTASK_NAME "mwbio701.dsp" - - -/* DSP configuration values for 3780i based Thinkpads */ -#define TP_CFG_NumTransfers 3 /* 16 transfers */ -#define TP_CFG_RerequestTimer 1 /* 2 usec */ -#define TP_CFG_MEMCS16 0 /* Disabled, 16-bit memory assumed */ -#define TP_CFG_IsaMemCmdWidth 3 /* 295 nsec (16-bit) */ -#define TP_CFG_GateIOCHRDY 0 /* No IOCHRDY gating */ -#define TP_CFG_EnablePwrMgmt 1 /* Enable low poser suspend/resume */ -#define TP_CFG_HBusTimerValue 255 /* HBus timer load value */ -#define TP_CFG_DisableLBusTimeout 0 /* Enable LBus timeout */ -#define TP_CFG_N_Divisor 32 /* Clock = 39.1608 Mhz */ -#define TP_CFG_M_Multiplier 37 /* " */ -#define TP_CFG_PllBypass 0 /* don't bypass */ -#define TP_CFG_ChipletEnable 0xFFFF /* Enable all chiplets */ - -struct thinkpad_bd_data { - int bDSPEnabled; - int bShareDspIrq; - int bShareUartIrq; - struct dsp_3780i_config_settings rDspSettings; -}; - -int tp3780I_InitializeBoardData(struct thinkpad_bd_data *pBDData); -int tp3780I_CalcResources(struct thinkpad_bd_data *pBDData); -int tp3780I_ClaimResources(struct thinkpad_bd_data *pBDData); -int tp3780I_ReleaseResources(struct thinkpad_bd_data *pBDData); -int tp3780I_EnableDSP(struct thinkpad_bd_data *pBDData); -int tp3780I_DisableDSP(struct thinkpad_bd_data *pBDData); -int tp3780I_ResetDSP(struct thinkpad_bd_data *pBDData); -int tp3780I_StartDSP(struct thinkpad_bd_data *pBDData); -int tp3780I_QueryAbilities(struct thinkpad_bd_data *pBDData, struct mw_abilities *pAbilities); -void tp3780I_Cleanup(struct thinkpad_bd_data *pBDData); -int tp3780I_ReadWriteDspDStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode, - void __user *pvBuffer, unsigned int uCount, - unsigned long ulDSPAddr); -int tp3780I_ReadWriteDspIStore(struct thinkpad_bd_data *pBDData, unsigned int uOpcode, - void __user *pvBuffer, unsigned int uCount, - unsigned long ulDSPAddr); - - -#endif diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index 7d0aa718499c..fa9000f68523 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -52,7 +52,6 @@ #define PXA3XX_GCU_MINOR 197 #define TUN_MINOR 200 #define CUSE_MINOR 203 -#define MWAVE_MINOR 219 /* ACP/Mwave Modem */ #define MPT_MINOR 220 #define MPT2SAS_MINOR 221 #define MPT3SAS_MINOR 222 -- cgit v1.2.3 From 4a9ba211d0264131dcfca0cbc10bff5ff277ff0a Mon Sep 17 00:00:00 2001 From: Manivannan Sadhasivam Date: Thu, 18 Dec 2025 22:21:45 +0530 Subject: bus: mhi: host: Drop the auto_queue support Now that the only user of the 'auto_queue' feature, (QRTR) has been converted to manage the buffers on its own, drop the code related to it. Signed-off-by: Manivannan Sadhasivam Reviewed-by: Loic Poulain Reviewed-by: Jeff Hugo Link: https://patch.msgid.link/20251218-qrtr-fix-v2-2-c7499bfcfbe0@oss.qualcomm.com --- drivers/bus/mhi/host/init.c | 10 ----- drivers/bus/mhi/host/internal.h | 3 -- drivers/bus/mhi/host/main.c | 81 +---------------------------------------- include/linux/mhi.h | 14 ------- 4 files changed, 2 insertions(+), 106 deletions(-) (limited to 'include/linux') diff --git a/drivers/bus/mhi/host/init.c b/drivers/bus/mhi/host/init.c index 099be8dd1900..b020a6489c07 100644 --- a/drivers/bus/mhi/host/init.c +++ b/drivers/bus/mhi/host/init.c @@ -841,18 +841,8 @@ static int parse_ch_cfg(struct mhi_controller *mhi_cntrl, mhi_chan->lpm_notify = ch_cfg->lpm_notify; mhi_chan->offload_ch = ch_cfg->offload_channel; mhi_chan->db_cfg.reset_req = ch_cfg->doorbell_mode_switch; - mhi_chan->pre_alloc = ch_cfg->auto_queue; mhi_chan->wake_capable = ch_cfg->wake_capable; - /* - * If MHI host allocates buffers, then the channel direction - * should be DMA_FROM_DEVICE - */ - if (mhi_chan->pre_alloc && mhi_chan->dir != DMA_FROM_DEVICE) { - dev_err(dev, "Invalid channel configuration\n"); - goto error_chan_cfg; - } - /* * Bi-directional and direction less channel must be an * offload channel diff --git a/drivers/bus/mhi/host/internal.h b/drivers/bus/mhi/host/internal.h index 7937bb1f742c..7b0ee5e3a12d 100644 --- a/drivers/bus/mhi/host/internal.h +++ b/drivers/bus/mhi/host/internal.h @@ -286,7 +286,6 @@ struct mhi_chan { bool lpm_notify; bool configured; bool offload_ch; - bool pre_alloc; bool wake_capable; }; @@ -389,8 +388,6 @@ int mhi_rddm_prepare(struct mhi_controller *mhi_cntrl, struct image_info *img_info); void mhi_fw_load_handler(struct mhi_controller *mhi_cntrl); -/* Automatically allocate and queue inbound buffers */ -#define MHI_CH_INBOUND_ALLOC_BUFS BIT(0) int mhi_init_chan_ctxt(struct mhi_controller *mhi_cntrl, struct mhi_chan *mhi_chan); void mhi_deinit_chan_ctxt(struct mhi_controller *mhi_cntrl, diff --git a/drivers/bus/mhi/host/main.c b/drivers/bus/mhi/host/main.c index 861551274319..53c0ffe30070 100644 --- a/drivers/bus/mhi/host/main.c +++ b/drivers/bus/mhi/host/main.c @@ -664,23 +664,6 @@ static int parse_xfer_event(struct mhi_controller *mhi_cntrl, mhi_cntrl->runtime_put(mhi_cntrl); } - /* - * Recycle the buffer if buffer is pre-allocated, - * if there is an error, not much we can do apart - * from dropping the packet - */ - if (mhi_chan->pre_alloc) { - if (mhi_queue_buf(mhi_chan->mhi_dev, - mhi_chan->dir, - buf_info->cb_buf, - buf_info->len, MHI_EOT)) { - dev_err(dev, - "Error recycling buffer for chan:%d\n", - mhi_chan->chan); - kfree(buf_info->cb_buf); - } - } - read_lock_bh(&mhi_chan->lock); } break; @@ -1177,17 +1160,12 @@ static int mhi_queue(struct mhi_device *mhi_dev, struct mhi_buf_info *buf_info, int mhi_queue_skb(struct mhi_device *mhi_dev, enum dma_data_direction dir, struct sk_buff *skb, size_t len, enum mhi_flags mflags) { - struct mhi_chan *mhi_chan = (dir == DMA_TO_DEVICE) ? mhi_dev->ul_chan : - mhi_dev->dl_chan; struct mhi_buf_info buf_info = { }; buf_info.v_addr = skb->data; buf_info.cb_buf = skb; buf_info.len = len; - if (unlikely(mhi_chan->pre_alloc)) - return -EINVAL; - return mhi_queue(mhi_dev, &buf_info, dir, mflags); } EXPORT_SYMBOL_GPL(mhi_queue_skb); @@ -1472,45 +1450,6 @@ static int mhi_prepare_channel(struct mhi_controller *mhi_cntrl, if (ret) goto error_pm_state; - if (mhi_chan->dir == DMA_FROM_DEVICE) - mhi_chan->pre_alloc = !!(flags & MHI_CH_INBOUND_ALLOC_BUFS); - - /* Pre-allocate buffer for xfer ring */ - if (mhi_chan->pre_alloc) { - int nr_el = get_nr_avail_ring_elements(mhi_cntrl, - &mhi_chan->tre_ring); - size_t len = mhi_cntrl->buffer_len; - - while (nr_el--) { - void *buf; - struct mhi_buf_info info = { }; - - buf = kmalloc(len, GFP_KERNEL); - if (!buf) { - ret = -ENOMEM; - goto error_pre_alloc; - } - - /* Prepare transfer descriptors */ - info.v_addr = buf; - info.cb_buf = buf; - info.len = len; - ret = mhi_gen_tre(mhi_cntrl, mhi_chan, &info, MHI_EOT); - if (ret) { - kfree(buf); - goto error_pre_alloc; - } - } - - read_lock_bh(&mhi_cntrl->pm_lock); - if (MHI_DB_ACCESS_VALID(mhi_cntrl)) { - read_lock_irq(&mhi_chan->lock); - mhi_ring_chan_db(mhi_cntrl, mhi_chan); - read_unlock_irq(&mhi_chan->lock); - } - read_unlock_bh(&mhi_cntrl->pm_lock); - } - mutex_unlock(&mhi_chan->mutex); return 0; @@ -1522,12 +1461,6 @@ error_pm_state: error_init_chan: mutex_unlock(&mhi_chan->mutex); - return ret; - -error_pre_alloc: - mutex_unlock(&mhi_chan->mutex); - mhi_unprepare_channel(mhi_cntrl, mhi_chan); - return ret; } @@ -1600,12 +1533,8 @@ static void mhi_reset_data_chan(struct mhi_controller *mhi_cntrl, mhi_del_ring_element(mhi_cntrl, buf_ring); mhi_del_ring_element(mhi_cntrl, tre_ring); - if (mhi_chan->pre_alloc) { - kfree(buf_info->cb_buf); - } else { - result.buf_addr = buf_info->cb_buf; - mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); - } + result.buf_addr = buf_info->cb_buf; + mhi_chan->xfer_cb(mhi_chan->mhi_dev, &result); } } @@ -1666,12 +1595,6 @@ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev) } EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer); -int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev) -{ - return __mhi_prepare_for_transfer(mhi_dev, MHI_CH_INBOUND_ALLOC_BUFS); -} -EXPORT_SYMBOL_GPL(mhi_prepare_for_transfer_autoqueue); - void mhi_unprepare_from_transfer(struct mhi_device *mhi_dev) { struct mhi_controller *mhi_cntrl = mhi_dev->mhi_cntrl; diff --git a/include/linux/mhi.h b/include/linux/mhi.h index dd372b0123a6..88ccb3e14f48 100644 --- a/include/linux/mhi.h +++ b/include/linux/mhi.h @@ -215,7 +215,6 @@ enum mhi_db_brst_mode { * @lpm_notify: The channel master requires low power mode notifications * @offload_channel: The client manages the channel completely * @doorbell_mode_switch: Channel switches to doorbell mode on M0 transition - * @auto_queue: Framework will automatically queue buffers for DL traffic * @wake-capable: Channel capable of waking up the system */ struct mhi_channel_config { @@ -232,7 +231,6 @@ struct mhi_channel_config { bool lpm_notify; bool offload_channel; bool doorbell_mode_switch; - bool auto_queue; bool wake_capable; }; @@ -743,18 +741,6 @@ void mhi_device_put(struct mhi_device *mhi_dev); */ int mhi_prepare_for_transfer(struct mhi_device *mhi_dev); -/** - * mhi_prepare_for_transfer_autoqueue - Setup UL and DL channels with auto queue - * buffers for DL traffic - * @mhi_dev: Device associated with the channels - * - * Allocate and initialize the channel context and also issue the START channel - * command to both channels. Channels can be started only if both host and - * device execution environments match and channels are in a DISABLED state. - * The MHI core will automatically allocate and queue buffers for the DL traffic. - */ -int mhi_prepare_for_transfer_autoqueue(struct mhi_device *mhi_dev); - /** * mhi_unprepare_from_transfer - Reset UL and DL channels for data transfer. * Issue the RESET channel command and let the -- cgit v1.2.3 From f7386f545e49e5e6229a14d92b39340d155b0b3f Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Mon, 3 Nov 2025 22:29:08 +0100 Subject: sysctl: Remove unused ctl_table forward declarations Remove superfluous forward declarations of ctl_table from header files where they are no longer needed. These declarations were left behind after sysctl code refactoring and cleanup. Reviewed-by: Jan Kara Acked-by: Muchun Song Reviewed-by: Petr Mladek Acked-by: Paolo Abeni Signed-off-by: Joel Granados --- include/linux/fs.h | 1 - include/linux/hugetlb.h | 2 -- include/linux/printk.h | 1 - include/net/ax25.h | 2 -- kernel/printk/internal.h | 2 +- kernel/printk/sysctl.c | 1 - 6 files changed, 1 insertion(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/fs.h b/include/linux/fs.h index 04ceeca12a0d..77f6302fdced 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -3487,7 +3487,6 @@ ssize_t simple_attr_write(struct file *file, const char __user *buf, ssize_t simple_attr_write_signed(struct file *file, const char __user *buf, size_t len, loff_t *ppos); -struct ctl_table; int __init list_bdev_fs_names(char *buf, size_t size); #define __FMODE_EXEC ((__force int) FMODE_EXEC) diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 019a1c5281e4..18d1c4ecc4f9 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -16,8 +16,6 @@ #include #include -struct ctl_table; -struct user_struct; struct mmu_gather; struct node; diff --git a/include/linux/printk.h b/include/linux/printk.h index 45c663124c9b..63d516c873b4 100644 --- a/include/linux/printk.h +++ b/include/linux/printk.h @@ -78,7 +78,6 @@ extern void console_verbose(void); /* strlen("ratelimit") + 1 */ #define DEVKMSG_STR_MAX_SIZE 10 extern char devkmsg_log_str[DEVKMSG_STR_MAX_SIZE]; -struct ctl_table; extern int suppress_printk; diff --git a/include/net/ax25.h b/include/net/ax25.h index a7bba42dde15..beec9712e9c7 100644 --- a/include/net/ax25.h +++ b/include/net/ax25.h @@ -215,8 +215,6 @@ typedef struct { unsigned short slave_timeout; /* when? */ } ax25_dama_info; -struct ctl_table; - typedef struct ax25_dev { struct list_head list; diff --git a/kernel/printk/internal.h b/kernel/printk/internal.h index 5f5f626f4279..29a3bd1799d4 100644 --- a/kernel/printk/internal.h +++ b/kernel/printk/internal.h @@ -4,9 +4,9 @@ */ #include #include +#include #if defined(CONFIG_PRINTK) && defined(CONFIG_SYSCTL) -struct ctl_table; void __init printk_sysctl_init(void); int devkmsg_sysctl_set_loglvl(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos); diff --git a/kernel/printk/sysctl.c b/kernel/printk/sysctl.c index da77f3f5c1fe..f15732e93c2e 100644 --- a/kernel/printk/sysctl.c +++ b/kernel/printk/sysctl.c @@ -3,7 +3,6 @@ * sysctl.c: General linux system control interface */ -#include #include #include #include -- cgit v1.2.3 From 6036dc03c39a3cb0df14899f29323b6b4b58dfe9 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 5 Dec 2025 11:18:43 +0100 Subject: sysctl: Add CONFIG_PROC_SYSCTL guards for converter macros Wrap sysctl converter macros with CONFIG_PROC_SYSCTL conditional compilation. When CONFIG_PROC_SYSCTL is disabled, provide stub implementations that return -ENOSYS to prevent link errors while maintaining API compatibility. This ensures converter macros are only compiled when procfs sysctl support is enabled in the kernel configuration. Signed-off-by: Joel Granados --- include/linux/sysctl.h | 43 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 43 insertions(+) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 288fe0055cd5..0a64212a0ceb 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -73,6 +73,7 @@ extern const int sysctl_vals[]; #define SYSCTL_USER_TO_KERN(dir) (!!(dir)) #define SYSCTL_KERN_TO_USER(dir) (!dir) +#ifdef CONFIG_PROC_SYSCTL #define SYSCTL_USER_TO_KERN_INT_CONV(name, u_ptr_op) \ int sysctl_user_to_kern_int_conv##name(const bool *negp, \ const unsigned long *u_ptr,\ @@ -173,6 +174,48 @@ int do_proc_uint_conv##name(unsigned long *u_ptr, unsigned int *k_ptr, \ return 0; \ } +#else // CONFIG_PROC_SYSCTL +#define SYSCTL_USER_TO_KERN_INT_CONV(name, u_ptr_op) \ +int sysctl_user_to_kern_int_conv##name(const bool *negp, \ + const unsigned long *u_ptr,\ + int *k_ptr) \ +{ \ + return -ENOSYS; \ +} + +#define SYSCTL_KERN_TO_USER_INT_CONV(name, k_ptr_op) \ +int sysctl_kern_to_user_int_conv##name(bool *negp, \ + unsigned long *u_ptr, \ + const int *k_ptr) \ +{ \ + return -ENOSYS; \ +} + +#define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ + k_ptr_range_check) \ +int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\ + int dir, const struct ctl_table *tbl) \ +{ \ + return -ENOSYS; \ +} + +#define SYSCTL_USER_TO_KERN_UINT_CONV(name, u_ptr_op) \ +int sysctl_user_to_kern_uint_conv##name(const unsigned long *u_ptr,\ + unsigned int *k_ptr) \ +{ \ + return -ENOSYS; \ +} + +#define SYSCTL_UINT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ + k_ptr_range_check) \ +int do_proc_uint_conv##name(unsigned long *u_ptr, unsigned int *k_ptr, \ + int dir, const struct ctl_table *tbl) \ +{ \ + return -ENOSYS; \ +} + +#endif // CONFIG_PROC_SYSCTL + extern const unsigned long sysctl_long_vals[]; -- cgit v1.2.3 From 8fc344a5af7e73178e6ac54d396327655e9ea358 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Fri, 28 Nov 2025 21:53:00 +0100 Subject: sysctl: Replace UINT converter macros with functions Replace the SYSCTL_USER_TO_KERN_UINT_CONV and SYSCTL_UINT_CONV_CUSTOM macros with functions with the same logic. This makes debugging easier and aligns with the functions preference described in coding-style.rst. Update the only user of this API: pipe.c. Signed-off-by: Joel Granados --- fs/pipe.c | 22 ++++++-- include/linux/sysctl.h | 63 +++------------------- kernel/sysctl.c | 140 +++++++++++++++++++++++++++++++++++++++++++------ 3 files changed, 148 insertions(+), 77 deletions(-) (limited to 'include/linux') diff --git a/fs/pipe.c b/fs/pipe.c index 9e6a01475815..22647f50b286 100644 --- a/fs/pipe.c +++ b/fs/pipe.c @@ -1481,10 +1481,24 @@ static struct file_system_type pipe_fs_type = { }; #ifdef CONFIG_SYSCTL -static SYSCTL_USER_TO_KERN_UINT_CONV(_pipe_maxsz, round_pipe_size) -static SYSCTL_UINT_CONV_CUSTOM(_pipe_maxsz, - sysctl_user_to_kern_uint_conv_pipe_maxsz, - sysctl_kern_to_user_uint_conv, true) + +static ulong round_pipe_size_ul(ulong size) +{ + return round_pipe_size(size); +} + +static int u2k_pipe_maxsz(const ulong *u_ptr, uint *k_ptr) +{ + return proc_uint_u2k_conv_uop(u_ptr, k_ptr, round_pipe_size_ul); +} + +static int do_proc_uint_conv_pipe_maxsz(ulong *u_ptr, uint *k_ptr, + int dir, const struct ctl_table *table) +{ + return proc_uint_conv(u_ptr, k_ptr, dir, table, true, + u2k_pipe_maxsz, + proc_uint_k2u_conv); +} static int proc_dopipe_max_size(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos) diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 0a64212a0ceb..d712992789f0 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -135,45 +135,6 @@ int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\ return user_to_kern(negp, u_ptr, k_ptr); \ return 0; \ } - -#define SYSCTL_USER_TO_KERN_UINT_CONV(name, u_ptr_op) \ -int sysctl_user_to_kern_uint_conv##name(const unsigned long *u_ptr,\ - unsigned int *k_ptr) \ -{ \ - unsigned long u = u_ptr_op(*u_ptr); \ - if (u > UINT_MAX) \ - return -EINVAL; \ - WRITE_ONCE(*k_ptr, u); \ - return 0; \ -} - -#define SYSCTL_UINT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ - k_ptr_range_check) \ -int do_proc_uint_conv##name(unsigned long *u_ptr, unsigned int *k_ptr, \ - int dir, const struct ctl_table *tbl) \ -{ \ - if (SYSCTL_KERN_TO_USER(dir)) \ - return kern_to_user(u_ptr, k_ptr); \ - \ - if (k_ptr_range_check) { \ - unsigned int tmp_k; \ - int ret; \ - if (!tbl) \ - return -EINVAL; \ - ret = user_to_kern(u_ptr, &tmp_k); \ - if (ret) \ - return ret; \ - if ((tbl->extra1 && \ - *(unsigned int *)tbl->extra1 > tmp_k) || \ - (tbl->extra2 && \ - *(unsigned int *)tbl->extra2 < tmp_k)) \ - return -ERANGE; \ - WRITE_ONCE(*k_ptr, tmp_k); \ - } else \ - return user_to_kern(u_ptr, k_ptr); \ - return 0; \ -} - #else // CONFIG_PROC_SYSCTL #define SYSCTL_USER_TO_KERN_INT_CONV(name, u_ptr_op) \ int sysctl_user_to_kern_int_conv##name(const bool *negp, \ @@ -199,24 +160,8 @@ int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\ return -ENOSYS; \ } -#define SYSCTL_USER_TO_KERN_UINT_CONV(name, u_ptr_op) \ -int sysctl_user_to_kern_uint_conv##name(const unsigned long *u_ptr,\ - unsigned int *k_ptr) \ -{ \ - return -ENOSYS; \ -} - -#define SYSCTL_UINT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ - k_ptr_range_check) \ -int do_proc_uint_conv##name(unsigned long *u_ptr, unsigned int *k_ptr, \ - int dir, const struct ctl_table *tbl) \ -{ \ - return -ENOSYS; \ -} - #endif // CONFIG_PROC_SYSCTL - extern const unsigned long sysctl_long_vals[]; typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer, @@ -239,6 +184,13 @@ int proc_douintvec_conv(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos, int (*conv)(unsigned long *lvalp, unsigned int *valp, int write, const struct ctl_table *table)); +int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr); +int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr, + ulong (*u_ptr_op)(const ulong)); +int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir, + const struct ctl_table *tbl, bool k_ptr_range_check, + int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr), + int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr)); int proc_dou8vec_minmax(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos); @@ -249,7 +201,6 @@ int proc_doulongvec_minmax_conv(const struct ctl_table *table, int dir, int proc_do_large_bitmap(const struct ctl_table *, int, void *, size_t *, loff_t *); int proc_do_static_key(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos); -int sysctl_kern_to_user_uint_conv(unsigned long *u_ptr, const unsigned int *k_ptr); /* * Register a set of sysctl names by calling register_sysctl diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 4ea56c71c7ef..00df21b84900 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -354,29 +354,117 @@ static void proc_put_char(void **buf, size_t *size, char c) } } -static SYSCTL_USER_TO_KERN_INT_CONV(, SYSCTL_CONV_IDENTITY) -static SYSCTL_KERN_TO_USER_INT_CONV(, SYSCTL_CONV_IDENTITY) - -static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv, - sysctl_kern_to_user_int_conv, false) -static SYSCTL_INT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_int_conv, - sysctl_kern_to_user_int_conv, true) +/** + * proc_uint_u2k_conv_uop - Assign user value to a kernel pointer + * + * @u_ptr: pointer to user space variable + * @k_ptr: pointer to kernel variable + * @u_ptr_op: execute this function before assigning to k_ptr + * + * Uses WRITE_ONCE to assign value to k_ptr. Executes u_ptr_op if + * not NULL. Check that the values are less than UINT_MAX to avoid + * having to support wrap around from userspace. + * + * returns 0 on success. + */ +int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr, + ulong (*u_ptr_op)(const ulong)) +{ + ulong u = u_ptr_op ? u_ptr_op(*u_ptr) : *u_ptr; + if (u > UINT_MAX) + return -EINVAL; + WRITE_ONCE(*k_ptr, u); + return 0; +} -static SYSCTL_USER_TO_KERN_UINT_CONV(, SYSCTL_CONV_IDENTITY) +/** + * proc_uint_k2u_conv - Assign kernel value to a user space pointer + * + * @u_ptr: pointer to user space variable + * @k_ptr: pointer to kernel variable + * + * Uses READ_ONCE to assign value to u_ptr. + * + * returns 0 on success. + */ +int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr) +{ + uint val = READ_ONCE(*k_ptr); + *u_ptr = (ulong)val; + return 0; +} -int sysctl_kern_to_user_uint_conv(unsigned long *u_ptr, - const unsigned int *k_ptr) +/** + * proc_uint_conv - Change user or kernel pointer based on direction + * + * @u_ptr: pointer to user variable + * @k_ptr: pointer to kernel variable + * @dir: %TRUE if this is a write to the sysctl file + * @tbl: the sysctl table + * @k_ptr_range_check: Check range for k_ptr when %TRUE + * @user_to_kern: Callback used to assign value from user to kernel var + * @kern_to_user: Callback used to assign value from kernel to user var + * + * When direction is kernel to user, then the u_ptr is modified. + * When direction is user to kernel, then the k_ptr is modified. + * + * Returns 0 on success + */ +int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir, + const struct ctl_table *tbl, bool k_ptr_range_check, + int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr), + int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr)) { - unsigned int val = READ_ONCE(*k_ptr); - *u_ptr = (unsigned long)val; + if (SYSCTL_KERN_TO_USER(dir)) + return kern_to_user(u_ptr, k_ptr); + + if (k_ptr_range_check) { + uint tmp_k; + int ret; + + if (!tbl) + return -EINVAL; + ret = user_to_kern(u_ptr, &tmp_k); + if (ret) + return ret; + if ((tbl->extra1 && + *(uint *)tbl->extra1 > tmp_k) || + (tbl->extra2 && + *(uint *)tbl->extra2 < tmp_k)) + return -ERANGE; + WRITE_ONCE(*k_ptr, tmp_k); + } else + return user_to_kern(u_ptr, k_ptr); return 0; } -static SYSCTL_UINT_CONV_CUSTOM(, sysctl_user_to_kern_uint_conv, - sysctl_kern_to_user_uint_conv, false) -static SYSCTL_UINT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_uint_conv, - sysctl_kern_to_user_uint_conv, true) +static int proc_uint_u2k_conv(const ulong *u_ptr, uint *k_ptr) +{ + return proc_uint_u2k_conv_uop(u_ptr, k_ptr, NULL); +} + +static int do_proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir, + const struct ctl_table *tbl) +{ + return proc_uint_conv(u_ptr, k_ptr, dir, tbl, false, + proc_uint_u2k_conv, proc_uint_k2u_conv); +} + +static int do_proc_uint_conv_minmax(ulong *u_ptr, uint *k_ptr, int dir, + const struct ctl_table *tbl) +{ + return proc_uint_conv(u_ptr, k_ptr, dir, tbl, true, + proc_uint_u2k_conv, proc_uint_k2u_conv); +} + +static SYSCTL_USER_TO_KERN_INT_CONV(, SYSCTL_CONV_IDENTITY) +static SYSCTL_KERN_TO_USER_INT_CONV(, SYSCTL_CONV_IDENTITY) + +static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv, + sysctl_kern_to_user_int_conv, false) +static SYSCTL_INT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_int_conv, + sysctl_kern_to_user_int_conv, true) static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; @@ -576,7 +664,6 @@ int proc_douintvec_conv(const struct ctl_table *table, int dir, void *buffer, return do_proc_douintvec(table, dir, buffer, lenp, ppos, conv); } - /** * proc_dobool - read/write a bool * @table: the sysctl table @@ -1079,6 +1166,25 @@ int proc_douintvec_conv(const struct ctl_table *table, int write, void *buffer, return -ENOSYS; } +int proc_uint_k2u_conv(ulong *u_ptr, const uint *k_ptr) +{ + return -ENOSYS; +} + +int proc_uint_u2k_conv_uop(const ulong *u_ptr, uint *k_ptr, + ulong (*u_ptr_op)(const ulong)) +{ + return -ENOSYS; +} + +int proc_uint_conv(ulong *u_ptr, uint *k_ptr, int dir, + const struct ctl_table *tbl, bool k_ptr_range_check, + int (*user_to_kern)(const ulong *u_ptr, uint *k_ptr), + int (*kern_to_user)(ulong *u_ptr, const uint *k_ptr)) +{ + return -ENOSYS; +} + int proc_dou8vec_minmax(const struct ctl_table *table, int dir, void *buffer, size_t *lenp, loff_t *ppos) { -- cgit v1.2.3 From ef153851af5b05c23b3484e7eebaadd18f2da6a9 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Wed, 3 Dec 2025 22:42:32 +0100 Subject: sysctl: Replace unidirectional INT converter macros with functions Replace SYSCTL_USER_TO_KERN_INT_CONV and SYSCTL_KERN_TO_USER_INT_CONV macros with function implementing the same logic.This makes debugging easier and aligns with the functions preference described in coding-style.rst. Update all jiffies converters to use explicit function implementations instead of macro-generated versions. Signed-off-by: Joel Granados --- include/linux/sysctl.h | 56 ++++--------------------- kernel/sysctl.c | 69 +++++++++++++++++++++++++++++- kernel/time/jiffies.c | 111 +++++++++++++++++++++++++++++++++++++++++-------- 3 files changed, 167 insertions(+), 69 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index d712992789f0..655fb85ec292 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -74,40 +74,6 @@ extern const int sysctl_vals[]; #define SYSCTL_KERN_TO_USER(dir) (!dir) #ifdef CONFIG_PROC_SYSCTL -#define SYSCTL_USER_TO_KERN_INT_CONV(name, u_ptr_op) \ -int sysctl_user_to_kern_int_conv##name(const bool *negp, \ - const unsigned long *u_ptr,\ - int *k_ptr) \ -{ \ - unsigned long u = u_ptr_op(*u_ptr); \ - if (*negp) { \ - if (u > (unsigned long) INT_MAX + 1) \ - return -EINVAL; \ - WRITE_ONCE(*k_ptr, -u); \ - } else { \ - if (u > (unsigned long) INT_MAX) \ - return -EINVAL; \ - WRITE_ONCE(*k_ptr, u); \ - } \ - return 0; \ -} - -#define SYSCTL_KERN_TO_USER_INT_CONV(name, k_ptr_op) \ -int sysctl_kern_to_user_int_conv##name(bool *negp, \ - unsigned long *u_ptr, \ - const int *k_ptr) \ -{ \ - int val = READ_ONCE(*k_ptr); \ - if (val < 0) { \ - *negp = true; \ - *u_ptr = -k_ptr_op((unsigned long)val); \ - } else { \ - *negp = false; \ - *u_ptr = k_ptr_op((unsigned long)val); \ - } \ - return 0; \ -} - /** * To range check on a converted value, use a temp k_ptr * When checking range, value should be within (tbl->extra1, tbl->extra2) @@ -135,22 +101,8 @@ int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\ return user_to_kern(negp, u_ptr, k_ptr); \ return 0; \ } -#else // CONFIG_PROC_SYSCTL -#define SYSCTL_USER_TO_KERN_INT_CONV(name, u_ptr_op) \ -int sysctl_user_to_kern_int_conv##name(const bool *negp, \ - const unsigned long *u_ptr,\ - int *k_ptr) \ -{ \ - return -ENOSYS; \ -} -#define SYSCTL_KERN_TO_USER_INT_CONV(name, k_ptr_op) \ -int sysctl_kern_to_user_int_conv##name(bool *negp, \ - unsigned long *u_ptr, \ - const int *k_ptr) \ -{ \ - return -ENOSYS; \ -} +#else // CONFIG_PROC_SYSCTL #define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ k_ptr_range_check) \ @@ -170,6 +122,7 @@ typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer, int proc_dostring(const struct ctl_table *, int, void *, size_t *, loff_t *); int proc_dobool(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos); + int proc_dointvec(const struct ctl_table *, int, void *, size_t *, loff_t *); int proc_dointvec_minmax(const struct ctl_table *table, int dir, void *buffer, size_t *lenp, loff_t *ppos); @@ -177,6 +130,11 @@ int proc_dointvec_conv(const struct ctl_table *table, int dir, void *buffer, size_t *lenp, loff_t *ppos, int (*conv)(bool *negp, unsigned long *u_ptr, int *k_ptr, int dir, const struct ctl_table *table)); +int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp, + ulong (*k_ptr_op)(const ulong)); +int proc_int_u2k_conv_uop(const ulong *u_ptr, int *k_ptr, const bool *negp, + ulong (*u_ptr_op)(const ulong)); + int proc_douintvec(const struct ctl_table *, int, void *, size_t *, loff_t *); int proc_douintvec_minmax(const struct ctl_table *table, int write, void *buffer, size_t *lenp, loff_t *ppos); diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 54deced9ab69..42975aa84ee3 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -458,8 +458,73 @@ static int do_proc_uint_conv_minmax(ulong *u_ptr, uint *k_ptr, int dir, proc_uint_u2k_conv, proc_uint_k2u_conv); } -static SYSCTL_USER_TO_KERN_INT_CONV(, SYSCTL_CONV_IDENTITY) -static SYSCTL_KERN_TO_USER_INT_CONV(, SYSCTL_CONV_IDENTITY) +/** + * proc_int_k2u_conv_kop - Assign kernel value to a user space pointer + * @u_ptr: pointer to user space variable + * @k_ptr: pointer to kernel variable + * @negp: assigned %TRUE if the converted kernel value is negative; + * %FALSE otherweise + * @k_ptr_op: execute this function before assigning to u_ptr + * + * Uses READ_ONCE to get value from k_ptr. Executes k_ptr_op before assigning + * to u_ptr if not NULL. Does **not** check for overflow. + * + * Returns: 0 on success. + */ +int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp, + ulong (*k_ptr_op)(const ulong)) +{ + int val = READ_ONCE(*k_ptr); + + if (val < 0) { + *negp = true; + *u_ptr = k_ptr_op ? -k_ptr_op((ulong)val) : -(ulong)val; + } else { + *negp = false; + *u_ptr = k_ptr_op ? k_ptr_op((ulong)val) : (ulong) val; + } + return 0; +} + +/** + * proc_int_u2k_conv_uop - Assign user value to a kernel pointer + * @u_ptr: pointer to user space variable + * @k_ptr: pointer to kernel variable + * @negp: If %TRUE, the converted user value is made negative. + * @u_ptr_op: execute this function before assigning to k_ptr + * + * Uses WRITE_ONCE to assign value to k_ptr. Executes u_ptr_op if + * not NULL. Check for overflow with UINT_MAX. + * + * Returns: 0 on success. + */ +int proc_int_u2k_conv_uop(const ulong *u_ptr, int *k_ptr, const bool *negp, + ulong (*u_ptr_op)(const ulong)) +{ + ulong u = u_ptr_op ? u_ptr_op(*u_ptr) : *u_ptr; + + if (*negp) { + if (u > (ulong) INT_MAX + 1) + return -EINVAL; + WRITE_ONCE(*k_ptr, -u); + } else { + if (u > (ulong) INT_MAX) + return -EINVAL; + WRITE_ONCE(*k_ptr, u); + } + return 0; +} + +static int sysctl_user_to_kern_int_conv(const bool *negp, const ulong *u_ptr, + int *k_ptr) +{ + return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, NULL); +} + +static int sysctl_kern_to_user_int_conv(bool *negp, ulong *u_ptr, const int *k_ptr) +{ + return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, NULL); +} static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv, sysctl_kern_to_user_int_conv, false) diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index d31a6d40d38d..825e4c9fd26a 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -100,26 +100,101 @@ void __init register_refined_jiffies(long cycles_per_second) __clocksource_register(&refined_jiffies); } -#define SYSCTL_CONV_MULT_HZ(val) ((val) * HZ) -#define SYSCTL_CONV_DIV_HZ(val) ((val) / HZ) - -static SYSCTL_USER_TO_KERN_INT_CONV(_hz, SYSCTL_CONV_MULT_HZ) -static SYSCTL_KERN_TO_USER_INT_CONV(_hz, SYSCTL_CONV_DIV_HZ) -static SYSCTL_USER_TO_KERN_INT_CONV(_userhz, clock_t_to_jiffies) -static SYSCTL_KERN_TO_USER_INT_CONV(_userhz, jiffies_to_clock_t) -static SYSCTL_USER_TO_KERN_INT_CONV(_ms, msecs_to_jiffies) -static SYSCTL_KERN_TO_USER_INT_CONV(_ms, jiffies_to_msecs) - -static SYSCTL_INT_CONV_CUSTOM(_jiffies, sysctl_user_to_kern_int_conv_hz, - sysctl_kern_to_user_int_conv_hz, false) +#ifdef CONFIG_PROC_SYSCTL +static ulong mult_hz(const ulong val) +{ + return val * HZ; +} + +static ulong div_hz(const ulong val) +{ + return val / HZ; +} + +static int sysctl_u2k_int_conv_hz(const bool *negp, const ulong *u_ptr, int *k_ptr) +{ + return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, mult_hz); +} + +static int sysctl_k2u_int_conv_hz(bool *negp, ulong *u_ptr, const int *k_ptr) +{ + return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, div_hz); +} + +static int sysctl_u2k_int_conv_userhz(const bool *negp, const ulong *u_ptr, int *k_ptr) +{ + return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, clock_t_to_jiffies); +} + +static ulong sysctl_jiffies_to_clock_t(const ulong val) +{ + return jiffies_to_clock_t(val); +} + +static int sysctl_k2u_int_conv_userhz(bool *negp, ulong *u_ptr, const int *k_ptr) +{ + return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_clock_t); +} + +static ulong sysctl_msecs_to_jiffies(const ulong val) +{ + return msecs_to_jiffies(val); +} + +static int sysctl_u2k_int_conv_ms(const bool *negp, const ulong *u_ptr, int *k_ptr) +{ + return proc_int_u2k_conv_uop(u_ptr, k_ptr, negp, sysctl_msecs_to_jiffies); +} + +static ulong sysctl_jiffies_to_msecs(const ulong val) +{ + return jiffies_to_msecs(val); +} + +static int sysctl_k2u_int_conv_ms(bool *negp, ulong *u_ptr, const int *k_ptr) +{ + return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_msecs); +} + + +static SYSCTL_INT_CONV_CUSTOM(_jiffies, sysctl_u2k_int_conv_hz, + sysctl_k2u_int_conv_hz, false) static SYSCTL_INT_CONV_CUSTOM(_userhz_jiffies, - sysctl_user_to_kern_int_conv_userhz, - sysctl_kern_to_user_int_conv_userhz, false) -static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies, sysctl_user_to_kern_int_conv_ms, - sysctl_kern_to_user_int_conv_ms, false) + sysctl_u2k_int_conv_userhz, + sysctl_k2u_int_conv_userhz, false) +static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies, sysctl_u2k_int_conv_ms, + sysctl_k2u_int_conv_ms, false) static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies_minmax, - sysctl_user_to_kern_int_conv_ms, - sysctl_kern_to_user_int_conv_ms, true) + sysctl_u2k_int_conv_ms, + sysctl_k2u_int_conv_ms, true) + +#else // CONFIG_PROC_SYSCTL +static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr, + int dir, const struct ctl_table *tbl) +{ + return -ENOSYS; +} + +static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr, + int *k_ptr, int dir, + const struct ctl_table *tbl) +{ + return -ENOSYS; +} + +static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr, + int dir, const struct ctl_table *tbl) +{ + return -ENOSYS; +} + +static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, + int *k_ptr, int dir, + const struct ctl_table *tbl) +{ + return -ENOSYS; +} +#endif /** * proc_dointvec_jiffies - read a vector of integers as seconds -- cgit v1.2.3 From d174174c6776a340f5c25aab1ac47a2dd950f380 Mon Sep 17 00:00:00 2001 From: Joel Granados Date: Thu, 4 Dec 2025 13:11:58 +0100 Subject: sysctl: replace SYSCTL_INT_CONV_CUSTOM macro with functions Remove SYSCTL_INT_CONV_CUSTOM and replace it with proc_int_conv. This converter function expects a negp argument as it can take on negative values. Update all jiffies converters to use explicit function calls. Remove SYSCTL_CONV_IDENTITY as it is no longer used. Signed-off-by: Joel Granados --- include/linux/sysctl.h | 46 ++++------------------------------------------ kernel/sysctl.c | 47 +++++++++++++++++++++++++++++++++++++++++++---- kernel/time/jiffies.c | 39 +++++++++++++++++++++++++++++---------- 3 files changed, 76 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/include/linux/sysctl.h b/include/linux/sysctl.h index 655fb85ec292..2886fbceb5d6 100644 --- a/include/linux/sysctl.h +++ b/include/linux/sysctl.h @@ -59,7 +59,6 @@ extern const int sysctl_vals[]; #define SYSCTL_LONG_ONE ((void *)&sysctl_long_vals[1]) #define SYSCTL_LONG_MAX ((void *)&sysctl_long_vals[2]) -#define SYSCTL_CONV_IDENTITY(val) (val) /** * * "dir" originates from read_iter (dir = 0) or write_iter (dir = 1) @@ -73,47 +72,6 @@ extern const int sysctl_vals[]; #define SYSCTL_USER_TO_KERN(dir) (!!(dir)) #define SYSCTL_KERN_TO_USER(dir) (!dir) -#ifdef CONFIG_PROC_SYSCTL -/** - * To range check on a converted value, use a temp k_ptr - * When checking range, value should be within (tbl->extra1, tbl->extra2) - */ -#define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ - k_ptr_range_check) \ -int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\ - int dir, const struct ctl_table *tbl) \ -{ \ - if (SYSCTL_KERN_TO_USER(dir)) \ - return kern_to_user(negp, u_ptr, k_ptr); \ - \ - if (k_ptr_range_check) { \ - int tmp_k, ret; \ - if (!tbl) \ - return -EINVAL; \ - ret = user_to_kern(negp, u_ptr, &tmp_k); \ - if (ret) \ - return ret; \ - if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) || \ - (tbl->extra2 && *(int *)tbl->extra2 < tmp_k)) \ - return -EINVAL; \ - WRITE_ONCE(*k_ptr, tmp_k); \ - } else \ - return user_to_kern(negp, u_ptr, k_ptr); \ - return 0; \ -} - -#else // CONFIG_PROC_SYSCTL - -#define SYSCTL_INT_CONV_CUSTOM(name, user_to_kern, kern_to_user, \ - k_ptr_range_check) \ -int do_proc_int_conv##name(bool *negp, unsigned long *u_ptr, int *k_ptr,\ - int dir, const struct ctl_table *tbl) \ -{ \ - return -ENOSYS; \ -} - -#endif // CONFIG_PROC_SYSCTL - extern const unsigned long sysctl_long_vals[]; typedef int proc_handler(const struct ctl_table *ctl, int write, void *buffer, @@ -134,6 +92,10 @@ int proc_int_k2u_conv_kop(ulong *u_ptr, const int *k_ptr, bool *negp, ulong (*k_ptr_op)(const ulong)); int proc_int_u2k_conv_uop(const ulong *u_ptr, int *k_ptr, const bool *negp, ulong (*u_ptr_op)(const ulong)); +int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir, + const struct ctl_table *tbl, bool k_ptr_range_check, + int (*user_to_kern)(const bool *negp, const ulong *u_ptr, int *k_ptr), + int (*kern_to_user)(bool *negp, ulong *u_ptr, const int *k_ptr)); int proc_douintvec(const struct ctl_table *, int, void *, size_t *, loff_t *); int proc_douintvec_minmax(const struct ctl_table *table, int write, void *buffer, diff --git a/kernel/sysctl.c b/kernel/sysctl.c index 42975aa84ee3..9d3a666ffde1 100644 --- a/kernel/sysctl.c +++ b/kernel/sysctl.c @@ -515,6 +515,33 @@ int proc_int_u2k_conv_uop(const ulong *u_ptr, int *k_ptr, const bool *negp, return 0; } +int proc_int_conv(bool *negp, ulong *u_ptr, int *k_ptr, int dir, + const struct ctl_table *tbl, bool k_ptr_range_check, + int (*user_to_kern)(const bool *negp, const ulong *u_ptr, int *k_ptr), + int (*kern_to_user)(bool *negp, ulong *u_ptr, const int *k_ptr)) +{ + if (SYSCTL_KERN_TO_USER(dir)) + return kern_to_user(negp, u_ptr, k_ptr); + + if (k_ptr_range_check) { + int tmp_k, ret; + + if (!tbl) + return -EINVAL; + ret = user_to_kern(negp, u_ptr, &tmp_k); + if (ret) + return ret; + if ((tbl->extra1 && *(int *)tbl->extra1 > tmp_k) || + (tbl->extra2 && *(int *)tbl->extra2 < tmp_k)) + return -EINVAL; + WRITE_ONCE(*k_ptr, tmp_k); + } else + return user_to_kern(negp, u_ptr, k_ptr); + return 0; +} + + + static int sysctl_user_to_kern_int_conv(const bool *negp, const ulong *u_ptr, int *k_ptr) { @@ -526,10 +553,22 @@ static int sysctl_kern_to_user_int_conv(bool *negp, ulong *u_ptr, const int *k_p return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, NULL); } -static SYSCTL_INT_CONV_CUSTOM(, sysctl_user_to_kern_int_conv, - sysctl_kern_to_user_int_conv, false) -static SYSCTL_INT_CONV_CUSTOM(_minmax, sysctl_user_to_kern_int_conv, - sysctl_kern_to_user_int_conv, true) +static int do_proc_int_conv(bool *negp, unsigned long *u_ptr, int *k_ptr, + int dir, const struct ctl_table *tbl) +{ + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false, + sysctl_user_to_kern_int_conv, + sysctl_kern_to_user_int_conv); + +} + +static int do_proc_int_conv_minmax(bool *negp, unsigned long *u_ptr, int *k_ptr, + int dir, const struct ctl_table *tbl) +{ + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, true, + sysctl_user_to_kern_int_conv, + sysctl_kern_to_user_int_conv); +} static const char proc_wspace_sep[] = { ' ', '\t', '\n' }; diff --git a/kernel/time/jiffies.c b/kernel/time/jiffies.c index 825e4c9fd26a..a5c7d15fce72 100644 --- a/kernel/time/jiffies.c +++ b/kernel/time/jiffies.c @@ -156,17 +156,36 @@ static int sysctl_k2u_int_conv_ms(bool *negp, ulong *u_ptr, const int *k_ptr) return proc_int_k2u_conv_kop(u_ptr, k_ptr, negp, sysctl_jiffies_to_msecs); } +static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr, + int dir, const struct ctl_table *tbl) +{ + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false, + sysctl_u2k_int_conv_hz, sysctl_k2u_int_conv_hz); +} + +static int do_proc_int_conv_userhz_jiffies(bool *negp, ulong *u_ptr, + int *k_ptr, int dir, + const struct ctl_table *tbl) +{ + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false, + sysctl_u2k_int_conv_userhz, + sysctl_k2u_int_conv_userhz); +} -static SYSCTL_INT_CONV_CUSTOM(_jiffies, sysctl_u2k_int_conv_hz, - sysctl_k2u_int_conv_hz, false) -static SYSCTL_INT_CONV_CUSTOM(_userhz_jiffies, - sysctl_u2k_int_conv_userhz, - sysctl_k2u_int_conv_userhz, false) -static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies, sysctl_u2k_int_conv_ms, - sysctl_k2u_int_conv_ms, false) -static SYSCTL_INT_CONV_CUSTOM(_ms_jiffies_minmax, - sysctl_u2k_int_conv_ms, - sysctl_k2u_int_conv_ms, true) +static int do_proc_int_conv_ms_jiffies(bool *negp, ulong *u_ptr, int *k_ptr, + int dir, const struct ctl_table *tbl) +{ + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false, + sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms); +} + +static int do_proc_int_conv_ms_jiffies_minmax(bool *negp, ulong *u_ptr, + int *k_ptr, int dir, + const struct ctl_table *tbl) +{ + return proc_int_conv(negp, u_ptr, k_ptr, dir, tbl, false, + sysctl_u2k_int_conv_ms, sysctl_k2u_int_conv_ms); +} #else // CONFIG_PROC_SYSCTL static int do_proc_int_conv_jiffies(bool *negp, ulong *u_ptr, int *k_ptr, -- cgit v1.2.3 From 6e0e8375f2e6854e8c35faa638f4448e3b8209af Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Fri, 26 Dec 2025 02:02:37 +0200 Subject: usb: isp1362-hcd: remove Philips ISP1362 USB OTG controller driver The last user of the platform driver was a Blackfin BF533 powered board, and it was removed in commit 4ba66a976072 ("arch: remove blackfin port") along with the whole Blackfin architecture support 7 years ago. Signed-off-by: Vladimir Zapolskiy Link: https://patch.msgid.link/20251226000237.1440642-1-vz@mleia.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/Makefile | 1 - drivers/usb/host/Kconfig | 12 - drivers/usb/host/Makefile | 1 - drivers/usb/host/isp1362-hcd.c | 2769 ---------------------------------------- drivers/usb/host/isp1362.h | 914 ------------- include/linux/usb/isp1362.h | 47 - 6 files changed, 3744 deletions(-) delete mode 100644 drivers/usb/host/isp1362-hcd.c delete mode 100644 drivers/usb/host/isp1362.h delete mode 100644 include/linux/usb/isp1362.h (limited to 'include/linux') diff --git a/drivers/usb/Makefile b/drivers/usb/Makefile index 949eca0adebe..6f3c86149887 100644 --- a/drivers/usb/Makefile +++ b/drivers/usb/Makefile @@ -30,7 +30,6 @@ obj-$(CONFIG_USB_UHCI_HCD) += host/ obj-$(CONFIG_USB_FHCI_HCD) += host/ obj-$(CONFIG_USB_XHCI_HCD) += host/ obj-$(CONFIG_USB_SL811_HCD) += host/ -obj-$(CONFIG_USB_ISP1362_HCD) += host/ obj-$(CONFIG_USB_R8A66597_HCD) += host/ obj-$(CONFIG_USB_FSL_USB2) += host/ obj-$(CONFIG_USB_FOTG210_HCD) += host/ diff --git a/drivers/usb/host/Kconfig b/drivers/usb/host/Kconfig index c4f17ce5c77b..11bc71013f0c 100644 --- a/drivers/usb/host/Kconfig +++ b/drivers/usb/host/Kconfig @@ -383,18 +383,6 @@ config USB_ISP116X_HCD To compile this driver as a module, choose M here: the module will be called isp116x-hcd. -config USB_ISP1362_HCD - tristate "ISP1362 HCD support" - depends on HAS_IOPORT - depends on COMPILE_TEST # nothing uses this - help - Supports the Philips ISP1362 chip as a host controller - - This driver does not support isochronous transfers. - - To compile this driver as a module, choose M here: the - module will be called isp1362-hcd. - config USB_MAX3421_HCD tristate "MAX3421 HCD (USB-over-SPI) support" depends on USB && SPI diff --git a/drivers/usb/host/Makefile b/drivers/usb/host/Makefile index 4df946c05ba0..a07e7ba9cd53 100644 --- a/drivers/usb/host/Makefile +++ b/drivers/usb/host/Makefile @@ -52,7 +52,6 @@ obj-$(CONFIG_USB_EHCI_BRCMSTB) += ehci-brcm.o obj-$(CONFIG_USB_OXU210HP_HCD) += oxu210hp-hcd.o obj-$(CONFIG_USB_ISP116X_HCD) += isp116x-hcd.o -obj-$(CONFIG_USB_ISP1362_HCD) += isp1362-hcd.o obj-$(CONFIG_USB_OHCI_HCD) += ohci-hcd.o obj-$(CONFIG_USB_OHCI_HCD_PCI) += ohci-pci.o diff --git a/drivers/usb/host/isp1362-hcd.c b/drivers/usb/host/isp1362-hcd.c deleted file mode 100644 index 954fc5ad565b..000000000000 --- a/drivers/usb/host/isp1362-hcd.c +++ /dev/null @@ -1,2769 +0,0 @@ -// SPDX-License-Identifier: GPL-2.0 -/* - * ISP1362 HCD (Host Controller Driver) for USB. - * - * Copyright (C) 2005 Lothar Wassmann - * - * Derived from the SL811 HCD, rewritten for ISP116x. - * Copyright (C) 2005 Olav Kongas - * - * Portions: - * Copyright (C) 2004 Psion Teklogix (for NetBook PRO) - * Copyright (C) 2004 David Brownell - */ - -/* - * The ISP1362 chip requires a large delay (300ns and 462ns) between - * accesses to the address and data register. - * The following timing options exist: - * - * 1. Configure your memory controller to add such delays if it can (the best) - * 2. Implement platform-specific delay function possibly - * combined with configuring the memory controller; see - * include/linux/usb_isp1362.h for more info. - * 3. Use ndelay (easiest, poorest). - * - * Use the corresponding macros USE_PLATFORM_DELAY and USE_NDELAY in the - * platform specific section of isp1362.h to select the appropriate variant. - * - * Also note that according to the Philips "ISP1362 Errata" document - * Rev 1.00 from 27 May data corruption may occur when the #WR signal - * is reasserted (even with #CS deasserted) within 132ns after a - * write cycle to any controller register. If the hardware doesn't - * implement the recommended fix (gating the #WR with #CS) software - * must ensure that no further write cycle (not necessarily to the chip!) - * is issued by the CPU within this interval. - - * For PXA25x this can be ensured by using VLIO with the maximum - * recovery time (MSCx = 0x7f8c) with a memory clock of 99.53 MHz. - */ - -#undef ISP1362_DEBUG - -/* - * The PXA255 UDC apparently doesn't handle GET_STATUS, GET_CONFIG and - * GET_INTERFACE requests correctly when the SETUP and DATA stages of the - * requests are carried out in separate frames. This will delay any SETUP - * packets until the start of the next frame so that this situation is - * unlikely to occur (and makes usbtest happy running with a PXA255 target - * device). - */ -#undef BUGGY_PXA2XX_UDC_USBTEST - -#undef PTD_TRACE -#undef URB_TRACE -#undef VERBOSE -#undef REGISTERS - -/* This enables a memory test on the ISP1362 chip memory to make sure the - * chip access timing is correct. - */ -#undef CHIP_BUFFER_TEST - -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include -#include - -#include -#include -#include - -static int dbg_level; -#ifdef ISP1362_DEBUG -module_param(dbg_level, int, 0644); -#else -module_param(dbg_level, int, 0); -#endif - -#include "../core/usb.h" -#include "isp1362.h" - - -#define DRIVER_VERSION "2005-04-04" -#define DRIVER_DESC "ISP1362 USB Host Controller Driver" - -MODULE_DESCRIPTION(DRIVER_DESC); -MODULE_LICENSE("GPL"); - -static const char hcd_name[] = "isp1362-hcd"; - -static void isp1362_hc_stop(struct usb_hcd *hcd); -static int isp1362_hc_start(struct usb_hcd *hcd); - -/*-------------------------------------------------------------------------*/ - -/* - * When called from the interrupthandler only isp1362_hcd->irqenb is modified, - * since the interrupt handler will write isp1362_hcd->irqenb to HCuPINT upon - * completion. - * We don't need a 'disable' counterpart, since interrupts will be disabled - * only by the interrupt handler. - */ -static inline void isp1362_enable_int(struct isp1362_hcd *isp1362_hcd, u16 mask) -{ - if ((isp1362_hcd->irqenb | mask) == isp1362_hcd->irqenb) - return; - if (mask & ~isp1362_hcd->irqenb) - isp1362_write_reg16(isp1362_hcd, HCuPINT, mask & ~isp1362_hcd->irqenb); - isp1362_hcd->irqenb |= mask; - if (isp1362_hcd->irq_active) - return; - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); -} - -/*-------------------------------------------------------------------------*/ - -static inline struct isp1362_ep_queue *get_ptd_queue(struct isp1362_hcd *isp1362_hcd, - u16 offset) -{ - struct isp1362_ep_queue *epq = NULL; - - if (offset < isp1362_hcd->istl_queue[1].buf_start) - epq = &isp1362_hcd->istl_queue[0]; - else if (offset < isp1362_hcd->intl_queue.buf_start) - epq = &isp1362_hcd->istl_queue[1]; - else if (offset < isp1362_hcd->atl_queue.buf_start) - epq = &isp1362_hcd->intl_queue; - else if (offset < isp1362_hcd->atl_queue.buf_start + - isp1362_hcd->atl_queue.buf_size) - epq = &isp1362_hcd->atl_queue; - - if (epq) - DBG(1, "%s: PTD $%04x is on %s queue\n", __func__, offset, epq->name); - else - pr_warn("%s: invalid PTD $%04x\n", __func__, offset); - - return epq; -} - -static inline int get_ptd_offset(struct isp1362_ep_queue *epq, u8 index) -{ - int offset; - - if (index * epq->blk_size > epq->buf_size) { - pr_warn("%s: Bad %s index %d(%d)\n", - __func__, epq->name, index, - epq->buf_size / epq->blk_size); - return -EINVAL; - } - offset = epq->buf_start + index * epq->blk_size; - DBG(3, "%s: %s PTD[%02x] # %04x\n", __func__, epq->name, index, offset); - - return offset; -} - -/*-------------------------------------------------------------------------*/ - -static inline u16 max_transfer_size(struct isp1362_ep_queue *epq, size_t size, - int mps) -{ - u16 xfer_size = min_t(size_t, MAX_XFER_SIZE, size); - - xfer_size = min_t(size_t, xfer_size, epq->buf_avail * epq->blk_size - PTD_HEADER_SIZE); - if (xfer_size < size && xfer_size % mps) - xfer_size -= xfer_size % mps; - - return xfer_size; -} - -static int claim_ptd_buffers(struct isp1362_ep_queue *epq, - struct isp1362_ep *ep, u16 len) -{ - int ptd_offset = -EINVAL; - int num_ptds = ((len + PTD_HEADER_SIZE - 1) / epq->blk_size) + 1; - int found; - - BUG_ON(len > epq->buf_size); - - if (!epq->buf_avail) - return -ENOMEM; - - if (ep->num_ptds) - pr_err("%s: %s len %d/%d num_ptds %d buf_map %08lx skip_map %08lx\n", __func__, - epq->name, len, epq->blk_size, num_ptds, epq->buf_map, epq->skip_map); - BUG_ON(ep->num_ptds != 0); - - found = bitmap_find_next_zero_area(&epq->buf_map, epq->buf_count, 0, - num_ptds, 0); - if (found >= epq->buf_count) - return -EOVERFLOW; - - DBG(1, "%s: Found %d PTDs[%d] for %d/%d byte\n", __func__, - num_ptds, found, len, (int)(epq->blk_size - PTD_HEADER_SIZE)); - ptd_offset = get_ptd_offset(epq, found); - WARN_ON(ptd_offset < 0); - ep->ptd_offset = ptd_offset; - ep->num_ptds += num_ptds; - epq->buf_avail -= num_ptds; - BUG_ON(epq->buf_avail > epq->buf_count); - ep->ptd_index = found; - bitmap_set(&epq->buf_map, found, num_ptds); - DBG(1, "%s: Done %s PTD[%d] $%04x, avail %d count %d claimed %d %08lx:%08lx\n", - __func__, epq->name, ep->ptd_index, ep->ptd_offset, - epq->buf_avail, epq->buf_count, num_ptds, epq->buf_map, epq->skip_map); - - return found; -} - -static inline void release_ptd_buffers(struct isp1362_ep_queue *epq, struct isp1362_ep *ep) -{ - int last = ep->ptd_index + ep->num_ptds; - - if (last > epq->buf_count) - pr_err("%s: ep %p req %d len %d %s PTD[%d] $%04x num_ptds %d buf_count %d buf_avail %d buf_map %08lx skip_map %08lx\n", - __func__, ep, ep->num_req, ep->length, epq->name, ep->ptd_index, - ep->ptd_offset, ep->num_ptds, epq->buf_count, epq->buf_avail, - epq->buf_map, epq->skip_map); - BUG_ON(last > epq->buf_count); - - bitmap_clear(&epq->buf_map, ep->ptd_index, ep->num_ptds); - bitmap_set(&epq->skip_map, ep->ptd_index, ep->num_ptds); - epq->buf_avail += ep->num_ptds; - epq->ptd_count--; - - BUG_ON(epq->buf_avail > epq->buf_count); - BUG_ON(epq->ptd_count > epq->buf_count); - - DBG(1, "%s: Done %s PTDs $%04x released %d avail %d count %d\n", - __func__, epq->name, - ep->ptd_offset, ep->num_ptds, epq->buf_avail, epq->buf_count); - DBG(1, "%s: buf_map %08lx skip_map %08lx\n", __func__, - epq->buf_map, epq->skip_map); - - ep->num_ptds = 0; - ep->ptd_offset = -EINVAL; - ep->ptd_index = -EINVAL; -} - -/*-------------------------------------------------------------------------*/ - -/* - Set up PTD's. -*/ -static void prepare_ptd(struct isp1362_hcd *isp1362_hcd, struct urb *urb, - struct isp1362_ep *ep, struct isp1362_ep_queue *epq, - u16 fno) -{ - struct ptd *ptd; - int toggle; - int dir; - u16 len; - size_t buf_len = urb->transfer_buffer_length - urb->actual_length; - - DBG(3, "%s: %s ep %p\n", __func__, epq->name, ep); - - ptd = &ep->ptd; - - ep->data = (unsigned char *)urb->transfer_buffer + urb->actual_length; - - switch (ep->nextpid) { - case USB_PID_IN: - toggle = usb_gettoggle(urb->dev, ep->epnum, 0); - dir = PTD_DIR_IN; - if (usb_pipecontrol(urb->pipe)) { - len = min_t(size_t, ep->maxpacket, buf_len); - } else if (usb_pipeisoc(urb->pipe)) { - len = min_t(size_t, urb->iso_frame_desc[fno].length, MAX_XFER_SIZE); - ep->data = urb->transfer_buffer + urb->iso_frame_desc[fno].offset; - } else - len = max_transfer_size(epq, buf_len, ep->maxpacket); - DBG(1, "%s: IN len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, - (int)buf_len); - break; - case USB_PID_OUT: - toggle = usb_gettoggle(urb->dev, ep->epnum, 1); - dir = PTD_DIR_OUT; - if (usb_pipecontrol(urb->pipe)) - len = min_t(size_t, ep->maxpacket, buf_len); - else if (usb_pipeisoc(urb->pipe)) - len = min_t(size_t, urb->iso_frame_desc[0].length, MAX_XFER_SIZE); - else - len = max_transfer_size(epq, buf_len, ep->maxpacket); - if (len == 0) - pr_info("%s: Sending ZERO packet: %d\n", __func__, - urb->transfer_flags & URB_ZERO_PACKET); - DBG(1, "%s: OUT len %d/%d/%d from URB\n", __func__, len, ep->maxpacket, - (int)buf_len); - break; - case USB_PID_SETUP: - toggle = 0; - dir = PTD_DIR_SETUP; - len = sizeof(struct usb_ctrlrequest); - DBG(1, "%s: SETUP len %d\n", __func__, len); - ep->data = urb->setup_packet; - break; - case USB_PID_ACK: - toggle = 1; - len = 0; - dir = (urb->transfer_buffer_length && usb_pipein(urb->pipe)) ? - PTD_DIR_OUT : PTD_DIR_IN; - DBG(1, "%s: ACK len %d\n", __func__, len); - break; - default: - toggle = dir = len = 0; - pr_err("%s@%d: ep->nextpid %02x\n", __func__, __LINE__, ep->nextpid); - BUG_ON(1); - } - - ep->length = len; - if (!len) - ep->data = NULL; - - ptd->count = PTD_CC_MSK | PTD_ACTIVE_MSK | PTD_TOGGLE(toggle); - ptd->mps = PTD_MPS(ep->maxpacket) | PTD_SPD(urb->dev->speed == USB_SPEED_LOW) | - PTD_EP(ep->epnum); - ptd->len = PTD_LEN(len) | PTD_DIR(dir); - ptd->faddr = PTD_FA(usb_pipedevice(urb->pipe)); - - if (usb_pipeint(urb->pipe)) { - ptd->faddr |= PTD_SF_INT(ep->branch); - ptd->faddr |= PTD_PR(ep->interval ? __ffs(ep->interval) : 0); - } - if (usb_pipeisoc(urb->pipe)) - ptd->faddr |= PTD_SF_ISO(fno); - - DBG(1, "%s: Finished\n", __func__); -} - -static void isp1362_write_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, - struct isp1362_ep_queue *epq) -{ - struct ptd *ptd = &ep->ptd; - int len = PTD_GET_DIR(ptd) == PTD_DIR_IN ? 0 : ep->length; - - prefetch(ptd); - isp1362_write_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE); - if (len) - isp1362_write_buffer(isp1362_hcd, ep->data, - ep->ptd_offset + PTD_HEADER_SIZE, len); - - dump_ptd(ptd); - dump_ptd_out_data(ptd, ep->data); -} - -static void isp1362_read_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, - struct isp1362_ep_queue *epq) -{ - struct ptd *ptd = &ep->ptd; - int act_len; - - WARN_ON(list_empty(&ep->active)); - BUG_ON(ep->ptd_offset < 0); - - list_del_init(&ep->active); - DBG(1, "%s: ep %p removed from active list %p\n", __func__, ep, &epq->active); - - prefetchw(ptd); - isp1362_read_buffer(isp1362_hcd, ptd, ep->ptd_offset, PTD_HEADER_SIZE); - dump_ptd(ptd); - act_len = PTD_GET_COUNT(ptd); - if (PTD_GET_DIR(ptd) != PTD_DIR_IN || act_len == 0) - return; - if (act_len > ep->length) - pr_err("%s: ep %p PTD $%04x act_len %d ep->length %d\n", __func__, ep, - ep->ptd_offset, act_len, ep->length); - BUG_ON(act_len > ep->length); - /* Only transfer the amount of data that has actually been overwritten - * in the chip buffer. We don't want any data that doesn't belong to the - * transfer to leak out of the chip to the callers transfer buffer! - */ - prefetchw(ep->data); - isp1362_read_buffer(isp1362_hcd, ep->data, - ep->ptd_offset + PTD_HEADER_SIZE, act_len); - dump_ptd_in_data(ptd, ep->data); -} - -/* - * INT PTDs will stay in the chip until data is available. - * This function will remove a PTD from the chip when the URB is dequeued. - * Must be called with the spinlock held and IRQs disabled - */ -static void remove_ptd(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep) - -{ - int index; - struct isp1362_ep_queue *epq; - - DBG(1, "%s: ep %p PTD[%d] $%04x\n", __func__, ep, ep->ptd_index, ep->ptd_offset); - BUG_ON(ep->ptd_offset < 0); - - epq = get_ptd_queue(isp1362_hcd, ep->ptd_offset); - BUG_ON(!epq); - - /* put ep in remove_list for cleanup */ - WARN_ON(!list_empty(&ep->remove_list)); - list_add_tail(&ep->remove_list, &isp1362_hcd->remove_list); - /* let SOF interrupt handle the cleanup */ - isp1362_enable_int(isp1362_hcd, HCuPINT_SOF); - - index = ep->ptd_index; - if (index < 0) - /* ISO queues don't have SKIP registers */ - return; - - DBG(1, "%s: Disabling PTD[%02x] $%04x %08lx|%08x\n", __func__, - index, ep->ptd_offset, epq->skip_map, 1 << index); - - /* prevent further processing of PTD (will be effective after next SOF) */ - epq->skip_map |= 1 << index; - if (epq == &isp1362_hcd->atl_queue) { - DBG(2, "%s: ATLSKIP = %08x -> %08lx\n", __func__, - isp1362_read_reg32(isp1362_hcd, HCATLSKIP), epq->skip_map); - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, epq->skip_map); - if (~epq->skip_map == 0) - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); - } else if (epq == &isp1362_hcd->intl_queue) { - DBG(2, "%s: INTLSKIP = %08x -> %08lx\n", __func__, - isp1362_read_reg32(isp1362_hcd, HCINTLSKIP), epq->skip_map); - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, epq->skip_map); - if (~epq->skip_map == 0) - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); - } -} - -/* - Take done or failed requests out of schedule. Give back - processed urbs. -*/ -static void finish_request(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep, - struct urb *urb, int status) - __releases(isp1362_hcd->lock) - __acquires(isp1362_hcd->lock) -{ - urb->hcpriv = NULL; - ep->error_count = 0; - - if (usb_pipecontrol(urb->pipe)) - ep->nextpid = USB_PID_SETUP; - - URB_DBG("%s: req %d FA %d ep%d%s %s: len %d/%d %s stat %d\n", __func__, - ep->num_req, usb_pipedevice(urb->pipe), - usb_pipeendpoint(urb->pipe), - !usb_pipein(urb->pipe) ? "out" : "in", - usb_pipecontrol(urb->pipe) ? "ctrl" : - usb_pipeint(urb->pipe) ? "int" : - usb_pipebulk(urb->pipe) ? "bulk" : - "iso", - urb->actual_length, urb->transfer_buffer_length, - !(urb->transfer_flags & URB_SHORT_NOT_OK) ? - "short_ok" : "", urb->status); - - - usb_hcd_unlink_urb_from_ep(isp1362_hcd_to_hcd(isp1362_hcd), urb); - spin_unlock(&isp1362_hcd->lock); - usb_hcd_giveback_urb(isp1362_hcd_to_hcd(isp1362_hcd), urb, status); - spin_lock(&isp1362_hcd->lock); - - /* take idle endpoints out of the schedule right away */ - if (!list_empty(&ep->hep->urb_list)) - return; - - /* async deschedule */ - if (!list_empty(&ep->schedule)) { - list_del_init(&ep->schedule); - return; - } - - - if (ep->interval) { - /* periodic deschedule */ - DBG(1, "deschedule qh%d/%p branch %d load %d bandwidth %d -> %d\n", ep->interval, - ep, ep->branch, ep->load, - isp1362_hcd->load[ep->branch], - isp1362_hcd->load[ep->branch] - ep->load); - isp1362_hcd->load[ep->branch] -= ep->load; - ep->branch = PERIODIC_SIZE; - } -} - -/* - * Analyze transfer results, handle partial transfers and errors -*/ -static void postproc_ep(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep *ep) -{ - struct urb *urb = get_urb(ep); - struct usb_device *udev; - struct ptd *ptd; - int short_ok; - u16 len; - int urbstat = -EINPROGRESS; - u8 cc; - - DBG(2, "%s: ep %p req %d\n", __func__, ep, ep->num_req); - - udev = urb->dev; - ptd = &ep->ptd; - cc = PTD_GET_CC(ptd); - if (cc == PTD_NOTACCESSED) { - pr_err("%s: req %d PTD %p Untouched by ISP1362\n", __func__, - ep->num_req, ptd); - cc = PTD_DEVNOTRESP; - } - - short_ok = !(urb->transfer_flags & URB_SHORT_NOT_OK); - len = urb->transfer_buffer_length - urb->actual_length; - - /* Data underrun is special. For allowed underrun - we clear the error and continue as normal. For - forbidden underrun we finish the DATA stage - immediately while for control transfer, - we do a STATUS stage. - */ - if (cc == PTD_DATAUNDERRUN) { - if (short_ok) { - DBG(1, "%s: req %d Allowed data underrun short_%sok %d/%d/%d byte\n", - __func__, ep->num_req, short_ok ? "" : "not_", - PTD_GET_COUNT(ptd), ep->maxpacket, len); - cc = PTD_CC_NOERROR; - urbstat = 0; - } else { - DBG(1, "%s: req %d Data Underrun %s nextpid %02x short_%sok %d/%d/%d byte\n", - __func__, ep->num_req, - usb_pipein(urb->pipe) ? "IN" : "OUT", ep->nextpid, - short_ok ? "" : "not_", - PTD_GET_COUNT(ptd), ep->maxpacket, len); - /* save the data underrun error code for later and - * proceed with the status stage - */ - urb->actual_length += PTD_GET_COUNT(ptd); - if (usb_pipecontrol(urb->pipe)) { - ep->nextpid = USB_PID_ACK; - BUG_ON(urb->actual_length > urb->transfer_buffer_length); - - if (urb->status == -EINPROGRESS) - urb->status = cc_to_error[PTD_DATAUNDERRUN]; - } else { - usb_settoggle(udev, ep->epnum, ep->nextpid == USB_PID_OUT, - PTD_GET_TOGGLE(ptd)); - urbstat = cc_to_error[PTD_DATAUNDERRUN]; - } - goto out; - } - } - - if (cc != PTD_CC_NOERROR) { - if (++ep->error_count >= 3 || cc == PTD_CC_STALL || cc == PTD_DATAOVERRUN) { - urbstat = cc_to_error[cc]; - DBG(1, "%s: req %d nextpid %02x, status %d, error %d, error_count %d\n", - __func__, ep->num_req, ep->nextpid, urbstat, cc, - ep->error_count); - } - goto out; - } - - switch (ep->nextpid) { - case USB_PID_OUT: - if (PTD_GET_COUNT(ptd) != ep->length) - pr_err("%s: count=%d len=%d\n", __func__, - PTD_GET_COUNT(ptd), ep->length); - BUG_ON(PTD_GET_COUNT(ptd) != ep->length); - urb->actual_length += ep->length; - BUG_ON(urb->actual_length > urb->transfer_buffer_length); - usb_settoggle(udev, ep->epnum, 1, PTD_GET_TOGGLE(ptd)); - if (urb->actual_length == urb->transfer_buffer_length) { - DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__, - ep->num_req, len, ep->maxpacket, urbstat); - if (usb_pipecontrol(urb->pipe)) { - DBG(3, "%s: req %d %s Wait for ACK\n", __func__, - ep->num_req, - usb_pipein(urb->pipe) ? "IN" : "OUT"); - ep->nextpid = USB_PID_ACK; - } else { - if (len % ep->maxpacket || - !(urb->transfer_flags & URB_ZERO_PACKET)) { - urbstat = 0; - DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n", - __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT", - urbstat, len, ep->maxpacket, urb->actual_length); - } - } - } - break; - case USB_PID_IN: - len = PTD_GET_COUNT(ptd); - BUG_ON(len > ep->length); - urb->actual_length += len; - BUG_ON(urb->actual_length > urb->transfer_buffer_length); - usb_settoggle(udev, ep->epnum, 0, PTD_GET_TOGGLE(ptd)); - /* if transfer completed or (allowed) data underrun */ - if ((urb->transfer_buffer_length == urb->actual_length) || - len % ep->maxpacket) { - DBG(3, "%s: req %d xfer complete %d/%d status %d -> 0\n", __func__, - ep->num_req, len, ep->maxpacket, urbstat); - if (usb_pipecontrol(urb->pipe)) { - DBG(3, "%s: req %d %s Wait for ACK\n", __func__, - ep->num_req, - usb_pipein(urb->pipe) ? "IN" : "OUT"); - ep->nextpid = USB_PID_ACK; - } else { - urbstat = 0; - DBG(3, "%s: req %d URB %s status %d count %d/%d/%d\n", - __func__, ep->num_req, usb_pipein(urb->pipe) ? "IN" : "OUT", - urbstat, len, ep->maxpacket, urb->actual_length); - } - } - break; - case USB_PID_SETUP: - if (urb->transfer_buffer_length == urb->actual_length) { - ep->nextpid = USB_PID_ACK; - } else if (usb_pipeout(urb->pipe)) { - usb_settoggle(udev, 0, 1, 1); - ep->nextpid = USB_PID_OUT; - } else { - usb_settoggle(udev, 0, 0, 1); - ep->nextpid = USB_PID_IN; - } - break; - case USB_PID_ACK: - DBG(3, "%s: req %d got ACK %d -> 0\n", __func__, ep->num_req, - urbstat); - WARN_ON(urbstat != -EINPROGRESS); - urbstat = 0; - ep->nextpid = 0; - break; - default: - BUG_ON(1); - } - - out: - if (urbstat != -EINPROGRESS) { - DBG(2, "%s: Finishing ep %p req %d urb %p status %d\n", __func__, - ep, ep->num_req, urb, urbstat); - finish_request(isp1362_hcd, ep, urb, urbstat); - } -} - -static void finish_unlinks(struct isp1362_hcd *isp1362_hcd) -{ - struct isp1362_ep *ep; - struct isp1362_ep *tmp; - - list_for_each_entry_safe(ep, tmp, &isp1362_hcd->remove_list, remove_list) { - struct isp1362_ep_queue *epq = - get_ptd_queue(isp1362_hcd, ep->ptd_offset); - int index = ep->ptd_index; - - BUG_ON(epq == NULL); - if (index >= 0) { - DBG(1, "%s: remove PTD[%d] $%04x\n", __func__, index, ep->ptd_offset); - BUG_ON(ep->num_ptds == 0); - release_ptd_buffers(epq, ep); - } - if (!list_empty(&ep->hep->urb_list)) { - struct urb *urb = get_urb(ep); - - DBG(1, "%s: Finishing req %d ep %p from remove_list\n", __func__, - ep->num_req, ep); - finish_request(isp1362_hcd, ep, urb, -ESHUTDOWN); - } - WARN_ON(list_empty(&ep->active)); - if (!list_empty(&ep->active)) { - list_del_init(&ep->active); - DBG(1, "%s: ep %p removed from active list\n", __func__, ep); - } - list_del_init(&ep->remove_list); - DBG(1, "%s: ep %p removed from remove_list\n", __func__, ep); - } - DBG(1, "%s: Done\n", __func__); -} - -static inline void enable_atl_transfers(struct isp1362_hcd *isp1362_hcd, int count) -{ - if (count > 0) { - if (count < isp1362_hcd->atl_queue.ptd_count) - isp1362_write_reg16(isp1362_hcd, HCATLDTC, count); - isp1362_enable_int(isp1362_hcd, HCuPINT_ATL); - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, isp1362_hcd->atl_queue.skip_map); - isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); - } else - isp1362_enable_int(isp1362_hcd, HCuPINT_SOF); -} - -static inline void enable_intl_transfers(struct isp1362_hcd *isp1362_hcd) -{ - isp1362_enable_int(isp1362_hcd, HCuPINT_INTL); - isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, isp1362_hcd->intl_queue.skip_map); -} - -static inline void enable_istl_transfers(struct isp1362_hcd *isp1362_hcd, int flip) -{ - isp1362_enable_int(isp1362_hcd, flip ? HCuPINT_ISTL1 : HCuPINT_ISTL0); - isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, flip ? - HCBUFSTAT_ISTL1_FULL : HCBUFSTAT_ISTL0_FULL); -} - -static int submit_req(struct isp1362_hcd *isp1362_hcd, struct urb *urb, - struct isp1362_ep *ep, struct isp1362_ep_queue *epq) -{ - int index; - - prepare_ptd(isp1362_hcd, urb, ep, epq, 0); - index = claim_ptd_buffers(epq, ep, ep->length); - if (index == -ENOMEM) { - DBG(1, "%s: req %d No free %s PTD available: %d, %08lx:%08lx\n", __func__, - ep->num_req, epq->name, ep->num_ptds, epq->buf_map, epq->skip_map); - return index; - } else if (index == -EOVERFLOW) { - DBG(1, "%s: req %d Not enough space for %d byte %s PTD %d %08lx:%08lx\n", - __func__, ep->num_req, ep->length, epq->name, ep->num_ptds, - epq->buf_map, epq->skip_map); - return index; - } else - BUG_ON(index < 0); - list_add_tail(&ep->active, &epq->active); - DBG(1, "%s: ep %p req %d len %d added to active list %p\n", __func__, - ep, ep->num_req, ep->length, &epq->active); - DBG(1, "%s: Submitting %s PTD $%04x for ep %p req %d\n", __func__, epq->name, - ep->ptd_offset, ep, ep->num_req); - isp1362_write_ptd(isp1362_hcd, ep, epq); - __clear_bit(ep->ptd_index, &epq->skip_map); - - return 0; -} - -static void start_atl_transfers(struct isp1362_hcd *isp1362_hcd) -{ - int ptd_count = 0; - struct isp1362_ep_queue *epq = &isp1362_hcd->atl_queue; - struct isp1362_ep *ep; - int defer = 0; - - if (atomic_read(&epq->finishing)) { - DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); - return; - } - - list_for_each_entry(ep, &isp1362_hcd->async, schedule) { - struct urb *urb = get_urb(ep); - int ret; - - if (!list_empty(&ep->active)) { - DBG(2, "%s: Skipping active %s ep %p\n", __func__, epq->name, ep); - continue; - } - - DBG(1, "%s: Processing %s ep %p req %d\n", __func__, epq->name, - ep, ep->num_req); - - ret = submit_req(isp1362_hcd, urb, ep, epq); - if (ret == -ENOMEM) { - defer = 1; - break; - } else if (ret == -EOVERFLOW) { - defer = 1; - continue; - } -#ifdef BUGGY_PXA2XX_UDC_USBTEST - defer = ep->nextpid == USB_PID_SETUP; -#endif - ptd_count++; - } - - /* Avoid starving of endpoints */ - if (isp1362_hcd->async.next != isp1362_hcd->async.prev) { - DBG(2, "%s: Cycling ASYNC schedule %d\n", __func__, ptd_count); - list_move(&isp1362_hcd->async, isp1362_hcd->async.next); - } - if (ptd_count || defer) - enable_atl_transfers(isp1362_hcd, defer ? 0 : ptd_count); - - epq->ptd_count += ptd_count; - if (epq->ptd_count > epq->stat_maxptds) { - epq->stat_maxptds = epq->ptd_count; - DBG(0, "%s: max_ptds: %d\n", __func__, epq->stat_maxptds); - } -} - -static void start_intl_transfers(struct isp1362_hcd *isp1362_hcd) -{ - int ptd_count = 0; - struct isp1362_ep_queue *epq = &isp1362_hcd->intl_queue; - struct isp1362_ep *ep; - - if (atomic_read(&epq->finishing)) { - DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); - return; - } - - list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) { - struct urb *urb = get_urb(ep); - int ret; - - if (!list_empty(&ep->active)) { - DBG(1, "%s: Skipping active %s ep %p\n", __func__, - epq->name, ep); - continue; - } - - DBG(1, "%s: Processing %s ep %p req %d\n", __func__, - epq->name, ep, ep->num_req); - ret = submit_req(isp1362_hcd, urb, ep, epq); - if (ret == -ENOMEM) - break; - else if (ret == -EOVERFLOW) - continue; - ptd_count++; - } - - if (ptd_count) { - static int last_count; - - if (ptd_count != last_count) { - DBG(0, "%s: ptd_count: %d\n", __func__, ptd_count); - last_count = ptd_count; - } - enable_intl_transfers(isp1362_hcd); - } - - epq->ptd_count += ptd_count; - if (epq->ptd_count > epq->stat_maxptds) - epq->stat_maxptds = epq->ptd_count; -} - -static inline int next_ptd(struct isp1362_ep_queue *epq, struct isp1362_ep *ep) -{ - u16 ptd_offset = ep->ptd_offset; - int num_ptds = (ep->length + PTD_HEADER_SIZE + (epq->blk_size - 1)) / epq->blk_size; - - DBG(2, "%s: PTD offset $%04x + %04x => %d * %04x -> $%04x\n", __func__, ptd_offset, - ep->length, num_ptds, epq->blk_size, ptd_offset + num_ptds * epq->blk_size); - - ptd_offset += num_ptds * epq->blk_size; - if (ptd_offset < epq->buf_start + epq->buf_size) - return ptd_offset; - else - return -ENOMEM; -} - -static void start_iso_transfers(struct isp1362_hcd *isp1362_hcd) -{ - int ptd_count = 0; - int flip = isp1362_hcd->istl_flip; - struct isp1362_ep_queue *epq; - int ptd_offset; - struct isp1362_ep *ep; - struct isp1362_ep *tmp; - u16 fno = isp1362_read_reg32(isp1362_hcd, HCFMNUM); - - fill2: - epq = &isp1362_hcd->istl_queue[flip]; - if (atomic_read(&epq->finishing)) { - DBG(1, "%s: finish_transfers is active for %s\n", __func__, epq->name); - return; - } - - if (!list_empty(&epq->active)) - return; - - ptd_offset = epq->buf_start; - list_for_each_entry_safe(ep, tmp, &isp1362_hcd->isoc, schedule) { - struct urb *urb = get_urb(ep); - s16 diff = fno - (u16)urb->start_frame; - - DBG(1, "%s: Processing %s ep %p\n", __func__, epq->name, ep); - - if (diff > urb->number_of_packets) { - /* time frame for this URB has elapsed */ - finish_request(isp1362_hcd, ep, urb, -EOVERFLOW); - continue; - } else if (diff < -1) { - /* URB is not due in this frame or the next one. - * Comparing with '-1' instead of '0' accounts for double - * buffering in the ISP1362 which enables us to queue the PTD - * one frame ahead of time - */ - } else if (diff == -1) { - /* submit PTD's that are due in the next frame */ - prepare_ptd(isp1362_hcd, urb, ep, epq, fno); - if (ptd_offset + PTD_HEADER_SIZE + ep->length > - epq->buf_start + epq->buf_size) { - pr_err("%s: Not enough ISO buffer space for %d byte PTD\n", - __func__, ep->length); - continue; - } - ep->ptd_offset = ptd_offset; - list_add_tail(&ep->active, &epq->active); - - ptd_offset = next_ptd(epq, ep); - if (ptd_offset < 0) { - pr_warn("%s: req %d No more %s PTD buffers available\n", - __func__, ep->num_req, epq->name); - break; - } - } - } - list_for_each_entry(ep, &epq->active, active) { - if (epq->active.next == &ep->active) - ep->ptd.mps |= PTD_LAST_MSK; - isp1362_write_ptd(isp1362_hcd, ep, epq); - ptd_count++; - } - - if (ptd_count) - enable_istl_transfers(isp1362_hcd, flip); - - epq->ptd_count += ptd_count; - if (epq->ptd_count > epq->stat_maxptds) - epq->stat_maxptds = epq->ptd_count; - - /* check, whether the second ISTL buffer may also be filled */ - if (!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - (flip ? HCBUFSTAT_ISTL0_FULL : HCBUFSTAT_ISTL1_FULL))) { - fno++; - ptd_count = 0; - flip = 1 - flip; - goto fill2; - } -} - -static void finish_transfers(struct isp1362_hcd *isp1362_hcd, unsigned long done_map, - struct isp1362_ep_queue *epq) -{ - struct isp1362_ep *ep; - struct isp1362_ep *tmp; - - if (list_empty(&epq->active)) { - DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name); - return; - } - - DBG(1, "%s: Finishing %s transfers %08lx\n", __func__, epq->name, done_map); - - atomic_inc(&epq->finishing); - list_for_each_entry_safe(ep, tmp, &epq->active, active) { - int index = ep->ptd_index; - - DBG(1, "%s: Checking %s PTD[%02x] $%04x\n", __func__, epq->name, - index, ep->ptd_offset); - - BUG_ON(index < 0); - if (__test_and_clear_bit(index, &done_map)) { - isp1362_read_ptd(isp1362_hcd, ep, epq); - epq->free_ptd = index; - BUG_ON(ep->num_ptds == 0); - release_ptd_buffers(epq, ep); - - DBG(1, "%s: ep %p req %d removed from active list\n", __func__, - ep, ep->num_req); - if (!list_empty(&ep->remove_list)) { - list_del_init(&ep->remove_list); - DBG(1, "%s: ep %p removed from remove list\n", __func__, ep); - } - DBG(1, "%s: Postprocessing %s ep %p req %d\n", __func__, epq->name, - ep, ep->num_req); - postproc_ep(isp1362_hcd, ep); - } - if (!done_map) - break; - } - if (done_map) - pr_warn("%s: done_map not clear: %08lx:%08lx\n", - __func__, done_map, epq->skip_map); - atomic_dec(&epq->finishing); -} - -static void finish_iso_transfers(struct isp1362_hcd *isp1362_hcd, struct isp1362_ep_queue *epq) -{ - struct isp1362_ep *ep; - struct isp1362_ep *tmp; - - if (list_empty(&epq->active)) { - DBG(1, "%s: Nothing to do for %s queue\n", __func__, epq->name); - return; - } - - DBG(1, "%s: Finishing %s transfers\n", __func__, epq->name); - - atomic_inc(&epq->finishing); - list_for_each_entry_safe(ep, tmp, &epq->active, active) { - DBG(1, "%s: Checking PTD $%04x\n", __func__, ep->ptd_offset); - - isp1362_read_ptd(isp1362_hcd, ep, epq); - DBG(1, "%s: Postprocessing %s ep %p\n", __func__, epq->name, ep); - postproc_ep(isp1362_hcd, ep); - } - WARN_ON(epq->blk_size != 0); - atomic_dec(&epq->finishing); -} - -static irqreturn_t isp1362_irq(struct usb_hcd *hcd) -{ - int handled = 0; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - u16 irqstat; - u16 svc_mask; - - spin_lock(&isp1362_hcd->lock); - - BUG_ON(isp1362_hcd->irq_active++); - - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); - - irqstat = isp1362_read_reg16(isp1362_hcd, HCuPINT); - DBG(3, "%s: got IRQ %04x:%04x\n", __func__, irqstat, isp1362_hcd->irqenb); - - /* only handle interrupts that are currently enabled */ - irqstat &= isp1362_hcd->irqenb; - isp1362_write_reg16(isp1362_hcd, HCuPINT, irqstat); - svc_mask = irqstat; - - if (irqstat & HCuPINT_SOF) { - isp1362_hcd->irqenb &= ~HCuPINT_SOF; - isp1362_hcd->irq_stat[ISP1362_INT_SOF]++; - handled = 1; - svc_mask &= ~HCuPINT_SOF; - DBG(3, "%s: SOF\n", __func__); - isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM); - if (!list_empty(&isp1362_hcd->remove_list)) - finish_unlinks(isp1362_hcd); - if (!list_empty(&isp1362_hcd->async) && !(irqstat & HCuPINT_ATL)) { - if (list_empty(&isp1362_hcd->atl_queue.active)) { - start_atl_transfers(isp1362_hcd); - } else { - isp1362_enable_int(isp1362_hcd, HCuPINT_ATL); - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, - isp1362_hcd->atl_queue.skip_map); - isp1362_set_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); - } - } - } - - if (irqstat & HCuPINT_ISTL0) { - isp1362_hcd->irq_stat[ISP1362_INT_ISTL0]++; - handled = 1; - svc_mask &= ~HCuPINT_ISTL0; - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL0_FULL); - DBG(1, "%s: ISTL0\n", __func__); - WARN_ON((int)!!isp1362_hcd->istl_flip); - WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - HCBUFSTAT_ISTL0_ACTIVE); - WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - HCBUFSTAT_ISTL0_DONE)); - isp1362_hcd->irqenb &= ~HCuPINT_ISTL0; - } - - if (irqstat & HCuPINT_ISTL1) { - isp1362_hcd->irq_stat[ISP1362_INT_ISTL1]++; - handled = 1; - svc_mask &= ~HCuPINT_ISTL1; - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ISTL1_FULL); - DBG(1, "%s: ISTL1\n", __func__); - WARN_ON(!(int)isp1362_hcd->istl_flip); - WARN_ON(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - HCBUFSTAT_ISTL1_ACTIVE); - WARN_ON(!(isp1362_read_reg16(isp1362_hcd, HCBUFSTAT) & - HCBUFSTAT_ISTL1_DONE)); - isp1362_hcd->irqenb &= ~HCuPINT_ISTL1; - } - - if (irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) { - WARN_ON((irqstat & (HCuPINT_ISTL0 | HCuPINT_ISTL1)) == - (HCuPINT_ISTL0 | HCuPINT_ISTL1)); - finish_iso_transfers(isp1362_hcd, - &isp1362_hcd->istl_queue[isp1362_hcd->istl_flip]); - start_iso_transfers(isp1362_hcd); - isp1362_hcd->istl_flip = 1 - isp1362_hcd->istl_flip; - } - - if (irqstat & HCuPINT_INTL) { - u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE); - u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCINTLSKIP); - isp1362_hcd->irq_stat[ISP1362_INT_INTL]++; - - DBG(2, "%s: INTL\n", __func__); - - svc_mask &= ~HCuPINT_INTL; - - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, skip_map | done_map); - if (~(done_map | skip_map) == 0) - /* All PTDs are finished, disable INTL processing entirely */ - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_INTL_ACTIVE); - - handled = 1; - WARN_ON(!done_map); - if (done_map) { - DBG(3, "%s: INTL done_map %08x\n", __func__, done_map); - finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue); - start_intl_transfers(isp1362_hcd); - } - } - - if (irqstat & HCuPINT_ATL) { - u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE); - u32 skip_map = isp1362_read_reg32(isp1362_hcd, HCATLSKIP); - isp1362_hcd->irq_stat[ISP1362_INT_ATL]++; - - DBG(2, "%s: ATL\n", __func__); - - svc_mask &= ~HCuPINT_ATL; - - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, skip_map | done_map); - if (~(done_map | skip_map) == 0) - isp1362_clr_mask16(isp1362_hcd, HCBUFSTAT, HCBUFSTAT_ATL_ACTIVE); - if (done_map) { - DBG(3, "%s: ATL done_map %08x\n", __func__, done_map); - finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue); - start_atl_transfers(isp1362_hcd); - } - handled = 1; - } - - if (irqstat & HCuPINT_OPR) { - u32 intstat = isp1362_read_reg32(isp1362_hcd, HCINTSTAT); - isp1362_hcd->irq_stat[ISP1362_INT_OPR]++; - - svc_mask &= ~HCuPINT_OPR; - DBG(2, "%s: OPR %08x:%08x\n", __func__, intstat, isp1362_hcd->intenb); - intstat &= isp1362_hcd->intenb; - if (intstat & OHCI_INTR_UE) { - pr_err("Unrecoverable error\n"); - /* FIXME: do here reset or cleanup or whatever */ - } - if (intstat & OHCI_INTR_RHSC) { - isp1362_hcd->rhstatus = isp1362_read_reg32(isp1362_hcd, HCRHSTATUS); - isp1362_hcd->rhport[0] = isp1362_read_reg32(isp1362_hcd, HCRHPORT1); - isp1362_hcd->rhport[1] = isp1362_read_reg32(isp1362_hcd, HCRHPORT2); - } - if (intstat & OHCI_INTR_RD) { - pr_info("%s: RESUME DETECTED\n", __func__); - isp1362_show_reg(isp1362_hcd, HCCONTROL); - usb_hcd_resume_root_hub(hcd); - } - isp1362_write_reg32(isp1362_hcd, HCINTSTAT, intstat); - irqstat &= ~HCuPINT_OPR; - handled = 1; - } - - if (irqstat & HCuPINT_SUSP) { - isp1362_hcd->irq_stat[ISP1362_INT_SUSP]++; - handled = 1; - svc_mask &= ~HCuPINT_SUSP; - - pr_info("%s: SUSPEND IRQ\n", __func__); - } - - if (irqstat & HCuPINT_CLKRDY) { - isp1362_hcd->irq_stat[ISP1362_INT_CLKRDY]++; - handled = 1; - isp1362_hcd->irqenb &= ~HCuPINT_CLKRDY; - svc_mask &= ~HCuPINT_CLKRDY; - pr_info("%s: CLKRDY IRQ\n", __func__); - } - - if (svc_mask) - pr_err("%s: Unserviced interrupt(s) %04x\n", __func__, svc_mask); - - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); - isp1362_hcd->irq_active--; - spin_unlock(&isp1362_hcd->lock); - - return IRQ_RETVAL(handled); -} - -/*-------------------------------------------------------------------------*/ - -#define MAX_PERIODIC_LOAD 900 /* out of 1000 usec */ -static int balance(struct isp1362_hcd *isp1362_hcd, u16 interval, u16 load) -{ - int i, branch = -ENOSPC; - - /* search for the least loaded schedule branch of that interval - * which has enough bandwidth left unreserved. - */ - for (i = 0; i < interval; i++) { - if (branch < 0 || isp1362_hcd->load[branch] > isp1362_hcd->load[i]) { - int j; - - for (j = i; j < PERIODIC_SIZE; j += interval) { - if ((isp1362_hcd->load[j] + load) > MAX_PERIODIC_LOAD) { - pr_err("%s: new load %d load[%02x] %d max %d\n", __func__, - load, j, isp1362_hcd->load[j], MAX_PERIODIC_LOAD); - break; - } - } - if (j < PERIODIC_SIZE) - continue; - branch = i; - } - } - return branch; -} - -/* NB! ALL the code above this point runs with isp1362_hcd->lock - held, irqs off -*/ - -/*-------------------------------------------------------------------------*/ - -static int isp1362_urb_enqueue(struct usb_hcd *hcd, - struct urb *urb, - gfp_t mem_flags) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - struct usb_device *udev = urb->dev; - unsigned int pipe = urb->pipe; - int is_out = !usb_pipein(pipe); - int type = usb_pipetype(pipe); - int epnum = usb_pipeendpoint(pipe); - struct usb_host_endpoint *hep = urb->ep; - struct isp1362_ep *ep = NULL; - unsigned long flags; - int retval = 0; - - DBG(3, "%s: urb %p\n", __func__, urb); - - if (type == PIPE_ISOCHRONOUS) { - pr_err("Isochronous transfers not supported\n"); - return -ENOSPC; - } - - URB_DBG("%s: FA %d ep%d%s %s: len %d %s%s\n", __func__, - usb_pipedevice(pipe), epnum, - is_out ? "out" : "in", - usb_pipecontrol(pipe) ? "ctrl" : - usb_pipeint(pipe) ? "int" : - usb_pipebulk(pipe) ? "bulk" : - "iso", - urb->transfer_buffer_length, - (urb->transfer_flags & URB_ZERO_PACKET) ? "ZERO_PACKET " : "", - !(urb->transfer_flags & URB_SHORT_NOT_OK) ? - "short_ok" : ""); - - /* avoid all allocations within spinlocks: request or endpoint */ - if (!hep->hcpriv) { - ep = kzalloc(sizeof *ep, mem_flags); - if (!ep) - return -ENOMEM; - } - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - /* don't submit to a dead or disabled port */ - if (!((isp1362_hcd->rhport[0] | isp1362_hcd->rhport[1]) & - USB_PORT_STAT_ENABLE) || - !HC_IS_RUNNING(hcd->state)) { - kfree(ep); - retval = -ENODEV; - goto fail_not_linked; - } - - retval = usb_hcd_link_urb_to_ep(hcd, urb); - if (retval) { - kfree(ep); - goto fail_not_linked; - } - - if (hep->hcpriv) { - ep = hep->hcpriv; - } else { - INIT_LIST_HEAD(&ep->schedule); - INIT_LIST_HEAD(&ep->active); - INIT_LIST_HEAD(&ep->remove_list); - ep->udev = usb_get_dev(udev); - ep->hep = hep; - ep->epnum = epnum; - ep->maxpacket = usb_maxpacket(udev, urb->pipe); - ep->ptd_offset = -EINVAL; - ep->ptd_index = -EINVAL; - usb_settoggle(udev, epnum, is_out, 0); - - if (type == PIPE_CONTROL) - ep->nextpid = USB_PID_SETUP; - else if (is_out) - ep->nextpid = USB_PID_OUT; - else - ep->nextpid = USB_PID_IN; - - switch (type) { - case PIPE_ISOCHRONOUS: - case PIPE_INTERRUPT: - if (urb->interval > PERIODIC_SIZE) - urb->interval = PERIODIC_SIZE; - ep->interval = urb->interval; - ep->branch = PERIODIC_SIZE; - ep->load = usb_calc_bus_time(udev->speed, !is_out, - type == PIPE_ISOCHRONOUS, - usb_maxpacket(udev, pipe)) / 1000; - break; - } - hep->hcpriv = ep; - } - ep->num_req = isp1362_hcd->req_serial++; - - /* maybe put endpoint into schedule */ - switch (type) { - case PIPE_CONTROL: - case PIPE_BULK: - if (list_empty(&ep->schedule)) { - DBG(1, "%s: Adding ep %p req %d to async schedule\n", - __func__, ep, ep->num_req); - list_add_tail(&ep->schedule, &isp1362_hcd->async); - } - break; - case PIPE_ISOCHRONOUS: - case PIPE_INTERRUPT: - urb->interval = ep->interval; - - /* urb submitted for already existing EP */ - if (ep->branch < PERIODIC_SIZE) - break; - - retval = balance(isp1362_hcd, ep->interval, ep->load); - if (retval < 0) { - pr_err("%s: balance returned %d\n", __func__, retval); - goto fail; - } - ep->branch = retval; - retval = 0; - isp1362_hcd->fmindex = isp1362_read_reg32(isp1362_hcd, HCFMNUM); - DBG(1, "%s: Current frame %04x branch %02x start_frame %04x(%04x)\n", - __func__, isp1362_hcd->fmindex, ep->branch, - ((isp1362_hcd->fmindex + PERIODIC_SIZE - 1) & - ~(PERIODIC_SIZE - 1)) + ep->branch, - (isp1362_hcd->fmindex & (PERIODIC_SIZE - 1)) + ep->branch); - - if (list_empty(&ep->schedule)) { - if (type == PIPE_ISOCHRONOUS) { - u16 frame = isp1362_hcd->fmindex; - - frame += max_t(u16, 8, ep->interval); - frame &= ~(ep->interval - 1); - frame |= ep->branch; - if (frame_before(frame, isp1362_hcd->fmindex)) - frame += ep->interval; - urb->start_frame = frame; - - DBG(1, "%s: Adding ep %p to isoc schedule\n", __func__, ep); - list_add_tail(&ep->schedule, &isp1362_hcd->isoc); - } else { - DBG(1, "%s: Adding ep %p to periodic schedule\n", __func__, ep); - list_add_tail(&ep->schedule, &isp1362_hcd->periodic); - } - } else - DBG(1, "%s: ep %p already scheduled\n", __func__, ep); - - DBG(2, "%s: load %d bandwidth %d -> %d\n", __func__, - ep->load / ep->interval, isp1362_hcd->load[ep->branch], - isp1362_hcd->load[ep->branch] + ep->load); - isp1362_hcd->load[ep->branch] += ep->load; - } - - urb->hcpriv = hep; - ALIGNSTAT(isp1362_hcd, urb->transfer_buffer); - - switch (type) { - case PIPE_CONTROL: - case PIPE_BULK: - start_atl_transfers(isp1362_hcd); - break; - case PIPE_INTERRUPT: - start_intl_transfers(isp1362_hcd); - break; - case PIPE_ISOCHRONOUS: - start_iso_transfers(isp1362_hcd); - break; - default: - BUG(); - } - fail: - if (retval) - usb_hcd_unlink_urb_from_ep(hcd, urb); - - - fail_not_linked: - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (retval) - DBG(0, "%s: urb %p failed with %d\n", __func__, urb, retval); - return retval; -} - -static int isp1362_urb_dequeue(struct usb_hcd *hcd, struct urb *urb, int status) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - struct usb_host_endpoint *hep; - unsigned long flags; - struct isp1362_ep *ep; - int retval = 0; - - DBG(3, "%s: urb %p\n", __func__, urb); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - retval = usb_hcd_check_unlink_urb(hcd, urb, status); - if (retval) - goto done; - - hep = urb->hcpriv; - - if (!hep) { - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - return -EIDRM; - } - - ep = hep->hcpriv; - if (ep) { - /* In front of queue? */ - if (ep->hep->urb_list.next == &urb->urb_list) { - if (!list_empty(&ep->active)) { - DBG(1, "%s: urb %p ep %p req %d active PTD[%d] $%04x\n", __func__, - urb, ep, ep->num_req, ep->ptd_index, ep->ptd_offset); - /* disable processing and queue PTD for removal */ - remove_ptd(isp1362_hcd, ep); - urb = NULL; - } - } - if (urb) { - DBG(1, "%s: Finishing ep %p req %d\n", __func__, ep, - ep->num_req); - finish_request(isp1362_hcd, ep, urb, status); - } else - DBG(1, "%s: urb %p active; wait4irq\n", __func__, urb); - } else { - pr_warn("%s: No EP in URB %p\n", __func__, urb); - retval = -EINVAL; - } -done: - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - DBG(3, "%s: exit\n", __func__); - - return retval; -} - -static void isp1362_endpoint_disable(struct usb_hcd *hcd, struct usb_host_endpoint *hep) -{ - struct isp1362_ep *ep = hep->hcpriv; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - - DBG(1, "%s: ep %p\n", __func__, ep); - if (!ep) - return; - spin_lock_irqsave(&isp1362_hcd->lock, flags); - if (!list_empty(&hep->urb_list)) { - if (!list_empty(&ep->active) && list_empty(&ep->remove_list)) { - DBG(1, "%s: Removing ep %p req %d PTD[%d] $%04x\n", __func__, - ep, ep->num_req, ep->ptd_index, ep->ptd_offset); - remove_ptd(isp1362_hcd, ep); - pr_info("%s: Waiting for Interrupt to clean up\n", __func__); - } - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - /* Wait for interrupt to clear out active list */ - while (!list_empty(&ep->active)) - msleep(1); - - DBG(1, "%s: Freeing EP %p\n", __func__, ep); - - usb_put_dev(ep->udev); - kfree(ep); - hep->hcpriv = NULL; -} - -static int isp1362_get_frame(struct usb_hcd *hcd) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - u32 fmnum; - unsigned long flags; - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - fmnum = isp1362_read_reg32(isp1362_hcd, HCFMNUM); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - return (int)fmnum; -} - -/*-------------------------------------------------------------------------*/ - -/* Adapted from ohci-hub.c */ -static int isp1362_hub_status_data(struct usb_hcd *hcd, char *buf) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - int ports, i, changed = 0; - unsigned long flags; - - if (!HC_IS_RUNNING(hcd->state)) - return -ESHUTDOWN; - - /* Report no status change now, if we are scheduled to be - called later */ - if (timer_pending(&hcd->rh_timer)) - return 0; - - ports = isp1362_hcd->rhdesca & RH_A_NDP; - BUG_ON(ports > 2); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - /* init status */ - if (isp1362_hcd->rhstatus & (RH_HS_LPSC | RH_HS_OCIC)) - buf[0] = changed = 1; - else - buf[0] = 0; - - for (i = 0; i < ports; i++) { - u32 status = isp1362_hcd->rhport[i]; - - if (status & (RH_PS_CSC | RH_PS_PESC | RH_PS_PSSC | - RH_PS_OCIC | RH_PS_PRSC)) { - changed = 1; - buf[0] |= 1 << (i + 1); - continue; - } - - if (!(status & RH_PS_CCS)) - continue; - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - return changed; -} - -static void isp1362_hub_descriptor(struct isp1362_hcd *isp1362_hcd, - struct usb_hub_descriptor *desc) -{ - u32 reg = isp1362_hcd->rhdesca; - - DBG(3, "%s: enter\n", __func__); - - desc->bDescriptorType = USB_DT_HUB; - desc->bDescLength = 9; - desc->bHubContrCurrent = 0; - desc->bNbrPorts = reg & 0x3; - /* Power switching, device type, overcurrent. */ - desc->wHubCharacteristics = cpu_to_le16((reg >> 8) & - (HUB_CHAR_LPSM | - HUB_CHAR_COMPOUND | - HUB_CHAR_OCPM)); - DBG(0, "%s: hubcharacteristics = %02x\n", __func__, - desc->wHubCharacteristics); - desc->bPwrOn2PwrGood = (reg >> 24) & 0xff; - /* ports removable, and legacy PortPwrCtrlMask */ - desc->u.hs.DeviceRemovable[0] = desc->bNbrPorts == 1 ? 1 << 1 : 3 << 1; - desc->u.hs.DeviceRemovable[1] = ~0; - - DBG(3, "%s: exit\n", __func__); -} - -/* Adapted from ohci-hub.c */ -static int isp1362_hub_control(struct usb_hcd *hcd, u16 typeReq, u16 wValue, - u16 wIndex, char *buf, u16 wLength) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - int retval = 0; - unsigned long flags; - unsigned long t1; - int ports = isp1362_hcd->rhdesca & RH_A_NDP; - u32 tmp = 0; - - switch (typeReq) { - case ClearHubFeature: - DBG(0, "ClearHubFeature: "); - switch (wValue) { - case C_HUB_OVER_CURRENT: - DBG(0, "C_HUB_OVER_CURRENT\n"); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_OCIC); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - case C_HUB_LOCAL_POWER: - DBG(0, "C_HUB_LOCAL_POWER\n"); - break; - default: - goto error; - } - break; - case SetHubFeature: - DBG(0, "SetHubFeature: "); - switch (wValue) { - case C_HUB_OVER_CURRENT: - case C_HUB_LOCAL_POWER: - DBG(0, "C_HUB_OVER_CURRENT or C_HUB_LOCAL_POWER\n"); - break; - default: - goto error; - } - break; - case GetHubDescriptor: - DBG(0, "GetHubDescriptor\n"); - isp1362_hub_descriptor(isp1362_hcd, (struct usb_hub_descriptor *)buf); - break; - case GetHubStatus: - DBG(0, "GetHubStatus\n"); - put_unaligned(cpu_to_le32(0), (__le32 *) buf); - break; - case GetPortStatus: -#ifndef VERBOSE - DBG(0, "GetPortStatus\n"); -#endif - if (!wIndex || wIndex > ports) - goto error; - tmp = isp1362_hcd->rhport[--wIndex]; - put_unaligned(cpu_to_le32(tmp), (__le32 *) buf); - break; - case ClearPortFeature: - DBG(0, "ClearPortFeature: "); - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - - switch (wValue) { - case USB_PORT_FEAT_ENABLE: - DBG(0, "USB_PORT_FEAT_ENABLE\n"); - tmp = RH_PS_CCS; - break; - case USB_PORT_FEAT_C_ENABLE: - DBG(0, "USB_PORT_FEAT_C_ENABLE\n"); - tmp = RH_PS_PESC; - break; - case USB_PORT_FEAT_SUSPEND: - DBG(0, "USB_PORT_FEAT_SUSPEND\n"); - tmp = RH_PS_POCI; - break; - case USB_PORT_FEAT_C_SUSPEND: - DBG(0, "USB_PORT_FEAT_C_SUSPEND\n"); - tmp = RH_PS_PSSC; - break; - case USB_PORT_FEAT_POWER: - DBG(0, "USB_PORT_FEAT_POWER\n"); - tmp = RH_PS_LSDA; - - break; - case USB_PORT_FEAT_C_CONNECTION: - DBG(0, "USB_PORT_FEAT_C_CONNECTION\n"); - tmp = RH_PS_CSC; - break; - case USB_PORT_FEAT_C_OVER_CURRENT: - DBG(0, "USB_PORT_FEAT_C_OVER_CURRENT\n"); - tmp = RH_PS_OCIC; - break; - case USB_PORT_FEAT_C_RESET: - DBG(0, "USB_PORT_FEAT_C_RESET\n"); - tmp = RH_PS_PRSC; - break; - default: - goto error; - } - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, tmp); - isp1362_hcd->rhport[wIndex] = - isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - case SetPortFeature: - DBG(0, "SetPortFeature: "); - if (!wIndex || wIndex > ports) - goto error; - wIndex--; - switch (wValue) { - case USB_PORT_FEAT_SUSPEND: - DBG(0, "USB_PORT_FEAT_SUSPEND\n"); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PSS); - isp1362_hcd->rhport[wIndex] = - isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - case USB_PORT_FEAT_POWER: - DBG(0, "USB_PORT_FEAT_POWER\n"); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, RH_PS_PPS); - isp1362_hcd->rhport[wIndex] = - isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - case USB_PORT_FEAT_RESET: - DBG(0, "USB_PORT_FEAT_RESET\n"); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - t1 = jiffies + msecs_to_jiffies(USB_RESET_WIDTH); - while (time_before(jiffies, t1)) { - /* spin until any current reset finishes */ - for (;;) { - tmp = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + wIndex); - if (!(tmp & RH_PS_PRS)) - break; - udelay(500); - } - if (!(tmp & RH_PS_CCS)) - break; - /* Reset lasts 10ms (claims datasheet) */ - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + wIndex, (RH_PS_PRS)); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - msleep(10); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - } - - isp1362_hcd->rhport[wIndex] = isp1362_read_reg32(isp1362_hcd, - HCRHPORT1 + wIndex); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - break; - default: - goto error; - } - break; - - default: - error: - /* "protocol stall" on error */ - DBG(0, "PROTOCOL STALL\n"); - retval = -EPIPE; - } - - return retval; -} - -#ifdef CONFIG_PM -static int isp1362_bus_suspend(struct usb_hcd *hcd) -{ - int status = 0; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - - if (time_before(jiffies, isp1362_hcd->next_statechange)) - msleep(5); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); - switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) { - case OHCI_USB_RESUME: - DBG(0, "%s: resume/suspend?\n", __func__); - isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS; - isp1362_hcd->hc_control |= OHCI_USB_RESET; - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - fallthrough; - case OHCI_USB_RESET: - status = -EBUSY; - pr_warn("%s: needs reinit!\n", __func__); - goto done; - case OHCI_USB_SUSPEND: - pr_warn("%s: already suspended?\n", __func__); - goto done; - } - DBG(0, "%s: suspend root hub\n", __func__); - - /* First stop any processing */ - hcd->state = HC_STATE_QUIESCING; - if (!list_empty(&isp1362_hcd->atl_queue.active) || - !list_empty(&isp1362_hcd->intl_queue.active) || - !list_empty(&isp1362_hcd->istl_queue[0] .active) || - !list_empty(&isp1362_hcd->istl_queue[1] .active)) { - int limit; - - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0); - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0); - isp1362_write_reg16(isp1362_hcd, HCBUFSTAT, 0); - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); - isp1362_write_reg32(isp1362_hcd, HCINTSTAT, OHCI_INTR_SF); - - DBG(0, "%s: stopping schedules ...\n", __func__); - limit = 2000; - while (limit > 0) { - udelay(250); - limit -= 250; - if (isp1362_read_reg32(isp1362_hcd, HCINTSTAT) & OHCI_INTR_SF) - break; - } - mdelay(7); - if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ATL) { - u32 done_map = isp1362_read_reg32(isp1362_hcd, HCATLDONE); - finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->atl_queue); - } - if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_INTL) { - u32 done_map = isp1362_read_reg32(isp1362_hcd, HCINTLDONE); - finish_transfers(isp1362_hcd, done_map, &isp1362_hcd->intl_queue); - } - if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL0) - finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[0]); - if (isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_ISTL1) - finish_iso_transfers(isp1362_hcd, &isp1362_hcd->istl_queue[1]); - } - DBG(0, "%s: HCINTSTAT: %08x\n", __func__, - isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); - isp1362_write_reg32(isp1362_hcd, HCINTSTAT, - isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); - - /* Suspend hub */ - isp1362_hcd->hc_control = OHCI_USB_SUSPEND; - isp1362_show_reg(isp1362_hcd, HCCONTROL); - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - isp1362_show_reg(isp1362_hcd, HCCONTROL); - -#if 1 - isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); - if ((isp1362_hcd->hc_control & OHCI_CTRL_HCFS) != OHCI_USB_SUSPEND) { - pr_err("%s: controller won't suspend %08x\n", __func__, - isp1362_hcd->hc_control); - status = -EBUSY; - } else -#endif - { - /* no resumes until devices finish suspending */ - isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(5); - } -done: - if (status == 0) { - hcd->state = HC_STATE_SUSPENDED; - DBG(0, "%s: HCD suspended: %08x\n", __func__, - isp1362_read_reg32(isp1362_hcd, HCCONTROL)); - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - return status; -} - -static int isp1362_bus_resume(struct usb_hcd *hcd) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - u32 port; - unsigned long flags; - int status = -EINPROGRESS; - - if (time_before(jiffies, isp1362_hcd->next_statechange)) - msleep(5); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_hcd->hc_control = isp1362_read_reg32(isp1362_hcd, HCCONTROL); - pr_info("%s: HCCONTROL: %08x\n", __func__, isp1362_hcd->hc_control); - if (hcd->state == HC_STATE_RESUMING) { - pr_warn("%s: duplicate resume\n", __func__); - status = 0; - } else - switch (isp1362_hcd->hc_control & OHCI_CTRL_HCFS) { - case OHCI_USB_SUSPEND: - DBG(0, "%s: resume root hub\n", __func__); - isp1362_hcd->hc_control &= ~OHCI_CTRL_HCFS; - isp1362_hcd->hc_control |= OHCI_USB_RESUME; - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - break; - case OHCI_USB_RESUME: - /* HCFS changes sometime after INTR_RD */ - DBG(0, "%s: remote wakeup\n", __func__); - break; - case OHCI_USB_OPER: - DBG(0, "%s: odd resume\n", __func__); - status = 0; - hcd->self.root_hub->dev.power.power_state = PMSG_ON; - break; - default: /* RESET, we lost power */ - DBG(0, "%s: root hub hardware reset\n", __func__); - status = -EBUSY; - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (status == -EBUSY) { - DBG(0, "%s: Restarting HC\n", __func__); - isp1362_hc_stop(hcd); - return isp1362_hc_start(hcd); - } - if (status != -EINPROGRESS) - return status; - spin_lock_irqsave(&isp1362_hcd->lock, flags); - port = isp1362_read_reg32(isp1362_hcd, HCRHDESCA) & RH_A_NDP; - while (port--) { - u32 stat = isp1362_read_reg32(isp1362_hcd, HCRHPORT1 + port); - - /* force global, not selective, resume */ - if (!(stat & RH_PS_PSS)) { - DBG(0, "%s: Not Resuming RH port %d\n", __func__, port); - continue; - } - DBG(0, "%s: Resuming RH port %d\n", __func__, port); - isp1362_write_reg32(isp1362_hcd, HCRHPORT1 + port, RH_PS_POCI); - } - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - /* Some controllers (lucent) need extra-long delays */ - hcd->state = HC_STATE_RESUMING; - mdelay(20 /* usb 11.5.1.10 */ + 15); - - isp1362_hcd->hc_control = OHCI_USB_OPER; - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_show_reg(isp1362_hcd, HCCONTROL); - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - /* TRSMRCY */ - msleep(10); - - /* keep it alive for ~5x suspend + resume costs */ - isp1362_hcd->next_statechange = jiffies + msecs_to_jiffies(250); - - hcd->self.root_hub->dev.power.power_state = PMSG_ON; - hcd->state = HC_STATE_RUNNING; - return 0; -} -#else -#define isp1362_bus_suspend NULL -#define isp1362_bus_resume NULL -#endif - -/*-------------------------------------------------------------------------*/ - -static void dump_irq(struct seq_file *s, char *label, u16 mask) -{ - seq_printf(s, "%-15s %04x%s%s%s%s%s%s\n", label, mask, - mask & HCuPINT_CLKRDY ? " clkrdy" : "", - mask & HCuPINT_SUSP ? " susp" : "", - mask & HCuPINT_OPR ? " opr" : "", - mask & HCuPINT_EOT ? " eot" : "", - mask & HCuPINT_ATL ? " atl" : "", - mask & HCuPINT_SOF ? " sof" : ""); -} - -static void dump_int(struct seq_file *s, char *label, u32 mask) -{ - seq_printf(s, "%-15s %08x%s%s%s%s%s%s%s\n", label, mask, - mask & OHCI_INTR_MIE ? " MIE" : "", - mask & OHCI_INTR_RHSC ? " rhsc" : "", - mask & OHCI_INTR_FNO ? " fno" : "", - mask & OHCI_INTR_UE ? " ue" : "", - mask & OHCI_INTR_RD ? " rd" : "", - mask & OHCI_INTR_SF ? " sof" : "", - mask & OHCI_INTR_SO ? " so" : ""); -} - -static void dump_ctrl(struct seq_file *s, char *label, u32 mask) -{ - seq_printf(s, "%-15s %08x%s%s%s\n", label, mask, - mask & OHCI_CTRL_RWC ? " rwc" : "", - mask & OHCI_CTRL_RWE ? " rwe" : "", - ({ - char *hcfs; - switch (mask & OHCI_CTRL_HCFS) { - case OHCI_USB_OPER: - hcfs = " oper"; - break; - case OHCI_USB_RESET: - hcfs = " reset"; - break; - case OHCI_USB_RESUME: - hcfs = " resume"; - break; - case OHCI_USB_SUSPEND: - hcfs = " suspend"; - break; - default: - hcfs = " ?"; - } - hcfs; - })); -} - -static void dump_regs(struct seq_file *s, struct isp1362_hcd *isp1362_hcd) -{ - seq_printf(s, "HCREVISION [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCREVISION), - isp1362_read_reg32(isp1362_hcd, HCREVISION)); - seq_printf(s, "HCCONTROL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCONTROL), - isp1362_read_reg32(isp1362_hcd, HCCONTROL)); - seq_printf(s, "HCCMDSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCCMDSTAT), - isp1362_read_reg32(isp1362_hcd, HCCMDSTAT)); - seq_printf(s, "HCINTSTAT [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTSTAT), - isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); - seq_printf(s, "HCINTENB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTENB), - isp1362_read_reg32(isp1362_hcd, HCINTENB)); - seq_printf(s, "HCFMINTVL [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMINTVL), - isp1362_read_reg32(isp1362_hcd, HCFMINTVL)); - seq_printf(s, "HCFMREM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMREM), - isp1362_read_reg32(isp1362_hcd, HCFMREM)); - seq_printf(s, "HCFMNUM [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCFMNUM), - isp1362_read_reg32(isp1362_hcd, HCFMNUM)); - seq_printf(s, "HCLSTHRESH [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCLSTHRESH), - isp1362_read_reg32(isp1362_hcd, HCLSTHRESH)); - seq_printf(s, "HCRHDESCA [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCA), - isp1362_read_reg32(isp1362_hcd, HCRHDESCA)); - seq_printf(s, "HCRHDESCB [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHDESCB), - isp1362_read_reg32(isp1362_hcd, HCRHDESCB)); - seq_printf(s, "HCRHSTATUS [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHSTATUS), - isp1362_read_reg32(isp1362_hcd, HCRHSTATUS)); - seq_printf(s, "HCRHPORT1 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT1), - isp1362_read_reg32(isp1362_hcd, HCRHPORT1)); - seq_printf(s, "HCRHPORT2 [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCRHPORT2), - isp1362_read_reg32(isp1362_hcd, HCRHPORT2)); - seq_printf(s, "\n"); - seq_printf(s, "HCHWCFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCHWCFG), - isp1362_read_reg16(isp1362_hcd, HCHWCFG)); - seq_printf(s, "HCDMACFG [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCDMACFG), - isp1362_read_reg16(isp1362_hcd, HCDMACFG)); - seq_printf(s, "HCXFERCTR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCXFERCTR), - isp1362_read_reg16(isp1362_hcd, HCXFERCTR)); - seq_printf(s, "HCuPINT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINT), - isp1362_read_reg16(isp1362_hcd, HCuPINT)); - seq_printf(s, "HCuPINTENB [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCuPINTENB), - isp1362_read_reg16(isp1362_hcd, HCuPINTENB)); - seq_printf(s, "HCCHIPID [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCCHIPID), - isp1362_read_reg16(isp1362_hcd, HCCHIPID)); - seq_printf(s, "HCSCRATCH [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCSCRATCH), - isp1362_read_reg16(isp1362_hcd, HCSCRATCH)); - seq_printf(s, "HCBUFSTAT [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCBUFSTAT), - isp1362_read_reg16(isp1362_hcd, HCBUFSTAT)); - seq_printf(s, "HCDIRADDR [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCDIRADDR), - isp1362_read_reg32(isp1362_hcd, HCDIRADDR)); -#if 0 - seq_printf(s, "HCDIRDATA [%02x] %04x\n", ISP1362_REG_NO(HCDIRDATA), - isp1362_read_reg16(isp1362_hcd, HCDIRDATA)); -#endif - seq_printf(s, "HCISTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLBUFSZ), - isp1362_read_reg16(isp1362_hcd, HCISTLBUFSZ)); - seq_printf(s, "HCISTLRATE [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCISTLRATE), - isp1362_read_reg16(isp1362_hcd, HCISTLRATE)); - seq_printf(s, "\n"); - seq_printf(s, "HCINTLBUFSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBUFSZ), - isp1362_read_reg16(isp1362_hcd, HCINTLBUFSZ)); - seq_printf(s, "HCINTLBLKSZ[%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLBLKSZ), - isp1362_read_reg16(isp1362_hcd, HCINTLBLKSZ)); - seq_printf(s, "HCINTLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLDONE), - isp1362_read_reg32(isp1362_hcd, HCINTLDONE)); - seq_printf(s, "HCINTLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLSKIP), - isp1362_read_reg32(isp1362_hcd, HCINTLSKIP)); - seq_printf(s, "HCINTLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLLAST), - isp1362_read_reg32(isp1362_hcd, HCINTLLAST)); - seq_printf(s, "HCINTLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCINTLCURR), - isp1362_read_reg16(isp1362_hcd, HCINTLCURR)); - seq_printf(s, "\n"); - seq_printf(s, "HCATLBUFSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBUFSZ), - isp1362_read_reg16(isp1362_hcd, HCATLBUFSZ)); - seq_printf(s, "HCATLBLKSZ [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLBLKSZ), - isp1362_read_reg16(isp1362_hcd, HCATLBLKSZ)); -#if 0 - seq_printf(s, "HCATLDONE [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDONE), - isp1362_read_reg32(isp1362_hcd, HCATLDONE)); -#endif - seq_printf(s, "HCATLSKIP [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLSKIP), - isp1362_read_reg32(isp1362_hcd, HCATLSKIP)); - seq_printf(s, "HCATLLAST [%02x] %08x\n", ISP1362_REG_NO(ISP1362_REG_HCATLLAST), - isp1362_read_reg32(isp1362_hcd, HCATLLAST)); - seq_printf(s, "HCATLCURR [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLCURR), - isp1362_read_reg16(isp1362_hcd, HCATLCURR)); - seq_printf(s, "\n"); - seq_printf(s, "HCATLDTC [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTC), - isp1362_read_reg16(isp1362_hcd, HCATLDTC)); - seq_printf(s, "HCATLDTCTO [%02x] %04x\n", ISP1362_REG_NO(ISP1362_REG_HCATLDTCTO), - isp1362_read_reg16(isp1362_hcd, HCATLDTCTO)); -} - -static int isp1362_show(struct seq_file *s, void *unused) -{ - struct isp1362_hcd *isp1362_hcd = s->private; - struct isp1362_ep *ep; - int i; - - seq_printf(s, "%s\n%s version %s\n", - isp1362_hcd_to_hcd(isp1362_hcd)->product_desc, hcd_name, DRIVER_VERSION); - - /* collect statistics to help estimate potential win for - * DMA engines that care about alignment (PXA) - */ - seq_printf(s, "alignment: 16b/%ld 8b/%ld 4b/%ld 2b/%ld 1b/%ld\n", - isp1362_hcd->stat16, isp1362_hcd->stat8, isp1362_hcd->stat4, - isp1362_hcd->stat2, isp1362_hcd->stat1); - seq_printf(s, "max # ptds in ATL fifo: %d\n", isp1362_hcd->atl_queue.stat_maxptds); - seq_printf(s, "max # ptds in INTL fifo: %d\n", isp1362_hcd->intl_queue.stat_maxptds); - seq_printf(s, "max # ptds in ISTL fifo: %d\n", - max(isp1362_hcd->istl_queue[0] .stat_maxptds, - isp1362_hcd->istl_queue[1] .stat_maxptds)); - - /* FIXME: don't show the following in suspended state */ - spin_lock_irq(&isp1362_hcd->lock); - - dump_irq(s, "hc_irq_enable", isp1362_read_reg16(isp1362_hcd, HCuPINTENB)); - dump_irq(s, "hc_irq_status", isp1362_read_reg16(isp1362_hcd, HCuPINT)); - dump_int(s, "ohci_int_enable", isp1362_read_reg32(isp1362_hcd, HCINTENB)); - dump_int(s, "ohci_int_status", isp1362_read_reg32(isp1362_hcd, HCINTSTAT)); - dump_ctrl(s, "ohci_control", isp1362_read_reg32(isp1362_hcd, HCCONTROL)); - - for (i = 0; i < NUM_ISP1362_IRQS; i++) - if (isp1362_hcd->irq_stat[i]) - seq_printf(s, "%-15s: %d\n", - ISP1362_INT_NAME(i), isp1362_hcd->irq_stat[i]); - - dump_regs(s, isp1362_hcd); - list_for_each_entry(ep, &isp1362_hcd->async, schedule) { - struct urb *urb; - - seq_printf(s, "%p, ep%d%s, maxpacket %d:\n", ep, ep->epnum, - ({ - char *s; - switch (ep->nextpid) { - case USB_PID_IN: - s = "in"; - break; - case USB_PID_OUT: - s = "out"; - break; - case USB_PID_SETUP: - s = "setup"; - break; - case USB_PID_ACK: - s = "status"; - break; - default: - s = "?"; - break; - } - s;}), ep->maxpacket) ; - list_for_each_entry(urb, &ep->hep->urb_list, urb_list) { - seq_printf(s, " urb%p, %d/%d\n", urb, - urb->actual_length, - urb->transfer_buffer_length); - } - } - if (!list_empty(&isp1362_hcd->async)) - seq_printf(s, "\n"); - dump_ptd_queue(&isp1362_hcd->atl_queue); - - seq_printf(s, "periodic size= %d\n", PERIODIC_SIZE); - - list_for_each_entry(ep, &isp1362_hcd->periodic, schedule) { - seq_printf(s, "branch:%2d load:%3d PTD[%d] $%04x:\n", ep->branch, - isp1362_hcd->load[ep->branch], ep->ptd_index, ep->ptd_offset); - - seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", - ep->interval, ep, - (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ", - ep->udev->devnum, ep->epnum, - (ep->epnum == 0) ? "" : - ((ep->nextpid == USB_PID_IN) ? - "in" : "out"), ep->maxpacket); - } - dump_ptd_queue(&isp1362_hcd->intl_queue); - - seq_printf(s, "ISO:\n"); - - list_for_each_entry(ep, &isp1362_hcd->isoc, schedule) { - seq_printf(s, " %d/%p (%sdev%d ep%d%s max %d)\n", - ep->interval, ep, - (ep->udev->speed == USB_SPEED_FULL) ? "" : "ls ", - ep->udev->devnum, ep->epnum, - (ep->epnum == 0) ? "" : - ((ep->nextpid == USB_PID_IN) ? - "in" : "out"), ep->maxpacket); - } - - spin_unlock_irq(&isp1362_hcd->lock); - seq_printf(s, "\n"); - - return 0; -} -DEFINE_SHOW_ATTRIBUTE(isp1362); - -/* expect just one isp1362_hcd per system */ -static void create_debug_file(struct isp1362_hcd *isp1362_hcd) -{ - debugfs_create_file("isp1362", S_IRUGO, usb_debug_root, isp1362_hcd, - &isp1362_fops); -} - -static void remove_debug_file(struct isp1362_hcd *isp1362_hcd) -{ - debugfs_lookup_and_remove("isp1362", usb_debug_root); -} - -/*-------------------------------------------------------------------------*/ - -static void __isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd) -{ - int tmp = 20; - - isp1362_write_reg16(isp1362_hcd, HCSWRES, HCSWRES_MAGIC); - isp1362_write_reg32(isp1362_hcd, HCCMDSTAT, OHCI_HCR); - while (--tmp) { - mdelay(1); - if (!(isp1362_read_reg32(isp1362_hcd, HCCMDSTAT) & OHCI_HCR)) - break; - } - if (!tmp) - pr_err("Software reset timeout\n"); -} - -static void isp1362_sw_reset(struct isp1362_hcd *isp1362_hcd) -{ - unsigned long flags; - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - __isp1362_sw_reset(isp1362_hcd); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); -} - -static int isp1362_mem_config(struct usb_hcd *hcd) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - u32 total; - u16 istl_size = ISP1362_ISTL_BUFSIZE; - u16 intl_blksize = ISP1362_INTL_BLKSIZE + PTD_HEADER_SIZE; - u16 intl_size = ISP1362_INTL_BUFFERS * intl_blksize; - u16 atl_blksize = ISP1362_ATL_BLKSIZE + PTD_HEADER_SIZE; - u16 atl_buffers = (ISP1362_BUF_SIZE - (istl_size + intl_size)) / atl_blksize; - u16 atl_size; - int i; - - WARN_ON(istl_size & 3); - WARN_ON(atl_blksize & 3); - WARN_ON(intl_blksize & 3); - WARN_ON(atl_blksize < PTD_HEADER_SIZE); - WARN_ON(intl_blksize < PTD_HEADER_SIZE); - - BUG_ON((unsigned)ISP1362_INTL_BUFFERS > 32); - if (atl_buffers > 32) - atl_buffers = 32; - atl_size = atl_buffers * atl_blksize; - total = atl_size + intl_size + istl_size; - dev_info(hcd->self.controller, "ISP1362 Memory usage:\n"); - dev_info(hcd->self.controller, " ISTL: 2 * %4d: %4d @ $%04x:$%04x\n", - istl_size / 2, istl_size, 0, istl_size / 2); - dev_info(hcd->self.controller, " INTL: %4d * (%3zu+8): %4d @ $%04x\n", - ISP1362_INTL_BUFFERS, intl_blksize - PTD_HEADER_SIZE, - intl_size, istl_size); - dev_info(hcd->self.controller, " ATL : %4d * (%3zu+8): %4d @ $%04x\n", - atl_buffers, atl_blksize - PTD_HEADER_SIZE, - atl_size, istl_size + intl_size); - dev_info(hcd->self.controller, " USED/FREE: %4d %4d\n", total, - ISP1362_BUF_SIZE - total); - - if (total > ISP1362_BUF_SIZE) { - dev_err(hcd->self.controller, "%s: Memory requested: %d, available %d\n", - __func__, total, ISP1362_BUF_SIZE); - return -ENOMEM; - } - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - for (i = 0; i < 2; i++) { - isp1362_hcd->istl_queue[i].buf_start = i * istl_size / 2, - isp1362_hcd->istl_queue[i].buf_size = istl_size / 2; - isp1362_hcd->istl_queue[i].blk_size = 4; - INIT_LIST_HEAD(&isp1362_hcd->istl_queue[i].active); - snprintf(isp1362_hcd->istl_queue[i].name, - sizeof(isp1362_hcd->istl_queue[i].name), "ISTL%d", i); - DBG(3, "%s: %5s buf $%04x %d\n", __func__, - isp1362_hcd->istl_queue[i].name, - isp1362_hcd->istl_queue[i].buf_start, - isp1362_hcd->istl_queue[i].buf_size); - } - isp1362_write_reg16(isp1362_hcd, HCISTLBUFSZ, istl_size / 2); - - isp1362_hcd->intl_queue.buf_start = istl_size; - isp1362_hcd->intl_queue.buf_size = intl_size; - isp1362_hcd->intl_queue.buf_count = ISP1362_INTL_BUFFERS; - isp1362_hcd->intl_queue.blk_size = intl_blksize; - isp1362_hcd->intl_queue.buf_avail = isp1362_hcd->intl_queue.buf_count; - isp1362_hcd->intl_queue.skip_map = ~0; - INIT_LIST_HEAD(&isp1362_hcd->intl_queue.active); - - isp1362_write_reg16(isp1362_hcd, HCINTLBUFSZ, - isp1362_hcd->intl_queue.buf_size); - isp1362_write_reg16(isp1362_hcd, HCINTLBLKSZ, - isp1362_hcd->intl_queue.blk_size - PTD_HEADER_SIZE); - isp1362_write_reg32(isp1362_hcd, HCINTLSKIP, ~0); - isp1362_write_reg32(isp1362_hcd, HCINTLLAST, - 1 << (ISP1362_INTL_BUFFERS - 1)); - - isp1362_hcd->atl_queue.buf_start = istl_size + intl_size; - isp1362_hcd->atl_queue.buf_size = atl_size; - isp1362_hcd->atl_queue.buf_count = atl_buffers; - isp1362_hcd->atl_queue.blk_size = atl_blksize; - isp1362_hcd->atl_queue.buf_avail = isp1362_hcd->atl_queue.buf_count; - isp1362_hcd->atl_queue.skip_map = ~0; - INIT_LIST_HEAD(&isp1362_hcd->atl_queue.active); - - isp1362_write_reg16(isp1362_hcd, HCATLBUFSZ, - isp1362_hcd->atl_queue.buf_size); - isp1362_write_reg16(isp1362_hcd, HCATLBLKSZ, - isp1362_hcd->atl_queue.blk_size - PTD_HEADER_SIZE); - isp1362_write_reg32(isp1362_hcd, HCATLSKIP, ~0); - isp1362_write_reg32(isp1362_hcd, HCATLLAST, - 1 << (atl_buffers - 1)); - - snprintf(isp1362_hcd->atl_queue.name, - sizeof(isp1362_hcd->atl_queue.name), "ATL"); - snprintf(isp1362_hcd->intl_queue.name, - sizeof(isp1362_hcd->intl_queue.name), "INTL"); - DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__, - isp1362_hcd->intl_queue.name, - isp1362_hcd->intl_queue.buf_start, - ISP1362_INTL_BUFFERS, isp1362_hcd->intl_queue.blk_size, - isp1362_hcd->intl_queue.buf_size); - DBG(3, "%s: %5s buf $%04x %2d * %4d = %4d\n", __func__, - isp1362_hcd->atl_queue.name, - isp1362_hcd->atl_queue.buf_start, - atl_buffers, isp1362_hcd->atl_queue.blk_size, - isp1362_hcd->atl_queue.buf_size); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - return 0; -} - -static int isp1362_hc_reset(struct usb_hcd *hcd) -{ - int ret = 0; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long t; - unsigned long timeout = 100; - unsigned long flags; - int clkrdy = 0; - - pr_debug("%s:\n", __func__); - - if (isp1362_hcd->board && isp1362_hcd->board->reset) { - isp1362_hcd->board->reset(hcd->self.controller, 1); - msleep(20); - if (isp1362_hcd->board->clock) - isp1362_hcd->board->clock(hcd->self.controller, 1); - isp1362_hcd->board->reset(hcd->self.controller, 0); - } else - isp1362_sw_reset(isp1362_hcd); - - /* chip has been reset. First we need to see a clock */ - t = jiffies + msecs_to_jiffies(timeout); - while (!clkrdy && time_before_eq(jiffies, t)) { - spin_lock_irqsave(&isp1362_hcd->lock, flags); - clkrdy = isp1362_read_reg16(isp1362_hcd, HCuPINT) & HCuPINT_CLKRDY; - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (!clkrdy) - msleep(4); - } - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_CLKRDY); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (!clkrdy) { - pr_err("Clock not ready after %lums\n", timeout); - ret = -ENODEV; - } - return ret; -} - -static void isp1362_hc_stop(struct usb_hcd *hcd) -{ - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - u32 tmp; - - pr_debug("%s:\n", __func__); - - timer_delete_sync(&hcd->rh_timer); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); - - /* Switch off power for all ports */ - tmp = isp1362_read_reg32(isp1362_hcd, HCRHDESCA); - tmp &= ~(RH_A_NPS | RH_A_PSM); - isp1362_write_reg32(isp1362_hcd, HCRHDESCA, tmp); - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS); - - /* Reset the chip */ - if (isp1362_hcd->board && isp1362_hcd->board->reset) - isp1362_hcd->board->reset(hcd->self.controller, 1); - else - __isp1362_sw_reset(isp1362_hcd); - - if (isp1362_hcd->board && isp1362_hcd->board->clock) - isp1362_hcd->board->clock(hcd->self.controller, 0); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); -} - -#ifdef CHIP_BUFFER_TEST -static int isp1362_chip_test(struct isp1362_hcd *isp1362_hcd) -{ - int ret = 0; - u16 *ref; - unsigned long flags; - - ref = kmalloc(2 * ISP1362_BUF_SIZE, GFP_KERNEL); - if (ref) { - int offset; - u16 *tst = &ref[ISP1362_BUF_SIZE / 2]; - - for (offset = 0; offset < ISP1362_BUF_SIZE / 2; offset++) { - ref[offset] = ~offset; - tst[offset] = offset; - } - - for (offset = 0; offset < 4; offset++) { - int j; - - for (j = 0; j < 8; j++) { - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_buffer(isp1362_hcd, (u8 *)ref + offset, 0, j); - isp1362_read_buffer(isp1362_hcd, (u8 *)tst + offset, 0, j); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - if (memcmp(ref, tst, j)) { - ret = -ENODEV; - pr_err("%s: memory check with %d byte offset %d failed\n", - __func__, j, offset); - dump_data((u8 *)ref + offset, j); - dump_data((u8 *)tst + offset, j); - } - } - } - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_buffer(isp1362_hcd, ref, 0, ISP1362_BUF_SIZE); - isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - if (memcmp(ref, tst, ISP1362_BUF_SIZE)) { - ret = -ENODEV; - pr_err("%s: memory check failed\n", __func__); - dump_data((u8 *)tst, ISP1362_BUF_SIZE / 2); - } - - for (offset = 0; offset < 256; offset++) { - int test_size = 0; - - yield(); - - memset(tst, 0, ISP1362_BUF_SIZE); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); - isp1362_read_buffer(isp1362_hcd, tst, 0, ISP1362_BUF_SIZE); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (memcmp(tst, tst + (ISP1362_BUF_SIZE / (2 * sizeof(*tst))), - ISP1362_BUF_SIZE / 2)) { - pr_err("%s: Failed to clear buffer\n", __func__); - dump_data((u8 *)tst, ISP1362_BUF_SIZE); - break; - } - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_buffer(isp1362_hcd, ref, offset * 2, PTD_HEADER_SIZE); - isp1362_write_buffer(isp1362_hcd, ref + PTD_HEADER_SIZE / sizeof(*ref), - offset * 2 + PTD_HEADER_SIZE, test_size); - isp1362_read_buffer(isp1362_hcd, tst, offset * 2, - PTD_HEADER_SIZE + test_size); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) { - dump_data(((u8 *)ref) + offset, PTD_HEADER_SIZE + test_size); - dump_data((u8 *)tst, PTD_HEADER_SIZE + test_size); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_read_buffer(isp1362_hcd, tst, offset * 2, - PTD_HEADER_SIZE + test_size); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - if (memcmp(ref, tst, PTD_HEADER_SIZE + test_size)) { - ret = -ENODEV; - pr_err("%s: memory check with offset %02x failed\n", - __func__, offset); - break; - } - pr_warn("%s: memory check with offset %02x ok after second read\n", - __func__, offset); - } - } - kfree(ref); - } - return ret; -} -#endif - -static int isp1362_hc_start(struct usb_hcd *hcd) -{ - int ret; - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - struct isp1362_platform_data *board = isp1362_hcd->board; - u16 hwcfg; - u16 chipid; - unsigned long flags; - - pr_debug("%s:\n", __func__); - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - chipid = isp1362_read_reg16(isp1362_hcd, HCCHIPID); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - if ((chipid & HCCHIPID_MASK) != HCCHIPID_MAGIC) { - pr_err("%s: Invalid chip ID %04x\n", __func__, chipid); - return -ENODEV; - } - -#ifdef CHIP_BUFFER_TEST - ret = isp1362_chip_test(isp1362_hcd); - if (ret) - return -ENODEV; -#endif - spin_lock_irqsave(&isp1362_hcd->lock, flags); - /* clear interrupt status and disable all interrupt sources */ - isp1362_write_reg16(isp1362_hcd, HCuPINT, 0xff); - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, 0); - - /* HW conf */ - hwcfg = HCHWCFG_INT_ENABLE | HCHWCFG_DBWIDTH(1); - if (board->sel15Kres) - hwcfg |= HCHWCFG_PULLDOWN_DS2 | - ((MAX_ROOT_PORTS > 1) ? HCHWCFG_PULLDOWN_DS1 : 0); - if (board->clknotstop) - hwcfg |= HCHWCFG_CLKNOTSTOP; - if (board->oc_enable) - hwcfg |= HCHWCFG_ANALOG_OC; - if (board->int_act_high) - hwcfg |= HCHWCFG_INT_POL; - if (board->int_edge_triggered) - hwcfg |= HCHWCFG_INT_TRIGGER; - if (board->dreq_act_high) - hwcfg |= HCHWCFG_DREQ_POL; - if (board->dack_act_high) - hwcfg |= HCHWCFG_DACK_POL; - isp1362_write_reg16(isp1362_hcd, HCHWCFG, hwcfg); - isp1362_show_reg(isp1362_hcd, HCHWCFG); - isp1362_write_reg16(isp1362_hcd, HCDMACFG, 0); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - ret = isp1362_mem_config(hcd); - if (ret) - return ret; - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - - /* Root hub conf */ - isp1362_hcd->rhdesca = 0; - if (board->no_power_switching) - isp1362_hcd->rhdesca |= RH_A_NPS; - if (board->power_switching_mode) - isp1362_hcd->rhdesca |= RH_A_PSM; - if (board->potpg) - isp1362_hcd->rhdesca |= (board->potpg << 24) & RH_A_POTPGT; - else - isp1362_hcd->rhdesca |= (25 << 24) & RH_A_POTPGT; - - isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca & ~RH_A_OCPM); - isp1362_write_reg32(isp1362_hcd, HCRHDESCA, isp1362_hcd->rhdesca | RH_A_OCPM); - isp1362_hcd->rhdesca = isp1362_read_reg32(isp1362_hcd, HCRHDESCA); - - isp1362_hcd->rhdescb = RH_B_PPCM; - isp1362_write_reg32(isp1362_hcd, HCRHDESCB, isp1362_hcd->rhdescb); - isp1362_hcd->rhdescb = isp1362_read_reg32(isp1362_hcd, HCRHDESCB); - - isp1362_read_reg32(isp1362_hcd, HCFMINTVL); - isp1362_write_reg32(isp1362_hcd, HCFMINTVL, (FSMP(FI) << 16) | FI); - isp1362_write_reg32(isp1362_hcd, HCLSTHRESH, LSTHRESH); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - isp1362_hcd->hc_control = OHCI_USB_OPER; - hcd->state = HC_STATE_RUNNING; - - spin_lock_irqsave(&isp1362_hcd->lock, flags); - /* Set up interrupts */ - isp1362_hcd->intenb = OHCI_INTR_MIE | OHCI_INTR_RHSC | OHCI_INTR_UE; - isp1362_hcd->intenb |= OHCI_INTR_RD; - isp1362_hcd->irqenb = HCuPINT_OPR | HCuPINT_SUSP; - isp1362_write_reg32(isp1362_hcd, HCINTENB, isp1362_hcd->intenb); - isp1362_write_reg16(isp1362_hcd, HCuPINTENB, isp1362_hcd->irqenb); - - /* Go operational */ - isp1362_write_reg32(isp1362_hcd, HCCONTROL, isp1362_hcd->hc_control); - /* enable global power */ - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC | RH_HS_DRWE); - - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - - return 0; -} - -/*-------------------------------------------------------------------------*/ - -static const struct hc_driver isp1362_hc_driver = { - .description = hcd_name, - .product_desc = "ISP1362 Host Controller", - .hcd_priv_size = sizeof(struct isp1362_hcd), - - .irq = isp1362_irq, - .flags = HCD_USB11 | HCD_MEMORY, - - .reset = isp1362_hc_reset, - .start = isp1362_hc_start, - .stop = isp1362_hc_stop, - - .urb_enqueue = isp1362_urb_enqueue, - .urb_dequeue = isp1362_urb_dequeue, - .endpoint_disable = isp1362_endpoint_disable, - - .get_frame_number = isp1362_get_frame, - - .hub_status_data = isp1362_hub_status_data, - .hub_control = isp1362_hub_control, - .bus_suspend = isp1362_bus_suspend, - .bus_resume = isp1362_bus_resume, -}; - -/*-------------------------------------------------------------------------*/ - -static void isp1362_remove(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - - remove_debug_file(isp1362_hcd); - DBG(0, "%s: Removing HCD\n", __func__); - usb_remove_hcd(hcd); - DBG(0, "%s: put_hcd\n", __func__); - usb_put_hcd(hcd); - DBG(0, "%s: Done\n", __func__); -} - -static int isp1362_probe(struct platform_device *pdev) -{ - struct usb_hcd *hcd; - struct isp1362_hcd *isp1362_hcd; - struct resource *data, *irq_res; - void __iomem *addr_reg; - void __iomem *data_reg; - int irq; - int retval = 0; - unsigned int irq_flags = 0; - - if (usb_disabled()) - return -ENODEV; - - /* basic sanity checks first. board-specific init logic should - * have initialized this the three resources and probably board - * specific platform_data. we don't probe for IRQs, and do only - * minimal sanity checking. - */ - if (pdev->num_resources < 3) - return -ENODEV; - - irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0); - if (!irq_res) - return -ENODEV; - - irq = irq_res->start; - - addr_reg = devm_platform_ioremap_resource(pdev, 1); - if (IS_ERR(addr_reg)) - return PTR_ERR(addr_reg); - - data_reg = devm_platform_get_and_ioremap_resource(pdev, 0, &data); - if (IS_ERR(data_reg)) - return PTR_ERR(data_reg); - - /* allocate and initialize hcd */ - hcd = usb_create_hcd(&isp1362_hc_driver, &pdev->dev, dev_name(&pdev->dev)); - if (!hcd) - return -ENOMEM; - - hcd->rsrc_start = data->start; - isp1362_hcd = hcd_to_isp1362_hcd(hcd); - isp1362_hcd->data_reg = data_reg; - isp1362_hcd->addr_reg = addr_reg; - - isp1362_hcd->next_statechange = jiffies; - spin_lock_init(&isp1362_hcd->lock); - INIT_LIST_HEAD(&isp1362_hcd->async); - INIT_LIST_HEAD(&isp1362_hcd->periodic); - INIT_LIST_HEAD(&isp1362_hcd->isoc); - INIT_LIST_HEAD(&isp1362_hcd->remove_list); - isp1362_hcd->board = dev_get_platdata(&pdev->dev); -#if USE_PLATFORM_DELAY - if (!isp1362_hcd->board->delay) { - dev_err(hcd->self.controller, "No platform delay function given\n"); - retval = -ENODEV; - goto err; - } -#endif - - if (irq_res->flags & IORESOURCE_IRQ_HIGHEDGE) - irq_flags |= IRQF_TRIGGER_RISING; - if (irq_res->flags & IORESOURCE_IRQ_LOWEDGE) - irq_flags |= IRQF_TRIGGER_FALLING; - if (irq_res->flags & IORESOURCE_IRQ_HIGHLEVEL) - irq_flags |= IRQF_TRIGGER_HIGH; - if (irq_res->flags & IORESOURCE_IRQ_LOWLEVEL) - irq_flags |= IRQF_TRIGGER_LOW; - - retval = usb_add_hcd(hcd, irq, irq_flags | IRQF_SHARED); - if (retval != 0) - goto err; - device_wakeup_enable(hcd->self.controller); - - dev_info(&pdev->dev, "%s, irq %d\n", hcd->product_desc, irq); - - create_debug_file(isp1362_hcd); - - return 0; - - err: - usb_put_hcd(hcd); - - return retval; -} - -#ifdef CONFIG_PM -static int isp1362_suspend(struct platform_device *pdev, pm_message_t state) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - int retval = 0; - - DBG(0, "%s: Suspending device\n", __func__); - - if (state.event == PM_EVENT_FREEZE) { - DBG(0, "%s: Suspending root hub\n", __func__); - retval = isp1362_bus_suspend(hcd); - } else { - DBG(0, "%s: Suspending RH ports\n", __func__); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPS); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - } - if (retval == 0) - pdev->dev.power.power_state = state; - return retval; -} - -static int isp1362_resume(struct platform_device *pdev) -{ - struct usb_hcd *hcd = platform_get_drvdata(pdev); - struct isp1362_hcd *isp1362_hcd = hcd_to_isp1362_hcd(hcd); - unsigned long flags; - - DBG(0, "%s: Resuming\n", __func__); - - if (pdev->dev.power.power_state.event == PM_EVENT_SUSPEND) { - DBG(0, "%s: Resume RH ports\n", __func__); - spin_lock_irqsave(&isp1362_hcd->lock, flags); - isp1362_write_reg32(isp1362_hcd, HCRHSTATUS, RH_HS_LPSC); - spin_unlock_irqrestore(&isp1362_hcd->lock, flags); - return 0; - } - - pdev->dev.power.power_state = PMSG_ON; - - return isp1362_bus_resume(isp1362_hcd_to_hcd(isp1362_hcd)); -} -#else -#define isp1362_suspend NULL -#define isp1362_resume NULL -#endif - -static struct platform_driver isp1362_driver = { - .probe = isp1362_probe, - .remove = isp1362_remove, - - .suspend = isp1362_suspend, - .resume = isp1362_resume, - .driver = { - .name = hcd_name, - }, -}; - -module_platform_driver(isp1362_driver); diff --git a/drivers/usb/host/isp1362.h b/drivers/usb/host/isp1362.h deleted file mode 100644 index 74ca4be24723..000000000000 --- a/drivers/usb/host/isp1362.h +++ /dev/null @@ -1,914 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * ISP1362 HCD (Host Controller Driver) for USB. - * - * COPYRIGHT (C) by L. Wassmann - */ - -/* ------------------------------------------------------------------------- */ - -#define MAX_ROOT_PORTS 2 - -#define USE_32BIT 0 - -/* These options are mutually exclusive */ -#define USE_PLATFORM_DELAY 0 -#define USE_NDELAY 0 - -#define DUMMY_DELAY_ACCESS do {} while (0) - -/* ------------------------------------------------------------------------- */ - -#define USB_RESET_WIDTH 50 -#define MAX_XFER_SIZE 1023 - -/* Buffer sizes */ -#define ISP1362_BUF_SIZE 4096 -#define ISP1362_ISTL_BUFSIZE 512 -#define ISP1362_INTL_BLKSIZE 64 -#define ISP1362_INTL_BUFFERS 16 -#define ISP1362_ATL_BLKSIZE 64 - -#define ISP1362_REG_WRITE_OFFSET 0x80 - -#define REG_WIDTH_16 0x000 -#define REG_WIDTH_32 0x100 -#define REG_WIDTH_MASK 0x100 -#define REG_NO_MASK 0x0ff - -#ifdef ISP1362_DEBUG -typedef const unsigned int isp1362_reg_t; - -#define REG_ACCESS_R 0x200 -#define REG_ACCESS_W 0x400 -#define REG_ACCESS_RW 0x600 -#define REG_ACCESS_MASK 0x600 - -#define ISP1362_REG_NO(r) ((r) & REG_NO_MASK) - -#define ISP1362_REG(name, addr, width, rw) \ -static isp1362_reg_t ISP1362_REG_##name = ((addr) | (width) | (rw)) - -#define REG_ACCESS_TEST(r) BUG_ON(((r) & ISP1362_REG_WRITE_OFFSET) && !((r) & REG_ACCESS_W)) -#define REG_WIDTH_TEST(r, w) BUG_ON(((r) & REG_WIDTH_MASK) != (w)) -#else -typedef const unsigned char isp1362_reg_t; -#define ISP1362_REG_NO(r) (r) - -#define ISP1362_REG(name, addr, width, rw) \ -static isp1362_reg_t __maybe_unused ISP1362_REG_##name = addr - -#define REG_ACCESS_TEST(r) do {} while (0) -#define REG_WIDTH_TEST(r, w) do {} while (0) -#endif - -/* OHCI compatible registers */ -/* - * Note: Some of the ISP1362 'OHCI' registers implement only - * a subset of the bits defined in the OHCI spec. - * - * Bitmasks for the individual bits of these registers are defined in "ohci.h" - */ -ISP1362_REG(HCREVISION, 0x00, REG_WIDTH_32, REG_ACCESS_R); -ISP1362_REG(HCCONTROL, 0x01, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCCMDSTAT, 0x02, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTSTAT, 0x03, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTENB, 0x04, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTDIS, 0x05, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCFMINTVL, 0x0d, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCFMREM, 0x0e, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCFMNUM, 0x0f, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCLSTHRESH, 0x11, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHDESCA, 0x12, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHDESCB, 0x13, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHSTATUS, 0x14, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHPORT1, 0x15, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCRHPORT2, 0x16, REG_WIDTH_32, REG_ACCESS_RW); - -/* Philips ISP1362 specific registers */ -ISP1362_REG(HCHWCFG, 0x20, REG_WIDTH_16, REG_ACCESS_RW); -#define HCHWCFG_DISABLE_SUSPEND (1 << 15) -#define HCHWCFG_GLOBAL_PWRDOWN (1 << 14) -#define HCHWCFG_PULLDOWN_DS2 (1 << 13) -#define HCHWCFG_PULLDOWN_DS1 (1 << 12) -#define HCHWCFG_CLKNOTSTOP (1 << 11) -#define HCHWCFG_ANALOG_OC (1 << 10) -#define HCHWCFG_ONEINT (1 << 9) -#define HCHWCFG_DACK_MODE (1 << 8) -#define HCHWCFG_ONEDMA (1 << 7) -#define HCHWCFG_DACK_POL (1 << 6) -#define HCHWCFG_DREQ_POL (1 << 5) -#define HCHWCFG_DBWIDTH_MASK (0x03 << 3) -#define HCHWCFG_DBWIDTH(n) (((n) << 3) & HCHWCFG_DBWIDTH_MASK) -#define HCHWCFG_INT_POL (1 << 2) -#define HCHWCFG_INT_TRIGGER (1 << 1) -#define HCHWCFG_INT_ENABLE (1 << 0) - -ISP1362_REG(HCDMACFG, 0x21, REG_WIDTH_16, REG_ACCESS_RW); -#define HCDMACFG_CTR_ENABLE (1 << 7) -#define HCDMACFG_BURST_LEN_MASK (0x03 << 5) -#define HCDMACFG_BURST_LEN(n) (((n) << 5) & HCDMACFG_BURST_LEN_MASK) -#define HCDMACFG_BURST_LEN_1 HCDMACFG_BURST_LEN(0) -#define HCDMACFG_BURST_LEN_4 HCDMACFG_BURST_LEN(1) -#define HCDMACFG_BURST_LEN_8 HCDMACFG_BURST_LEN(2) -#define HCDMACFG_DMA_ENABLE (1 << 4) -#define HCDMACFG_BUF_TYPE_MASK (0x07 << 1) -#define HCDMACFG_BUF_TYPE(n) (((n) << 1) & HCDMACFG_BUF_TYPE_MASK) -#define HCDMACFG_BUF_ISTL0 HCDMACFG_BUF_TYPE(0) -#define HCDMACFG_BUF_ISTL1 HCDMACFG_BUF_TYPE(1) -#define HCDMACFG_BUF_INTL HCDMACFG_BUF_TYPE(2) -#define HCDMACFG_BUF_ATL HCDMACFG_BUF_TYPE(3) -#define HCDMACFG_BUF_DIRECT HCDMACFG_BUF_TYPE(4) -#define HCDMACFG_DMA_RW_SELECT (1 << 0) - -ISP1362_REG(HCXFERCTR, 0x22, REG_WIDTH_16, REG_ACCESS_RW); - -ISP1362_REG(HCuPINT, 0x24, REG_WIDTH_16, REG_ACCESS_RW); -#define HCuPINT_SOF (1 << 0) -#define HCuPINT_ISTL0 (1 << 1) -#define HCuPINT_ISTL1 (1 << 2) -#define HCuPINT_EOT (1 << 3) -#define HCuPINT_OPR (1 << 4) -#define HCuPINT_SUSP (1 << 5) -#define HCuPINT_CLKRDY (1 << 6) -#define HCuPINT_INTL (1 << 7) -#define HCuPINT_ATL (1 << 8) -#define HCuPINT_OTG (1 << 9) - -ISP1362_REG(HCuPINTENB, 0x25, REG_WIDTH_16, REG_ACCESS_RW); -/* same bit definitions apply as for HCuPINT */ - -ISP1362_REG(HCCHIPID, 0x27, REG_WIDTH_16, REG_ACCESS_R); -#define HCCHIPID_MASK 0xff00 -#define HCCHIPID_MAGIC 0x3600 - -ISP1362_REG(HCSCRATCH, 0x28, REG_WIDTH_16, REG_ACCESS_RW); - -ISP1362_REG(HCSWRES, 0x29, REG_WIDTH_16, REG_ACCESS_W); -#define HCSWRES_MAGIC 0x00f6 - -ISP1362_REG(HCBUFSTAT, 0x2c, REG_WIDTH_16, REG_ACCESS_RW); -#define HCBUFSTAT_ISTL0_FULL (1 << 0) -#define HCBUFSTAT_ISTL1_FULL (1 << 1) -#define HCBUFSTAT_INTL_ACTIVE (1 << 2) -#define HCBUFSTAT_ATL_ACTIVE (1 << 3) -#define HCBUFSTAT_RESET_HWPP (1 << 4) -#define HCBUFSTAT_ISTL0_ACTIVE (1 << 5) -#define HCBUFSTAT_ISTL1_ACTIVE (1 << 6) -#define HCBUFSTAT_ISTL0_DONE (1 << 8) -#define HCBUFSTAT_ISTL1_DONE (1 << 9) -#define HCBUFSTAT_PAIRED_PTDPP (1 << 10) - -ISP1362_REG(HCDIRADDR, 0x32, REG_WIDTH_32, REG_ACCESS_RW); -#define HCDIRADDR_ADDR_MASK 0x0000ffff -#define HCDIRADDR_ADDR(n) (((n) << 0) & HCDIRADDR_ADDR_MASK) -#define HCDIRADDR_COUNT_MASK 0xffff0000 -#define HCDIRADDR_COUNT(n) (((n) << 16) & HCDIRADDR_COUNT_MASK) -ISP1362_REG(HCDIRDATA, 0x45, REG_WIDTH_16, REG_ACCESS_RW); - -ISP1362_REG(HCISTLBUFSZ, 0x30, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCISTL0PORT, 0x40, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCISTL1PORT, 0x42, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCISTLRATE, 0x47, REG_WIDTH_16, REG_ACCESS_RW); - -ISP1362_REG(HCINTLBUFSZ, 0x33, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCINTLPORT, 0x43, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCINTLBLKSZ, 0x53, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCINTLDONE, 0x17, REG_WIDTH_32, REG_ACCESS_R); -ISP1362_REG(HCINTLSKIP, 0x18, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTLLAST, 0x19, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCINTLCURR, 0x1a, REG_WIDTH_16, REG_ACCESS_R); - -ISP1362_REG(HCATLBUFSZ, 0x34, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCATLPORT, 0x44, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCATLBLKSZ, 0x54, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCATLDONE, 0x1b, REG_WIDTH_32, REG_ACCESS_R); -ISP1362_REG(HCATLSKIP, 0x1c, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCATLLAST, 0x1d, REG_WIDTH_32, REG_ACCESS_RW); -ISP1362_REG(HCATLCURR, 0x1e, REG_WIDTH_16, REG_ACCESS_R); - -ISP1362_REG(HCATLDTC, 0x51, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(HCATLDTCTO, 0x52, REG_WIDTH_16, REG_ACCESS_RW); - - -ISP1362_REG(OTGCONTROL, 0x62, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(OTGSTATUS, 0x67, REG_WIDTH_16, REG_ACCESS_R); -ISP1362_REG(OTGINT, 0x68, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(OTGINTENB, 0x69, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(OTGTIMER, 0x6A, REG_WIDTH_16, REG_ACCESS_RW); -ISP1362_REG(OTGALTTMR, 0x6C, REG_WIDTH_16, REG_ACCESS_RW); - -/* Philips transfer descriptor, cpu-endian */ -struct ptd { - u16 count; -#define PTD_COUNT_MSK (0x3ff << 0) -#define PTD_TOGGLE_MSK (1 << 10) -#define PTD_ACTIVE_MSK (1 << 11) -#define PTD_CC_MSK (0xf << 12) - u16 mps; -#define PTD_MPS_MSK (0x3ff << 0) -#define PTD_SPD_MSK (1 << 10) -#define PTD_LAST_MSK (1 << 11) -#define PTD_EP_MSK (0xf << 12) - u16 len; -#define PTD_LEN_MSK (0x3ff << 0) -#define PTD_DIR_MSK (3 << 10) -#define PTD_DIR_SETUP (0) -#define PTD_DIR_OUT (1) -#define PTD_DIR_IN (2) - u16 faddr; -#define PTD_FA_MSK (0x7f << 0) -/* PTD Byte 7: [StartingFrame (if ISO PTD) | StartingFrame[0..4], PollingRate[0..2] (if INT PTD)] */ -#define PTD_SF_ISO_MSK (0xff << 8) -#define PTD_SF_INT_MSK (0x1f << 8) -#define PTD_PR_MSK (0x07 << 13) -} __attribute__ ((packed, aligned(2))); -#define PTD_HEADER_SIZE sizeof(struct ptd) - -/* ------------------------------------------------------------------------- */ -/* Copied from ohci.h: */ -/* - * Hardware transfer status codes -- CC from PTD - */ -#define PTD_CC_NOERROR 0x00 -#define PTD_CC_CRC 0x01 -#define PTD_CC_BITSTUFFING 0x02 -#define PTD_CC_DATATOGGLEM 0x03 -#define PTD_CC_STALL 0x04 -#define PTD_DEVNOTRESP 0x05 -#define PTD_PIDCHECKFAIL 0x06 -#define PTD_UNEXPECTEDPID 0x07 -#define PTD_DATAOVERRUN 0x08 -#define PTD_DATAUNDERRUN 0x09 - /* 0x0A, 0x0B reserved for hardware */ -#define PTD_BUFFEROVERRUN 0x0C -#define PTD_BUFFERUNDERRUN 0x0D - /* 0x0E, 0x0F reserved for HCD */ -#define PTD_NOTACCESSED 0x0F - - -/* map OHCI TD status codes (CC) to errno values */ -static const int cc_to_error[16] = { - /* No Error */ 0, - /* CRC Error */ -EILSEQ, - /* Bit Stuff */ -EPROTO, - /* Data Togg */ -EILSEQ, - /* Stall */ -EPIPE, - /* DevNotResp */ -ETIMEDOUT, - /* PIDCheck */ -EPROTO, - /* UnExpPID */ -EPROTO, - /* DataOver */ -EOVERFLOW, - /* DataUnder */ -EREMOTEIO, - /* (for hw) */ -EIO, - /* (for hw) */ -EIO, - /* BufferOver */ -ECOMM, - /* BuffUnder */ -ENOSR, - /* (for HCD) */ -EALREADY, - /* (for HCD) */ -EALREADY -}; - - -/* - * HcControl (control) register masks - */ -#define OHCI_CTRL_HCFS (3 << 6) /* host controller functional state */ -#define OHCI_CTRL_RWC (1 << 9) /* remote wakeup connected */ -#define OHCI_CTRL_RWE (1 << 10) /* remote wakeup enable */ - -/* pre-shifted values for HCFS */ -# define OHCI_USB_RESET (0 << 6) -# define OHCI_USB_RESUME (1 << 6) -# define OHCI_USB_OPER (2 << 6) -# define OHCI_USB_SUSPEND (3 << 6) - -/* - * HcCommandStatus (cmdstatus) register masks - */ -#define OHCI_HCR (1 << 0) /* host controller reset */ -#define OHCI_SOC (3 << 16) /* scheduling overrun count */ - -/* - * masks used with interrupt registers: - * HcInterruptStatus (intrstatus) - * HcInterruptEnable (intrenable) - * HcInterruptDisable (intrdisable) - */ -#define OHCI_INTR_SO (1 << 0) /* scheduling overrun */ -#define OHCI_INTR_WDH (1 << 1) /* writeback of done_head */ -#define OHCI_INTR_SF (1 << 2) /* start frame */ -#define OHCI_INTR_RD (1 << 3) /* resume detect */ -#define OHCI_INTR_UE (1 << 4) /* unrecoverable error */ -#define OHCI_INTR_FNO (1 << 5) /* frame number overflow */ -#define OHCI_INTR_RHSC (1 << 6) /* root hub status change */ -#define OHCI_INTR_OC (1 << 30) /* ownership change */ -#define OHCI_INTR_MIE (1 << 31) /* master interrupt enable */ - -/* roothub.portstatus [i] bits */ -#define RH_PS_CCS 0x00000001 /* current connect status */ -#define RH_PS_PES 0x00000002 /* port enable status*/ -#define RH_PS_PSS 0x00000004 /* port suspend status */ -#define RH_PS_POCI 0x00000008 /* port over current indicator */ -#define RH_PS_PRS 0x00000010 /* port reset status */ -#define RH_PS_PPS 0x00000100 /* port power status */ -#define RH_PS_LSDA 0x00000200 /* low speed device attached */ -#define RH_PS_CSC 0x00010000 /* connect status change */ -#define RH_PS_PESC 0x00020000 /* port enable status change */ -#define RH_PS_PSSC 0x00040000 /* port suspend status change */ -#define RH_PS_OCIC 0x00080000 /* over current indicator change */ -#define RH_PS_PRSC 0x00100000 /* port reset status change */ - -/* roothub.status bits */ -#define RH_HS_LPS 0x00000001 /* local power status */ -#define RH_HS_OCI 0x00000002 /* over current indicator */ -#define RH_HS_DRWE 0x00008000 /* device remote wakeup enable */ -#define RH_HS_LPSC 0x00010000 /* local power status change */ -#define RH_HS_OCIC 0x00020000 /* over current indicator change */ -#define RH_HS_CRWE 0x80000000 /* clear remote wakeup enable */ - -/* roothub.b masks */ -#define RH_B_DR 0x0000ffff /* device removable flags */ -#define RH_B_PPCM 0xffff0000 /* port power control mask */ - -/* roothub.a masks */ -#define RH_A_NDP (0xff << 0) /* number of downstream ports */ -#define RH_A_PSM (1 << 8) /* power switching mode */ -#define RH_A_NPS (1 << 9) /* no power switching */ -#define RH_A_DT (1 << 10) /* device type (mbz) */ -#define RH_A_OCPM (1 << 11) /* over current protection mode */ -#define RH_A_NOCP (1 << 12) /* no over current protection */ -#define RH_A_POTPGT (0xff << 24) /* power on to power good time */ - -#define FI 0x2edf /* 12000 bits per frame (-1) */ -#define FSMP(fi) (0x7fff & ((6 * ((fi) - 210)) / 7)) -#define LSTHRESH 0x628 /* lowspeed bit threshold */ - -/* ------------------------------------------------------------------------- */ - -/* PTD accessor macros. */ -#define PTD_GET_COUNT(p) (((p)->count & PTD_COUNT_MSK) >> 0) -#define PTD_COUNT(v) (((v) << 0) & PTD_COUNT_MSK) -#define PTD_GET_TOGGLE(p) (((p)->count & PTD_TOGGLE_MSK) >> 10) -#define PTD_TOGGLE(v) (((v) << 10) & PTD_TOGGLE_MSK) -#define PTD_GET_ACTIVE(p) (((p)->count & PTD_ACTIVE_MSK) >> 11) -#define PTD_ACTIVE(v) (((v) << 11) & PTD_ACTIVE_MSK) -#define PTD_GET_CC(p) (((p)->count & PTD_CC_MSK) >> 12) -#define PTD_CC(v) (((v) << 12) & PTD_CC_MSK) -#define PTD_GET_MPS(p) (((p)->mps & PTD_MPS_MSK) >> 0) -#define PTD_MPS(v) (((v) << 0) & PTD_MPS_MSK) -#define PTD_GET_SPD(p) (((p)->mps & PTD_SPD_MSK) >> 10) -#define PTD_SPD(v) (((v) << 10) & PTD_SPD_MSK) -#define PTD_GET_LAST(p) (((p)->mps & PTD_LAST_MSK) >> 11) -#define PTD_LAST(v) (((v) << 11) & PTD_LAST_MSK) -#define PTD_GET_EP(p) (((p)->mps & PTD_EP_MSK) >> 12) -#define PTD_EP(v) (((v) << 12) & PTD_EP_MSK) -#define PTD_GET_LEN(p) (((p)->len & PTD_LEN_MSK) >> 0) -#define PTD_LEN(v) (((v) << 0) & PTD_LEN_MSK) -#define PTD_GET_DIR(p) (((p)->len & PTD_DIR_MSK) >> 10) -#define PTD_DIR(v) (((v) << 10) & PTD_DIR_MSK) -#define PTD_GET_FA(p) (((p)->faddr & PTD_FA_MSK) >> 0) -#define PTD_FA(v) (((v) << 0) & PTD_FA_MSK) -#define PTD_GET_SF_INT(p) (((p)->faddr & PTD_SF_INT_MSK) >> 8) -#define PTD_SF_INT(v) (((v) << 8) & PTD_SF_INT_MSK) -#define PTD_GET_SF_ISO(p) (((p)->faddr & PTD_SF_ISO_MSK) >> 8) -#define PTD_SF_ISO(v) (((v) << 8) & PTD_SF_ISO_MSK) -#define PTD_GET_PR(p) (((p)->faddr & PTD_PR_MSK) >> 13) -#define PTD_PR(v) (((v) << 13) & PTD_PR_MSK) - -#define LOG2_PERIODIC_SIZE 5 /* arbitrary; this matches OHCI */ -#define PERIODIC_SIZE (1 << LOG2_PERIODIC_SIZE) - -struct isp1362_ep { - struct usb_host_endpoint *hep; - struct usb_device *udev; - - /* philips transfer descriptor */ - struct ptd ptd; - - u8 maxpacket; - u8 epnum; - u8 nextpid; - u16 error_count; - u16 length; /* of current packet */ - s16 ptd_offset; /* buffer offset in ISP1362 where - PTD has been stored - (for access thru HCDIRDATA) */ - int ptd_index; - int num_ptds; - void *data; /* to databuf */ - /* queue of active EPs (the ones transmitted to the chip) */ - struct list_head active; - - /* periodic schedule */ - u8 branch; - u16 interval; - u16 load; - u16 last_iso; - - /* async schedule */ - struct list_head schedule; /* list of all EPs that need processing */ - struct list_head remove_list; - int num_req; -}; - -struct isp1362_ep_queue { - struct list_head active; /* list of PTDs currently processed by HC */ - atomic_t finishing; - unsigned long buf_map; - unsigned long skip_map; - int free_ptd; - u16 buf_start; - u16 buf_size; - u16 blk_size; /* PTD buffer block size for ATL and INTL */ - u8 buf_count; - u8 buf_avail; - char name[16]; - - /* for statistical tracking */ - u8 stat_maxptds; /* Max # of ptds seen simultaneously in fifo */ - u8 ptd_count; /* number of ptds submitted to this queue */ -}; - -struct isp1362_hcd { - spinlock_t lock; - void __iomem *addr_reg; - void __iomem *data_reg; - - struct isp1362_platform_data *board; - - unsigned long stat1, stat2, stat4, stat8, stat16; - - /* HC registers */ - u32 intenb; /* "OHCI" interrupts */ - u16 irqenb; /* uP interrupts */ - - /* Root hub registers */ - u32 rhdesca; - u32 rhdescb; - u32 rhstatus; - u32 rhport[MAX_ROOT_PORTS]; - unsigned long next_statechange; - - /* HC control reg shadow copy */ - u32 hc_control; - - /* async schedule: control, bulk */ - struct list_head async; - - /* periodic schedule: int */ - u16 load[PERIODIC_SIZE]; - struct list_head periodic; - u16 fmindex; - - /* periodic schedule: isochronous */ - struct list_head isoc; - unsigned int istl_flip:1; - unsigned int irq_active:1; - - /* Schedules for the current frame */ - struct isp1362_ep_queue atl_queue; - struct isp1362_ep_queue intl_queue; - struct isp1362_ep_queue istl_queue[2]; - - /* list of PTDs retrieved from HC */ - struct list_head remove_list; - enum { - ISP1362_INT_SOF, - ISP1362_INT_ISTL0, - ISP1362_INT_ISTL1, - ISP1362_INT_EOT, - ISP1362_INT_OPR, - ISP1362_INT_SUSP, - ISP1362_INT_CLKRDY, - ISP1362_INT_INTL, - ISP1362_INT_ATL, - ISP1362_INT_OTG, - NUM_ISP1362_IRQS - } IRQ_NAMES; - unsigned int irq_stat[NUM_ISP1362_IRQS]; - int req_serial; -}; - -static inline const char *ISP1362_INT_NAME(int n) -{ - switch (n) { - case ISP1362_INT_SOF: return "SOF"; - case ISP1362_INT_ISTL0: return "ISTL0"; - case ISP1362_INT_ISTL1: return "ISTL1"; - case ISP1362_INT_EOT: return "EOT"; - case ISP1362_INT_OPR: return "OPR"; - case ISP1362_INT_SUSP: return "SUSP"; - case ISP1362_INT_CLKRDY: return "CLKRDY"; - case ISP1362_INT_INTL: return "INTL"; - case ISP1362_INT_ATL: return "ATL"; - case ISP1362_INT_OTG: return "OTG"; - default: return "unknown"; - } -} - -static inline void ALIGNSTAT(struct isp1362_hcd *isp1362_hcd, void *ptr) -{ - unsigned long p = (unsigned long)ptr; - if (!(p & 0xf)) - isp1362_hcd->stat16++; - else if (!(p & 0x7)) - isp1362_hcd->stat8++; - else if (!(p & 0x3)) - isp1362_hcd->stat4++; - else if (!(p & 0x1)) - isp1362_hcd->stat2++; - else - isp1362_hcd->stat1++; -} - -static inline struct isp1362_hcd *hcd_to_isp1362_hcd(struct usb_hcd *hcd) -{ - return (struct isp1362_hcd *) (hcd->hcd_priv); -} - -static inline struct usb_hcd *isp1362_hcd_to_hcd(struct isp1362_hcd *isp1362_hcd) -{ - return container_of((void *)isp1362_hcd, struct usb_hcd, hcd_priv); -} - -#define frame_before(f1, f2) ((s16)((u16)f1 - (u16)f2) < 0) - -/* - * ISP1362 HW Interface - */ - -#define DBG(level, fmt...) \ - do { \ - if (dbg_level > level) \ - pr_debug(fmt); \ - } while (0) - -#ifdef VERBOSE -# define VDBG(fmt...) DBG(3, fmt) -#else -# define VDBG(fmt...) do {} while (0) -#endif - -#ifdef REGISTERS -# define RDBG(fmt...) DBG(1, fmt) -#else -# define RDBG(fmt...) do {} while (0) -#endif - -#ifdef URB_TRACE -#define URB_DBG(fmt...) DBG(0, fmt) -#else -#define URB_DBG(fmt...) do {} while (0) -#endif - - -#if USE_PLATFORM_DELAY -#if USE_NDELAY -#error USE_PLATFORM_DELAY and USE_NDELAY defined simultaneously. -#endif -#define isp1362_delay(h, d) (h)->board->delay(isp1362_hcd_to_hcd(h)->self.controller, d) -#elif USE_NDELAY -#define isp1362_delay(h, d) ndelay(d) -#else -#define isp1362_delay(h, d) do {} while (0) -#endif - -#define get_urb(ep) ({ \ - BUG_ON(list_empty(&ep->hep->urb_list)); \ - container_of(ep->hep->urb_list.next, struct urb, urb_list); \ -}) - -/* basic access functions for ISP1362 chip registers */ -/* NOTE: The contents of the address pointer register cannot be read back! The driver must ensure, - * that all register accesses are performed with interrupts disabled, since the interrupt - * handler has no way of restoring the previous state. - */ -static void isp1362_write_addr(struct isp1362_hcd *isp1362_hcd, isp1362_reg_t reg) -{ - REG_ACCESS_TEST(reg); - DUMMY_DELAY_ACCESS; - writew(ISP1362_REG_NO(reg), isp1362_hcd->addr_reg); - DUMMY_DELAY_ACCESS; - isp1362_delay(isp1362_hcd, 1); -} - -static void isp1362_write_data16(struct isp1362_hcd *isp1362_hcd, u16 val) -{ - DUMMY_DELAY_ACCESS; - writew(val, isp1362_hcd->data_reg); -} - -static u16 isp1362_read_data16(struct isp1362_hcd *isp1362_hcd) -{ - u16 val; - - DUMMY_DELAY_ACCESS; - val = readw(isp1362_hcd->data_reg); - - return val; -} - -static void isp1362_write_data32(struct isp1362_hcd *isp1362_hcd, u32 val) -{ -#if USE_32BIT - DUMMY_DELAY_ACCESS; - writel(val, isp1362_hcd->data_reg); -#else - DUMMY_DELAY_ACCESS; - writew((u16)val, isp1362_hcd->data_reg); - DUMMY_DELAY_ACCESS; - writew(val >> 16, isp1362_hcd->data_reg); -#endif -} - -static u32 isp1362_read_data32(struct isp1362_hcd *isp1362_hcd) -{ - u32 val; - -#if USE_32BIT - DUMMY_DELAY_ACCESS; - val = readl(isp1362_hcd->data_reg); -#else - DUMMY_DELAY_ACCESS; - val = (u32)readw(isp1362_hcd->data_reg); - DUMMY_DELAY_ACCESS; - val |= (u32)readw(isp1362_hcd->data_reg) << 16; -#endif - return val; -} - -/* use readsw/writesw to access the fifo whenever possible */ -/* assume HCDIRDATA or XFERCTR & addr_reg have been set up */ -static void isp1362_read_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len) -{ - u8 *dp = buf; - u16 data; - - if (!len) - return; - - RDBG("%s: Reading %d byte from fifo to mem @ %p\n", __func__, len, buf); -#if USE_32BIT - if (len >= 4) { - RDBG("%s: Using readsl for %d dwords\n", __func__, len >> 2); - readsl(isp1362_hcd->data_reg, dp, len >> 2); - dp += len & ~3; - len &= 3; - } -#endif - if (len >= 2) { - RDBG("%s: Using readsw for %d words\n", __func__, len >> 1); - insw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1); - dp += len & ~1; - len &= 1; - } - - BUG_ON(len & ~1); - if (len > 0) { - data = isp1362_read_data16(isp1362_hcd); - RDBG("%s: Reading trailing byte %02x to mem @ %08x\n", __func__, - (u8)data, (u32)dp); - *dp = (u8)data; - } -} - -static void isp1362_write_fifo(struct isp1362_hcd *isp1362_hcd, void *buf, u16 len) -{ - u8 *dp = buf; - u16 data; - - if (!len) - return; - - if ((unsigned long)dp & 0x1) { - /* not aligned */ - for (; len > 1; len -= 2) { - data = *dp++; - data |= *dp++ << 8; - isp1362_write_data16(isp1362_hcd, data); - } - if (len) - isp1362_write_data16(isp1362_hcd, *dp); - return; - } - - RDBG("%s: Writing %d byte to fifo from memory @%p\n", __func__, len, buf); -#if USE_32BIT - if (len >= 4) { - RDBG("%s: Using writesl for %d dwords\n", __func__, len >> 2); - writesl(isp1362_hcd->data_reg, dp, len >> 2); - dp += len & ~3; - len &= 3; - } -#endif - if (len >= 2) { - RDBG("%s: Using writesw for %d words\n", __func__, len >> 1); - outsw((unsigned long)isp1362_hcd->data_reg, dp, len >> 1); - dp += len & ~1; - len &= 1; - } - - BUG_ON(len & ~1); - if (len > 0) { - /* finally write any trailing byte; we don't need to care - * about the high byte of the last word written - */ - data = (u16)*dp; - RDBG("%s: Sending trailing byte %02x from mem @ %08x\n", __func__, - data, (u32)dp); - isp1362_write_data16(isp1362_hcd, data); - } -} - -#define isp1362_read_reg16(d, r) ({ \ - u16 __v; \ - REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \ - isp1362_write_addr(d, ISP1362_REG_##r); \ - __v = isp1362_read_data16(d); \ - RDBG("%s: Read %04x from %s[%02x]\n", __func__, __v, #r, \ - ISP1362_REG_NO(ISP1362_REG_##r)); \ - __v; \ -}) - -#define isp1362_read_reg32(d, r) ({ \ - u32 __v; \ - REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \ - isp1362_write_addr(d, ISP1362_REG_##r); \ - __v = isp1362_read_data32(d); \ - RDBG("%s: Read %08x from %s[%02x]\n", __func__, __v, #r, \ - ISP1362_REG_NO(ISP1362_REG_##r)); \ - __v; \ -}) - -#define isp1362_write_reg16(d, r, v) { \ - REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_16); \ - isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \ - isp1362_write_data16(d, (u16)(v)); \ - RDBG("%s: Wrote %04x to %s[%02x]\n", __func__, (u16)(v), #r, \ - ISP1362_REG_NO(ISP1362_REG_##r)); \ -} - -#define isp1362_write_reg32(d, r, v) { \ - REG_WIDTH_TEST(ISP1362_REG_##r, REG_WIDTH_32); \ - isp1362_write_addr(d, (ISP1362_REG_##r) | ISP1362_REG_WRITE_OFFSET); \ - isp1362_write_data32(d, (u32)(v)); \ - RDBG("%s: Wrote %08x to %s[%02x]\n", __func__, (u32)(v), #r, \ - ISP1362_REG_NO(ISP1362_REG_##r)); \ -} - -#define isp1362_set_mask16(d, r, m) { \ - u16 __v; \ - __v = isp1362_read_reg16(d, r); \ - if ((__v | m) != __v) \ - isp1362_write_reg16(d, r, __v | m); \ -} - -#define isp1362_clr_mask16(d, r, m) { \ - u16 __v; \ - __v = isp1362_read_reg16(d, r); \ - if ((__v & ~m) != __v) \ - isp1362_write_reg16(d, r, __v & ~m); \ -} - -#define isp1362_set_mask32(d, r, m) { \ - u32 __v; \ - __v = isp1362_read_reg32(d, r); \ - if ((__v | m) != __v) \ - isp1362_write_reg32(d, r, __v | m); \ -} - -#define isp1362_clr_mask32(d, r, m) { \ - u32 __v; \ - __v = isp1362_read_reg32(d, r); \ - if ((__v & ~m) != __v) \ - isp1362_write_reg32(d, r, __v & ~m); \ -} - -#define isp1362_show_reg(d, r) { \ - if ((ISP1362_REG_##r & REG_WIDTH_MASK) == REG_WIDTH_32) \ - DBG(0, "%-12s[%02x]: %08x\n", #r, \ - ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg32(d, r)); \ - else \ - DBG(0, "%-12s[%02x]: %04x\n", #r, \ - ISP1362_REG_NO(ISP1362_REG_##r), isp1362_read_reg16(d, r)); \ -} - -static void isp1362_write_diraddr(struct isp1362_hcd *isp1362_hcd, u16 offset, u16 len) -{ - len = (len + 1) & ~1; - - isp1362_clr_mask16(isp1362_hcd, HCDMACFG, HCDMACFG_CTR_ENABLE); - isp1362_write_reg32(isp1362_hcd, HCDIRADDR, - HCDIRADDR_ADDR(offset) | HCDIRADDR_COUNT(len)); -} - -static void isp1362_read_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) -{ - isp1362_write_diraddr(isp1362_hcd, offset, len); - - DBG(3, "%s: Reading %d byte from buffer @%04x to memory @ %p\n", - __func__, len, offset, buf); - - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); - - isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA); - - isp1362_read_fifo(isp1362_hcd, buf, len); - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); -} - -static void isp1362_write_buffer(struct isp1362_hcd *isp1362_hcd, void *buf, u16 offset, int len) -{ - isp1362_write_diraddr(isp1362_hcd, offset, len); - - DBG(3, "%s: Writing %d byte to buffer @%04x from memory @ %p\n", - __func__, len, offset, buf); - - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); - - isp1362_write_addr(isp1362_hcd, ISP1362_REG_HCDIRDATA | ISP1362_REG_WRITE_OFFSET); - isp1362_write_fifo(isp1362_hcd, buf, len); - - isp1362_write_reg16(isp1362_hcd, HCuPINT, HCuPINT_EOT); -} - -static void __attribute__((unused)) dump_data(char *buf, int len) -{ - if (dbg_level > 0) { - int k; - int lf = 0; - - for (k = 0; k < len; ++k) { - if (!lf) - DBG(0, "%04x:", k); - printk(" %02x", ((u8 *) buf)[k]); - lf = 1; - if (!k) - continue; - if (k % 16 == 15) { - printk("\n"); - lf = 0; - continue; - } - if (k % 8 == 7) - printk(" "); - if (k % 4 == 3) - printk(" "); - } - if (lf) - printk("\n"); - } -} - -#if defined(PTD_TRACE) - -static void dump_ptd(struct ptd *ptd) -{ - DBG(0, "EP %p: CC=%x EP=%d DIR=%x CNT=%d LEN=%d MPS=%d TGL=%x ACT=%x FA=%d SPD=%x SF=%x PR=%x LST=%x\n", - container_of(ptd, struct isp1362_ep, ptd), - PTD_GET_CC(ptd), PTD_GET_EP(ptd), PTD_GET_DIR(ptd), - PTD_GET_COUNT(ptd), PTD_GET_LEN(ptd), PTD_GET_MPS(ptd), - PTD_GET_TOGGLE(ptd), PTD_GET_ACTIVE(ptd), PTD_GET_FA(ptd), - PTD_GET_SPD(ptd), PTD_GET_SF_INT(ptd), PTD_GET_PR(ptd), PTD_GET_LAST(ptd)); - DBG(0, " %04x %04x %04x %04x\n", ptd->count, ptd->mps, ptd->len, ptd->faddr); -} - -static void dump_ptd_out_data(struct ptd *ptd, u8 *buf) -{ - if (dbg_level > 0) { - if (PTD_GET_DIR(ptd) != PTD_DIR_IN && PTD_GET_LEN(ptd)) { - DBG(0, "--out->\n"); - dump_data(buf, PTD_GET_LEN(ptd)); - } - } -} - -static void dump_ptd_in_data(struct ptd *ptd, u8 *buf) -{ - if (dbg_level > 0) { - if (PTD_GET_DIR(ptd) == PTD_DIR_IN && PTD_GET_COUNT(ptd)) { - DBG(0, "<--in--\n"); - dump_data(buf, PTD_GET_COUNT(ptd)); - } - DBG(0, "-----\n"); - } -} - -static void dump_ptd_queue(struct isp1362_ep_queue *epq) -{ - struct isp1362_ep *ep; - int dbg = dbg_level; - - dbg_level = 1; - list_for_each_entry(ep, &epq->active, active) { - dump_ptd(&ep->ptd); - dump_data(ep->data, ep->length); - } - dbg_level = dbg; -} -#else -#define dump_ptd(ptd) do {} while (0) -#define dump_ptd_in_data(ptd, buf) do {} while (0) -#define dump_ptd_out_data(ptd, buf) do {} while (0) -#define dump_ptd_data(ptd, buf) do {} while (0) -#define dump_ptd_queue(epq) do {} while (0) -#endif diff --git a/include/linux/usb/isp1362.h b/include/linux/usb/isp1362.h deleted file mode 100644 index 5356c4ae386e..000000000000 --- a/include/linux/usb/isp1362.h +++ /dev/null @@ -1,47 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -/* - * board initialization code should put one of these into dev->platform_data - * and place the isp1362 onto platform_bus. - */ - -#ifndef __LINUX_USB_ISP1362_H__ -#define __LINUX_USB_ISP1362_H__ - -struct isp1362_platform_data { - /* Enable internal pulldown resistors on downstream ports */ - unsigned sel15Kres:1; - /* Clock cannot be stopped */ - unsigned clknotstop:1; - /* On-chip overcurrent protection */ - unsigned oc_enable:1; - /* INT output polarity */ - unsigned int_act_high:1; - /* INT edge or level triggered */ - unsigned int_edge_triggered:1; - /* DREQ output polarity */ - unsigned dreq_act_high:1; - /* DACK input polarity */ - unsigned dack_act_high:1; - /* chip can be resumed via H_WAKEUP pin */ - unsigned remote_wakeup_connected:1; - /* Switch or not to switch (keep always powered) */ - unsigned no_power_switching:1; - /* Ganged port power switching (0) or individual port power switching (1) */ - unsigned power_switching_mode:1; - /* Given port_power, msec/2 after power on till power good */ - u8 potpg; - /* Hardware reset set/clear */ - void (*reset) (struct device *dev, int set); - /* Clock start/stop */ - void (*clock) (struct device *dev, int start); - /* Inter-io delay (ns). The chip is picky about access timings; it - * expects at least: - * 110ns delay between consecutive accesses to DATA_REG, - * 300ns delay between access to ADDR_REG and DATA_REG (registers) - * 462ns delay between access to ADDR_REG and DATA_REG (buffer memory) - * WE MUST NOT be activated during these intervals (even without CS!) - */ - void (*delay) (struct device *dev, unsigned int delay); -}; - -#endif -- cgit v1.2.3 From 8a840ab0567ff2b7d382694ba24a58a893d2c7af Mon Sep 17 00:00:00 2001 From: Robin Murphy Date: Tue, 6 Jan 2026 19:27:53 +0000 Subject: dma-mapping: Remove dma_mark_clean (again) With IA-64 now gone, there are no users of the dma_mark_clean hook, so we can retire it for good. Signed-off-by: Robin Murphy Signed-off-by: Marek Szyprowski Link: https://lore.kernel.org/r/c004927f01962726ff1dcf94d1b4efff84db805a.1767727673.git.robin.murphy@arm.com --- include/linux/dma-map-ops.h | 8 -------- kernel/dma/Kconfig | 6 ------ kernel/dma/direct.c | 3 --- kernel/dma/direct.h | 3 --- 4 files changed, 20 deletions(-) (limited to 'include/linux') diff --git a/include/linux/dma-map-ops.h b/include/linux/dma-map-ops.h index 4809204c674c..64349e1b6535 100644 --- a/include/linux/dma-map-ops.h +++ b/include/linux/dma-map-ops.h @@ -377,14 +377,6 @@ static inline void arch_dma_prep_coherent(struct page *page, size_t size) } #endif /* CONFIG_ARCH_HAS_DMA_PREP_COHERENT */ -#ifdef CONFIG_ARCH_HAS_DMA_MARK_CLEAN -void arch_dma_mark_clean(phys_addr_t paddr, size_t size); -#else -static inline void arch_dma_mark_clean(phys_addr_t paddr, size_t size) -{ -} -#endif /* ARCH_HAS_DMA_MARK_CLEAN */ - void *arch_dma_set_uncached(void *addr, size_t size); void arch_dma_clear_uncached(void *addr, size_t size); diff --git a/kernel/dma/Kconfig b/kernel/dma/Kconfig index 31cfdb6b4bc3..159900736f25 100644 --- a/kernel/dma/Kconfig +++ b/kernel/dma/Kconfig @@ -47,12 +47,6 @@ config ARCH_HAS_DMA_SET_MASK config ARCH_HAS_DMA_WRITE_COMBINE bool -# -# Select if the architectures provides the arch_dma_mark_clean hook -# -config ARCH_HAS_DMA_MARK_CLEAN - bool - config DMA_DECLARE_COHERENT bool diff --git a/kernel/dma/direct.c b/kernel/dma/direct.c index 50c3fe2a1d55..c9fa983990cd 100644 --- a/kernel/dma/direct.c +++ b/kernel/dma/direct.c @@ -425,9 +425,6 @@ void dma_direct_sync_sg_for_cpu(struct device *dev, arch_sync_dma_for_cpu(paddr, sg->length, dir); swiotlb_sync_single_for_cpu(dev, paddr, sg->length, dir); - - if (dir == DMA_FROM_DEVICE) - arch_dma_mark_clean(paddr, sg->length); } if (!dev_is_dma_coherent(dev)) diff --git a/kernel/dma/direct.h b/kernel/dma/direct.h index da2fadf45bcd..f476c63b668c 100644 --- a/kernel/dma/direct.h +++ b/kernel/dma/direct.h @@ -75,9 +75,6 @@ static inline void dma_direct_sync_single_for_cpu(struct device *dev, } swiotlb_sync_single_for_cpu(dev, paddr, size, dir); - - if (dir == DMA_FROM_DEVICE) - arch_dma_mark_clean(paddr, size); } static inline dma_addr_t dma_direct_map_phys(struct device *dev, -- cgit v1.2.3 From 80c70bfb95cdbe0c644070f4ca4754a60f0a4830 Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Thu, 8 Jan 2026 11:50:12 +0100 Subject: scatterlist: introduce sg_nents_for_dma() helper Sometimes the user needs to split each entry on the mapped scatter list due to DMA length constrains. This helper returns a number of entities assuming that each of them is not bigger than supplied maximum length. Reviewed-by: Bjorn Andersson Signed-off-by: Andy Shevchenko Link: https://patch.msgid.link/20260108105619.3513561-2-andriy.shevchenko@linux.intel.com Signed-off-by: Vinod Koul --- include/linux/scatterlist.h | 2 ++ lib/scatterlist.c | 26 ++++++++++++++++++++++++++ 2 files changed, 28 insertions(+) (limited to 'include/linux') diff --git a/include/linux/scatterlist.h b/include/linux/scatterlist.h index 29f6ceb98d74..6de1a2434299 100644 --- a/include/linux/scatterlist.h +++ b/include/linux/scatterlist.h @@ -441,6 +441,8 @@ static inline void sg_init_marker(struct scatterlist *sgl, int sg_nents(struct scatterlist *sg); int sg_nents_for_len(struct scatterlist *sg, u64 len); +int sg_nents_for_dma(struct scatterlist *sgl, unsigned int sglen, size_t len); + struct scatterlist *sg_last(struct scatterlist *s, unsigned int); void sg_init_table(struct scatterlist *, unsigned int); void sg_init_one(struct scatterlist *, const void *, unsigned int); diff --git a/lib/scatterlist.c b/lib/scatterlist.c index 4af1c8b0775a..21bc9c1f7c06 100644 --- a/lib/scatterlist.c +++ b/lib/scatterlist.c @@ -64,6 +64,32 @@ int sg_nents_for_len(struct scatterlist *sg, u64 len) } EXPORT_SYMBOL(sg_nents_for_len); +/** + * sg_nents_for_dma - return the count of DMA-capable entries in scatterlist + * @sgl: The scatterlist + * @sglen: The current number of entries + * @len: The maximum length of DMA-capable block + * + * Description: + * Determines the number of entries in @sgl which would be permitted in + * DMA-capable transfer if list had been split accordingly, taking into + * account chaining as well. + * + * Returns: + * the number of sgl entries needed + * + **/ +int sg_nents_for_dma(struct scatterlist *sgl, unsigned int sglen, size_t len) +{ + struct scatterlist *sg; + int i, nents = 0; + + for_each_sg(sgl, sg, sglen, i) + nents += DIV_ROUND_UP(sg_dma_len(sg), len); + return nents; +} +EXPORT_SYMBOL(sg_nents_for_dma); + /** * sg_last - return the last scatterlist entry in a list * @sgl: First entry in the scatterlist -- cgit v1.2.3 From 5083dba0fde5446c00ee1a82a3911c8f88a2c72e Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Mon, 12 Jan 2026 14:46:09 +0100 Subject: units: Add HZ_PER_GHZ The is going to be a new user of the HZ_PER_GHZ definition besides possibly existing ones. Add that one to the header. While at it, split Hz and kHz groups of the multipliers for better maintenance and readability. Signed-off-by: Andy Shevchenko Reviewed-by: Andi Shyti Reviewed-by: Linus Walleij Reviewed-by: Wolfram Sang Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Andi Shyti Link: https://lore.kernel.org/r/20260112134900.4142954-2-andriy.shevchenko@linux.intel.com --- include/linux/units.h | 3 +++ 1 file changed, 3 insertions(+) (limited to 'include/linux') diff --git a/include/linux/units.h b/include/linux/units.h index 00e15de33eca..0c296a004e89 100644 --- a/include/linux/units.h +++ b/include/linux/units.h @@ -25,9 +25,12 @@ #define MICROHZ_PER_HZ 1000000UL #define MILLIHZ_PER_HZ 1000UL +/* Hz based multipliers */ #define HZ_PER_KHZ 1000UL #define HZ_PER_MHZ 1000000UL +#define HZ_PER_GHZ 1000000000UL +/* kHz based multipliers */ #define KHZ_PER_MHZ 1000UL #define KHZ_PER_GHZ 1000000UL -- cgit v1.2.3 From f8a6e5eac701369afb5d69aba875dc5fec93003d Mon Sep 17 00:00:00 2001 From: Vladimir Zapolskiy Date: Tue, 13 Jan 2026 17:11:40 +0200 Subject: Input: adp5589 - remove a leftover header file MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In commit 3bdbd0858df6 ("Input: adp5589: remove the driver") the last user of include/linux/input/adp5589.h was removed along with the whole driver, thus the header file can be also removed. Signed-off-by: Vladimir Zapolskiy Reviewed-by: Laurent Pinchart Reviewed-by: Nuno Sá Fixes: 3bdbd0858df6 ("Input: adp5589: remove the driver") Link: https://patch.msgid.link/20260113151140.3843753-1-vz@mleia.com Signed-off-by: Dmitry Torokhov --- include/linux/input/adp5589.h | 180 ------------------------------------------ 1 file changed, 180 deletions(-) delete mode 100644 include/linux/input/adp5589.h (limited to 'include/linux') diff --git a/include/linux/input/adp5589.h b/include/linux/input/adp5589.h deleted file mode 100644 index 0e4742c8c81e..000000000000 --- a/include/linux/input/adp5589.h +++ /dev/null @@ -1,180 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0-only */ -/* - * Analog Devices ADP5589/ADP5585 I/O Expander and QWERTY Keypad Controller - * - * Copyright 2010-2011 Analog Devices Inc. - */ - -#ifndef _ADP5589_H -#define _ADP5589_H - -/* - * ADP5589 specific GPI and Keymap defines - */ - -#define ADP5589_KEYMAPSIZE 88 - -#define ADP5589_GPI_PIN_ROW0 97 -#define ADP5589_GPI_PIN_ROW1 98 -#define ADP5589_GPI_PIN_ROW2 99 -#define ADP5589_GPI_PIN_ROW3 100 -#define ADP5589_GPI_PIN_ROW4 101 -#define ADP5589_GPI_PIN_ROW5 102 -#define ADP5589_GPI_PIN_ROW6 103 -#define ADP5589_GPI_PIN_ROW7 104 -#define ADP5589_GPI_PIN_COL0 105 -#define ADP5589_GPI_PIN_COL1 106 -#define ADP5589_GPI_PIN_COL2 107 -#define ADP5589_GPI_PIN_COL3 108 -#define ADP5589_GPI_PIN_COL4 109 -#define ADP5589_GPI_PIN_COL5 110 -#define ADP5589_GPI_PIN_COL6 111 -#define ADP5589_GPI_PIN_COL7 112 -#define ADP5589_GPI_PIN_COL8 113 -#define ADP5589_GPI_PIN_COL9 114 -#define ADP5589_GPI_PIN_COL10 115 -#define GPI_LOGIC1 116 -#define GPI_LOGIC2 117 - -#define ADP5589_GPI_PIN_ROW_BASE ADP5589_GPI_PIN_ROW0 -#define ADP5589_GPI_PIN_ROW_END ADP5589_GPI_PIN_ROW7 -#define ADP5589_GPI_PIN_COL_BASE ADP5589_GPI_PIN_COL0 -#define ADP5589_GPI_PIN_COL_END ADP5589_GPI_PIN_COL10 - -#define ADP5589_GPI_PIN_BASE ADP5589_GPI_PIN_ROW_BASE -#define ADP5589_GPI_PIN_END ADP5589_GPI_PIN_COL_END - -#define ADP5589_GPIMAPSIZE_MAX (ADP5589_GPI_PIN_END - ADP5589_GPI_PIN_BASE + 1) - -/* - * ADP5585 specific GPI and Keymap defines - */ - -#define ADP5585_KEYMAPSIZE 30 - -#define ADP5585_GPI_PIN_ROW0 37 -#define ADP5585_GPI_PIN_ROW1 38 -#define ADP5585_GPI_PIN_ROW2 39 -#define ADP5585_GPI_PIN_ROW3 40 -#define ADP5585_GPI_PIN_ROW4 41 -#define ADP5585_GPI_PIN_ROW5 42 -#define ADP5585_GPI_PIN_COL0 43 -#define ADP5585_GPI_PIN_COL1 44 -#define ADP5585_GPI_PIN_COL2 45 -#define ADP5585_GPI_PIN_COL3 46 -#define ADP5585_GPI_PIN_COL4 47 -#define GPI_LOGIC 48 - -#define ADP5585_GPI_PIN_ROW_BASE ADP5585_GPI_PIN_ROW0 -#define ADP5585_GPI_PIN_ROW_END ADP5585_GPI_PIN_ROW5 -#define ADP5585_GPI_PIN_COL_BASE ADP5585_GPI_PIN_COL0 -#define ADP5585_GPI_PIN_COL_END ADP5585_GPI_PIN_COL4 - -#define ADP5585_GPI_PIN_BASE ADP5585_GPI_PIN_ROW_BASE -#define ADP5585_GPI_PIN_END ADP5585_GPI_PIN_COL_END - -#define ADP5585_GPIMAPSIZE_MAX (ADP5585_GPI_PIN_END - ADP5585_GPI_PIN_BASE + 1) - -struct adp5589_gpi_map { - unsigned short pin; - unsigned short sw_evt; -}; - -/* scan_cycle_time */ -#define ADP5589_SCAN_CYCLE_10ms 0 -#define ADP5589_SCAN_CYCLE_20ms 1 -#define ADP5589_SCAN_CYCLE_30ms 2 -#define ADP5589_SCAN_CYCLE_40ms 3 - -/* RESET_CFG */ -#define RESET_PULSE_WIDTH_500us 0 -#define RESET_PULSE_WIDTH_1ms 1 -#define RESET_PULSE_WIDTH_2ms 2 -#define RESET_PULSE_WIDTH_10ms 3 - -#define RESET_TRIG_TIME_0ms (0 << 2) -#define RESET_TRIG_TIME_1000ms (1 << 2) -#define RESET_TRIG_TIME_1500ms (2 << 2) -#define RESET_TRIG_TIME_2000ms (3 << 2) -#define RESET_TRIG_TIME_2500ms (4 << 2) -#define RESET_TRIG_TIME_3000ms (5 << 2) -#define RESET_TRIG_TIME_3500ms (6 << 2) -#define RESET_TRIG_TIME_4000ms (7 << 2) - -#define RESET_PASSTHRU_EN (1 << 5) -#define RESET1_POL_HIGH (1 << 6) -#define RESET1_POL_LOW (0 << 6) -#define RESET2_POL_HIGH (1 << 7) -#define RESET2_POL_LOW (0 << 7) - -/* ADP5589 Mask Bits: - * C C C C C C C C C C C | R R R R R R R R - * 1 9 8 7 6 5 4 3 2 1 0 | 7 6 5 4 3 2 1 0 - * 0 - * ---------------- BIT ------------------ - * 1 1 1 1 1 1 1 1 1 0 0 | 0 0 0 0 0 0 0 0 - * 8 7 6 5 4 3 2 1 0 9 8 | 7 6 5 4 3 2 1 0 - */ - -#define ADP_ROW(x) (1 << (x)) -#define ADP_COL(x) (1 << (x + 8)) -#define ADP5589_ROW_MASK 0xFF -#define ADP5589_COL_MASK 0xFF -#define ADP5589_COL_SHIFT 8 -#define ADP5589_MAX_ROW_NUM 7 -#define ADP5589_MAX_COL_NUM 10 - -/* ADP5585 Mask Bits: - * C C C C C | R R R R R R - * 4 3 2 1 0 | 5 4 3 2 1 0 - * - * ---- BIT -- ----------- - * 1 0 0 0 0 | 0 0 0 0 0 0 - * 0 9 8 7 6 | 5 4 3 2 1 0 - */ - -#define ADP5585_ROW_MASK 0x3F -#define ADP5585_COL_MASK 0x1F -#define ADP5585_ROW_SHIFT 0 -#define ADP5585_COL_SHIFT 6 -#define ADP5585_MAX_ROW_NUM 5 -#define ADP5585_MAX_COL_NUM 4 - -#define ADP5585_ROW(x) (1 << ((x) & ADP5585_ROW_MASK)) -#define ADP5585_COL(x) (1 << (((x) & ADP5585_COL_MASK) + ADP5585_COL_SHIFT)) - -/* Put one of these structures in i2c_board_info platform_data */ - -struct adp5589_kpad_platform_data { - unsigned keypad_en_mask; /* Keypad (Rows/Columns) enable mask */ - const unsigned short *keymap; /* Pointer to keymap */ - unsigned short keymapsize; /* Keymap size */ - bool repeat; /* Enable key repeat */ - bool en_keylock; /* Enable key lock feature (ADP5589 only)*/ - unsigned char unlock_key1; /* Unlock Key 1 (ADP5589 only) */ - unsigned char unlock_key2; /* Unlock Key 2 (ADP5589 only) */ - unsigned char unlock_timer; /* Time in seconds [0..7] between the two unlock keys 0=disable (ADP5589 only) */ - unsigned char scan_cycle_time; /* Time between consecutive scan cycles */ - unsigned char reset_cfg; /* Reset config */ - unsigned short reset1_key_1; /* Reset Key 1 */ - unsigned short reset1_key_2; /* Reset Key 2 */ - unsigned short reset1_key_3; /* Reset Key 3 */ - unsigned short reset2_key_1; /* Reset Key 1 */ - unsigned short reset2_key_2; /* Reset Key 2 */ - unsigned debounce_dis_mask; /* Disable debounce mask */ - unsigned pull_dis_mask; /* Disable all pull resistors mask */ - unsigned pullup_en_100k; /* Pull-Up 100k Enable Mask */ - unsigned pullup_en_300k; /* Pull-Up 300k Enable Mask */ - unsigned pulldown_en_300k; /* Pull-Down 300k Enable Mask */ - const struct adp5589_gpi_map *gpimap; - unsigned short gpimapsize; - const struct adp5589_gpio_platform_data *gpio_data; -}; - -struct i2c_client; /* forward declaration */ - -struct adp5589_gpio_platform_data { - int gpio_start; /* GPIO Chip base # */ -}; - -#endif -- cgit v1.2.3 From f9de0dd246ed14996e62c731ebccf162cb015ff9 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 7 Jan 2026 16:25:40 +0100 Subject: USB: HCD: remove logic about which hcd is loaded It turns out that warning about which USB host controller is loaded before another one doesn't really matter. All that really is needed is the PCI softdep module loading logic, which has been present in the kernel ever since commit 05c92da0c524 ("usb: ohci/uhci - add soft dependencies on ehci_pci") So remove the warning messages, they are not useful, not needed, and only confuse people. As can be seen in the discussion at https://lore.kernel.org/r/20251230080014.3934590-1-chenhuacai@loongson.cn Cc: Huacai Chen Suggested-by: Alan Stern Reviewed-by: Huacai Chen Link: https://patch.msgid.link/2026010739-diffuser-shelter-e31c@gregkh Signed-off-by: Greg Kroah-Hartman --- drivers/usb/core/hcd.c | 4 ---- drivers/usb/fotg210/fotg210-hcd.c | 6 ------ drivers/usb/host/ehci-hcd.c | 8 -------- drivers/usb/host/ohci-hcd.c | 3 --- drivers/usb/host/uhci-hcd.c | 5 ----- include/linux/usb/hcd.h | 6 ------ 6 files changed, 32 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/core/hcd.c b/drivers/usb/core/hcd.c index 24feb0de1c00..2d99a59d9f3f 100644 --- a/drivers/usb/core/hcd.c +++ b/drivers/usb/core/hcd.c @@ -77,10 +77,6 @@ /*-------------------------------------------------------------------------*/ -/* Keep track of which host controller drivers are loaded */ -unsigned long usb_hcds_loaded; -EXPORT_SYMBOL_GPL(usb_hcds_loaded); - /* host controllers we manage */ DEFINE_IDR (usb_bus_idr); EXPORT_SYMBOL_GPL (usb_bus_idr); diff --git a/drivers/usb/fotg210/fotg210-hcd.c b/drivers/usb/fotg210/fotg210-hcd.c index 64c4965a160f..fbb5d590eab6 100644 --- a/drivers/usb/fotg210/fotg210-hcd.c +++ b/drivers/usb/fotg210/fotg210-hcd.c @@ -5625,11 +5625,6 @@ int __init fotg210_hcd_init(void) if (usb_disabled()) return -ENODEV; - set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); - if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || - test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) - pr_warn("Warning! fotg210_hcd should always be loaded before uhci_hcd and ohci_hcd, not after\n"); - pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd\n", hcd_name, sizeof(struct fotg210_qh), sizeof(struct fotg210_qtd), @@ -5643,5 +5638,4 @@ int __init fotg210_hcd_init(void) void __exit fotg210_hcd_cleanup(void) { debugfs_remove(fotg210_debug_root); - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); } diff --git a/drivers/usb/host/ehci-hcd.c b/drivers/usb/host/ehci-hcd.c index 6d1d190c914d..3c46bb18c7f3 100644 --- a/drivers/usb/host/ehci-hcd.c +++ b/drivers/usb/host/ehci-hcd.c @@ -1354,12 +1354,6 @@ static int __init ehci_hcd_init(void) if (usb_disabled()) return -ENODEV; - set_bit(USB_EHCI_LOADED, &usb_hcds_loaded); - if (test_bit(USB_UHCI_LOADED, &usb_hcds_loaded) || - test_bit(USB_OHCI_LOADED, &usb_hcds_loaded)) - printk(KERN_WARNING "Warning! ehci_hcd should always be loaded" - " before uhci_hcd and ohci_hcd, not after\n"); - pr_debug("%s: block sizes: qh %zd qtd %zd itd %zd sitd %zd\n", hcd_name, sizeof(struct ehci_qh), sizeof(struct ehci_qtd), @@ -1390,7 +1384,6 @@ clean0: debugfs_remove(ehci_debug_root); ehci_debug_root = NULL; #endif - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); return retval; } module_init(ehci_hcd_init); @@ -1404,6 +1397,5 @@ static void __exit ehci_hcd_cleanup(void) #ifdef CONFIG_DYNAMIC_DEBUG debugfs_remove(ehci_debug_root); #endif - clear_bit(USB_EHCI_LOADED, &usb_hcds_loaded); } module_exit(ehci_hcd_cleanup); diff --git a/drivers/usb/host/ohci-hcd.c b/drivers/usb/host/ohci-hcd.c index 9c7f3008646e..30840922f729 100644 --- a/drivers/usb/host/ohci-hcd.c +++ b/drivers/usb/host/ohci-hcd.c @@ -1282,7 +1282,6 @@ static int __init ohci_hcd_mod_init(void) pr_debug ("%s: block sizes: ed %zd td %zd\n", hcd_name, sizeof (struct ed), sizeof (struct td)); - set_bit(USB_OHCI_LOADED, &usb_hcds_loaded); ohci_debug_root = debugfs_create_dir("ohci", usb_debug_root); @@ -1332,7 +1331,6 @@ static int __init ohci_hcd_mod_init(void) debugfs_remove(ohci_debug_root); ohci_debug_root = NULL; - clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded); return retval; } module_init(ohci_hcd_mod_init); @@ -1352,7 +1350,6 @@ static void __exit ohci_hcd_mod_exit(void) ps3_ohci_driver_unregister(&PS3_SYSTEM_BUS_DRIVER); #endif debugfs_remove(ohci_debug_root); - clear_bit(USB_OHCI_LOADED, &usb_hcds_loaded); } module_exit(ohci_hcd_mod_exit); diff --git a/drivers/usb/host/uhci-hcd.c b/drivers/usb/host/uhci-hcd.c index 14e6dfef16c6..8bb11109b66c 100644 --- a/drivers/usb/host/uhci-hcd.c +++ b/drivers/usb/host/uhci-hcd.c @@ -867,8 +867,6 @@ static int __init uhci_hcd_init(void) if (usb_disabled()) return -ENODEV; - set_bit(USB_UHCI_LOADED, &usb_hcds_loaded); - #ifdef CONFIG_DYNAMIC_DEBUG errbuf = kmalloc(ERRBUF_LEN, GFP_KERNEL); if (!errbuf) @@ -912,8 +910,6 @@ up_failed: errbuf_failed: #endif - - clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded); return retval; } @@ -930,7 +926,6 @@ static void __exit uhci_hcd_cleanup(void) #ifdef CONFIG_DYNAMIC_DEBUG kfree(errbuf); #endif - clear_bit(USB_UHCI_LOADED, &usb_hcds_loaded); } module_init(uhci_hcd_init); diff --git a/include/linux/usb/hcd.h b/include/linux/usb/hcd.h index ac95e7c89df5..181db044c4d2 100644 --- a/include/linux/usb/hcd.h +++ b/include/linux/usb/hcd.h @@ -760,12 +760,6 @@ static inline void usbmon_urb_complete(struct usb_bus *bus, struct urb *urb, */ extern struct rw_semaphore ehci_cf_port_reset_rwsem; -/* Keep track of which host controller drivers are loaded */ -#define USB_UHCI_LOADED 0 -#define USB_OHCI_LOADED 1 -#define USB_EHCI_LOADED 2 -extern unsigned long usb_hcds_loaded; - #endif /* __KERNEL__ */ #endif /* __USB_CORE_HCD_H */ -- cgit v1.2.3 From f47c1b77d0a2a9c0d49ec14302e74f933398d1a3 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 1 Dec 2025 10:42:26 +0100 Subject: clk: Move clk_{save,restore}_context() to COMMON_CLK section The clk_save_context() and clk_restore_context() helpers are only implemented by the Common Clock Framework. They are not available when using legacy clock frameworks. Dummy implementations are provided, but only if no clock support is available at all. Hence when CONFIG_HAVE_CLK=y, but CONFIG_COMMON_CLK is not enabled: m68k-linux-gnu-ld: drivers/net/phy/air_en8811h.o: in function `en8811h_resume': air_en8811h.c:(.text+0x83e): undefined reference to `clk_restore_context' m68k-linux-gnu-ld: drivers/net/phy/air_en8811h.o: in function `en8811h_suspend': air_en8811h.c:(.text+0x856): undefined reference to `clk_save_context' Fix this by moving forward declarations and dummy implementions from the HAVE_CLK to the COMMON_CLK section. Fixes: 8b95d1ce3300c411 ("clk: Add functions to save/restore clock context en-masse") Reported-by: kernel test robot Closes: https://lore.kernel.org/oe-kbuild-all/202511301553.eaEz1nEW-lkp@intel.com/ Signed-off-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd --- include/linux/clk.h | 48 ++++++++++++++++++++++++------------------------ 1 file changed, 24 insertions(+), 24 deletions(-) (limited to 'include/linux') diff --git a/include/linux/clk.h b/include/linux/clk.h index b607482ca77e..64ff118ffb1a 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -228,6 +228,23 @@ int devm_clk_rate_exclusive_get(struct device *dev, struct clk *clk); */ void clk_rate_exclusive_put(struct clk *clk); +/** + * clk_save_context - save clock context for poweroff + * + * Saves the context of the clock register for powerstates in which the + * contents of the registers will be lost. Occurs deep within the suspend + * code so locking is not necessary. + */ +int clk_save_context(void); + +/** + * clk_restore_context - restore clock context after poweroff + * + * This occurs with all clocks enabled. Occurs deep within the resume code + * so locking is not necessary. + */ +void clk_restore_context(void); + #else static inline int clk_notifier_register(struct clk *clk, @@ -293,6 +310,13 @@ static inline int devm_clk_rate_exclusive_get(struct device *dev, struct clk *cl static inline void clk_rate_exclusive_put(struct clk *clk) {} +static inline int clk_save_context(void) +{ + return 0; +} + +static inline void clk_restore_context(void) {} + #endif #ifdef CONFIG_HAVE_CLK_PREPARE @@ -933,23 +957,6 @@ struct clk *clk_get_parent(struct clk *clk); */ struct clk *clk_get_sys(const char *dev_id, const char *con_id); -/** - * clk_save_context - save clock context for poweroff - * - * Saves the context of the clock register for powerstates in which the - * contents of the registers will be lost. Occurs deep within the suspend - * code so locking is not necessary. - */ -int clk_save_context(void); - -/** - * clk_restore_context - restore clock context after poweroff - * - * This occurs with all clocks enabled. Occurs deep within the resume code - * so locking is not necessary. - */ -void clk_restore_context(void); - #else /* !CONFIG_HAVE_CLK */ static inline struct clk *clk_get(struct device *dev, const char *id) @@ -1129,13 +1136,6 @@ static inline struct clk *clk_get_sys(const char *dev_id, const char *con_id) return NULL; } -static inline int clk_save_context(void) -{ - return 0; -} - -static inline void clk_restore_context(void) {} - #endif /* clk_prepare_enable helps cases using clk_enable in non-atomic context. */ -- cgit v1.2.3 From d94f0f096ccf83b1a212788c62122c4b97ac8907 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 1 Dec 2025 10:42:27 +0100 Subject: clk: Merge prepare and unprepare sections contains two consecutive #ifdef/#else/#endif sections that check for CONFIG_HAVE_CLK_PREPARE: one for prepare-related functionality, and a second for unprepare-related functionality. Reduce #ifdef clutter by merging them. Signed-off-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd --- include/linux/clk.h | 46 +++++++++++++++++++++++----------------------- 1 file changed, 23 insertions(+), 23 deletions(-) (limited to 'include/linux') diff --git a/include/linux/clk.h b/include/linux/clk.h index 64ff118ffb1a..9e5291f37c50 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -329,8 +329,21 @@ static inline void clk_restore_context(void) {} * Must not be called from within atomic context. */ int clk_prepare(struct clk *clk); + +/** + * clk_unprepare - undo preparation of a clock source + * @clk: clock source + * + * This undoes a previously prepared clock. The caller must balance + * the number of prepare and unprepare calls. + * + * Must not be called from within atomic context. + */ +void clk_unprepare(struct clk *clk); + int __must_check clk_bulk_prepare(int num_clks, const struct clk_bulk_data *clks); +void clk_bulk_unprepare(int num_clks, const struct clk_bulk_data *clks); /** * clk_is_enabled_when_prepared - indicate if preparing a clock also enables it. @@ -355,41 +368,28 @@ static inline int clk_prepare(struct clk *clk) return 0; } -static inline int __must_check -clk_bulk_prepare(int num_clks, const struct clk_bulk_data *clks) +static inline void clk_unprepare(struct clk *clk) { might_sleep(); - return 0; } -static inline bool clk_is_enabled_when_prepared(struct clk *clk) -{ - return false; -} -#endif - -/** - * clk_unprepare - undo preparation of a clock source - * @clk: clock source - * - * This undoes a previously prepared clock. The caller must balance - * the number of prepare and unprepare calls. - * - * Must not be called from within atomic context. - */ -#ifdef CONFIG_HAVE_CLK_PREPARE -void clk_unprepare(struct clk *clk); -void clk_bulk_unprepare(int num_clks, const struct clk_bulk_data *clks); -#else -static inline void clk_unprepare(struct clk *clk) +static inline int __must_check +clk_bulk_prepare(int num_clks, const struct clk_bulk_data *clks) { might_sleep(); + return 0; } + static inline void clk_bulk_unprepare(int num_clks, const struct clk_bulk_data *clks) { might_sleep(); } + +static inline bool clk_is_enabled_when_prepared(struct clk *clk) +{ + return false; +} #endif #ifdef CONFIG_HAVE_CLK -- cgit v1.2.3 From abe368db117ea61ace90880c80a12ee3c0d619e6 Mon Sep 17 00:00:00 2001 From: Geert Uytterhoeven Date: Mon, 1 Dec 2025 10:42:28 +0100 Subject: clk: Annotate #else and #endif Annotate the #else and #endif keywords in large #ifdef/#else/#endif sections, to improve readability. Signed-off-by: Geert Uytterhoeven Signed-off-by: Stephen Boyd --- include/linux/clk.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/clk.h b/include/linux/clk.h index 9e5291f37c50..efb91604d3f6 100644 --- a/include/linux/clk.h +++ b/include/linux/clk.h @@ -245,7 +245,7 @@ int clk_save_context(void); */ void clk_restore_context(void); -#else +#else /* !CONFIG_COMMON_CLK */ static inline int clk_notifier_register(struct clk *clk, struct notifier_block *nb) @@ -317,7 +317,7 @@ static inline int clk_save_context(void) static inline void clk_restore_context(void) {} -#endif +#endif /* !CONFIG_COMMON_CLK */ #ifdef CONFIG_HAVE_CLK_PREPARE /** @@ -361,7 +361,7 @@ void clk_bulk_unprepare(int num_clks, const struct clk_bulk_data *clks); * to be right. */ bool clk_is_enabled_when_prepared(struct clk *clk); -#else +#else /* !CONFIG_HAVE_CLK_PREPARE */ static inline int clk_prepare(struct clk *clk) { might_sleep(); @@ -390,7 +390,7 @@ static inline bool clk_is_enabled_when_prepared(struct clk *clk) { return false; } -#endif +#endif /* !CONFIG_HAVE_CLK_PREPARE */ #ifdef CONFIG_HAVE_CLK /** @@ -1136,7 +1136,7 @@ static inline struct clk *clk_get_sys(const char *dev_id, const char *con_id) return NULL; } -#endif +#endif /* !CONFIG_HAVE_CLK */ /* clk_prepare_enable helps cases using clk_enable in non-atomic context. */ static inline int clk_prepare_enable(struct clk *clk) -- cgit v1.2.3 From 5a4326f2e3b1edfb3329c1bee59035dc9f048b59 Mon Sep 17 00:00:00 2001 From: Chris Brandt Date: Mon, 24 Nov 2025 08:10:02 -0500 Subject: clk: renesas: rzg2l: Remove DSI clock rate restrictions Convert the limited MIPI clock calculations to a full range of settings based on math including H/W limitation validation. Since the required DSI division setting must be specified from external sources before calculations, expose a new API to set it. Signed-off-by: Chris Brandt Reviewed-by: Biju Das Tested-by: Biju Das Reviewed-by: Hugo Villeneuve Tested-by: Hugo Villeneuve Reviewed-by: Geert Uytterhoeven Link: https://patch.msgid.link/20251124131003.992554-2-chris.brandt@renesas.com Signed-off-by: Geert Uytterhoeven --- drivers/clk/renesas/rzg2l-cpg.c | 174 +++++++++++++++++++++++++++++++++------- include/linux/clk/renesas.h | 11 +++ 2 files changed, 154 insertions(+), 31 deletions(-) (limited to 'include/linux') diff --git a/drivers/clk/renesas/rzg2l-cpg.c b/drivers/clk/renesas/rzg2l-cpg.c index f670c6408ea1..3ce28d553e4c 100644 --- a/drivers/clk/renesas/rzg2l-cpg.c +++ b/drivers/clk/renesas/rzg2l-cpg.c @@ -22,6 +22,7 @@ #include #include #include +#include #include #include #include @@ -74,6 +75,17 @@ #define MSTOP_OFF(conf) FIELD_GET(GENMASK(31, 16), (conf)) #define MSTOP_MASK(conf) FIELD_GET(GENMASK(15, 0), (conf)) +#define PLL5_FOUTVCO_MIN 800000000 +#define PLL5_FOUTVCO_MAX 3000000000 +#define PLL5_POSTDIV_MIN 1 +#define PLL5_POSTDIV_MAX 7 +#define PLL5_REFDIV_MIN 1 +#define PLL5_REFDIV_MAX 2 +#define PLL5_INTIN_MIN 20 +#define PLL5_INTIN_MAX 320 +#define PLL5_HSCLK_MIN 10000000 +#define PLL5_HSCLK_MAX 187500000 + /** * struct clk_hw_data - clock hardware data * @hw: clock hw @@ -129,6 +141,12 @@ struct rzg2l_pll5_param { u8 pl5_spread; }; +/* PLL5 output will be used for DPI or MIPI-DSI */ +static int dsi_div_target = PLL5_TARGET_DPI; + +/* Required division ratio for MIPI D-PHY clock depending on number of lanes and bpp. */ +static u8 dsi_div_ab_desired; + struct rzg2l_pll5_mux_dsi_div_param { u8 clksrc; u8 dsi_div_a; @@ -170,6 +188,11 @@ struct rzg2l_cpg_priv { struct rzg2l_pll5_mux_dsi_div_param mux_dsi_div_params; }; +static inline u8 rzg2l_cpg_div_ab(u8 a, u8 b) +{ + return (b + 1) << a; +} + static void rzg2l_cpg_del_clk_provider(void *data) { of_clk_del_provider(data); @@ -556,24 +579,121 @@ rzg2l_cpg_sd_mux_clk_register(const struct cpg_core_clk *core, return clk_hw->clk; } +/* + * VCO-->[POSTDIV1,2]--FOUTPOSTDIV--------------->| + * | |-->[1/(DSI DIV A * B)]--> MIPI_DSI_VCLK + * |-->[1/2]--FOUT1PH0-->| + * | + * |------->[1/16]--------------------------------> hsclk (MIPI-PHY) + */ static unsigned long -rzg2l_cpg_get_foutpostdiv_rate(struct rzg2l_pll5_param *params, +rzg2l_cpg_get_foutpostdiv_rate(struct rzg2l_cpg_priv *priv, + struct rzg2l_pll5_param *params, unsigned long rate) { - unsigned long foutpostdiv_rate, foutvco_rate; + const u32 extal_hz = EXTAL_FREQ_IN_MEGA_HZ * MEGA; + unsigned long foutpostdiv_rate; + unsigned int a, b, odd; + unsigned long hsclk; + u8 dsi_div_ab_calc; + u64 foutvco_rate; + + if (dsi_div_target == PLL5_TARGET_DSI) { + /* Check hsclk */ + hsclk = rate * dsi_div_ab_desired / 16; + if (hsclk < PLL5_HSCLK_MIN || hsclk > PLL5_HSCLK_MAX) { + dev_err(priv->dev, "hsclk out of range\n"); + return 0; + } + + /* Determine the correct clock source based on even/odd of the divider */ + odd = dsi_div_ab_desired & 1; + if (odd) { + priv->mux_dsi_div_params.clksrc = 0; /* FOUTPOSTDIV */ + dsi_div_ab_calc = dsi_div_ab_desired; + } else { + priv->mux_dsi_div_params.clksrc = 1; /* FOUT1PH0 */ + dsi_div_ab_calc = dsi_div_ab_desired / 2; + } + + /* Calculate the DIV_DSI_A and DIV_DSI_B based on the desired divider */ + for (a = 0; a < 4; a++) { + /* FOUT1PH0: Max output of DIV_DSI_A is 750MHz so at least 1/2 to be safe */ + if (!odd && a == 0) + continue; + + /* FOUTPOSTDIV: DIV_DSI_A must always be 1/1 */ + if (odd && a != 0) + break; + + for (b = 0; b < 16; b++) { + /* FOUTPOSTDIV: DIV_DSI_B must always be odd divider 1/(b+1) */ + if (odd && b & 1) + continue; + + if (rzg2l_cpg_div_ab(a, b) == dsi_div_ab_calc) { + priv->mux_dsi_div_params.dsi_div_a = a; + priv->mux_dsi_div_params.dsi_div_b = b; + goto calc_pll_clk; + } + } + } + + dev_err(priv->dev, "Failed to calculate DIV_DSI_A,B\n"); + + return 0; + } else if (dsi_div_target == PLL5_TARGET_DPI) { + /* Fixed settings for DPI */ + priv->mux_dsi_div_params.clksrc = 0; + priv->mux_dsi_div_params.dsi_div_a = 3; /* Divided by 8 */ + priv->mux_dsi_div_params.dsi_div_b = 0; /* Divided by 1 */ + dsi_div_ab_desired = rzg2l_cpg_div_ab(priv->mux_dsi_div_params.dsi_div_a, + priv->mux_dsi_div_params.dsi_div_b); + } - params->pl5_intin = rate / MEGA; - params->pl5_fracin = div_u64(((u64)rate % MEGA) << 24, MEGA); - params->pl5_refdiv = 2; - params->pl5_postdiv1 = 1; - params->pl5_postdiv2 = 1; +calc_pll_clk: + /* PLL5 (MIPI_DSI_PLLCLK) = VCO / POSTDIV1 / POSTDIV2 */ + for (params->pl5_postdiv1 = PLL5_POSTDIV_MIN; + params->pl5_postdiv1 <= PLL5_POSTDIV_MAX; + params->pl5_postdiv1++) { + for (params->pl5_postdiv2 = PLL5_POSTDIV_MIN; + params->pl5_postdiv2 <= PLL5_POSTDIV_MAX; + params->pl5_postdiv2++) { + foutvco_rate = rate * params->pl5_postdiv1 * params->pl5_postdiv2 * + dsi_div_ab_desired; + if (foutvco_rate <= PLL5_FOUTVCO_MIN || foutvco_rate >= PLL5_FOUTVCO_MAX) + continue; + + for (params->pl5_refdiv = PLL5_REFDIV_MIN; + params->pl5_refdiv <= PLL5_REFDIV_MAX; + params->pl5_refdiv++) { + u32 rem; + + params->pl5_intin = div_u64_rem(foutvco_rate * params->pl5_refdiv, + extal_hz, &rem); + + if (params->pl5_intin < PLL5_INTIN_MIN || + params->pl5_intin > PLL5_INTIN_MAX) + continue; + + params->pl5_fracin = div_u64((u64)rem << 24, extal_hz); + + goto clk_valid; + } + } + } + + dev_err(priv->dev, "Failed to calculate PLL5 settings\n"); + return 0; + +clk_valid: params->pl5_spread = 0x16; foutvco_rate = div_u64(mul_u32_u32(EXTAL_FREQ_IN_MEGA_HZ * MEGA, (params->pl5_intin << 24) + params->pl5_fracin), params->pl5_refdiv) >> 24; - foutpostdiv_rate = DIV_ROUND_CLOSEST(foutvco_rate, - params->pl5_postdiv1 * params->pl5_postdiv2); + foutpostdiv_rate = DIV_U64_ROUND_CLOSEST(foutvco_rate, + params->pl5_postdiv1 * params->pl5_postdiv2); return foutpostdiv_rate; } @@ -607,7 +727,7 @@ static unsigned long rzg2l_cpg_get_vclk_parent_rate(struct clk_hw *hw, struct rzg2l_pll5_param params; unsigned long parent_rate; - parent_rate = rzg2l_cpg_get_foutpostdiv_rate(¶ms, rate); + parent_rate = rzg2l_cpg_get_foutpostdiv_rate(priv, ¶ms, rate); if (priv->mux_dsi_div_params.clksrc) parent_rate /= 2; @@ -623,9 +743,19 @@ static int rzg2l_cpg_dsi_div_determine_rate(struct clk_hw *hw, req->best_parent_rate = rzg2l_cpg_get_vclk_parent_rate(hw, req->rate); + if (!req->best_parent_rate) + return -EINVAL; + return 0; } +void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) +{ + dsi_div_ab_desired = divider; + dsi_div_target = target; +} +EXPORT_SYMBOL_GPL(rzg2l_cpg_dsi_div_set_divider); + static int rzg2l_cpg_dsi_div_set_rate(struct clk_hw *hw, unsigned long rate, unsigned long parent_rate) @@ -796,22 +926,6 @@ struct sipll5 { #define to_sipll5(_hw) container_of(_hw, struct sipll5, hw) -static unsigned long rzg2l_cpg_get_vclk_rate(struct clk_hw *hw, - unsigned long rate) -{ - struct sipll5 *sipll5 = to_sipll5(hw); - struct rzg2l_cpg_priv *priv = sipll5->priv; - unsigned long vclk; - - vclk = rate / ((1 << priv->mux_dsi_div_params.dsi_div_a) * - (priv->mux_dsi_div_params.dsi_div_b + 1)); - - if (priv->mux_dsi_div_params.clksrc) - vclk /= 2; - - return vclk; -} - static unsigned long rzg2l_cpg_sipll5_recalc_rate(struct clk_hw *hw, unsigned long parent_rate) { @@ -856,9 +970,9 @@ static int rzg2l_cpg_sipll5_set_rate(struct clk_hw *hw, if (!rate) return -EINVAL; - vclk_rate = rzg2l_cpg_get_vclk_rate(hw, rate); + vclk_rate = rate / dsi_div_ab_desired; sipll5->foutpostdiv_rate = - rzg2l_cpg_get_foutpostdiv_rate(¶ms, vclk_rate); + rzg2l_cpg_get_foutpostdiv_rate(priv, ¶ms, vclk_rate); /* Put PLL5 into standby mode */ writel(CPG_SIPLL5_STBY_RESETB_WEN, priv->base + CPG_SIPLL5_STBY); @@ -945,9 +1059,7 @@ rzg2l_cpg_sipll5_register(const struct cpg_core_clk *core, if (ret) return ERR_PTR(ret); - priv->mux_dsi_div_params.clksrc = 1; /* Use clk src 1 for DSI */ - priv->mux_dsi_div_params.dsi_div_a = 1; /* Divided by 2 */ - priv->mux_dsi_div_params.dsi_div_b = 2; /* Divided by 3 */ + rzg2l_cpg_dsi_div_set_divider(8, PLL5_TARGET_DPI); return clk_hw->clk; } diff --git a/include/linux/clk/renesas.h b/include/linux/clk/renesas.h index 69d8159deee3..c360df9fa735 100644 --- a/include/linux/clk/renesas.h +++ b/include/linux/clk/renesas.h @@ -35,6 +35,17 @@ void cpg_mssr_detach_dev(struct generic_pm_domain *unused, struct device *dev); #define cpg_mssr_detach_dev NULL #endif +enum { + PLL5_TARGET_DPI, + PLL5_TARGET_DSI +}; + +#ifdef CONFIG_CLK_RZG2L +void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target); +#else +static inline void rzg2l_cpg_dsi_div_set_divider(u8 divider, int target) { } +#endif + /** * struct rzv2h_pll_limits - PLL parameter constraints * -- cgit v1.2.3 From 578d62a2e51614ea117ccf05fce6aaa6257dfd60 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 14 Dec 2025 12:27:27 -0800 Subject: comedi: comedi_8254: correct kernel-doc warnings Correct typos in 3 struct member descriptions to eliminate all kernel-doc warnings in comedi_8254.h: Warning: include/linux/comedi/comedi_8254.h:112 struct member 'next_div' not described in 'comedi_8254' Warning: include/linux/comedi/comedi_8254.h:112 struct member 'clock_src' not described in 'comedi_8254' Warning: include/linux/comedi/comedi_8254.h:112 struct member 'gate_src' not described in 'comedi_8254' Signed-off-by: Randy Dunlap Reviewed-by: Ian Abbott Link: https://patch.msgid.link/20251214202727.2215461-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/comedi/comedi_8254.h | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/comedi/comedi_8254.h b/include/linux/comedi/comedi_8254.h index d527f04400df..91f5861061ec 100644 --- a/include/linux/comedi/comedi_8254.h +++ b/include/linux/comedi/comedi_8254.h @@ -83,11 +83,11 @@ typedef unsigned int comedi_8254_iocb_fn(struct comedi_8254 *i8254, int dir, * @divisor: divisor for single counter * @divisor1: divisor loaded into first cascaded counter * @divisor2: divisor loaded into second cascaded counter - * #next_div: next divisor for single counter + * @next_div: next divisor for single counter * @next_div1: next divisor to use for first cascaded counter * @next_div2: next divisor to use for second cascaded counter - * @clock_src; current clock source for each counter (driver specific) - * @gate_src; current gate source for each counter (driver specific) + * @clock_src: current clock source for each counter (driver specific) + * @gate_src: current gate source for each counter (driver specific) * @busy: flags used to indicate that a counter is "busy" * @insn_config: driver specific (*insn_config) callback */ -- cgit v1.2.3 From a2450bddb75f4c0da86ed02adbc9477e49edd311 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 14 Dec 2025 12:27:39 -0800 Subject: eeprom_93cx6: fix struct member kernel-doc Remove the function parameter parts of the struct member descriptions to prevent kernel-doc warnings: Warning: include/linux/eeprom_93cx6.h:64 struct member 'register_read' not described in 'eeprom_93cx6' Warning: ../include/linux/eeprom_93cx6.h:64 struct member 'register_write' not described in 'eeprom_93cx6' Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251214202739.2216904-1-rdunlap@infradead.org Signed-off-by: Greg Kroah-Hartman --- include/linux/eeprom_93cx6.h | 8 ++++---- 1 file changed, 4 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/include/linux/eeprom_93cx6.h b/include/linux/eeprom_93cx6.h index 3a485cc0e0fa..194a4a49ff1d 100644 --- a/include/linux/eeprom_93cx6.h +++ b/include/linux/eeprom_93cx6.h @@ -31,10 +31,10 @@ * struct eeprom_93cx6 - control structure for setting the commands * for reading the eeprom data. * @data: private pointer for the driver. - * @register_read(struct eeprom_93cx6 *eeprom): handler to - * read the eeprom register, this function should set all reg_* fields. - * @register_write(struct eeprom_93cx6 *eeprom): handler to - * write to the eeprom register by using all reg_* fields. + * @register_read: handler to read the eeprom register; + * this function should set all reg_* fields. + * @register_write: handler to write to the eeprom register by using + * all reg_* fields. * @width: eeprom width, should be one of the PCI_EEPROM_WIDTH_* defines * @quirks: eeprom or controller quirks * @drive_data: Set if we're driving the data line. -- cgit v1.2.3 From 5f0bf80cc5e04d31eeb201683e0b477c24bd18e7 Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Sat, 3 Jan 2026 12:42:26 -0800 Subject: mmc: rtsx_pci: add quirk to disable MMC_CAP_AGGRESSIVE_PM for RTS525A Using MMC_CAP_AGGRESSIVE_PM on RTS525A card readers causes game performance issues when the card reader comes back from idle into active use. This can be observed in Hades II when loading new sections of the game or menu after the card reader puts itself into idle, and presents as a 1-2 second hang. Add EXTRA_CAPS_NO_AGGRESSIVE_PM quirk to allow cardreader drivers to opt-out of aggressive PM, and set it for RTS525A. Closes: https://lore.kernel.org/linux-mmc/ff9a7c20-f465-4afa-bf29-708d4a52974a@linux.dev/ Signed-off-by: Matthew Schwartz Link: https://patch.msgid.link/20260103204226.71752-1-matthew.schwartz@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rts5249.c | 3 +++ drivers/mmc/host/rtsx_pci_sdmmc.c | 4 ++-- include/linux/rtsx_pci.h | 1 + 3 files changed, 6 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/misc/cardreader/rts5249.c b/drivers/misc/cardreader/rts5249.c index 38aefd8db452..87d576a03e68 100644 --- a/drivers/misc/cardreader/rts5249.c +++ b/drivers/misc/cardreader/rts5249.c @@ -78,6 +78,9 @@ static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A)) pcr->rtd3_en = rtsx_reg_to_rtd3_uhsii(reg); + if (CHK_PCI_PID(pcr, PID_525A)) + pcr->extra_caps |= EXTRA_CAPS_NO_AGGRESSIVE_PM; + if (rtsx_check_mmc_support(reg)) pcr->extra_caps |= EXTRA_CAPS_NO_MMC; pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index dc2587ff8519..5d3599ee06bf 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1456,8 +1456,8 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host) mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; - if (pcr->rtd3_en) - mmc->caps = mmc->caps | MMC_CAP_AGGRESSIVE_PM; + if (pcr->rtd3_en && !(pcr->extra_caps & EXTRA_CAPS_NO_AGGRESSIVE_PM)) + mmc->caps |= MMC_CAP_AGGRESSIVE_PM; mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE | MMC_CAP2_NO_SDIO; mmc->max_current_330 = 400; diff --git a/include/linux/rtsx_pci.h b/include/linux/rtsx_pci.h index 3c5689356004..f6122349c00e 100644 --- a/include/linux/rtsx_pci.h +++ b/include/linux/rtsx_pci.h @@ -1230,6 +1230,7 @@ struct rtsx_pcr { #define EXTRA_CAPS_MMC_8BIT (1 << 5) #define EXTRA_CAPS_NO_MMC (1 << 7) #define EXTRA_CAPS_SD_EXPRESS (1 << 8) +#define EXTRA_CAPS_NO_AGGRESSIVE_PM (1 << 9) u32 extra_caps; #define IC_VER_A 0 -- cgit v1.2.3 From eac85fbd0867c25ac517f58fae401d65c627edff Mon Sep 17 00:00:00 2001 From: Matthew Schwartz Date: Sun, 4 Jan 2026 22:02:35 -0800 Subject: mmc: rtsx: reset power state on suspend When rtsx_pci suspends, the card reader hardware powers off but the sdmmc driver's prev_power_state remains as MMC_POWER_ON. This causes sd_power_on to skip reinitialization on the next I/O request, leading to DMA transfer timeouts and errors on resume 20% of the time. Add a power_off slot callback so the PCR can notify the sdmmc driver during suspend. The sdmmc driver resets prev_power_state, and sd_request checks this to reinitialize the card before the next I/O. Signed-off-by: Matthew Schwartz Link: https://patch.msgid.link/20260105060236.400366-2-matthew.schwartz@linux.dev Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 9 +++++++++ drivers/mmc/host/rtsx_pci_sdmmc.c | 22 ++++++++++++++++++++++ include/linux/rtsx_common.h | 1 + 3 files changed, 32 insertions(+) (limited to 'include/linux') diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index f9952d76d6ed..f1f4d8ed544d 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1654,6 +1654,7 @@ static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) struct pci_dev *pcidev = to_pci_dev(dev_d); struct pcr_handle *handle = pci_get_drvdata(pcidev); struct rtsx_pcr *pcr = handle->pcr; + struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD]; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); @@ -1661,6 +1662,9 @@ static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) mutex_lock(&pcr->pcr_mutex); + if (slot->p_dev && slot->power_off) + slot->power_off(slot->p_dev); + rtsx_pci_power_off(pcr, HOST_ENTER_S3, false); mutex_unlock(&pcr->pcr_mutex); @@ -1772,12 +1776,17 @@ static int rtsx_pci_runtime_suspend(struct device *device) struct pci_dev *pcidev = to_pci_dev(device); struct pcr_handle *handle = pci_get_drvdata(pcidev); struct rtsx_pcr *pcr = handle->pcr; + struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD]; dev_dbg(device, "--> %s\n", __func__); cancel_delayed_work_sync(&pcr->carddet_work); mutex_lock(&pcr->pcr_mutex); + + if (slot->p_dev && slot->power_off) + slot->power_off(slot->p_dev); + rtsx_pci_power_off(pcr, HOST_ENTER_S3, true); mutex_unlock(&pcr->pcr_mutex); diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 5d3599ee06bf..b847d79d4d8c 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -47,6 +47,7 @@ struct realtek_pci_sdmmc { }; static int sdmmc_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios); +static int sd_power_on(struct realtek_pci_sdmmc *host, unsigned char power_mode); static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host) { @@ -821,6 +822,15 @@ static void sd_request(struct work_struct *work) rtsx_pci_start_run(pcr); + if (host->prev_power_state == MMC_POWER_OFF) { + err = sd_power_on(host, MMC_POWER_ON); + if (err) { + cmd->error = err; + mutex_unlock(&pcr->pcr_mutex); + goto finish; + } + } + rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth, host->initial_mode, host->double_clk, host->vpclk); rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, SD_MOD_SEL); @@ -1481,6 +1491,16 @@ static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev) mmc_detect_change(host->mmc, 0); } +static void rtsx_pci_sdmmc_power_off(struct platform_device *pdev) +{ + struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); + + if (!host) + return; + + host->prev_power_state = MMC_POWER_OFF; +} + static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) { struct mmc_host *mmc; @@ -1513,6 +1533,7 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); pcr->slots[RTSX_SD_CARD].p_dev = pdev; pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event; + pcr->slots[RTSX_SD_CARD].power_off = rtsx_pci_sdmmc_power_off; mutex_init(&host->host_mutex); @@ -1544,6 +1565,7 @@ static void rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) pcr = host->pcr; pcr->slots[RTSX_SD_CARD].p_dev = NULL; pcr->slots[RTSX_SD_CARD].card_event = NULL; + pcr->slots[RTSX_SD_CARD].power_off = NULL; mmc = host->mmc; cancel_work_sync(&host->work); diff --git a/include/linux/rtsx_common.h b/include/linux/rtsx_common.h index da9c8c6b5d50..f294f478f0c0 100644 --- a/include/linux/rtsx_common.h +++ b/include/linux/rtsx_common.h @@ -32,6 +32,7 @@ struct platform_device; struct rtsx_slot { struct platform_device *p_dev; void (*card_event)(struct platform_device *p_dev); + void (*power_off)(struct platform_device *p_dev); }; #endif -- cgit v1.2.3 From e48e16f3e37fac76e2f0c14c58df2b0398a323b0 Mon Sep 17 00:00:00 2001 From: Daeho Jeong Date: Sat, 10 Jan 2026 15:54:05 -0800 Subject: f2fs: support non-4KB block size without packed_ssa feature Currently, F2FS requires the packed_ssa feature to be enabled when utilizing non-4KB block sizes (e.g., 16KB). This restriction limits the flexibility of filesystem formatting options. This patch allows F2FS to support non-4KB block sizes even when the packed_ssa feature is disabled. It adjusts the SSA calculation logic to correctly handle summary entries in larger blocks without the packed layout. Cc: stable@kernel.org Fixes: 7ee8bc3942f2 ("f2fs: revert summary entry count from 2048 to 512 in 16kb block support") Signed-off-by: Daeho Jeong Reviewed-by: Chao Yu Signed-off-by: Jaegeuk Kim --- fs/f2fs/f2fs.h | 54 ++++++++++++++++++++++--------- fs/f2fs/gc.c | 23 ++++++------- fs/f2fs/node.c | 12 +++---- fs/f2fs/recovery.c | 6 ++-- fs/f2fs/segment.c | 86 ++++++++++++++++++++++++++----------------------- fs/f2fs/segment.h | 9 +++--- fs/f2fs/super.c | 26 +++++++-------- include/linux/f2fs_fs.h | 73 +++++++++++++++++++++++++---------------- 8 files changed, 166 insertions(+), 123 deletions(-) (limited to 'include/linux') diff --git a/fs/f2fs/f2fs.h b/fs/f2fs/f2fs.h index 579aafb0055f..f82c9c424748 100644 --- a/fs/f2fs/f2fs.h +++ b/fs/f2fs/f2fs.h @@ -545,13 +545,25 @@ struct fsync_inode_entry { #define nats_in_cursum(jnl) (le16_to_cpu((jnl)->n_nats)) #define sits_in_cursum(jnl) (le16_to_cpu((jnl)->n_sits)) -#define nat_in_journal(jnl, i) ((jnl)->nat_j.entries[i].ne) -#define nid_in_journal(jnl, i) ((jnl)->nat_j.entries[i].nid) -#define sit_in_journal(jnl, i) ((jnl)->sit_j.entries[i].se) -#define segno_in_journal(jnl, i) ((jnl)->sit_j.entries[i].segno) - -#define MAX_NAT_JENTRIES(jnl) (NAT_JOURNAL_ENTRIES - nats_in_cursum(jnl)) -#define MAX_SIT_JENTRIES(jnl) (SIT_JOURNAL_ENTRIES - sits_in_cursum(jnl)) +#define nat_in_journal(jnl, i) \ + (((struct nat_journal_entry *)(jnl)->nat_j.entries)[i].ne) +#define nid_in_journal(jnl, i) \ + (((struct nat_journal_entry *)(jnl)->nat_j.entries)[i].nid) +#define sit_in_journal(jnl, i) \ + (((struct sit_journal_entry *)(jnl)->sit_j.entries)[i].se) +#define segno_in_journal(jnl, i) \ + (((struct sit_journal_entry *)(jnl)->sit_j.entries)[i].segno) + +#define sum_entries(sum) ((struct f2fs_summary *)(sum)) +#define sum_journal(sbi, sum) \ + ((struct f2fs_journal *)((char *)(sum) + \ + ((sbi)->entries_in_sum * sizeof(struct f2fs_summary)))) +#define sum_footer(sbi, sum) \ + ((struct summary_footer *)((char *)(sum) + (sbi)->sum_blocksize - \ + sizeof(struct summary_footer))) + +#define MAX_NAT_JENTRIES(sbi, jnl) ((sbi)->nat_journal_entries - nats_in_cursum(jnl)) +#define MAX_SIT_JENTRIES(sbi, jnl) ((sbi)->sit_journal_entries - sits_in_cursum(jnl)) static inline int update_nats_in_cursum(struct f2fs_journal *journal, int i) { @@ -569,14 +581,6 @@ static inline int update_sits_in_cursum(struct f2fs_journal *journal, int i) return before; } -static inline bool __has_cursum_space(struct f2fs_journal *journal, - int size, int type) -{ - if (type == NAT_JOURNAL) - return size <= MAX_NAT_JENTRIES(journal); - return size <= MAX_SIT_JENTRIES(journal); -} - /* for inline stuff */ #define DEF_INLINE_RESERVED_SIZE 1 static inline int get_extra_isize(struct inode *inode); @@ -1809,6 +1813,15 @@ struct f2fs_sb_info { bool readdir_ra; /* readahead inode in readdir */ unsigned int max_io_bytes; /* max io bytes to merge IOs */ + /* variable summary block units */ + unsigned int sum_blocksize; /* sum block size */ + unsigned int sums_per_block; /* sum block count per block */ + unsigned int entries_in_sum; /* entry count in sum block */ + unsigned int sum_entry_size; /* total entry size in sum block */ + unsigned int sum_journal_size; /* journal size in sum block */ + unsigned int nat_journal_entries; /* nat journal entry count in the journal */ + unsigned int sit_journal_entries; /* sit journal entry count in the journal */ + block_t user_block_count; /* # of user blocks */ block_t total_valid_block_count; /* # of valid blocks */ block_t discard_blks; /* discard command candidats */ @@ -2850,6 +2863,14 @@ static inline block_t __start_sum_addr(struct f2fs_sb_info *sbi) return le32_to_cpu(F2FS_CKPT(sbi)->cp_pack_start_sum); } +static inline bool __has_cursum_space(struct f2fs_sb_info *sbi, + struct f2fs_journal *journal, int size, int type) +{ + if (type == NAT_JOURNAL) + return size <= MAX_NAT_JENTRIES(sbi, journal); + return size <= MAX_SIT_JENTRIES(sbi, journal); +} + extern void f2fs_mark_inode_dirty_sync(struct inode *inode, bool sync); static inline int inc_valid_node_count(struct f2fs_sb_info *sbi, struct inode *inode, bool is_inode) @@ -3993,7 +4014,8 @@ void f2fs_wait_on_block_writeback_range(struct inode *inode, block_t blkaddr, block_t len); void f2fs_write_data_summaries(struct f2fs_sb_info *sbi, block_t start_blk); void f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk); -int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, +int f2fs_lookup_journal_in_cursum(struct f2fs_sb_info *sbi, + struct f2fs_journal *journal, int type, unsigned int val, int alloc); void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc); int f2fs_check_and_fix_write_pointer(struct f2fs_sb_info *sbi); diff --git a/fs/f2fs/gc.c b/fs/f2fs/gc.c index 1538f5b0a644..60378614bc54 100644 --- a/fs/f2fs/gc.c +++ b/fs/f2fs/gc.c @@ -1782,8 +1782,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, sanity_check_seg_type(sbi, get_seg_entry(sbi, segno)->type); - segno = rounddown(segno, SUMS_PER_BLOCK); - sum_blk_cnt = DIV_ROUND_UP(end_segno - segno, SUMS_PER_BLOCK); + segno = rounddown(segno, sbi->sums_per_block); + sum_blk_cnt = DIV_ROUND_UP(end_segno - segno, sbi->sums_per_block); /* readahead multi ssa blocks those have contiguous address */ if (__is_large_section(sbi)) f2fs_ra_meta_pages(sbi, GET_SUM_BLOCK(sbi, segno), @@ -1793,17 +1793,17 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, while (segno < end_segno) { struct folio *sum_folio = f2fs_get_sum_folio(sbi, segno); - segno += SUMS_PER_BLOCK; + segno += sbi->sums_per_block; if (IS_ERR(sum_folio)) { int err = PTR_ERR(sum_folio); - end_segno = segno - SUMS_PER_BLOCK; - segno = rounddown(start_segno, SUMS_PER_BLOCK); + end_segno = segno - sbi->sums_per_block; + segno = rounddown(start_segno, sbi->sums_per_block); while (segno < end_segno) { sum_folio = filemap_get_folio(META_MAPPING(sbi), GET_SUM_BLOCK(sbi, segno)); folio_put_refs(sum_folio, 2); - segno += SUMS_PER_BLOCK; + segno += sbi->sums_per_block; } return err; } @@ -1819,8 +1819,8 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, /* find segment summary of victim */ struct folio *sum_folio = filemap_get_folio(META_MAPPING(sbi), GET_SUM_BLOCK(sbi, segno)); - unsigned int block_end_segno = rounddown(segno, SUMS_PER_BLOCK) - + SUMS_PER_BLOCK; + unsigned int block_end_segno = rounddown(segno, sbi->sums_per_block) + + sbi->sums_per_block; if (block_end_segno > end_segno) block_end_segno = end_segno; @@ -1846,12 +1846,13 @@ static int do_garbage_collect(struct f2fs_sb_info *sbi, migrated >= sbi->migration_granularity) continue; - sum = SUM_BLK_PAGE_ADDR(sum_folio, cur_segno); - if (type != GET_SUM_TYPE((&sum->footer))) { + sum = SUM_BLK_PAGE_ADDR(sbi, sum_folio, cur_segno); + if (type != GET_SUM_TYPE(sum_footer(sbi, sum))) { f2fs_err(sbi, "Inconsistent segment (%u) type " "[%d, %d] in SSA and SIT", cur_segno, type, - GET_SUM_TYPE((&sum->footer))); + GET_SUM_TYPE( + sum_footer(sbi, sum))); f2fs_stop_checkpoint(sbi, false, STOP_CP_REASON_CORRUPTED_SUMMARY); continue; diff --git a/fs/f2fs/node.c b/fs/f2fs/node.c index 99e425e8c00a..00587e783b44 100644 --- a/fs/f2fs/node.c +++ b/fs/f2fs/node.c @@ -606,7 +606,7 @@ retry: goto retry; } - i = f2fs_lookup_journal_in_cursum(journal, NAT_JOURNAL, nid, 0); + i = f2fs_lookup_journal_in_cursum(sbi, journal, NAT_JOURNAL, nid, 0); if (i >= 0) { ne = nat_in_journal(journal, i); node_info_from_raw_nat(ni, &ne); @@ -2955,7 +2955,7 @@ int f2fs_restore_node_summary(struct f2fs_sb_info *sbi, /* scan the node segment */ last_offset = BLKS_PER_SEG(sbi); addr = START_BLOCK(sbi, segno); - sum_entry = &sum->entries[0]; + sum_entry = sum_entries(sum); for (i = 0; i < last_offset; i += nrpages, addr += nrpages) { nrpages = bio_max_segs(last_offset - i); @@ -3096,7 +3096,7 @@ static int __flush_nat_entry_set(struct f2fs_sb_info *sbi, * #2, flush nat entries to nat page. */ if (enabled_nat_bits(sbi, cpc) || - !__has_cursum_space(journal, set->entry_cnt, NAT_JOURNAL)) + !__has_cursum_space(sbi, journal, set->entry_cnt, NAT_JOURNAL)) to_journal = false; if (to_journal) { @@ -3119,7 +3119,7 @@ static int __flush_nat_entry_set(struct f2fs_sb_info *sbi, f2fs_bug_on(sbi, nat_get_blkaddr(ne) == NEW_ADDR); if (to_journal) { - offset = f2fs_lookup_journal_in_cursum(journal, + offset = f2fs_lookup_journal_in_cursum(sbi, journal, NAT_JOURNAL, nid, 1); f2fs_bug_on(sbi, offset < 0); raw_ne = &nat_in_journal(journal, offset); @@ -3190,7 +3190,7 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) * into nat entry set. */ if (enabled_nat_bits(sbi, cpc) || - !__has_cursum_space(journal, + !__has_cursum_space(sbi, journal, nm_i->nat_cnt[DIRTY_NAT], NAT_JOURNAL)) remove_nats_in_journal(sbi); @@ -3201,7 +3201,7 @@ int f2fs_flush_nat_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) set_idx = setvec[found - 1]->set + 1; for (idx = 0; idx < found; idx++) __adjust_nat_entry_set(setvec[idx], &sets, - MAX_NAT_JENTRIES(journal)); + MAX_NAT_JENTRIES(sbi, journal)); } /* flush dirty nats in nat entry set */ diff --git a/fs/f2fs/recovery.c b/fs/f2fs/recovery.c index 39f6e9830a9c..a26071f2b0bc 100644 --- a/fs/f2fs/recovery.c +++ b/fs/f2fs/recovery.c @@ -514,7 +514,7 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, struct curseg_info *curseg = CURSEG_I(sbi, i); if (curseg->segno == segno) { - sum = curseg->sum_blk->entries[blkoff]; + sum = sum_entries(curseg->sum_blk)[blkoff]; goto got_it; } } @@ -522,8 +522,8 @@ static int check_index_in_prev_nodes(struct f2fs_sb_info *sbi, sum_folio = f2fs_get_sum_folio(sbi, segno); if (IS_ERR(sum_folio)) return PTR_ERR(sum_folio); - sum_node = SUM_BLK_PAGE_ADDR(sum_folio, segno); - sum = sum_node->entries[blkoff]; + sum_node = SUM_BLK_PAGE_ADDR(sbi, sum_folio, segno); + sum = sum_entries(sum_node)[blkoff]; f2fs_folio_put(sum_folio, true); got_it: /* Use the locked dnode page and inode */ diff --git a/fs/f2fs/segment.c b/fs/f2fs/segment.c index 74fca1d4b8ed..00870a8fe387 100644 --- a/fs/f2fs/segment.c +++ b/fs/f2fs/segment.c @@ -2674,12 +2674,12 @@ int f2fs_npages_for_summary_flush(struct f2fs_sb_info *sbi, bool for_ra) valid_sum_count += f2fs_curseg_valid_blocks(sbi, i); } - sum_in_page = (PAGE_SIZE - 2 * SUM_JOURNAL_SIZE - + sum_in_page = (sbi->sum_blocksize - 2 * sbi->sum_journal_size - SUM_FOOTER_SIZE) / SUMMARY_SIZE; if (valid_sum_count <= sum_in_page) return 1; else if ((valid_sum_count - sum_in_page) <= - (PAGE_SIZE - SUM_FOOTER_SIZE) / SUMMARY_SIZE) + (sbi->sum_blocksize - SUM_FOOTER_SIZE) / SUMMARY_SIZE) return 2; return 3; } @@ -2699,7 +2699,7 @@ void f2fs_update_meta_page(struct f2fs_sb_info *sbi, { struct folio *folio; - if (SUMS_PER_BLOCK == 1) + if (!f2fs_sb_has_packed_ssa(sbi)) folio = f2fs_grab_meta_folio(sbi, blk_addr); else folio = f2fs_get_meta_folio_retry(sbi, blk_addr); @@ -2717,7 +2717,7 @@ static void write_sum_page(struct f2fs_sb_info *sbi, { struct folio *folio; - if (SUMS_PER_BLOCK == 1) + if (!f2fs_sb_has_packed_ssa(sbi)) return f2fs_update_meta_page(sbi, (void *)sum_blk, GET_SUM_BLOCK(sbi, segno)); @@ -2725,7 +2725,8 @@ static void write_sum_page(struct f2fs_sb_info *sbi, if (IS_ERR(folio)) return; - memcpy(SUM_BLK_PAGE_ADDR(folio, segno), sum_blk, sizeof(*sum_blk)); + memcpy(SUM_BLK_PAGE_ADDR(sbi, folio, segno), sum_blk, + sbi->sum_blocksize); folio_mark_dirty(folio); f2fs_folio_put(folio, true); } @@ -2744,11 +2745,11 @@ static void write_current_sum_page(struct f2fs_sb_info *sbi, mutex_lock(&curseg->curseg_mutex); down_read(&curseg->journal_rwsem); - memcpy(&dst->journal, curseg->journal, SUM_JOURNAL_SIZE); + memcpy(sum_journal(sbi, dst), curseg->journal, sbi->sum_journal_size); up_read(&curseg->journal_rwsem); - memcpy(dst->entries, src->entries, SUM_ENTRY_SIZE); - memcpy(&dst->footer, &src->footer, SUM_FOOTER_SIZE); + memcpy(sum_entries(dst), sum_entries(src), sbi->sum_entry_size); + memcpy(sum_footer(sbi, dst), sum_footer(sbi, src), SUM_FOOTER_SIZE); mutex_unlock(&curseg->curseg_mutex); @@ -2921,7 +2922,7 @@ static void reset_curseg(struct f2fs_sb_info *sbi, int type, int modified) curseg->next_blkoff = 0; curseg->next_segno = NULL_SEGNO; - sum_footer = &(curseg->sum_blk->footer); + sum_footer = sum_footer(sbi, curseg->sum_blk); memset(sum_footer, 0, sizeof(struct summary_footer)); sanity_check_seg_type(sbi, seg_type); @@ -3067,11 +3068,11 @@ static int change_curseg(struct f2fs_sb_info *sbi, int type) sum_folio = f2fs_get_sum_folio(sbi, new_segno); if (IS_ERR(sum_folio)) { /* GC won't be able to use stale summary pages by cp_error */ - memset(curseg->sum_blk, 0, SUM_ENTRY_SIZE); + memset(curseg->sum_blk, 0, sbi->sum_entry_size); return PTR_ERR(sum_folio); } - sum_node = SUM_BLK_PAGE_ADDR(sum_folio, new_segno); - memcpy(curseg->sum_blk, sum_node, SUM_ENTRY_SIZE); + sum_node = SUM_BLK_PAGE_ADDR(sbi, sum_folio, new_segno); + memcpy(curseg->sum_blk, sum_node, sbi->sum_entry_size); f2fs_folio_put(sum_folio, true); return 0; } @@ -3805,7 +3806,7 @@ int f2fs_allocate_data_block(struct f2fs_sb_info *sbi, struct folio *folio, f2fs_wait_discard_bio(sbi, *new_blkaddr); - curseg->sum_blk->entries[curseg->next_blkoff] = *sum; + sum_entries(curseg->sum_blk)[curseg->next_blkoff] = *sum; if (curseg->alloc_type == SSR) { curseg->next_blkoff = f2fs_find_next_ssr_block(sbi, curseg); } else { @@ -4174,7 +4175,7 @@ void f2fs_do_replace_block(struct f2fs_sb_info *sbi, struct f2fs_summary *sum, } curseg->next_blkoff = GET_BLKOFF_FROM_SEG0(sbi, new_blkaddr); - curseg->sum_blk->entries[curseg->next_blkoff] = *sum; + sum_entries(curseg->sum_blk)[curseg->next_blkoff] = *sum; if (!recover_curseg || recover_newaddr) { if (!from_gc) @@ -4294,12 +4295,12 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi) /* Step 1: restore nat cache */ seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); - memcpy(seg_i->journal, kaddr, SUM_JOURNAL_SIZE); + memcpy(seg_i->journal, kaddr, sbi->sum_journal_size); /* Step 2: restore sit cache */ seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(seg_i->journal, kaddr + SUM_JOURNAL_SIZE, SUM_JOURNAL_SIZE); - offset = 2 * SUM_JOURNAL_SIZE; + memcpy(seg_i->journal, kaddr + sbi->sum_journal_size, sbi->sum_journal_size); + offset = 2 * sbi->sum_journal_size; /* Step 3: restore summary entries */ for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { @@ -4321,9 +4322,9 @@ static int read_compacted_summaries(struct f2fs_sb_info *sbi) struct f2fs_summary *s; s = (struct f2fs_summary *)(kaddr + offset); - seg_i->sum_blk->entries[j] = *s; + sum_entries(seg_i->sum_blk)[j] = *s; offset += SUMMARY_SIZE; - if (offset + SUMMARY_SIZE <= PAGE_SIZE - + if (offset + SUMMARY_SIZE <= sbi->sum_blocksize - SUM_FOOTER_SIZE) continue; @@ -4379,7 +4380,7 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) if (IS_NODESEG(type)) { if (__exist_node_summaries(sbi)) { - struct f2fs_summary *ns = &sum->entries[0]; + struct f2fs_summary *ns = sum_entries(sum); int i; for (i = 0; i < BLKS_PER_SEG(sbi); i++, ns++) { @@ -4399,11 +4400,13 @@ static int read_normal_summaries(struct f2fs_sb_info *sbi, int type) /* update journal info */ down_write(&curseg->journal_rwsem); - memcpy(curseg->journal, &sum->journal, SUM_JOURNAL_SIZE); + memcpy(curseg->journal, sum_journal(sbi, sum), sbi->sum_journal_size); up_write(&curseg->journal_rwsem); - memcpy(curseg->sum_blk->entries, sum->entries, SUM_ENTRY_SIZE); - memcpy(&curseg->sum_blk->footer, &sum->footer, SUM_FOOTER_SIZE); + memcpy(sum_entries(curseg->sum_blk), sum_entries(sum), + sbi->sum_entry_size); + memcpy(sum_footer(sbi, curseg->sum_blk), sum_footer(sbi, sum), + SUM_FOOTER_SIZE); curseg->next_segno = segno; reset_curseg(sbi, type, 0); curseg->alloc_type = ckpt->alloc_type[type]; @@ -4447,8 +4450,8 @@ static int restore_curseg_summaries(struct f2fs_sb_info *sbi) } /* sanity check for summary blocks */ - if (nats_in_cursum(nat_j) > NAT_JOURNAL_ENTRIES || - sits_in_cursum(sit_j) > SIT_JOURNAL_ENTRIES) { + if (nats_in_cursum(nat_j) > sbi->nat_journal_entries || + sits_in_cursum(sit_j) > sbi->sit_journal_entries) { f2fs_err(sbi, "invalid journal entries nats %u sits %u", nats_in_cursum(nat_j), sits_in_cursum(sit_j)); return -EINVAL; @@ -4472,13 +4475,13 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) /* Step 1: write nat cache */ seg_i = CURSEG_I(sbi, CURSEG_HOT_DATA); - memcpy(kaddr, seg_i->journal, SUM_JOURNAL_SIZE); - written_size += SUM_JOURNAL_SIZE; + memcpy(kaddr, seg_i->journal, sbi->sum_journal_size); + written_size += sbi->sum_journal_size; /* Step 2: write sit cache */ seg_i = CURSEG_I(sbi, CURSEG_COLD_DATA); - memcpy(kaddr + written_size, seg_i->journal, SUM_JOURNAL_SIZE); - written_size += SUM_JOURNAL_SIZE; + memcpy(kaddr + written_size, seg_i->journal, sbi->sum_journal_size); + written_size += sbi->sum_journal_size; /* Step 3: write summary entries */ for (i = CURSEG_HOT_DATA; i <= CURSEG_COLD_DATA; i++) { @@ -4491,7 +4494,7 @@ static void write_compacted_summaries(struct f2fs_sb_info *sbi, block_t blkaddr) written_size = 0; } summary = (struct f2fs_summary *)(kaddr + written_size); - *summary = seg_i->sum_blk->entries[j]; + *summary = sum_entries(seg_i->sum_blk)[j]; written_size += SUMMARY_SIZE; if (written_size + SUMMARY_SIZE <= PAGE_SIZE - @@ -4536,8 +4539,9 @@ void f2fs_write_node_summaries(struct f2fs_sb_info *sbi, block_t start_blk) write_normal_summaries(sbi, start_blk, CURSEG_HOT_NODE); } -int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, - unsigned int val, int alloc) +int f2fs_lookup_journal_in_cursum(struct f2fs_sb_info *sbi, + struct f2fs_journal *journal, int type, + unsigned int val, int alloc) { int i; @@ -4546,13 +4550,13 @@ int f2fs_lookup_journal_in_cursum(struct f2fs_journal *journal, int type, if (le32_to_cpu(nid_in_journal(journal, i)) == val) return i; } - if (alloc && __has_cursum_space(journal, 1, NAT_JOURNAL)) + if (alloc && __has_cursum_space(sbi, journal, 1, NAT_JOURNAL)) return update_nats_in_cursum(journal, 1); } else if (type == SIT_JOURNAL) { for (i = 0; i < sits_in_cursum(journal); i++) if (le32_to_cpu(segno_in_journal(journal, i)) == val) return i; - if (alloc && __has_cursum_space(journal, 1, SIT_JOURNAL)) + if (alloc && __has_cursum_space(sbi, journal, 1, SIT_JOURNAL)) return update_sits_in_cursum(journal, 1); } return -1; @@ -4700,8 +4704,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) * entries, remove all entries from journal and add and account * them in sit entry set. */ - if (!__has_cursum_space(journal, sit_i->dirty_sentries, SIT_JOURNAL) || - !to_journal) + if (!__has_cursum_space(sbi, journal, + sit_i->dirty_sentries, SIT_JOURNAL) || !to_journal) remove_sits_in_journal(sbi); /* @@ -4718,7 +4722,8 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) unsigned int segno = start_segno; if (to_journal && - !__has_cursum_space(journal, ses->entry_cnt, SIT_JOURNAL)) + !__has_cursum_space(sbi, journal, ses->entry_cnt, + SIT_JOURNAL)) to_journal = false; if (to_journal) { @@ -4746,7 +4751,7 @@ void f2fs_flush_sit_entries(struct f2fs_sb_info *sbi, struct cp_control *cpc) } if (to_journal) { - offset = f2fs_lookup_journal_in_cursum(journal, + offset = f2fs_lookup_journal_in_cursum(sbi, journal, SIT_JOURNAL, segno, 1); f2fs_bug_on(sbi, offset < 0); segno_in_journal(journal, offset) = @@ -4953,12 +4958,13 @@ static int build_curseg(struct f2fs_sb_info *sbi) for (i = 0; i < NO_CHECK_TYPE; i++) { mutex_init(&array[i].curseg_mutex); - array[i].sum_blk = f2fs_kzalloc(sbi, PAGE_SIZE, GFP_KERNEL); + array[i].sum_blk = f2fs_kzalloc(sbi, sbi->sum_blocksize, + GFP_KERNEL); if (!array[i].sum_blk) return -ENOMEM; init_rwsem(&array[i].journal_rwsem); array[i].journal = f2fs_kzalloc(sbi, - sizeof(struct f2fs_journal), GFP_KERNEL); + sbi->sum_journal_size, GFP_KERNEL); if (!array[i].journal) return -ENOMEM; array[i].seg_type = log_type_to_seg_type(i); diff --git a/fs/f2fs/segment.h b/fs/f2fs/segment.h index 20daaccb34a5..068845660b0f 100644 --- a/fs/f2fs/segment.h +++ b/fs/f2fs/segment.h @@ -90,12 +90,11 @@ static inline void sanity_check_seg_type(struct f2fs_sb_info *sbi, #define GET_ZONE_FROM_SEG(sbi, segno) \ GET_ZONE_FROM_SEC(sbi, GET_SEC_FROM_SEG(sbi, segno)) -#define SUMS_PER_BLOCK (F2FS_BLKSIZE / F2FS_SUM_BLKSIZE) #define GET_SUM_BLOCK(sbi, segno) \ - (SM_I(sbi)->ssa_blkaddr + (segno / SUMS_PER_BLOCK)) -#define GET_SUM_BLKOFF(segno) (segno % SUMS_PER_BLOCK) -#define SUM_BLK_PAGE_ADDR(folio, segno) \ - (folio_address(folio) + GET_SUM_BLKOFF(segno) * F2FS_SUM_BLKSIZE) + (SM_I(sbi)->ssa_blkaddr + (segno / (sbi)->sums_per_block)) +#define GET_SUM_BLKOFF(sbi, segno) (segno % (sbi)->sums_per_block) +#define SUM_BLK_PAGE_ADDR(sbi, folio, segno) \ + (folio_address(folio) + GET_SUM_BLKOFF(sbi, segno) * (sbi)->sum_blocksize) #define GET_SUM_TYPE(footer) ((footer)->entry_type) #define SET_SUM_TYPE(footer, type) ((footer)->entry_type = (type)) diff --git a/fs/f2fs/super.c b/fs/f2fs/super.c index 25f796232ad9..1660d663a8c5 100644 --- a/fs/f2fs/super.c +++ b/fs/f2fs/super.c @@ -4104,20 +4104,6 @@ static int sanity_check_raw_super(struct f2fs_sb_info *sbi, if (sanity_check_area_boundary(sbi, folio, index)) return -EFSCORRUPTED; - /* - * Check for legacy summary layout on 16KB+ block devices. - * Modern f2fs-tools packs multiple 4KB summary areas into one block, - * whereas legacy versions used one block per summary, leading - * to a much larger SSA. - */ - if (SUMS_PER_BLOCK > 1 && - !(__F2FS_HAS_FEATURE(raw_super, F2FS_FEATURE_PACKED_SSA))) { - f2fs_info(sbi, "Error: Device formatted with a legacy version. " - "Please reformat with a tool supporting the packed ssa " - "feature for block sizes larger than 4kb."); - return -EOPNOTSUPP; - } - return 0; } @@ -4329,6 +4315,18 @@ static void init_sb_info(struct f2fs_sb_info *sbi) atomic64_set(&sbi->current_atomic_write, 0); sbi->max_lock_elapsed_time = MAX_LOCK_ELAPSED_TIME; + sbi->sum_blocksize = f2fs_sb_has_packed_ssa(sbi) ? + 4096 : sbi->blocksize; + sbi->sums_per_block = sbi->blocksize / sbi->sum_blocksize; + sbi->entries_in_sum = sbi->sum_blocksize / 8; + sbi->sum_entry_size = SUMMARY_SIZE * sbi->entries_in_sum; + sbi->sum_journal_size = sbi->sum_blocksize - SUM_FOOTER_SIZE - + sbi->sum_entry_size; + sbi->nat_journal_entries = (sbi->sum_journal_size - 2) / + sizeof(struct nat_journal_entry); + sbi->sit_journal_entries = (sbi->sum_journal_size - 2) / + sizeof(struct sit_journal_entry); + sbi->dir_level = DEF_DIR_LEVEL; sbi->interval_time[CP_TIME] = DEF_CP_INTERVAL; sbi->interval_time[REQ_TIME] = DEF_IDLE_INTERVAL; diff --git a/include/linux/f2fs_fs.h b/include/linux/f2fs_fs.h index a7880787cad3..dc41722fcc9d 100644 --- a/include/linux/f2fs_fs.h +++ b/include/linux/f2fs_fs.h @@ -17,7 +17,6 @@ #define F2FS_LOG_SECTORS_PER_BLOCK (PAGE_SHIFT - 9) /* log number for sector/blk */ #define F2FS_BLKSIZE PAGE_SIZE /* support only block == page */ #define F2FS_BLKSIZE_BITS PAGE_SHIFT /* bits for F2FS_BLKSIZE */ -#define F2FS_SUM_BLKSIZE 4096 /* only support 4096 byte sum block */ #define F2FS_MAX_EXTENSION 64 /* # of extension entries */ #define F2FS_EXTENSION_LEN 8 /* max size of extension */ @@ -442,10 +441,8 @@ struct f2fs_sit_block { * from node's page's beginning to get a data block address. * ex) data_blkaddr = (block_t)(nodepage_start_address + ofs_in_node) */ -#define ENTRIES_IN_SUM (F2FS_SUM_BLKSIZE / 8) #define SUMMARY_SIZE (7) /* sizeof(struct f2fs_summary) */ #define SUM_FOOTER_SIZE (5) /* sizeof(struct summary_footer) */ -#define SUM_ENTRY_SIZE (SUMMARY_SIZE * ENTRIES_IN_SUM) /* a summary entry for a block in a segment */ struct f2fs_summary { @@ -468,22 +465,6 @@ struct summary_footer { __le32 check_sum; /* summary checksum */ } __packed; -#define SUM_JOURNAL_SIZE (F2FS_SUM_BLKSIZE - SUM_FOOTER_SIZE -\ - SUM_ENTRY_SIZE) -#define NAT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ - sizeof(struct nat_journal_entry)) -#define NAT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ - sizeof(struct nat_journal_entry)) -#define SIT_JOURNAL_ENTRIES ((SUM_JOURNAL_SIZE - 2) /\ - sizeof(struct sit_journal_entry)) -#define SIT_JOURNAL_RESERVED ((SUM_JOURNAL_SIZE - 2) %\ - sizeof(struct sit_journal_entry)) - -/* Reserved area should make size of f2fs_extra_info equals to - * that of nat_journal and sit_journal. - */ -#define EXTRA_INFO_RESERVED (SUM_JOURNAL_SIZE - 2 - 8) - /* * frequently updated NAT/SIT entries can be stored in the spare area in * summary blocks @@ -498,9 +479,16 @@ struct nat_journal_entry { struct f2fs_nat_entry ne; } __packed; +/* + * The nat_journal structure is a placeholder whose actual size varies depending + * on the use of packed_ssa. Therefore, it must always be accessed only through + * specific sets of macros and fields, and size calculations should use + * size-related macros instead of sizeof(). + * Relevant macros: sbi->nat_journal_entries, nat_in_journal(), + * nid_in_journal(), MAX_NAT_JENTRIES(). + */ struct nat_journal { - struct nat_journal_entry entries[NAT_JOURNAL_ENTRIES]; - __u8 reserved[NAT_JOURNAL_RESERVED]; + struct nat_journal_entry entries[0]; } __packed; struct sit_journal_entry { @@ -508,14 +496,21 @@ struct sit_journal_entry { struct f2fs_sit_entry se; } __packed; +/* + * The sit_journal structure is a placeholder whose actual size varies depending + * on the use of packed_ssa. Therefore, it must always be accessed only through + * specific sets of macros and fields, and size calculations should use + * size-related macros instead of sizeof(). + * Relevant macros: sbi->sit_journal_entries, sit_in_journal(), + * segno_in_journal(), MAX_SIT_JENTRIES(). + */ struct sit_journal { - struct sit_journal_entry entries[SIT_JOURNAL_ENTRIES]; - __u8 reserved[SIT_JOURNAL_RESERVED]; + struct sit_journal_entry entries[0]; } __packed; struct f2fs_extra_info { __le64 kbytes_written; - __u8 reserved[EXTRA_INFO_RESERVED]; + __u8 reserved[]; } __packed; struct f2fs_journal { @@ -531,11 +526,33 @@ struct f2fs_journal { }; } __packed; -/* Block-sized summary block structure */ +/* + * Block-sized summary block structure + * + * The f2fs_summary_block structure is a placeholder whose actual size varies + * depending on the use of packed_ssa. Therefore, it must always be accessed + * only through specific sets of macros and fields, and size calculations should + * use size-related macros instead of sizeof(). + * Relevant macros: sbi->sum_blocksize, sbi->entries_in_sum, + * sbi->sum_entry_size, sum_entries(), sum_journal(), sum_footer(). + * + * Summary Block Layout + * + * +-----------------------+ <--- Block Start + * | struct f2fs_summary | + * | entries[0] | + * | ... | + * | entries[N-1] | + * +-----------------------+ + * | struct f2fs_journal | + * +-----------------------+ + * | struct summary_footer | + * +-----------------------+ <--- Block End + */ struct f2fs_summary_block { - struct f2fs_summary entries[ENTRIES_IN_SUM]; - struct f2fs_journal journal; - struct summary_footer footer; + struct f2fs_summary entries[0]; + // struct f2fs_journal journal; + // struct summary_footer footer; } __packed; /* -- cgit v1.2.3 From 439cf01b2e0030eed6020a03a7df4a528f8db7ba Mon Sep 17 00:00:00 2001 From: Kari Argillander Date: Fri, 19 Dec 2025 22:42:38 +0200 Subject: watchdog: Make API functions const correct Many watchdog API functions do not modify the watchdog_device nor device. Mark their arguments as const to reflect this and improve const-correctness of the API. No functional change intended. Signed-off-by: Kari Argillander Reviewed-by: Guenter Roeck Signed-off-by: Guenter Roeck Signed-off-by: Wim Van Sebroeck --- Documentation/watchdog/watchdog-kernel-api.rst | 2 +- drivers/watchdog/watchdog_core.c | 3 ++- include/linux/watchdog.h | 12 +++++++----- 3 files changed, 10 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/Documentation/watchdog/watchdog-kernel-api.rst b/Documentation/watchdog/watchdog-kernel-api.rst index 243231fe4c0a..5649c54cf6fb 100644 --- a/Documentation/watchdog/watchdog-kernel-api.rst +++ b/Documentation/watchdog/watchdog-kernel-api.rst @@ -293,7 +293,7 @@ To initialize the timeout field, the following function can be used:: extern int watchdog_init_timeout(struct watchdog_device *wdd, unsigned int timeout_parm, - struct device *dev); + const struct device *dev); The watchdog_init_timeout function allows you to initialize the timeout field using the module timeout parameter or by retrieving the timeout-sec property from diff --git a/drivers/watchdog/watchdog_core.c b/drivers/watchdog/watchdog_core.c index 6152dba4b52c..8300520688d0 100644 --- a/drivers/watchdog/watchdog_core.c +++ b/drivers/watchdog/watchdog_core.c @@ -117,7 +117,8 @@ static void watchdog_check_min_max_timeout(struct watchdog_device *wdd) * bounds. */ int watchdog_init_timeout(struct watchdog_device *wdd, - unsigned int timeout_parm, struct device *dev) + unsigned int timeout_parm, + const struct device *dev) { const char *dev_str = wdd->parent ? dev_name(wdd->parent) : (const char *)wdd->info->identity; diff --git a/include/linux/watchdog.h b/include/linux/watchdog.h index 8c60687a3e55..62cdd26fd025 100644 --- a/include/linux/watchdog.h +++ b/include/linux/watchdog.h @@ -129,7 +129,7 @@ struct watchdog_device { #define WATCHDOG_NOWAYOUT_INIT_STATUS (WATCHDOG_NOWAYOUT << WDOG_NO_WAY_OUT) /* Use the following function to check whether or not the watchdog is active */ -static inline bool watchdog_active(struct watchdog_device *wdd) +static inline bool watchdog_active(const struct watchdog_device *wdd) { return test_bit(WDOG_ACTIVE, &wdd->status); } @@ -138,7 +138,7 @@ static inline bool watchdog_active(struct watchdog_device *wdd) * Use the following function to check whether or not the hardware watchdog * is running */ -static inline bool watchdog_hw_running(struct watchdog_device *wdd) +static inline bool watchdog_hw_running(const struct watchdog_device *wdd) { return test_bit(WDOG_HW_RUNNING, &wdd->status); } @@ -169,7 +169,8 @@ static inline void watchdog_stop_ping_on_suspend(struct watchdog_device *wdd) } /* Use the following function to check if a timeout value is invalid */ -static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigned int t) +static inline bool watchdog_timeout_invalid(const struct watchdog_device *wdd, + unsigned int t) { /* * The timeout is invalid if @@ -188,7 +189,7 @@ static inline bool watchdog_timeout_invalid(struct watchdog_device *wdd, unsigne } /* Use the following function to check if a pretimeout value is invalid */ -static inline bool watchdog_pretimeout_invalid(struct watchdog_device *wdd, +static inline bool watchdog_pretimeout_invalid(const struct watchdog_device *wdd, unsigned int t) { return t && wdd->timeout && t >= wdd->timeout; @@ -218,7 +219,8 @@ static inline void watchdog_notify_pretimeout(struct watchdog_device *wdd) /* drivers/watchdog/watchdog_core.c */ void watchdog_set_restart_priority(struct watchdog_device *wdd, int priority); extern int watchdog_init_timeout(struct watchdog_device *wdd, - unsigned int timeout_parm, struct device *dev); + unsigned int timeout_parm, + const struct device *dev); extern int watchdog_register_device(struct watchdog_device *); extern void watchdog_unregister_device(struct watchdog_device *); int watchdog_dev_suspend(struct watchdog_device *wdd); -- cgit v1.2.3 From b562abd956726f57bb78813b4c77db51d28933a9 Mon Sep 17 00:00:00 2001 From: Jjian Zhou Date: Mon, 13 Oct 2025 14:31:36 +0800 Subject: mailbox: mediatek: Add mtk-vcp-mailbox driver Add mtk-vcp-mailbox driver to support the communication with VCP remote microprocessor. Signed-off-by: Jjian Zhou Reviewed-by: Chen-Yu Tsai Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Jassi Brar --- drivers/mailbox/Kconfig | 9 ++ drivers/mailbox/Makefile | 2 + drivers/mailbox/mtk-vcp-mailbox.c | 170 ++++++++++++++++++++++++++++++++ include/linux/mailbox/mtk-vcp-mailbox.h | 32 ++++++ 4 files changed, 213 insertions(+) create mode 100644 drivers/mailbox/mtk-vcp-mailbox.c create mode 100644 include/linux/mailbox/mtk-vcp-mailbox.h (limited to 'include/linux') diff --git a/drivers/mailbox/Kconfig b/drivers/mailbox/Kconfig index 29f16f220384..d7f8db25f3b3 100644 --- a/drivers/mailbox/Kconfig +++ b/drivers/mailbox/Kconfig @@ -304,6 +304,15 @@ config MTK_GPUEB_MBOX Say Y or m here if you want to support the MT8196 SoC in your kernel build. +config MTK_VCP_MBOX + tristate "MediaTek VCP Mailbox Support" + depends on ARCH_MEDIATEK || COMPILE_TEST + help + Say yes here to add support for the MediaTek VCP mailbox driver. + The mailbox implementation provides access from the application + processor to Video Companion Processor Unit. + If unsure say N. + config ZYNQMP_IPI_MBOX tristate "Xilinx ZynqMP IPI Mailbox" depends on ARCH_ZYNQMP && OF diff --git a/drivers/mailbox/Makefile b/drivers/mailbox/Makefile index 81820a4f5528..944d8ea39f34 100644 --- a/drivers/mailbox/Makefile +++ b/drivers/mailbox/Makefile @@ -65,6 +65,8 @@ obj-$(CONFIG_MTK_CMDQ_MBOX) += mtk-cmdq-mailbox.o obj-$(CONFIG_MTK_GPUEB_MBOX) += mtk-gpueb-mailbox.o +obj-$(CONFIG_MTK_VCP_MBOX) += mtk-vcp-mailbox.o + obj-$(CONFIG_ZYNQMP_IPI_MBOX) += zynqmp-ipi-mailbox.o obj-$(CONFIG_SUN6I_MSGBOX) += sun6i-msgbox.o diff --git a/drivers/mailbox/mtk-vcp-mailbox.c b/drivers/mailbox/mtk-vcp-mailbox.c new file mode 100644 index 000000000000..cedad575528f --- /dev/null +++ b/drivers/mailbox/mtk-vcp-mailbox.c @@ -0,0 +1,170 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025 MediaTek Corporation. All rights reserved. + * Author: Jjian Zhou + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +struct mtk_vcp_mbox { + struct mbox_controller mbox; + void __iomem *base; + struct device *dev; + const struct mtk_vcp_mbox_cfg *cfg; + struct mtk_ipi_info ipi_recv; + struct mbox_chan chans; +}; + +struct mtk_vcp_mbox_cfg { + u16 set_in; + u16 clr_out; +}; + +static irqreturn_t mtk_vcp_mbox_irq_thread(int irq, void *data) +{ + struct mtk_vcp_mbox *priv = data; + + /* get irq status */ + priv->ipi_recv.irq_status = readl(priv->base + priv->cfg->clr_out); + + __ioread32_copy(priv->ipi_recv.msg, priv->base, + MTK_VCP_MBOX_SLOT_MAX_SIZE / 4); + + mbox_chan_received_data(&priv->chans, &priv->ipi_recv); + + /* clear irq status */ + writel(priv->ipi_recv.irq_status, priv->base + priv->cfg->clr_out); + + return IRQ_HANDLED; +} + +static struct mbox_chan *mtk_vcp_mbox_xlate(struct mbox_controller *mbox, + const struct of_phandle_args *sp) +{ + if (sp->args_count) + return NULL; + + return &mbox->chans[0]; +} + +static int mtk_vcp_mbox_send_data(struct mbox_chan *chan, void *data) +{ + struct mtk_vcp_mbox *priv = chan->con_priv; + struct mtk_ipi_info *ipi_info = data; + u32 status; + + if (!ipi_info->msg) { + dev_err(priv->dev, "msg buffer is NULL.\n"); + return -EINVAL; + } + + status = readl(priv->base + priv->cfg->set_in); + if (status & BIT(ipi_info->index)) { + dev_warn(priv->dev, "mailbox IPI %d is busy.\n", ipi_info->id); + return -EBUSY; + } + + if (ipi_info->slot_ofs + ipi_info->len > MTK_VCP_MBOX_SLOT_MAX_SIZE) + return -EINVAL; + __iowrite32_copy(priv->base + ipi_info->slot_ofs, ipi_info->msg, + ipi_info->len); + + writel(BIT(ipi_info->index), priv->base + priv->cfg->set_in); + + return 0; +} + +static bool mtk_vcp_mbox_last_tx_done(struct mbox_chan *chan) +{ + struct mtk_ipi_info *ipi_info = chan->active_req; + struct mtk_vcp_mbox *priv = chan->con_priv; + + return !(readl(priv->base + priv->cfg->set_in) & BIT(ipi_info->index)); +} + +static const struct mbox_chan_ops mtk_vcp_mbox_chan_ops = { + .send_data = mtk_vcp_mbox_send_data, + .last_tx_done = mtk_vcp_mbox_last_tx_done, +}; + +static int mtk_vcp_mbox_probe(struct platform_device *pdev) +{ + struct device *dev = &pdev->dev; + struct mtk_vcp_mbox *priv; + struct mbox_controller *mbox; + int ret, irq; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + priv->dev = dev; + priv->chans.con_priv = priv; + mbox = &priv->mbox; + mbox->dev = dev; + mbox->ops = &mtk_vcp_mbox_chan_ops; + mbox->txdone_irq = false; + mbox->txdone_poll = true; + mbox->of_xlate = mtk_vcp_mbox_xlate; + mbox->num_chans = 1; + mbox->chans = &priv->chans; + + priv->ipi_recv.msg = devm_kzalloc(dev, MTK_VCP_MBOX_SLOT_MAX_SIZE, + GFP_KERNEL); + if (!priv->ipi_recv.msg) + return -ENOMEM; + + priv->base = devm_platform_ioremap_resource(pdev, 0); + if (IS_ERR(priv->base)) + return PTR_ERR(priv->base); + + priv->cfg = of_device_get_match_data(dev); + if (!priv->cfg) + return -EINVAL; + + platform_set_drvdata(pdev, priv); + + irq = platform_get_irq(pdev, 0); + if (irq < 0) + return irq; + + ret = devm_request_threaded_irq(dev, irq, NULL, + mtk_vcp_mbox_irq_thread, IRQF_ONESHOT, + dev_name(dev), priv); + if (ret < 0) + return ret; + + return devm_mbox_controller_register(dev, &priv->mbox); +} + +static const struct mtk_vcp_mbox_cfg mt8196_cfg = { + .set_in = 0x100, + .clr_out = 0x10c, +}; + +static const struct of_device_id mtk_vcp_mbox_of_match[] = { + { .compatible = "mediatek,mt8196-vcp-mbox", .data = &mt8196_cfg }, + {}, +}; +MODULE_DEVICE_TABLE(of, mtk_vcp_mbox_of_match); + +static struct platform_driver mtk_vcp_mbox_driver = { + .probe = mtk_vcp_mbox_probe, + .driver = { + .name = "mtk_vcp_mbox", + .of_match_table = mtk_vcp_mbox_of_match, + }, +}; +module_platform_driver(mtk_vcp_mbox_driver); + +MODULE_AUTHOR("Jjian Zhou "); +MODULE_DESCRIPTION("MTK VCP Mailbox Controller"); +MODULE_LICENSE("GPL"); diff --git a/include/linux/mailbox/mtk-vcp-mailbox.h b/include/linux/mailbox/mtk-vcp-mailbox.h new file mode 100644 index 000000000000..16e59d6780a7 --- /dev/null +++ b/include/linux/mailbox/mtk-vcp-mailbox.h @@ -0,0 +1,32 @@ +/* SPDX-License-Identifier: (GPL-2.0 OR MIT) */ +/* + * Copyright (c) 2025 MediaTek Inc. + */ + +#ifndef __MTK_VCP_MAILBOX_H__ +#define __MTK_VCP_MAILBOX_H__ + +#define MTK_VCP_MBOX_SLOT_MAX_SIZE 0x100 /* mbox max slot size */ + +/** + * struct mtk_ipi_info - mailbox message info for mtk-vcp-mailbox + * @msg: The share buffer between IPC and mailbox driver + * @len: Message length + * @id: This is for identification purposes and not actually used + * by the mailbox hardware. + * @index: The signal number of the mailbox message. + * @slot_ofs: Data slot offset. + * @irq_status: Captures incoming signals for the RX path. + * + * It is used between IPC with mailbox driver. + */ +struct mtk_ipi_info { + void *msg; + u32 len; + u32 id; + u32 index; + u32 slot_ofs; + u32 irq_status; +}; + +#endif -- cgit v1.2.3 From 7d834d5ce5cb780b926ea0484a80b47bafbb3b64 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:30 +0800 Subject: mailbox: mtk-cmdq: Add cmdq private data to cmdq_pkt for generating instruction Add the cmdq_mbox_priv structure to store the private data of GCE, such as the shift bits of the physical address. Then, include the cmdq_mbox_priv structure within the cmdq_pkt structure. This allows CMDQ users to utilize the private data in cmdq_pkt to generate GCE instructions when needed. Additionally, having cmdq_mbox_priv makes it easier to expand and reference other GCE private data in the future. Add cmdq_get_mbox_priv() for CMDQ users to get all the private data into the cmdq_mbox_priv of the cmdq_pkt. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Jassi Brar --- drivers/mailbox/mtk-cmdq-mailbox.c | 8 ++++++++ include/linux/mailbox/mtk-cmdq-mailbox.h | 18 ++++++++++++++++++ 2 files changed, 26 insertions(+) (limited to 'include/linux') diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index 5791f80f995a..95e8a5331b7c 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -104,6 +104,14 @@ static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *p return (dma_addr_t)addr << pdata->shift; } +void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv) +{ + struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); + + priv->shift_pa = cmdq->pdata->shift; +} +EXPORT_SYMBOL(cmdq_get_mbox_priv); + u8 cmdq_get_shift_pa(struct mbox_chan *chan) { struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h index e1555e06e7e5..73b70be4a8a7 100644 --- a/include/linux/mailbox/mtk-cmdq-mailbox.h +++ b/include/linux/mailbox/mtk-cmdq-mailbox.h @@ -70,13 +70,31 @@ struct cmdq_cb_data { struct cmdq_pkt *pkt; }; +struct cmdq_mbox_priv { + u8 shift_pa; +}; + struct cmdq_pkt { void *va_base; dma_addr_t pa_base; size_t cmd_buf_size; /* command occupied size */ size_t buf_size; /* real buffer size */ + struct cmdq_mbox_priv priv; /* for generating instruction */ }; +/** + * cmdq_get_mbox_priv() - get the private data of mailbox channel + * @chan: mailbox channel + * @priv: pointer to store the private data of mailbox channel + * + * While generating the GCE instruction to command buffer, the private data + * of GCE hardware may need to be referenced, such as the shift bits of + * physical address. + * + * This function should be called before generating the GCE instruction. + */ +void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv); + /** * cmdq_get_shift_pa() - get the shift bits of physical address * @chan: mailbox channel -- cgit v1.2.3 From ddb5d0c941c64b51b003f62df3596682a6591c35 Mon Sep 17 00:00:00 2001 From: Jason-JH Lin Date: Fri, 31 Oct 2025 23:56:32 +0800 Subject: mailbox: mtk-cmdq: Add mminfra_offset configuration for DRAM transaction The GCE in MT8196 is placed in MMINFRA and requires all addresses in GCE instructions for DRAM transactions to be IOVA. Due to MMIO, if the GCE needs to access a hardware register at 0x1000_0000, but the SMMU is also mapping a DRAM block at 0x1000_0000, the MMINFRA will not know whether to write to the hardware register or the DRAM. To solve this, MMINFRA treats addresses greater than 2G as data paths and those less than 2G as config paths because the DRAM start address is currently at 2G (0x8000_0000). On the data path, MMINFRA remaps DRAM addresses by subtracting 2G, allowing SMMU to map DRAM addresses less than 2G. For example, if the DRAM start address 0x8000_0000 is mapped to IOVA=0x0, when GCE accesses IOVA=0x0, it must add a 2G offset to the address in the GCE instruction. MMINFRA will then see it as a data path (IOVA >= 2G) and subtract 2G, allowing GCE to access IOVA=0x0. Since the MMINFRA remap subtracting 2G is done in hardware and cannot be configured by software, the address of DRAM in GCE instruction must always add 2G to ensure proper access. After that, the shift functions do more than just shift addresses, so the APIs were renamed to cmdq_convert_gce_addr() and cmdq_revert_gce_addr(). This 2G adjustment is referred to as mminfra_offset in the CMDQ driver. CMDQ helper can get the mminfra_offset from the cmdq_mbox_priv of cmdq_pkt and add the mminfra_offset to the DRAM address in GCE instructions. Signed-off-by: Jason-JH Lin Reviewed-by: AngeloGioacchino Del Regno Signed-off-by: Jassi Brar --- drivers/mailbox/mtk-cmdq-mailbox.c | 6 ++++-- include/linux/mailbox/mtk-cmdq-mailbox.h | 1 + 2 files changed, 5 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/mailbox/mtk-cmdq-mailbox.c b/drivers/mailbox/mtk-cmdq-mailbox.c index a544108ddae7..a9c06e4bbad4 100644 --- a/drivers/mailbox/mtk-cmdq-mailbox.c +++ b/drivers/mailbox/mtk-cmdq-mailbox.c @@ -94,6 +94,7 @@ struct cmdq { struct gce_plat { u32 thread_nr; u8 shift; + dma_addr_t mminfra_offset; bool control_by_sw; bool sw_ddr_en; bool gce_vm; @@ -103,13 +104,13 @@ struct gce_plat { static inline u32 cmdq_convert_gce_addr(dma_addr_t addr, const struct gce_plat *pdata) { /* Convert DMA addr (PA or IOVA) to GCE readable addr */ - return addr >> pdata->shift; + return (addr + pdata->mminfra_offset) >> pdata->shift; } static inline dma_addr_t cmdq_revert_gce_addr(u32 addr, const struct gce_plat *pdata) { /* Revert GCE readable addr to DMA addr (PA or IOVA) */ - return (dma_addr_t)addr << pdata->shift; + return ((dma_addr_t)addr << pdata->shift) - pdata->mminfra_offset; } void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv) @@ -117,6 +118,7 @@ void cmdq_get_mbox_priv(struct mbox_chan *chan, struct cmdq_mbox_priv *priv) struct cmdq *cmdq = container_of(chan->mbox, struct cmdq, mbox); priv->shift_pa = cmdq->pdata->shift; + priv->mminfra_offset = cmdq->pdata->mminfra_offset; } EXPORT_SYMBOL(cmdq_get_mbox_priv); diff --git a/include/linux/mailbox/mtk-cmdq-mailbox.h b/include/linux/mailbox/mtk-cmdq-mailbox.h index 73b70be4a8a7..07c1bfbdb8c4 100644 --- a/include/linux/mailbox/mtk-cmdq-mailbox.h +++ b/include/linux/mailbox/mtk-cmdq-mailbox.h @@ -72,6 +72,7 @@ struct cmdq_cb_data { struct cmdq_mbox_priv { u8 shift_pa; + dma_addr_t mminfra_offset; }; struct cmdq_pkt { -- cgit v1.2.3 From eb9eb4db98d68c680900d48ee672b92c59ffee71 Mon Sep 17 00:00:00 2001 From: Bartosz Golaszewski Date: Thu, 8 Jan 2026 13:52:37 +0100 Subject: driver core: make pinctrl_bind_pins() private pinctrl_bind_pins() is only used by driver core (as it should). Move it out of the public header into base.h. Signed-off-by: Bartosz Golaszewski Reviewed-by: Greg Kroah-Hartman Acked-by: Danilo Krummrich Signed-off-by: Linus Walleij --- drivers/base/base.h | 9 +++++++++ drivers/base/pinctrl.c | 2 ++ include/linux/pinctrl/devinfo.h | 6 ------ 3 files changed, 11 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/base/base.h b/drivers/base/base.h index 430cbefbc97f..aafc0adbe77d 100644 --- a/drivers/base/base.h +++ b/drivers/base/base.h @@ -289,3 +289,12 @@ static inline int devtmpfs_delete_node(struct device *dev) { return 0; } void software_node_notify(struct device *dev); void software_node_notify_remove(struct device *dev); + +#ifdef CONFIG_PINCTRL +int pinctrl_bind_pins(struct device *dev); +#else +static inline int pinctrl_bind_pins(struct device *dev) +{ + return 0; +} +#endif /* CONFIG_PINCTRL */ diff --git a/drivers/base/pinctrl.c b/drivers/base/pinctrl.c index c22864458511..6e250272c843 100644 --- a/drivers/base/pinctrl.c +++ b/drivers/base/pinctrl.c @@ -14,6 +14,8 @@ #include #include +#include "base.h" + /** * pinctrl_bind_pins() - called by the device core before probe * @dev: the device that is just about to probe diff --git a/include/linux/pinctrl/devinfo.h b/include/linux/pinctrl/devinfo.h index bb6653af4f92..de4228eea90a 100644 --- a/include/linux/pinctrl/devinfo.h +++ b/include/linux/pinctrl/devinfo.h @@ -43,7 +43,6 @@ struct dev_pin_info { #endif }; -extern int pinctrl_bind_pins(struct device *dev); extern int pinctrl_init_done(struct device *dev); static inline struct pinctrl *dev_pinctrl(struct device *dev) @@ -58,11 +57,6 @@ static inline struct pinctrl *dev_pinctrl(struct device *dev) /* Stubs if we're not using pinctrl */ -static inline int pinctrl_bind_pins(struct device *dev) -{ - return 0; -} - static inline int pinctrl_init_done(struct device *dev) { return 0; -- cgit v1.2.3 From 015b70a6ae697f5dac3562e4ab45ee275d98860b Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 16 Jan 2026 21:41:08 +0100 Subject: platform/wmi: Introduce marshalling support MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The Windows WMI-ACPI driver likely uses wmilib [1] to interact with the WMI service in userspace. Said library uses plain byte buffers for exchanging data, so the WMI-ACPI driver has to convert between those byte buffers and ACPI objects returned by the ACPI firmware. The format of the byte buffer is publicly documented [2], and after some reverse eingineering of the WMI-ACPI driver using a set of custom ACPI tables, the following conversion rules have been discovered: - ACPI integers are always converted into a uint32 - ACPI strings are converted into special WMI strings - ACPI buffers are copied as-is - ACPI packages are unpacked Extend the ACPI-WMI driver to also perform this kind of marshalling for WMI data blocks, methods and events. Doing so gives us a number of benefits: - WMI drivers are not restricted to a fixed set of supported ACPI data types anymore, see dell-wmi-aio (integer vs buffer) and hp-wmi-sensors (string vs buffer) - correct marshalling of WMI strings when data blocks are marked as requiring ACPI strings instead of ACPI buffers - development of WMI drivers without having to understand ACPI This eventually should result in better compatibility with some ACPI firmware implementations and in simpler WMI drivers. There are however some differences between the original Windows driver and the ACPI-WMI driver when it comes to ACPI object conversions: - the Windows driver copies internal _ACPI_METHOD_ARGUMENT_V1 data structures into the output buffer when encountering nested ACPI packages. This is very likely an error inside the driver itself, so we do not support nested ACPI packages. - when converting WMI strings (UTF-16LE) into ACPI strings (ASCII), the Windows driver replaces non-ascii characters (ä -> a, & -> ?) instead of returning an error. This behavior is not documented anywhere and might lead to severe errors in some cases (like setting BIOS passwords over WMI), so we simply return an error. As the current bus-based WMI API is based on ACPI buffers, a new API is necessary. The legacy GUID-based WMI API is not extended to support marshalling, as WMI drivers using said API are expected to move to the bus-based WMI API in the future. [1] https://learn.microsoft.com/de-de/windows-hardware/drivers/ddi/wmilib/ [2] https://learn.microsoft.com/en-us/windows-hardware/drivers/kernel/ driver-defined-wmi-data-items Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260116204116.4030-2-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/wmi/Makefile | 2 +- drivers/platform/wmi/core.c | 160 +++++++++++++++++++++++- drivers/platform/wmi/internal.h | 17 +++ drivers/platform/wmi/marshalling.c | 247 +++++++++++++++++++++++++++++++++++++ include/linux/wmi.h | 40 +++++- 5 files changed, 459 insertions(+), 7 deletions(-) create mode 100644 drivers/platform/wmi/internal.h create mode 100644 drivers/platform/wmi/marshalling.c (limited to 'include/linux') diff --git a/drivers/platform/wmi/Makefile b/drivers/platform/wmi/Makefile index 98393d7391ec..6f2bf8cc709e 100644 --- a/drivers/platform/wmi/Makefile +++ b/drivers/platform/wmi/Makefile @@ -4,5 +4,5 @@ # ACPI WMI core # -wmi-y := core.o +wmi-y := core.o marshalling.o obj-$(CONFIG_ACPI_WMI) += wmi.o diff --git a/drivers/platform/wmi/core.c b/drivers/platform/wmi/core.c index 6878c4fcb0b5..1601bf9fe135 100644 --- a/drivers/platform/wmi/core.c +++ b/drivers/platform/wmi/core.c @@ -23,6 +23,7 @@ #include #include #include +#include #include #include #include @@ -33,6 +34,8 @@ #include #include +#include "internal.h" + MODULE_AUTHOR("Carlos Corbacho"); MODULE_DESCRIPTION("ACPI-WMI Mapping Driver"); MODULE_LICENSE("GPL"); @@ -302,7 +305,7 @@ acpi_status wmi_evaluate_method(const char *guid_string, u8 instance, u32 method EXPORT_SYMBOL_GPL(wmi_evaluate_method); /** - * wmidev_evaluate_method - Evaluate a WMI method + * wmidev_evaluate_method - Evaluate a WMI method (deprecated) * @wdev: A wmi bus device from a driver * @instance: Instance index * @method_id: Method ID to call @@ -360,6 +363,70 @@ acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 met } EXPORT_SYMBOL_GPL(wmidev_evaluate_method); +/** + * wmidev_invoke_method - Invoke a WMI method + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * @method_id: Method ID to call + * @in: Mandatory WMI buffer containing input for the method call + * @out: Optional WMI buffer to return the method results + * + * Invoke a WMI method, the caller must free the resulting data inside @out. + * Said data is guaranteed to be aligned on a 8-byte boundary. + * + * Return: 0 on success or negative error code on failure. + */ +int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, + const struct wmi_buffer *in, struct wmi_buffer *out) +{ + struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); + struct acpi_buffer aout = { ACPI_ALLOCATE_BUFFER, NULL }; + struct acpi_buffer ain; + union acpi_object *obj; + acpi_status status; + int ret; + + if (wblock->gblock.flags & ACPI_WMI_STRING) { + ret = wmi_marshal_string(in, &ain); + if (ret < 0) + return ret; + } else { + if (in->length > U32_MAX) + return -E2BIG; + + ain.length = in->length; + ain.pointer = in->data; + } + + if (out) + status = wmidev_evaluate_method(wdev, instance, method_id, &ain, &aout); + else + status = wmidev_evaluate_method(wdev, instance, method_id, &ain, NULL); + + if (wblock->gblock.flags & ACPI_WMI_STRING) + kfree(ain.pointer); + + if (ACPI_FAILURE(status)) + return -EIO; + + if (!out) + return 0; + + obj = aout.pointer; + if (!obj) { + out->length = 0; + out->data = ZERO_SIZE_PTR; + + return 0; + } + + ret = wmi_unmarshal_acpi_object(obj, out); + kfree(obj); + + return ret; +} +EXPORT_SYMBOL_GPL(wmidev_invoke_method); + static acpi_status __query_block(struct wmi_block *wblock, u8 instance, struct acpi_buffer *out) { @@ -432,7 +499,7 @@ acpi_status wmi_query_block(const char *guid_string, u8 instance, EXPORT_SYMBOL_GPL(wmi_query_block); /** - * wmidev_block_query - Return contents of a WMI block + * wmidev_block_query - Return contents of a WMI block (deprectated) * @wdev: A wmi bus device from a driver * @instance: Instance index * @@ -452,6 +519,33 @@ union acpi_object *wmidev_block_query(struct wmi_device *wdev, u8 instance) } EXPORT_SYMBOL_GPL(wmidev_block_query); +/** + * wmidev_query_block - Return contents of a WMI data block + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * @out: WMI buffer to fill + * + * Query a WMI data block, the caller must free the resulting data inside @out. + * Said data is guaranteed to be aligned on a 8-byte boundary. + * + * Return: 0 on success or a negative error code on failure. + */ +int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out) +{ + union acpi_object *obj; + int ret; + + obj = wmidev_block_query(wdev, instance); + if (!obj) + return -EIO; + + ret = wmi_unmarshal_acpi_object(obj, out); + kfree(obj); + + return ret; +} +EXPORT_SYMBOL_GPL(wmidev_query_block); + /** * wmi_set_block - Write to a WMI block (deprecated) * @guid_string: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -486,7 +580,7 @@ acpi_status wmi_set_block(const char *guid_string, u8 instance, const struct acp EXPORT_SYMBOL_GPL(wmi_set_block); /** - * wmidev_block_set - Write to a WMI block + * wmidev_block_set - Write to a WMI block (deprecated) * @wdev: A wmi bus device from a driver * @instance: Instance index * @in: Buffer containing new values for the data block @@ -535,6 +629,46 @@ acpi_status wmidev_block_set(struct wmi_device *wdev, u8 instance, const struct } EXPORT_SYMBOL_GPL(wmidev_block_set); +/** + * wmidev_set_block - Write to a WMI data block + * @wdev: A wmi bus device from a driver + * @instance: Instance index + * @in: WMI buffer containing new values for the data block + * + * Write the content of @in into a WMI data block. + * + * Return: 0 on success or negative error code on failure. + */ +int wmidev_set_block(struct wmi_device *wdev, u8 instance, const struct wmi_buffer *in) +{ + struct wmi_block *wblock = container_of(wdev, struct wmi_block, dev); + struct acpi_buffer buffer; + acpi_status status; + int ret; + + if (wblock->gblock.flags & ACPI_WMI_STRING) { + ret = wmi_marshal_string(in, &buffer); + if (ret < 0) + return ret; + } else { + if (in->length > U32_MAX) + return -E2BIG; + + buffer.length = in->length; + buffer.pointer = in->data; + } + + status = wmidev_block_set(wdev, instance, &buffer); + if (wblock->gblock.flags & ACPI_WMI_STRING) + kfree(buffer.pointer); + + if (ACPI_FAILURE(status)) + return -EIO; + + return 0; +} +EXPORT_SYMBOL_GPL(wmidev_set_block); + /** * wmi_install_notify_handler - Register handler for WMI events (deprecated) * @guid: 36 char string of the form fa50ff2b-f2e8-45de-83fa-65417f2f49ba @@ -862,7 +996,7 @@ static int wmi_dev_probe(struct device *dev) return -ENODEV; } - if (wdriver->notify) { + if (wdriver->notify || wdriver->notify_new) { if (test_bit(WMI_NO_EVENT_DATA, &wblock->flags) && !wdriver->no_notify_data) return -ENODEV; } @@ -1221,6 +1355,8 @@ static int wmi_get_notify_data(struct wmi_block *wblock, union acpi_object **obj static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) { struct wmi_driver *driver = to_wmi_driver(wblock->dev.dev.driver); + struct wmi_buffer buffer; + int ret; if (!obj && !driver->no_notify_data) { dev_warn(&wblock->dev.dev, "Event contains no event data\n"); @@ -1229,6 +1365,22 @@ static void wmi_notify_driver(struct wmi_block *wblock, union acpi_object *obj) if (driver->notify) driver->notify(&wblock->dev, obj); + + if (driver->notify_new) { + if (!obj) { + driver->notify_new(&wblock->dev, NULL); + return; + } + + ret = wmi_unmarshal_acpi_object(obj, &buffer); + if (ret < 0) { + dev_warn(&wblock->dev.dev, "Failed to unmarshal event data: %d\n", ret); + return; + } + + driver->notify_new(&wblock->dev, &buffer); + kfree(buffer.data); + } } static int wmi_notify_device(struct device *dev, void *data) diff --git a/drivers/platform/wmi/internal.h b/drivers/platform/wmi/internal.h new file mode 100644 index 000000000000..9a39ffa31ad1 --- /dev/null +++ b/drivers/platform/wmi/internal.h @@ -0,0 +1,17 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * Internal interfaces used by the WMI core. + * + * Copyright (C) 2025 Armin Wolf + */ + +#ifndef _WMI_INTERNAL_H_ +#define _WMI_INTERNAL_H_ + +union acpi_object; +struct wmi_buffer; + +int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer); +int wmi_marshal_string(const struct wmi_buffer *buffer, struct acpi_buffer *out); + +#endif /* _WMI_INTERNAL_H_ */ diff --git a/drivers/platform/wmi/marshalling.c b/drivers/platform/wmi/marshalling.c new file mode 100644 index 000000000000..63a92c4ebab5 --- /dev/null +++ b/drivers/platform/wmi/marshalling.c @@ -0,0 +1,247 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * ACPI-WMI buffer marshalling. + * + * Copyright (C) 2025 Armin Wolf + */ + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include "internal.h" + +static int wmi_adjust_buffer_length(size_t *length, const union acpi_object *obj) +{ + size_t alignment, size; + + switch (obj->type) { + case ACPI_TYPE_INTEGER: + /* + * Integers are threated as 32 bit even if the ACPI DSDT + * declares 64 bit integer width. + */ + alignment = 4; + size = sizeof(u32); + break; + case ACPI_TYPE_STRING: + /* + * Strings begin with a single little-endian 16-bit field containing + * the string length in bytes and are encoded as UTF-16LE with a terminating + * nul character. + */ + if (obj->string.length + 1 > U16_MAX / 2) + return -EOVERFLOW; + + alignment = 2; + size = struct_size_t(struct wmi_string, chars, obj->string.length + 1); + break; + case ACPI_TYPE_BUFFER: + /* + * Buffers are copied as-is. + */ + alignment = 1; + size = obj->buffer.length; + break; + default: + return -EPROTO; + } + + *length = size_add(ALIGN(*length, alignment), size); + + return 0; +} + +static int wmi_obj_get_buffer_length(const union acpi_object *obj, size_t *length) +{ + size_t total = 0; + int ret; + + if (obj->type == ACPI_TYPE_PACKAGE) { + for (int i = 0; i < obj->package.count; i++) { + ret = wmi_adjust_buffer_length(&total, &obj->package.elements[i]); + if (ret < 0) + return ret; + } + } else { + ret = wmi_adjust_buffer_length(&total, obj); + if (ret < 0) + return ret; + } + + *length = total; + + return 0; +} + +static int wmi_obj_transform_simple(const union acpi_object *obj, u8 *buffer, size_t *consumed) +{ + struct wmi_string *string; + size_t length; + __le32 value; + u8 *aligned; + + switch (obj->type) { + case ACPI_TYPE_INTEGER: + aligned = PTR_ALIGN(buffer, 4); + length = sizeof(value); + + value = cpu_to_le32(obj->integer.value); + memcpy(aligned, &value, length); + break; + case ACPI_TYPE_STRING: + aligned = PTR_ALIGN(buffer, 2); + string = (struct wmi_string *)aligned; + length = struct_size(string, chars, obj->string.length + 1); + + /* We do not have to worry about unaligned accesses here as the WMI + * string will already be aligned on a two-byte boundary. + */ + string->length = cpu_to_le16((obj->string.length + 1) * 2); + for (int i = 0; i < obj->string.length; i++) + string->chars[i] = cpu_to_le16(obj->string.pointer[i]); + + /* + * The Windows WMI-ACPI driver always emits a terminating nul character, + * so we emulate this behavior here as well. + */ + string->chars[obj->string.length] = '\0'; + break; + case ACPI_TYPE_BUFFER: + aligned = buffer; + length = obj->buffer.length; + + memcpy(aligned, obj->buffer.pointer, length); + break; + default: + return -EPROTO; + } + + *consumed = (aligned - buffer) + length; + + return 0; +} + +static int wmi_obj_transform(const union acpi_object *obj, u8 *buffer) +{ + size_t consumed; + int ret; + + if (obj->type == ACPI_TYPE_PACKAGE) { + for (int i = 0; i < obj->package.count; i++) { + ret = wmi_obj_transform_simple(&obj->package.elements[i], buffer, + &consumed); + if (ret < 0) + return ret; + + buffer += consumed; + } + } else { + ret = wmi_obj_transform_simple(obj, buffer, &consumed); + if (ret < 0) + return ret; + } + + return 0; +} + +int wmi_unmarshal_acpi_object(const union acpi_object *obj, struct wmi_buffer *buffer) +{ + size_t length, alloc_length; + u8 *data; + int ret; + + ret = wmi_obj_get_buffer_length(obj, &length); + if (ret < 0) + return ret; + + if (ARCH_KMALLOC_MINALIGN < 8) { + /* + * kmalloc() guarantees that the alignment of the resulting memory allocation is at + * least the largest power-of-two divisor of the allocation size. The WMI buffer + * data needs to be aligned on a 8 byte boundary to properly support 64-bit WMI + * integers, so we have to round the allocation size to the next multiple of 8. + */ + alloc_length = round_up(length, 8); + } else { + alloc_length = length; + } + + data = kzalloc(alloc_length, GFP_KERNEL); + if (!data) + return -ENOMEM; + + ret = wmi_obj_transform(obj, data); + if (ret < 0) { + kfree(data); + return ret; + } + + buffer->length = length; + buffer->data = data; + + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(wmi_unmarshal_acpi_object); + +int wmi_marshal_string(const struct wmi_buffer *buffer, struct acpi_buffer *out) +{ + const struct wmi_string *string; + u16 length, value; + size_t chars; + char *str; + + if (buffer->length < sizeof(*string)) + return -ENODATA; + + string = buffer->data; + length = get_unaligned_le16(&string->length); + if (buffer->length < sizeof(*string) + length) + return -ENODATA; + + /* Each character needs to be 16 bits long */ + if (length % 2) + return -EINVAL; + + chars = length / 2; + str = kmalloc(chars + 1, GFP_KERNEL); + if (!str) + return -ENOMEM; + + for (int i = 0; i < chars; i++) { + value = get_unaligned_le16(&string->chars[i]); + + /* ACPI only accepts ASCII strings */ + if (value > 0x7F) { + kfree(str); + return -EINVAL; + } + + str[i] = value & 0xFF; + + /* + * ACPI strings should only contain a single nul character at the end. + * Because of this we must not copy any padding from the WMI string. + */ + if (!value) { + /* ACPICA wants the length of the string without the nul character */ + out->length = i; + out->pointer = str; + return 0; + } + } + + str[chars] = '\0'; + + out->length = chars; + out->pointer = str; + + return 0; +} +EXPORT_SYMBOL_IF_KUNIT(wmi_marshal_string); diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 665ea7dc8a92..81f24d238a2c 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -8,9 +8,11 @@ #ifndef _LINUX_WMI_H #define _LINUX_WMI_H +#include #include #include #include +#include /** * struct wmi_device - WMI device structure @@ -36,6 +38,37 @@ struct wmi_device { */ #define to_wmi_device(device) container_of_const(device, struct wmi_device, dev) +/** + * struct wmi_buffer - WMI data buffer + * @length: Buffer length in bytes + * @data: Pointer to the buffer content + * + * This structure is used to exchange data with the WMI driver core. + */ +struct wmi_buffer { + size_t length; + void *data; +}; + +/** + * struct wmi_string - WMI string representation + * @length: Size of @chars in bytes + * @chars: UTF16-LE characters with optional nul termination and padding + * + * This structure is used when exchanging string data over the WMI interface. + */ +struct wmi_string { + __le16 length; + __le16 chars[]; +} __packed; + +int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, + const struct wmi_buffer *in, struct wmi_buffer *out); + +int wmidev_query_block(struct wmi_device *wdev, u8 instance, struct wmi_buffer *out); + +int wmidev_set_block(struct wmi_device *wdev, u8 instance, const struct wmi_buffer *in); + acpi_status wmidev_evaluate_method(struct wmi_device *wdev, u8 instance, u32 method_id, const struct acpi_buffer *in, struct acpi_buffer *out); @@ -54,9 +87,11 @@ u8 wmidev_instance_count(struct wmi_device *wdev); * @probe: Callback for device binding * @remove: Callback for device unbinding * @shutdown: Callback for device shutdown - * @notify: Callback for receiving WMI events + * @notify: Callback for receiving WMI events (deprecated) + * @notify_new: Callback for receiving WMI events * - * This represents WMI drivers which handle WMI devices. + * This represents WMI drivers which handle WMI devices. The data inside the buffer + * passed to the @notify_new callback is guaranteed to be aligned on a 8-byte boundary. */ struct wmi_driver { struct device_driver driver; @@ -68,6 +103,7 @@ struct wmi_driver { void (*remove)(struct wmi_device *wdev); void (*shutdown)(struct wmi_device *wdev); void (*notify)(struct wmi_device *device, union acpi_object *data); + void (*notify_new)(struct wmi_device *device, const struct wmi_buffer *data); }; /** -- cgit v1.2.3 From b990a06f7ec6dc3ceecd8015c3b421690f267122 Mon Sep 17 00:00:00 2001 From: Armin Wolf Date: Fri, 16 Jan 2026 21:41:10 +0100 Subject: platform/wmi: Add helper functions for WMI string conversions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit WMI strings are encoded using UTF16-LE characters, forcing WMI drivers to manually convert them to/from standard UTF8 strings. Add a two helper functions for those tasks. Signed-off-by: Armin Wolf Link: https://patch.msgid.link/20260116204116.4030-4-W_Armin@gmx.de Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- Documentation/driver-api/wmi.rst | 3 ++ drivers/platform/wmi/Kconfig | 1 + drivers/platform/wmi/Makefile | 2 +- drivers/platform/wmi/string.c | 92 ++++++++++++++++++++++++++++++++++++++++ include/linux/wmi.h | 5 +++ 5 files changed, 102 insertions(+), 1 deletion(-) create mode 100644 drivers/platform/wmi/string.c (limited to 'include/linux') diff --git a/Documentation/driver-api/wmi.rst b/Documentation/driver-api/wmi.rst index db835b43c937..b847bcdcbb09 100644 --- a/Documentation/driver-api/wmi.rst +++ b/Documentation/driver-api/wmi.rst @@ -16,5 +16,8 @@ which will be bound to compatible WMI devices by the driver core. .. kernel-doc:: include/linux/wmi.h :internal: +.. kernel-doc:: drivers/platform/wmi/string.c + :export: + .. kernel-doc:: drivers/platform/wmi/core.c :export: diff --git a/drivers/platform/wmi/Kconfig b/drivers/platform/wmi/Kconfig index 21fa3e440042..d62f51ff3b7f 100644 --- a/drivers/platform/wmi/Kconfig +++ b/drivers/platform/wmi/Kconfig @@ -6,6 +6,7 @@ menuconfig ACPI_WMI tristate "ACPI-WMI support" depends on ACPI && X86 + select NLS help This option enables support for the ACPI-WMI driver core. diff --git a/drivers/platform/wmi/Makefile b/drivers/platform/wmi/Makefile index 93f37ce519ae..2feff94a5594 100644 --- a/drivers/platform/wmi/Makefile +++ b/drivers/platform/wmi/Makefile @@ -4,7 +4,7 @@ # ACPI WMI core # -wmi-y := core.o marshalling.o +wmi-y := core.o marshalling.o string.o obj-$(CONFIG_ACPI_WMI) += wmi.o # Unit tests diff --git a/drivers/platform/wmi/string.c b/drivers/platform/wmi/string.c new file mode 100644 index 000000000000..0fc43218aa5b --- /dev/null +++ b/drivers/platform/wmi/string.c @@ -0,0 +1,92 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * WMI string utility functions. + * + * Copyright (C) 2025 Armin Wolf + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +static_assert(sizeof(__le16) == sizeof(wchar_t)); + +/** + * wmi_string_to_utf8s - Convert a WMI string into a UTF8 string. + * @str: WMI string representation + * @dst: Buffer to fill with UTF8 characters + * @length: Length of the destination buffer + * + * Convert as WMI string into a standard UTF8 string. The conversion will stop + * once a NUL character is detected or when the buffer is full. Any invalid UTF16 + * characters will be ignored. The resulting UTF8 string will always be NUL-terminated + * when this function returns successfully. + * + * Return: Length of the resulting UTF8 string or negative errno code on failure. + */ +ssize_t wmi_string_to_utf8s(const struct wmi_string *str, u8 *dst, size_t length) +{ + /* Contains the maximum number of UTF16 code points to read */ + int inlen = le16_to_cpu(str->length) / 2; + int ret; + + if (length < 1) + return -EINVAL; + + /* We must leave room for the NUL character at the end of the destination buffer */ + ret = utf16s_to_utf8s((__force const wchar_t *)str->chars, inlen, UTF16_LITTLE_ENDIAN, dst, + length - 1); + if (ret < 0) + return ret; + + dst[ret] = '\0'; + + return ret; +} +EXPORT_SYMBOL_GPL(wmi_string_to_utf8s); + +/** + * wmi_string_from_utf8s - Convert a UTF8 string into a WMI string. + * @str: WMI string representation + * @max_chars: Maximum number of UTF16 code points to store inside the WMI string + * @src: UTF8 string to convert + * @src_length: Length of the source string without any trailing NUL-characters + * + * Convert a UTF8 string into a WMI string. The conversion will stop when the WMI string is + * full. The resulting WMI string will always be NUL-terminated and have its length field set + * to and appropriate value when this function returns successfully. + * + * Return: Number of UTF16 code points inside the WMI string or negative errno code on failure. + */ +ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8 *src, + size_t src_length) +{ + size_t str_length; + int ret; + + if (max_chars < 1) + return -EINVAL; + + /* We must leave room for the NUL character at the end of the WMI string */ + ret = utf8s_to_utf16s(src, src_length, UTF16_LITTLE_ENDIAN, (__force wchar_t *)str->chars, + max_chars - 1); + if (ret < 0) + return ret; + + str_length = (ret + 1) * sizeof(u16); + if (str_length > U16_MAX) + return -EOVERFLOW; + + str->length = cpu_to_le16(str_length); + str->chars[ret] = '\0'; + + return ret; +} +EXPORT_SYMBOL_GPL(wmi_string_from_utf8s); diff --git a/include/linux/wmi.h b/include/linux/wmi.h index 81f24d238a2c..75cb0c7cfe57 100644 --- a/include/linux/wmi.h +++ b/include/linux/wmi.h @@ -62,6 +62,11 @@ struct wmi_string { __le16 chars[]; } __packed; +ssize_t wmi_string_to_utf8s(const struct wmi_string *str, u8 *dst, size_t length); + +ssize_t wmi_string_from_utf8s(struct wmi_string *str, size_t max_chars, const u8 *src, + size_t src_length); + int wmidev_invoke_method(struct wmi_device *wdev, u8 instance, u32 method_id, const struct wmi_buffer *in, struct wmi_buffer *out); -- cgit v1.2.3 From 156442eb6e44d545f09559bd70c5b31fba39eb01 Mon Sep 17 00:00:00 2001 From: Joseph Chen Date: Mon, 12 Jan 2026 20:43:50 +0800 Subject: mfd: rk8xx: Add RK801 support The RK801 is a Power Management IC (PMIC) for multimedia and handheld devices. It contains the following components: - 4 BUCK - 2 LDO - 1 SWITCH Signed-off-by: Joseph Chen Link: https://patch.msgid.link/20260112124351.17707-3-chenjh@rock-chips.com Signed-off-by: Lee Jones --- drivers/mfd/Kconfig | 6 +-- drivers/mfd/rk8xx-core.c | 81 +++++++++++++++++++++++++++++++ drivers/mfd/rk8xx-i2c.c | 33 ++++++++++++- include/linux/mfd/rk808.h | 118 ++++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 234 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/mfd/Kconfig b/drivers/mfd/Kconfig index aace5766b38a..498d4260b777 100644 --- a/drivers/mfd/Kconfig +++ b/drivers/mfd/Kconfig @@ -1371,15 +1371,15 @@ config MFD_RK8XX select MFD_CORE config MFD_RK8XX_I2C - tristate "Rockchip RK805/RK808/RK809/RK816/RK817/RK818 Power Management Chip" + tristate "Rockchip RK8xx Power Management Chips" depends on I2C && OF select MFD_CORE select REGMAP_I2C select REGMAP_IRQ select MFD_RK8XX help - If you say yes here you get support for the RK805, RK808, RK809, - RK816, RK817 and RK818 Power Management chips. + If you say yes here you get support for the RK801, RK805, RK808, + RK809, RK816, RK817 and RK818 Power Management chips. This driver provides common support for accessing the device through I2C interface. The device supports multiple sub-devices including interrupts, RTC, LDO & DCDC regulators, and onkey. diff --git a/drivers/mfd/rk8xx-core.c b/drivers/mfd/rk8xx-core.c index def4587fdfb8..3dcf6abfda74 100644 --- a/drivers/mfd/rk8xx-core.c +++ b/drivers/mfd/rk8xx-core.c @@ -37,6 +37,11 @@ static const struct resource rk817_rtc_resources[] = { DEFINE_RES_IRQ(RK817_IRQ_RTC_ALARM), }; +static const struct resource rk801_key_resources[] = { + DEFINE_RES_IRQ(RK801_IRQ_PWRON_FALL), + DEFINE_RES_IRQ(RK801_IRQ_PWRON_RISE), +}; + static const struct resource rk805_key_resources[] = { DEFINE_RES_IRQ(RK805_IRQ_PWRON_RISE), DEFINE_RES_IRQ(RK805_IRQ_PWRON_FALL), @@ -57,6 +62,14 @@ static const struct resource rk817_charger_resources[] = { DEFINE_RES_IRQ(RK817_IRQ_PLUG_OUT), }; +static const struct mfd_cell rk801s[] = { + { .name = "rk808-regulator", }, + { .name = "rk805-pwrkey", + .num_resources = ARRAY_SIZE(rk801_key_resources), + .resources = &rk801_key_resources[0], + }, +}; + static const struct mfd_cell rk805s[] = { { .name = "rk808-clkout", }, { .name = "rk808-regulator", }, @@ -139,6 +152,15 @@ static const struct mfd_cell rk818s[] = { }, }; +static const struct rk808_reg_data rk801_pre_init_reg[] = { + { RK801_SLEEP_CFG_REG, RK801_SLEEP_FUN_MSK, RK801_NONE_FUN }, + { RK801_SYS_CFG2_REG, RK801_RST_MSK, RK801_RST_RESTART_REG_RESETB }, + { RK801_INT_CONFIG_REG, RK801_INT_POL_MSK, RK801_INT_ACT_L }, + { RK801_POWER_FPWM_EN_REG, RK801_PLDO_HRDEC_EN, RK801_PLDO_HRDEC_EN }, + { RK801_BUCK_DEBUG5_REG, MASK_ALL, 0x54 }, + { RK801_CON_BACK1_REG, MASK_ALL, 0x18 }, +}; + static const struct rk808_reg_data rk805_pre_init_reg[] = { {RK805_BUCK1_CONFIG_REG, RK805_BUCK1_2_ILMAX_MASK, RK805_BUCK1_2_ILMAX_4000MA}, @@ -284,6 +306,37 @@ static const struct rk808_reg_data rk818_pre_init_reg[] = { VB_LO_SEL_3500MV }, }; +static const struct regmap_irq rk801_irqs[] = { + [RK801_IRQ_PWRON_FALL] = { + .mask = RK801_IRQ_PWRON_FALL_MSK, + .reg_offset = 0, + }, + [RK801_IRQ_PWRON_RISE] = { + .mask = RK801_IRQ_PWRON_RISE_MSK, + .reg_offset = 0, + }, + [RK801_IRQ_PWRON] = { + .mask = RK801_IRQ_PWRON_MSK, + .reg_offset = 0, + }, + [RK801_IRQ_PWRON_LP] = { + .mask = RK801_IRQ_PWRON_LP_MSK, + .reg_offset = 0, + }, + [RK801_IRQ_HOTDIE] = { + .mask = RK801_IRQ_HOTDIE_MSK, + .reg_offset = 0, + }, + [RK801_IRQ_VDC_RISE] = { + .mask = RK801_IRQ_VDC_RISE_MSK, + .reg_offset = 0, + }, + [RK801_IRQ_VDC_FALL] = { + .mask = RK801_IRQ_VDC_FALL_MSK, + .reg_offset = 0, + }, +}; + static const struct regmap_irq rk805_irqs[] = { [RK805_IRQ_PWRON_RISE] = { .mask = RK805_IRQ_PWRON_RISE_MSK, @@ -532,6 +585,17 @@ static const struct regmap_irq rk817_irqs[RK817_IRQ_END] = { REGMAP_IRQ_REG_LINE(23, 8) }; +static const struct regmap_irq_chip rk801_irq_chip = { + .name = "rk801", + .irqs = rk801_irqs, + .num_irqs = ARRAY_SIZE(rk801_irqs), + .num_regs = 1, + .status_base = RK801_INT_STS0_REG, + .mask_base = RK801_INT_MASK0_REG, + .ack_base = RK801_INT_STS0_REG, + .init_ack_masked = true, +}; + static const struct regmap_irq_chip rk805_irq_chip = { .name = "rk805", .irqs = rk805_irqs, @@ -610,6 +674,10 @@ static int rk808_power_off(struct sys_off_data *data) unsigned int reg, bit; switch (rk808->variant) { + case RK801_ID: + reg = RK801_SYS_CFG2_REG; + bit = DEV_OFF; + break; case RK805_ID: reg = RK805_DEV_CTRL_REG; bit = DEV_OFF; @@ -714,6 +782,13 @@ int rk8xx_probe(struct device *dev, int variant, unsigned int irq, struct regmap dev_set_drvdata(dev, rk808); switch (rk808->variant) { + case RK801_ID: + rk808->regmap_irq_chip = &rk801_irq_chip; + pre_init_reg = rk801_pre_init_reg; + nr_pre_init_regs = ARRAY_SIZE(rk801_pre_init_reg); + cells = rk801s; + nr_cells = ARRAY_SIZE(rk801s); + break; case RK805_ID: rk808->regmap_irq_chip = &rk805_irq_chip; pre_init_reg = rk805_pre_init_reg; @@ -831,6 +906,12 @@ int rk8xx_suspend(struct device *dev) int ret = 0; switch (rk808->variant) { + case RK801_ID: + ret = regmap_update_bits(rk808->regmap, + RK801_SLEEP_CFG_REG, + RK801_SLEEP_FUN_MSK, + RK801_SLEEP_FUN); + break; case RK805_ID: ret = regmap_update_bits(rk808->regmap, RK805_GPIO_IO_POL_REG, diff --git a/drivers/mfd/rk8xx-i2c.c b/drivers/mfd/rk8xx-i2c.c index 37287b06dab0..2951b2911a37 100644 --- a/drivers/mfd/rk8xx-i2c.c +++ b/drivers/mfd/rk8xx-i2c.c @@ -1,6 +1,6 @@ // SPDX-License-Identifier: GPL-2.0-only /* - * Rockchip RK805/RK808/RK816/RK817/RK818 Core (I2C) driver + * Rockchip RK801/RK805/RK808/RK816/RK817/RK818 Core (I2C) driver * * Copyright (c) 2014, Fuzhou Rockchip Electronics Co., Ltd * Copyright (C) 2016 PHYTEC Messtechnik GmbH @@ -21,6 +21,23 @@ struct rk8xx_i2c_platform_data { int variant; }; +static bool rk801_is_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case RK801_SYS_STS_REG: + case RK801_INT_STS0_REG: + case RK801_SYS_CFG0_REG: + case RK801_SYS_CFG1_REG: + case RK801_SYS_CFG2_REG: + case RK801_SYS_CFG3_REG: + case RK801_SYS_CFG4_REG: + case RK801_SLEEP_CFG_REG: + return true; + } + + return false; +} + static bool rk806_is_volatile_reg(struct device *dev, unsigned int reg) { switch (reg) { @@ -124,6 +141,14 @@ static const struct regmap_config rk818_regmap_config = { .volatile_reg = rk808_is_volatile_reg, }; +static const struct regmap_config rk801_regmap_config = { + .reg_bits = 8, + .val_bits = 8, + .max_register = RK801_SYS_CFG3_OTP_REG, + .cache_type = REGCACHE_RBTREE, + .volatile_reg = rk801_is_volatile_reg, +}; + static const struct regmap_config rk805_regmap_config = { .reg_bits = 8, .val_bits = 8, @@ -164,6 +189,11 @@ static const struct regmap_config rk817_regmap_config = { .volatile_reg = rk817_is_volatile_reg, }; +static const struct rk8xx_i2c_platform_data rk801_data = { + .regmap_cfg = &rk801_regmap_config, + .variant = RK801_ID, +}; + static const struct rk8xx_i2c_platform_data rk805_data = { .regmap_cfg = &rk805_regmap_config, .variant = RK805_ID, @@ -224,6 +254,7 @@ static void rk8xx_i2c_shutdown(struct i2c_client *client) static SIMPLE_DEV_PM_OPS(rk8xx_i2c_pm_ops, rk8xx_suspend, rk8xx_resume); static const struct of_device_id rk8xx_i2c_of_match[] = { + { .compatible = "rockchip,rk801", .data = &rk801_data }, { .compatible = "rockchip,rk805", .data = &rk805_data }, { .compatible = "rockchip,rk806", .data = &rk806_data }, { .compatible = "rockchip,rk808", .data = &rk808_data }, diff --git a/include/linux/mfd/rk808.h b/include/linux/mfd/rk808.h index 28170ee08898..7ffc904c864c 100644 --- a/include/linux/mfd/rk808.h +++ b/include/linux/mfd/rk808.h @@ -340,6 +340,123 @@ enum rk818_reg { #define RK818_USB_ILMIN_2000MA 0x7 #define RK818_USB_CHG_SD_VSEL_MASK 0x70 +/* RK801 */ +enum rk801_reg { + RK801_ID_DCDC1, + RK801_ID_DCDC2, + RK801_ID_DCDC4, + RK801_ID_DCDC3, + RK801_ID_LDO1, + RK801_ID_LDO2, + RK801_ID_SWITCH, + RK801_ID_MAX, +}; + +#define RK801_SLP_REG_OFFSET 5 +#define RK801_NUM_REGULATORS 7 + +#define RK801_HW_SYNC_US 32 + +/* RK801 Register Definitions */ +#define RK801_ID_MSB 0x00 +#define RK801_ID_LSB 0x01 +#define RK801_OTP_VER_REG 0x02 +#define RK801_POWER_EN0_REG 0x03 +#define RK801_POWER_EN1_REG 0x04 +#define RK801_POWER_SLP_EN_REG 0x05 +#define RK801_POWER_FPWM_EN_REG 0x06 +#define RK801_SLP_LP_CONFIG_REG 0x07 +#define RK801_BUCK_CONFIG_REG 0x08 +#define RK801_BUCK1_ON_VSEL_REG 0x09 +#define RK801_BUCK2_ON_VSEL_REG 0x0a +#define RK801_BUCK4_ON_VSEL_REG 0x0b +#define RK801_LDO1_ON_VSEL_REG 0x0c +#define RK801_LDO2_ON_VSEL_REG 0x0d +#define RK801_BUCK1_SLP_VSEL_REG 0x0e +#define RK801_BUCK2_SLP_VSEL_REG 0x0f +#define RK801_BUCK4_SLP_VSEL_REG 0x10 +#define RK801_LDO1_SLP_VSEL_REG 0x11 +#define RK801_LDO2_SLP_VSEL_REG 0x12 +#define RK801_LDO_SW_IMAX_REG 0x13 +#define RK801_SYS_STS_REG 0x14 +#define RK801_SYS_CFG0_REG 0x15 +#define RK801_SYS_CFG1_REG 0x16 +#define RK801_SYS_CFG2_REG 0x17 +#define RK801_SYS_CFG3_REG 0x18 +#define RK801_SYS_CFG4_REG 0x19 +#define RK801_SLEEP_CFG_REG 0x1a +#define RK801_ON_SOURCE_REG 0x1b +#define RK801_OFF_SOURCE_REG 0x1c +#define RK801_PWRON_KEY_REG 0x1d +#define RK801_INT_STS0_REG 0x1e +#define RK801_INT_MASK0_REG 0x1f +#define RK801_INT_CONFIG_REG 0x20 +#define RK801_CON_BACK1_REG 0x21 +#define RK801_CON_BACK2_REG 0x22 +#define RK801_DATA_CON0_REG 0x23 +#define RK801_DATA_CON1_REG 0x24 +#define RK801_DATA_CON2_REG 0x25 +#define RK801_DATA_CON3_REG 0x26 +#define RK801_POWER_EXIT_SLP_SEQ0_REG 0x27 +#define RK801_POWER_EXIT_SLP_SEQ1_REG 0x28 +#define RK801_POWER_EXIT_SLP_SEQ2_REG 0x29 +#define RK801_POWER_EXIT_SLP_SEQ3_REG 0x2a +#define RK801_POWER_ENTER_SLP_OR_SHTD_SEQ0_REG 0x2b +#define RK801_POWER_ENTER_SLP_OR_SHTD_SEQ1_REG 0x2c +#define RK801_POWER_ENTER_SLP_OR_SHTD_SEQ2_REG 0x2d +#define RK801_POWER_ENTER_SLP_OR_SHTD_SEQ3_REG 0x2e +#define RK801_BUCK_DEBUG1_REG 0x2f +#define RK801_BUCK_DEBUG2_REG 0x30 +#define RK801_BUCK_DEBUG3_REG 0x31 +#define RK801_BUCK_DEBUG4_REG 0x32 +#define RK801_BUCK_DEBUG5_REG 0x33 +#define RK801_BUCK_DEBUG7_REG 0x34 +#define RK801_OTP_EN_CON_REG 0x35 +#define RK801_TEST_CON_REG 0x36 +#define RK801_EFUSE_CONTROL_REG 0x37 +#define RK801_SYS_CFG3_OTP_REG 0x38 + +/* RK801 IRQ Definitions */ +#define RK801_IRQ_PWRON_FALL 0 +#define RK801_IRQ_PWRON_RISE 1 +#define RK801_IRQ_PWRON 2 +#define RK801_IRQ_PWRON_LP 3 +#define RK801_IRQ_HOTDIE 4 +#define RK801_IRQ_VDC_RISE 5 +#define RK801_IRQ_VDC_FALL 6 +#define RK801_IRQ_PWRON_FALL_MSK BIT(0) +#define RK801_IRQ_PWRON_RISE_MSK BIT(1) +#define RK801_IRQ_PWRON_MSK BIT(2) +#define RK801_IRQ_PWRON_LP_MSK BIT(3) +#define RK801_IRQ_HOTDIE_MSK BIT(4) +#define RK801_IRQ_VDC_RISE_MSK BIT(5) +#define RK801_IRQ_VDC_FALL_MSK BIT(6) +/* RK801_SLP_LP_CONFIG_REG */ +#define RK801_BUCK_SLP_LP_EN BIT(3) +#define RK801_PLDO_SLP_LP_EN BIT(1) +#define RK801_SLP_LP_MASK (RK801_PLDO_SLP_LP_EN | RK801_BUCK_SLP_LP_EN) +/* RK801_SLEEP_CFG_REG */ +#define RK801_SLEEP_FUN_MSK 0x3 +#define RK801_NONE_FUN 0x0 +#define RK801_SLEEP_FUN 0x1 +#define RK801_SHUTDOWN_FUN 0x2 +#define RK801_RESET_FUN 0x3 +/* RK801_SYS_CFG2_REG */ +#define RK801_SLEEP_POL_MSK BIT(1) +#define RK801_SLEEP_ACT_H BIT(1) +#define RK801_SLEEP_ACT_L 0 +#define RK801_RST_MSK (0x3 << 4) +#define RK801_RST_RESTART_PMU (0x0 << 4) +#define RK801_RST_RESTART_REG (0x1 << 4) +#define RK801_RST_RESTART_REG_RESETB (0x2 << 4) +/* RK801_INT_CONFIG_REG */ +#define RK801_INT_POL_MSK BIT(1) +#define RK801_INT_ACT_H BIT(1) +#define RK801_INT_ACT_L 0 +#define RK801_FPWM_MODE 1 +#define RK801_AUTO_PWM_MODE 0 +#define RK801_PLDO_HRDEC_EN BIT(6) + /* RK805 */ enum rk805_reg { RK805_ID_DCDC1, @@ -1332,6 +1449,7 @@ enum { }; enum { + RK801_ID = 0x8010, RK805_ID = 0x8050, RK806_ID = 0x8060, RK808_ID = 0x0000, -- cgit v1.2.3 From 41c6cac6decd5123db1da8ca240a9c808b0ae6ce Mon Sep 17 00:00:00 2001 From: Cristian Ciocaltea Date: Tue, 13 Jan 2026 01:20:48 +0200 Subject: phy: hdmi: Add HDMI 2.1 FRL configuration options The HDMI 2.1 specification introduced the Fixed Rate Link (FRL) mode, aiming to replace the older Transition-Minimized Differential Signaling (TMDS) mode used in previous HDMI versions to support much higher bandwidths (up to 48 Gbps) for modern video and audio formats. FRL has been designed to support ultra high resolution formats at high refresh rates like 8K@60Hz or 4K@120Hz, and eliminates the need for dynamic bandwidth adjustments, which reduces latency. It operates with 3 or 4 lanes at different link rates: 3Gbps, 6Gbps, 8Gbps, 10Gbps or 12Gbps. Add support for configuring the FRL mode for HDMI PHYs. Signed-off-by: Cristian Ciocaltea Link: https://patch.msgid.link/20260113-phy-hdptx-frl-v6-1-8d5f97419c0b@collabora.com Signed-off-by: Vinod Koul --- include/linux/phy/phy-hdmi.h | 19 +++++++++++++++++-- 1 file changed, 17 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/phy/phy-hdmi.h b/include/linux/phy/phy-hdmi.h index f0ec963c6e84..d4cf4430ee8f 100644 --- a/include/linux/phy/phy-hdmi.h +++ b/include/linux/phy/phy-hdmi.h @@ -6,16 +6,31 @@ #ifndef __PHY_HDMI_H_ #define __PHY_HDMI_H_ +#include + +enum phy_hdmi_mode { + PHY_HDMI_MODE_TMDS, + PHY_HDMI_MODE_FRL, +}; + /** * struct phy_configure_opts_hdmi - HDMI configuration set - * @tmds_char_rate: HDMI TMDS Character Rate in Hertz. * @bpc: Bits per color channel. + * @tmds_char_rate: HDMI TMDS Character Rate in Hertz. + * @frl.rate_per_lane: HDMI FRL Rate per Lane in Gbps. + * @frl.lanes: HDMI FRL lanes count. * * This structure is used to represent the configuration state of a HDMI phy. */ struct phy_configure_opts_hdmi { - unsigned long long tmds_char_rate; unsigned int bpc; + union { + unsigned long long tmds_char_rate; + struct { + u8 rate_per_lane; + u8 lanes; + } frl; + }; }; #endif /* __PHY_HDMI_H_ */ -- cgit v1.2.3 From 7b85137caf110a09a4a18f00f730de4709f9afc8 Mon Sep 17 00:00:00 2001 From: "Mario Limonciello (AMD)" Date: Thu, 15 Jan 2026 22:11:32 -0600 Subject: crypto: ccp - Send PSP_CMD_TEE_RING_DESTROY when PSP_CMD_TEE_RING_INIT fails MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The hibernate resume sequence involves loading a resume kernel that is just used for loading the hibernate image before shifting back to the existing kernel. During that hibernate resume sequence the resume kernel may have loaded the ccp driver. If this happens the resume kernel will also have called PSP_CMD_TEE_RING_INIT but it will never have called PSP_CMD_TEE_RING_DESTROY. This is problematic because the existing kernel needs to re-initialize the ring. One could argue that the existing kernel should call destroy as part of restore() but there is no guarantee that the resume kernel did or didn't load the ccp driver. There is also no callback opportunity for the resume kernel to destroy before handing back control to the existing kernel. Similar problems could potentially exist with the use of kdump and crash handling. I actually reproduced this issue like this: 1) rmmod ccp 2) hibernate the system 3) resume the system 4) modprobe ccp The resume kernel will have loaded ccp but never destroyed and then when I try to modprobe it fails. Because of these possible cases add a flow that checks the error code from the PSP_CMD_TEE_RING_INIT call and tries to call PSP_CMD_TEE_RING_DESTROY if it failed. If this succeeds then call PSP_CMD_TEE_RING_INIT again. Fixes: f892a21f51162 ("crypto: ccp - use generic power management") Reported-by: Lars Francke Closes: https://lore.kernel.org/platform-driver-x86/CAD-Ua_gfJnQSo8ucS_7ZwzuhoBRJ14zXP7s8b-zX3ZcxcyWePw@mail.gmail.com/ Tested-by: Yijun Shen Signed-off-by: Mario Limonciello (AMD) Reviewed-by: Shyam Sundar S K Acked-by: Tom Lendacky Link: https://patch.msgid.link/20260116041132.153674-6-superm1@kernel.org Signed-off-by: Ilpo Järvinen --- drivers/crypto/ccp/tee-dev.c | 14 ++++++++++++++ include/linux/psp.h | 1 + 2 files changed, 15 insertions(+) (limited to 'include/linux') diff --git a/drivers/crypto/ccp/tee-dev.c b/drivers/crypto/ccp/tee-dev.c index ef1430f86ad6..92ffa412622a 100644 --- a/drivers/crypto/ccp/tee-dev.c +++ b/drivers/crypto/ccp/tee-dev.c @@ -113,6 +113,7 @@ static int tee_init_ring(struct psp_tee_device *tee) { int ring_size = MAX_RING_BUFFER_ENTRIES * sizeof(struct tee_ring_cmd); struct tee_init_ring_cmd *cmd; + bool retry = false; unsigned int reg; int ret; @@ -135,6 +136,7 @@ static int tee_init_ring(struct psp_tee_device *tee) /* Send command buffer details to Trusted OS by writing to * CPU-PSP message registers */ +retry_init: ret = psp_mailbox_command(tee->psp, PSP_CMD_TEE_RING_INIT, cmd, TEE_DEFAULT_CMD_TIMEOUT, ®); if (ret) { @@ -145,6 +147,18 @@ static int tee_init_ring(struct psp_tee_device *tee) } if (FIELD_GET(PSP_CMDRESP_STS, reg)) { + /* + * During the hibernate resume sequence driver may have gotten loaded + * but the ring not properly destroyed. If the ring doesn't work, try + * to destroy and re-init once. + */ + if (!retry && FIELD_GET(PSP_CMDRESP_STS, reg) == PSP_TEE_STS_RING_BUSY) { + dev_info(tee->dev, "tee: ring init command failed with busy status, retrying\n"); + if (tee_send_destroy_cmd(tee)) { + retry = true; + goto retry_init; + } + } dev_err(tee->dev, "tee: ring init command failed (%#010lx)\n", FIELD_GET(PSP_CMDRESP_STS, reg)); tee_free_ring(tee); diff --git a/include/linux/psp.h b/include/linux/psp.h index 92e60aeef21e..b337dcce1e99 100644 --- a/include/linux/psp.h +++ b/include/linux/psp.h @@ -18,6 +18,7 @@ * and should include an appropriate local definition in their source file. */ #define PSP_CMDRESP_STS GENMASK(15, 0) +#define PSP_TEE_STS_RING_BUSY 0x0000000d /* Ring already initialized */ #define PSP_CMDRESP_CMD GENMASK(23, 16) #define PSP_CMDRESP_RESERVED GENMASK(29, 24) #define PSP_CMDRESP_RECOVERY BIT(30) -- cgit v1.2.3 From 118222e20d16caf38264b850d7a386e5f063008c Mon Sep 17 00:00:00 2001 From: Shyam Sundar S K Date: Thu, 15 Jan 2026 09:34:48 -0800 Subject: platform/x86/amd/pmf: Introduce new interface to export NPU metrics MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The PMF driver retrieves NPU metrics data from the PMFW. Introduce a new interface to make NPU metrics accessible to other drivers like AMDXDNA driver, which can access and utilize this information as needed. Reviewed-by: Mario Limonciello Co-developed-by: Patil Rajesh Reddy Signed-off-by: Patil Rajesh Reddy Signed-off-by: Shyam Sundar S K [lizhi: save return value of is_npu_metrics_supported() and return it] Signed-off-by: Lizhi Hou Link: https://patch.msgid.link/20260115173448.403826-1-lizhi.hou@amd.com Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/amd/pmf/core.c | 76 +++++++++++++++++++++++++++++++++++++ drivers/platform/x86/amd/pmf/pmf.h | 2 + include/linux/amd-pmf-io.h | 21 ++++++++++ 3 files changed, 99 insertions(+) (limited to 'include/linux') diff --git a/drivers/platform/x86/amd/pmf/core.c b/drivers/platform/x86/amd/pmf/core.c index 1a59e9ea9fb3..bfc79905433e 100644 --- a/drivers/platform/x86/amd/pmf/core.c +++ b/drivers/platform/x86/amd/pmf/core.c @@ -8,6 +8,8 @@ * Author: Shyam Sundar S K */ +#include +#include #include #include #include @@ -15,6 +17,7 @@ #include #include #include +#include #include #include "pmf.h" @@ -54,6 +57,8 @@ static bool force_load; module_param(force_load, bool, 0444); MODULE_PARM_DESC(force_load, "Force load this driver on supported older platforms (experimental)"); +static struct device *pmf_device; + static int amd_pmf_pwr_src_notify_call(struct notifier_block *nb, unsigned long event, void *data) { struct amd_pmf_dev *pmf = container_of(nb, struct amd_pmf_dev, pwr_src_notifier); @@ -315,6 +320,71 @@ int amd_pmf_init_metrics_table(struct amd_pmf_dev *dev) return 0; } +static int is_npu_metrics_supported(struct amd_pmf_dev *pdev) +{ + switch (pdev->cpu_id) { + case PCI_DEVICE_ID_AMD_1AH_M20H_ROOT: + case PCI_DEVICE_ID_AMD_1AH_M60H_ROOT: + return 0; + default: + return -EOPNOTSUPP; + } +} + +static int amd_pmf_get_smu_metrics(struct amd_pmf_dev *dev, struct amd_pmf_npu_metrics *data) +{ + int ret, i; + + guard(mutex)(&dev->metrics_mutex); + + ret = is_npu_metrics_supported(dev); + if (ret) + return ret; + + ret = amd_pmf_set_dram_addr(dev, true); + if (ret) + return ret; + + memset(dev->buf, 0, dev->mtable_size); + + /* Send SMU command to get NPU metrics */ + ret = amd_pmf_send_cmd(dev, SET_TRANSFER_TABLE, SET_CMD, METRICS_TABLE_ID, NULL); + if (ret) { + dev_err(dev->dev, "SMU command failed to get NPU metrics: %d\n", ret); + return ret; + } + + memcpy(&dev->m_table_v2, dev->buf, dev->mtable_size); + + data->npuclk_freq = dev->m_table_v2.npuclk_freq; + for (i = 0; i < ARRAY_SIZE(data->npu_busy); i++) + data->npu_busy[i] = dev->m_table_v2.npu_busy[i]; + data->npu_power = dev->m_table_v2.npu_power; + data->mpnpuclk_freq = dev->m_table_v2.mpnpuclk_freq; + data->npu_reads = dev->m_table_v2.npu_reads; + data->npu_writes = dev->m_table_v2.npu_writes; + + return 0; +} + +int amd_pmf_get_npu_data(struct amd_pmf_npu_metrics *info) +{ + struct amd_pmf_dev *pdev; + + if (!info) + return -EINVAL; + + if (!pmf_device) + return -ENODEV; + + pdev = dev_get_drvdata(pmf_device); + if (!pdev) + return -ENODEV; + + return amd_pmf_get_smu_metrics(pdev, info); +} +EXPORT_SYMBOL_NS_GPL(amd_pmf_get_npu_data, "AMD_PMF"); + static int amd_pmf_reinit_ta(struct amd_pmf_dev *pdev) { bool status; @@ -542,6 +612,10 @@ static int amd_pmf_probe(struct platform_device *pdev) if (err) return err; + err = devm_mutex_init(dev->dev, &dev->metrics_mutex); + if (err) + return err; + apmf_acpi_init(dev); platform_set_drvdata(pdev, dev); amd_pmf_dbgfs_register(dev); @@ -550,6 +624,8 @@ static int amd_pmf_probe(struct platform_device *pdev) if (is_apmf_func_supported(dev, APMF_FUNC_SBIOS_HEARTBEAT_V2)) amd_pmf_notify_sbios_heartbeat_event_v2(dev, ON_LOAD); + pmf_device = dev->dev; + dev_info(dev->dev, "registered PMF device successfully\n"); return 0; diff --git a/drivers/platform/x86/amd/pmf/pmf.h b/drivers/platform/x86/amd/pmf/pmf.h index 78dc7706607d..69fef7448744 100644 --- a/drivers/platform/x86/amd/pmf/pmf.h +++ b/drivers/platform/x86/amd/pmf/pmf.h @@ -12,6 +12,7 @@ #define PMF_H #include +#include #include #include #include @@ -440,6 +441,7 @@ struct amd_pmf_dev { bool cb_flag; /* To handle first custom BIOS input */ struct pmf_cbi_ring_buffer cbi_buf; struct mutex cbi_mutex; /* Protects ring buffer access */ + struct mutex metrics_mutex; }; struct apmf_sps_prop_granular_v2 { diff --git a/include/linux/amd-pmf-io.h b/include/linux/amd-pmf-io.h index 6fa510f419c0..55198d2875cc 100644 --- a/include/linux/amd-pmf-io.h +++ b/include/linux/amd-pmf-io.h @@ -61,5 +61,26 @@ enum laptop_placement { LP_UNDEFINED, }; +/** + * struct amd_pmf_npu_metrics: Get NPU metrics data from PMF driver + * @npuclk_freq: NPU clock frequency [MHz] + * @npu_busy: NPU busy % [0-100] + * @npu_power: NPU power [mW] + * @mpnpuclk_freq: MPNPU [MHz] + * @npu_reads: NPU read bandwidth [MB/sec] + * @npu_writes: NPU write bandwidth [MB/sec] + */ +struct amd_pmf_npu_metrics { + u16 npuclk_freq; + u16 npu_busy[8]; + u16 npu_power; + u16 mpnpuclk_freq; + u16 npu_reads; + u16 npu_writes; +}; + int amd_get_sfh_info(struct amd_sfh_info *sfh_info, enum sfh_message_type op); + +/* AMD PMF and NPU interface */ +int amd_pmf_get_npu_data(struct amd_pmf_npu_metrics *info); #endif -- cgit v1.2.3 From c23f0550c05d40762b141808709667759291c938 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 Jan 2026 15:50:36 +0100 Subject: Revert "mmc: rtsx: reset power state on suspend" This reverts commit eac85fbd0867c25ac517f58fae401d65c627edff. This is not the correct change, so revert it for now. Fixes: eac85fbd0867 ("mmc: rtsx: reset power state on suspend") Cc: Matthew Schwartz Cc: Ulf Hansson Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rtsx_pcr.c | 9 --------- drivers/mmc/host/rtsx_pci_sdmmc.c | 22 ---------------------- include/linux/rtsx_common.h | 1 - 3 files changed, 32 deletions(-) (limited to 'include/linux') diff --git a/drivers/misc/cardreader/rtsx_pcr.c b/drivers/misc/cardreader/rtsx_pcr.c index f1f4d8ed544d..f9952d76d6ed 100644 --- a/drivers/misc/cardreader/rtsx_pcr.c +++ b/drivers/misc/cardreader/rtsx_pcr.c @@ -1654,7 +1654,6 @@ static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) struct pci_dev *pcidev = to_pci_dev(dev_d); struct pcr_handle *handle = pci_get_drvdata(pcidev); struct rtsx_pcr *pcr = handle->pcr; - struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD]; dev_dbg(&(pcidev->dev), "--> %s\n", __func__); @@ -1662,9 +1661,6 @@ static int __maybe_unused rtsx_pci_suspend(struct device *dev_d) mutex_lock(&pcr->pcr_mutex); - if (slot->p_dev && slot->power_off) - slot->power_off(slot->p_dev); - rtsx_pci_power_off(pcr, HOST_ENTER_S3, false); mutex_unlock(&pcr->pcr_mutex); @@ -1776,17 +1772,12 @@ static int rtsx_pci_runtime_suspend(struct device *device) struct pci_dev *pcidev = to_pci_dev(device); struct pcr_handle *handle = pci_get_drvdata(pcidev); struct rtsx_pcr *pcr = handle->pcr; - struct rtsx_slot *slot = &pcr->slots[RTSX_SD_CARD]; dev_dbg(device, "--> %s\n", __func__); cancel_delayed_work_sync(&pcr->carddet_work); mutex_lock(&pcr->pcr_mutex); - - if (slot->p_dev && slot->power_off) - slot->power_off(slot->p_dev); - rtsx_pci_power_off(pcr, HOST_ENTER_S3, true); mutex_unlock(&pcr->pcr_mutex); diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index b847d79d4d8c..5d3599ee06bf 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -47,7 +47,6 @@ struct realtek_pci_sdmmc { }; static int sdmmc_init_sd_express(struct mmc_host *mmc, struct mmc_ios *ios); -static int sd_power_on(struct realtek_pci_sdmmc *host, unsigned char power_mode); static inline struct device *sdmmc_dev(struct realtek_pci_sdmmc *host) { @@ -822,15 +821,6 @@ static void sd_request(struct work_struct *work) rtsx_pci_start_run(pcr); - if (host->prev_power_state == MMC_POWER_OFF) { - err = sd_power_on(host, MMC_POWER_ON); - if (err) { - cmd->error = err; - mutex_unlock(&pcr->pcr_mutex); - goto finish; - } - } - rtsx_pci_switch_clock(pcr, host->clock, host->ssc_depth, host->initial_mode, host->double_clk, host->vpclk); rtsx_pci_write_register(pcr, CARD_SELECT, 0x07, SD_MOD_SEL); @@ -1491,16 +1481,6 @@ static void rtsx_pci_sdmmc_card_event(struct platform_device *pdev) mmc_detect_change(host->mmc, 0); } -static void rtsx_pci_sdmmc_power_off(struct platform_device *pdev) -{ - struct realtek_pci_sdmmc *host = platform_get_drvdata(pdev); - - if (!host) - return; - - host->prev_power_state = MMC_POWER_OFF; -} - static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) { struct mmc_host *mmc; @@ -1533,7 +1513,6 @@ static int rtsx_pci_sdmmc_drv_probe(struct platform_device *pdev) platform_set_drvdata(pdev, host); pcr->slots[RTSX_SD_CARD].p_dev = pdev; pcr->slots[RTSX_SD_CARD].card_event = rtsx_pci_sdmmc_card_event; - pcr->slots[RTSX_SD_CARD].power_off = rtsx_pci_sdmmc_power_off; mutex_init(&host->host_mutex); @@ -1565,7 +1544,6 @@ static void rtsx_pci_sdmmc_drv_remove(struct platform_device *pdev) pcr = host->pcr; pcr->slots[RTSX_SD_CARD].p_dev = NULL; pcr->slots[RTSX_SD_CARD].card_event = NULL; - pcr->slots[RTSX_SD_CARD].power_off = NULL; mmc = host->mmc; cancel_work_sync(&host->work); diff --git a/include/linux/rtsx_common.h b/include/linux/rtsx_common.h index f294f478f0c0..da9c8c6b5d50 100644 --- a/include/linux/rtsx_common.h +++ b/include/linux/rtsx_common.h @@ -32,7 +32,6 @@ struct platform_device; struct rtsx_slot { struct platform_device *p_dev; void (*card_event)(struct platform_device *p_dev); - void (*power_off)(struct platform_device *p_dev); }; #endif -- cgit v1.2.3 From eb89b17f283b233ba721fce358fa0d15223ae69d Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Wed, 21 Jan 2026 15:51:04 +0100 Subject: Revert "mmc: rtsx_pci: add quirk to disable MMC_CAP_AGGRESSIVE_PM for RTS525A" This reverts commit 5f0bf80cc5e04d31eeb201683e0b477c24bd18e7. This was asked to be reverted as it is not the correct way to do this. Fixes: 5f0bf80cc5e0 ("mmc: rtsx_pci: add quirk to disable MMC_CAP_AGGRESSIVE_PM for RTS525A") Cc: Matthew Schwartz Signed-off-by: Greg Kroah-Hartman --- drivers/misc/cardreader/rts5249.c | 3 --- drivers/mmc/host/rtsx_pci_sdmmc.c | 4 ++-- include/linux/rtsx_pci.h | 1 - 3 files changed, 2 insertions(+), 6 deletions(-) (limited to 'include/linux') diff --git a/drivers/misc/cardreader/rts5249.c b/drivers/misc/cardreader/rts5249.c index 87d576a03e68..38aefd8db452 100644 --- a/drivers/misc/cardreader/rts5249.c +++ b/drivers/misc/cardreader/rts5249.c @@ -78,9 +78,6 @@ static void rtsx_base_fetch_vendor_settings(struct rtsx_pcr *pcr) if (CHK_PCI_PID(pcr, PID_524A) || CHK_PCI_PID(pcr, PID_525A)) pcr->rtd3_en = rtsx_reg_to_rtd3_uhsii(reg); - if (CHK_PCI_PID(pcr, PID_525A)) - pcr->extra_caps |= EXTRA_CAPS_NO_AGGRESSIVE_PM; - if (rtsx_check_mmc_support(reg)) pcr->extra_caps |= EXTRA_CAPS_NO_MMC; pcr->sd30_drive_sel_3v3 = rtsx_reg_to_sd30_drive_sel_3v3(reg); diff --git a/drivers/mmc/host/rtsx_pci_sdmmc.c b/drivers/mmc/host/rtsx_pci_sdmmc.c index 5d3599ee06bf..dc2587ff8519 100644 --- a/drivers/mmc/host/rtsx_pci_sdmmc.c +++ b/drivers/mmc/host/rtsx_pci_sdmmc.c @@ -1456,8 +1456,8 @@ static void realtek_init_host(struct realtek_pci_sdmmc *host) mmc->caps = MMC_CAP_4_BIT_DATA | MMC_CAP_SD_HIGHSPEED | MMC_CAP_MMC_HIGHSPEED | MMC_CAP_BUS_WIDTH_TEST | MMC_CAP_UHS_SDR12 | MMC_CAP_UHS_SDR25; - if (pcr->rtd3_en && !(pcr->extra_caps & EXTRA_CAPS_NO_AGGRESSIVE_PM)) - mmc->caps |= MMC_CAP_AGGRESSIVE_PM; + if (pcr->rtd3_en) + mmc->caps = mmc->caps | MMC_CAP_AGGRESSIVE_PM; mmc->caps2 = MMC_CAP2_NO_PRESCAN_POWERUP | MMC_CAP2_FULL_PWR_CYCLE | MMC_CAP2_NO_SDIO; mmc->max_current_330 = 400; diff --git a/include/linux/rtsx_pci.h b/include/linux/rtsx_pci.h index f6122349c00e..3c5689356004 100644 --- a/include/linux/rtsx_pci.h +++ b/include/linux/rtsx_pci.h @@ -1230,7 +1230,6 @@ struct rtsx_pcr { #define EXTRA_CAPS_MMC_8BIT (1 << 5) #define EXTRA_CAPS_NO_MMC (1 << 7) #define EXTRA_CAPS_SD_EXPRESS (1 << 8) -#define EXTRA_CAPS_NO_AGGRESSIVE_PM (1 << 9) u32 extra_caps; #define IC_VER_A 0 -- cgit v1.2.3 From ed30d0296373e354f35ffc14558f265934dfa790 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Mon, 24 Nov 2025 18:27:50 -0800 Subject: mfd: tps6105x: Fix kernel-doc warnings relating to the core struct and tps6105x_mode Fix spelling of an enum to fix a kernel-doc warning. Fix kernel-doc of struct tps6105x to prevent kernel-doc warnings. Warning: include/linux/mfd/tps6105x.h:68 Enum value 'TPS6105X_MODE_TORCH' not described in enum 'tps6105x_mode' Warning: include/linux/mfd/tps6105x.h:68 Excess enum value '%TPS61905X_MODE_TORCH' description in 'tps6105x_mode' Warning: include/linux/mfd/tps6105x.h:93 struct member 'pdata' not described in 'tps6105x' Warning: include/linux/mfd/tps6105x.h:93 struct member 'client' not described in 'tps6105x' Fixes: 798a8eee44da ("mfd: Add a core driver for TI TPS61050/TPS61052 chips v2") Signed-off-by: Randy Dunlap Link: https://patch.msgid.link/20251125022750.3165569-1-rdunlap@infradead.org Signed-off-by: Lee Jones --- include/linux/mfd/tps6105x.h | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mfd/tps6105x.h b/include/linux/mfd/tps6105x.h index b1313411ef09..94e699896240 100644 --- a/include/linux/mfd/tps6105x.h +++ b/include/linux/mfd/tps6105x.h @@ -53,7 +53,7 @@ /** * enum tps6105x_mode - desired mode for the TPS6105x * @TPS6105X_MODE_SHUTDOWN: this instance is inactive, not used for anything - * @TPS61905X_MODE_TORCH: this instance is used as a LED, usually a while + * @TPS6105X_MODE_TORCH: this instance is used as a LED, usually a while * LED, for example as backlight or flashlight. If this is set, the * TPS6105X will register to the LED framework * @TPS6105X_MODE_TORCH_FLASH: this instance is used as a flashgun, usually @@ -82,7 +82,8 @@ struct tps6105x_platform_data { /** * struct tps6105x - state holder for the TPS6105x drivers - * @i2c_client: corresponding I2C client + * @pdata: associated platform data + * @client: corresponding I2C client * @regulator: regulator device if used in voltage mode * @regmap: used for i2c communcation on accessing registers */ -- cgit v1.2.3 From a71fee8421cf05d022880ae102983cd46535dac0 Mon Sep 17 00:00:00 2001 From: Andreas Kemnade Date: Sun, 7 Dec 2025 09:50:24 +0100 Subject: mfd: bd71828: Add some missing charger related registers As there are some registers missing which are required for future charger extensions, add them. Signed-off-by: Andreas Kemnade Reviewed-by: Matti Vaittinen Link: https://patch.msgid.link/20251207085024.7375-1-andreas@kemnade.info Signed-off-by: Lee Jones --- include/linux/mfd/rohm-bd71828.h | 2 ++ 1 file changed, 2 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mfd/rohm-bd71828.h b/include/linux/mfd/rohm-bd71828.h index 73a71ef69152..39fe275a8117 100644 --- a/include/linux/mfd/rohm-bd71828.h +++ b/include/linux/mfd/rohm-bd71828.h @@ -249,6 +249,8 @@ enum { #define BD71828_REG_BATCAP_MON_LIMIT_U 0xcc #define BD71828_REG_CONF 0x64 +#define BD71828_REG_ILIM_STAT 0x6d +#define BD71828_REG_DCIN_SET 0x70 #define BD71828_REG_DCIN_CLPS 0x71 #define BD71828_REG_MEAS_CLEAR 0xaf -- cgit v1.2.3 From d3fcf276b501a82d4504fd5b1ed40249546530d1 Mon Sep 17 00:00:00 2001 From: "Kory Maincent (TI.com)" Date: Thu, 18 Dec 2025 16:06:28 +0100 Subject: mfd: tps65219: Implement LOCK register handling for TPS65214 The TPS65214 PMIC variant has a LOCK_REG register that prevents writes to nearly all registers when locked. Unlock the registers at probe time and leave them unlocked permanently. This approach is justified because: - Register locking is very uncommon in typical system operation - No code path is expected to lock the registers during runtime - Adding a custom regmap write function would add overhead to every register write, including voltage changes triggered by CPU OPP transitions from the cpufreq governor which could happen quite frequently Cc: stable@vger.kernel.org Fixes: 7947219ab1a2d ("mfd: tps65219: Add support for TI TPS65214 PMIC") Reviewed-by: Andrew Davis Signed-off-by: Kory Maincent (TI.com) Link: https://patch.msgid.link/20251218-fix_tps65219-v5-1-8bb511417f3a@bootlin.com Signed-off-by: Lee Jones --- drivers/mfd/tps65219.c | 9 +++++++++ include/linux/mfd/tps65219.h | 2 ++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/drivers/mfd/tps65219.c b/drivers/mfd/tps65219.c index 65a952555218..7275dcdb7c44 100644 --- a/drivers/mfd/tps65219.c +++ b/drivers/mfd/tps65219.c @@ -498,6 +498,15 @@ static int tps65219_probe(struct i2c_client *client) return ret; } + if (chip_id == TPS65214) { + ret = i2c_smbus_write_byte_data(client, TPS65214_REG_LOCK, + TPS65214_LOCK_ACCESS_CMD); + if (ret) { + dev_err(tps->dev, "Failed to unlock registers %d\n", ret); + return ret; + } + } + ret = devm_regmap_add_irq_chip(tps->dev, tps->regmap, client->irq, IRQF_ONESHOT, 0, pmic->irq_chip, &tps->irq_data); diff --git a/include/linux/mfd/tps65219.h b/include/linux/mfd/tps65219.h index 55234e771ba7..3abf937191d0 100644 --- a/include/linux/mfd/tps65219.h +++ b/include/linux/mfd/tps65219.h @@ -149,6 +149,8 @@ enum pmic_id { #define TPS65215_ENABLE_LDO2_EN_MASK BIT(5) #define TPS65214_ENABLE_LDO1_EN_MASK BIT(5) #define TPS65219_ENABLE_LDO4_EN_MASK BIT(6) +/* Register Unlock */ +#define TPS65214_LOCK_ACCESS_CMD 0x5a /* power ON-OFF sequence slot */ #define TPS65219_BUCKS_LDOS_SEQUENCE_OFF_SLOT_MASK GENMASK(3, 0) #define TPS65219_BUCKS_LDOS_SEQUENCE_ON_SLOT_MASK GENMASK(7, 4) -- cgit v1.2.3 From f78263a87336056b5948396c646d7d8413a2c2ea Mon Sep 17 00:00:00 2001 From: Chen-Yu Tsai Date: Thu, 25 Dec 2025 16:02:40 +0800 Subject: mfd: axp20x: AXP717: Add type-C CC registers The AXP717 has some extra registers related to type-C CC pin negotiation. They were missing from the original submission. Add them for completeness. Signed-off-by: Chen-Yu Tsai Reviewed-by: Jernej Skrabec Link: https://patch.msgid.link/20251225080241.3153453-1-wens@kernel.org Signed-off-by: Lee Jones --- drivers/mfd/axp20x.c | 5 ++++- include/linux/mfd/axp20x.h | 3 +++ 2 files changed, 7 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/drivers/mfd/axp20x.c b/drivers/mfd/axp20x.c index c5f0ebae327f..679364189ea5 100644 --- a/drivers/mfd/axp20x.c +++ b/drivers/mfd/axp20x.c @@ -229,6 +229,8 @@ static const struct regmap_range axp717_writeable_ranges[] = { regmap_reg_range(AXP717_DCDC_OUTPUT_CONTROL, AXP717_CPUSLDO_CONTROL), regmap_reg_range(AXP717_ADC_CH_EN_CONTROL, AXP717_ADC_CH_EN_CONTROL), regmap_reg_range(AXP717_ADC_DATA_SEL, AXP717_ADC_DATA_SEL), + regmap_reg_range(AXP717_TYPEC_CC_AA_EN, AXP717_TYPEC_CC_AA_EN), + regmap_reg_range(AXP717_TYPEC_CC_MODE_CONTROL, AXP717_TYPEC_CC_MODE_CONTROL), }; static const struct regmap_range axp717_volatile_ranges[] = { @@ -237,6 +239,7 @@ static const struct regmap_range axp717_volatile_ranges[] = { regmap_reg_range(AXP717_BATT_PERCENT_DATA, AXP717_BATT_PERCENT_DATA), regmap_reg_range(AXP717_BATT_V_H, AXP717_BATT_CHRG_I_L), regmap_reg_range(AXP717_ADC_DATA_H, AXP717_ADC_DATA_L), + regmap_reg_range(AXP717_TYPEC_CC_STATUS, AXP717_TYPEC_CC_STATUS), }; static const struct regmap_access_table axp717_writeable_table = { @@ -458,7 +461,7 @@ static const struct regmap_config axp717_regmap_config = { .val_bits = 8, .wr_table = &axp717_writeable_table, .volatile_table = &axp717_volatile_table, - .max_register = AXP717_ADC_DATA_L, + .max_register = AXP717_TYPEC_CC_STATUS, .cache_type = REGCACHE_MAPLE, }; diff --git a/include/linux/mfd/axp20x.h b/include/linux/mfd/axp20x.h index 3c5aecf1d4b5..b352661d99a1 100644 --- a/include/linux/mfd/axp20x.h +++ b/include/linux/mfd/axp20x.h @@ -174,6 +174,9 @@ enum axp20x_variants { #define AXP717_ADC_DATA_SEL 0xcd #define AXP717_ADC_DATA_H 0xce #define AXP717_ADC_DATA_L 0xcf +#define AXP717_TYPEC_CC_AA_EN 0xe1 +#define AXP717_TYPEC_CC_MODE_CONTROL 0xe3 +#define AXP717_TYPEC_CC_STATUS 0xe7 #define AXP806_STARTUP_SRC 0x00 #define AXP806_CHIP_ID 0x03 -- cgit v1.2.3 From 88fd1f90792411226f49e5f285bf3faca5ac970b Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 20 Jan 2026 01:20:41 -0500 Subject: iio: core: Add and export __iio_dev_mode_lock() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add unconditional wrappers around the internal IIO mode lock. As mentioned in the documentation, this is not meant to be used by drivers, instead this will aid in the eventual addition of cleanup classes around conditional locks. Reviewed-by: David Lechner Reviewed-by: Nuno Sá Signed-off-by: Kurt Borja Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 30 ++++++++++++++++++++++++++++++ include/linux/iio/iio.h | 3 +++ 2 files changed, 33 insertions(+) (limited to 'include/linux') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index f69deefcfb6f..db803267df6e 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -2171,6 +2171,36 @@ int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev, } EXPORT_SYMBOL_GPL(__devm_iio_device_register); +/** + * __iio_dev_mode_lock() - Locks the current IIO device mode + * @indio_dev: the iio_dev associated with the device + * + * If the device is either in direct or buffer mode, it's guaranteed to stay + * that way until __iio_dev_mode_unlock() is called. + * + * This function is not meant to be used directly by drivers to protect internal + * state; a driver should have it's own mechanisms for that matter. + * + * There are very few cases where a driver actually needs to lock the current + * mode unconditionally. It's recommended to use iio_device_claim_direct() or + * iio_device_claim_buffer_mode() pairs or related helpers instead. + */ +void __iio_dev_mode_lock(struct iio_dev *indio_dev) +{ + mutex_lock(&to_iio_dev_opaque(indio_dev)->mlock); +} +EXPORT_SYMBOL_GPL(__iio_dev_mode_lock); + +/** + * __iio_dev_mode_unlock() - Unlocks the current IIO device mode + * @indio_dev: the iio_dev associated with the device + */ +void __iio_dev_mode_unlock(struct iio_dev *indio_dev) +{ + mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock); +} +EXPORT_SYMBOL_GPL(__iio_dev_mode_unlock); + /** * __iio_device_claim_direct - Keep device in direct mode * @indio_dev: the iio_dev associated with the device diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 872ebdf0dd77..aecda887d833 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -661,6 +661,9 @@ void iio_device_unregister(struct iio_dev *indio_dev); int __devm_iio_device_register(struct device *dev, struct iio_dev *indio_dev, struct module *this_mod); int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp); + +void __iio_dev_mode_lock(struct iio_dev *indio_dev) __acquires(indio_dev); +void __iio_dev_mode_unlock(struct iio_dev *indio_dev) __releases(indio_dev); bool __iio_device_claim_direct(struct iio_dev *indio_dev); void __iio_device_release_direct(struct iio_dev *indio_dev); -- cgit v1.2.3 From c37ec9d507966a827913f42e06179e3475a00181 Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 20 Jan 2026 01:20:42 -0500 Subject: iio: core: Refactor iio_device_claim_direct() implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit In order to eventually unify the locking API, implement iio_device_claim_direct() fully inline, with the use of __iio_dev_mode_lock(), which takes care of sparse annotations. Reviewed-by: David Lechner Reviewed-by: Nuno Sá Signed-off-by: Kurt Borja Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-core.c | 44 ----------------------------------------- include/linux/iio/iio.h | 40 ++++++++++++++++++++++++++----------- 2 files changed, 28 insertions(+), 56 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index db803267df6e..0f8e3aa98b72 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -2201,50 +2201,6 @@ void __iio_dev_mode_unlock(struct iio_dev *indio_dev) } EXPORT_SYMBOL_GPL(__iio_dev_mode_unlock); -/** - * __iio_device_claim_direct - Keep device in direct mode - * @indio_dev: the iio_dev associated with the device - * - * If the device is in direct mode it is guaranteed to stay - * that way until __iio_device_release_direct() is called. - * - * Use with __iio_device_release_direct(). - * - * Drivers should only call iio_device_claim_direct(). - * - * Returns: true on success, false on failure. - */ -bool __iio_device_claim_direct(struct iio_dev *indio_dev) -{ - struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - - mutex_lock(&iio_dev_opaque->mlock); - - if (iio_buffer_enabled(indio_dev)) { - mutex_unlock(&iio_dev_opaque->mlock); - return false; - } - return true; -} -EXPORT_SYMBOL_GPL(__iio_device_claim_direct); - -/** - * __iio_device_release_direct - releases claim on direct mode - * @indio_dev: the iio_dev associated with the device - * - * Release the claim. Device is no longer guaranteed to stay - * in direct mode. - * - * Drivers should only call iio_device_release_direct(). - * - * Use with __iio_device_claim_direct() - */ -void __iio_device_release_direct(struct iio_dev *indio_dev) -{ - mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock); -} -EXPORT_SYMBOL_GPL(__iio_device_release_direct); - /** * iio_device_claim_buffer_mode - Keep device in buffer mode * @indio_dev: the iio_dev associated with the device diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index aecda887d833..e263ab5eeccf 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -664,31 +664,47 @@ int iio_push_event(struct iio_dev *indio_dev, u64 ev_code, s64 timestamp); void __iio_dev_mode_lock(struct iio_dev *indio_dev) __acquires(indio_dev); void __iio_dev_mode_unlock(struct iio_dev *indio_dev) __releases(indio_dev); -bool __iio_device_claim_direct(struct iio_dev *indio_dev); -void __iio_device_release_direct(struct iio_dev *indio_dev); /* * Helper functions that allow claim and release of direct mode * in a fashion that doesn't generate many false positives from sparse. * Note this must remain static inline in the header so that sparse - * can see the __acquire() marking. Revisit when sparse supports - * __cond_acquires() + * can see the __acquires() and __releases() annotations. + */ + +/** + * iio_device_claim_direct() - Keep device in direct mode + * @indio_dev: the iio_dev associated with the device + * + * If the device is in direct mode it is guaranteed to stay + * that way until iio_device_release_direct() is called. + * + * Use with iio_device_release_direct(). + * + * Returns: true on success, false on failure. */ static inline bool iio_device_claim_direct(struct iio_dev *indio_dev) { - if (!__iio_device_claim_direct(indio_dev)) - return false; + __iio_dev_mode_lock(indio_dev); - __acquire(iio_dev); + if (iio_buffer_enabled(indio_dev)) { + __iio_dev_mode_unlock(indio_dev); + return false; + } return true; } -static inline void iio_device_release_direct(struct iio_dev *indio_dev) -{ - __iio_device_release_direct(indio_dev); - __release(indio_dev); -} +/** + * iio_device_release_direct() - Releases claim on direct mode + * @indio_dev: the iio_dev associated with the device + * + * Release the claim. Device is no longer guaranteed to stay + * in direct mode. + * + * Use with iio_device_claim_direct(). + */ +#define iio_device_release_direct(indio_dev) __iio_dev_mode_unlock(indio_dev) int iio_device_claim_buffer_mode(struct iio_dev *indio_dev); void iio_device_release_buffer_mode(struct iio_dev *indio_dev); -- cgit v1.2.3 From 2daee817df13fb539be01a6a8094d52667d402f6 Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 20 Jan 2026 01:20:43 -0500 Subject: iio: core: Match iio_device_claim_*() semantics and implementation MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Implement iio_device_claim_buffer_mode() fully inline with the use of __iio_dev_mode_lock(), which takes care of sparse annotations. To completely match iio_device_claim_direct() semantics, we need to also change iio_device_claim_buffer_mode() return semantics to usual true/false conditional lock semantics. Additionally, to avoid silently breaking out-of-tree drivers, rename iio_device_claim_buffer_mode() to iio_device_claim_try_buffer_mode(). Reviewed-by: David Lechner Reviewed-by: Nuno Sá Signed-off-by: Kurt Borja Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ade9000.c | 2 +- .../common/cros_ec_sensors/cros_ec_sensors_core.c | 5 +-- drivers/iio/health/max30100.c | 8 +---- drivers/iio/health/max30102.c | 2 +- drivers/iio/industrialio-core.c | 42 +--------------------- drivers/iio/light/opt4060.c | 2 +- include/linux/iio/iio.h | 35 ++++++++++++++++-- 7 files changed, 39 insertions(+), 57 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/adc/ade9000.c b/drivers/iio/adc/ade9000.c index 2de8a718d62a..db085dc5e526 100644 --- a/drivers/iio/adc/ade9000.c +++ b/drivers/iio/adc/ade9000.c @@ -964,7 +964,7 @@ static irqreturn_t ade9000_dready_thread(int irq, void *data) struct iio_dev *indio_dev = data; /* Handle data ready interrupt from C4/EVENT/DREADY pin */ - if (!iio_device_claim_buffer_mode(indio_dev)) { + if (iio_device_try_claim_buffer_mode(indio_dev)) { ade9000_iio_push_buffer(indio_dev); iio_device_release_buffer_mode(indio_dev); } diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index 9ac80e4b7d75..ef53066b1735 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -188,11 +188,8 @@ int cros_ec_sensors_push_data(struct iio_dev *indio_dev, /* * Ignore samples if the buffer is not set: it is needed if the ODR is * set but the buffer is not enabled yet. - * - * Note: iio_device_claim_buffer_mode() returns -EBUSY if the buffer - * is not enabled. */ - if (iio_device_claim_buffer_mode(indio_dev) < 0) + if (!iio_device_try_claim_buffer_mode(indio_dev)) return 0; out = (s16 *)st->samples; diff --git a/drivers/iio/health/max30100.c b/drivers/iio/health/max30100.c index 3d441013893c..7dfdb5eb305e 100644 --- a/drivers/iio/health/max30100.c +++ b/drivers/iio/health/max30100.c @@ -417,13 +417,7 @@ static int max30100_read_raw(struct iio_dev *indio_dev, * Temperature reading can only be acquired while engine * is running */ - if (iio_device_claim_buffer_mode(indio_dev)) { - /* - * Replacing -EBUSY or other error code - * returned by iio_device_claim_buffer_mode() - * because user space may rely on the current - * one. - */ + if (!iio_device_try_claim_buffer_mode(indio_dev)) { ret = -EAGAIN; } else { ret = max30100_get_temp(data, val); diff --git a/drivers/iio/health/max30102.c b/drivers/iio/health/max30102.c index a48c0881a4c7..6918fcb5de2b 100644 --- a/drivers/iio/health/max30102.c +++ b/drivers/iio/health/max30102.c @@ -476,7 +476,7 @@ static int max30102_read_raw(struct iio_dev *indio_dev, * shutdown; leave shutdown briefly when buffer not running */ any_mode_retry: - if (iio_device_claim_buffer_mode(indio_dev)) { + if (!iio_device_try_claim_buffer_mode(indio_dev)) { /* * This one is a *bit* hacky. If we cannot claim buffer * mode, then try direct mode so that we make sure diff --git a/drivers/iio/industrialio-core.c b/drivers/iio/industrialio-core.c index 0f8e3aa98b72..3115d59c1372 100644 --- a/drivers/iio/industrialio-core.c +++ b/drivers/iio/industrialio-core.c @@ -2183,7 +2183,7 @@ EXPORT_SYMBOL_GPL(__devm_iio_device_register); * * There are very few cases where a driver actually needs to lock the current * mode unconditionally. It's recommended to use iio_device_claim_direct() or - * iio_device_claim_buffer_mode() pairs or related helpers instead. + * iio_device_try_claim_buffer_mode() pairs or related helpers instead. */ void __iio_dev_mode_lock(struct iio_dev *indio_dev) { @@ -2201,46 +2201,6 @@ void __iio_dev_mode_unlock(struct iio_dev *indio_dev) } EXPORT_SYMBOL_GPL(__iio_dev_mode_unlock); -/** - * iio_device_claim_buffer_mode - Keep device in buffer mode - * @indio_dev: the iio_dev associated with the device - * - * If the device is in buffer mode it is guaranteed to stay - * that way until iio_device_release_buffer_mode() is called. - * - * Use with iio_device_release_buffer_mode(). - * - * Returns: 0 on success, -EBUSY on failure. - */ -int iio_device_claim_buffer_mode(struct iio_dev *indio_dev) -{ - struct iio_dev_opaque *iio_dev_opaque = to_iio_dev_opaque(indio_dev); - - mutex_lock(&iio_dev_opaque->mlock); - - if (iio_buffer_enabled(indio_dev)) - return 0; - - mutex_unlock(&iio_dev_opaque->mlock); - return -EBUSY; -} -EXPORT_SYMBOL_GPL(iio_device_claim_buffer_mode); - -/** - * iio_device_release_buffer_mode - releases claim on buffer mode - * @indio_dev: the iio_dev associated with the device - * - * Release the claim. Device is no longer guaranteed to stay - * in buffer mode. - * - * Use with iio_device_claim_buffer_mode(). - */ -void iio_device_release_buffer_mode(struct iio_dev *indio_dev) -{ - mutex_unlock(&to_iio_dev_opaque(indio_dev)->mlock); -} -EXPORT_SYMBOL_GPL(iio_device_release_buffer_mode); - /** * iio_device_get_current_mode() - helper function providing read-only access to * the opaque @currentmode variable diff --git a/drivers/iio/light/opt4060.c b/drivers/iio/light/opt4060.c index 981c704e7df5..8c4a1f562a83 100644 --- a/drivers/iio/light/opt4060.c +++ b/drivers/iio/light/opt4060.c @@ -304,7 +304,7 @@ static int opt4060_set_driver_state(struct iio_dev *indio_dev, struct opt4060_chip *chip = iio_priv(indio_dev); int ret = 0; any_mode_retry: - if (iio_device_claim_buffer_mode(indio_dev)) { + if (!iio_device_try_claim_buffer_mode(indio_dev)) { /* * This one is a *bit* hacky. If we cannot claim buffer mode, * then try direct mode so that we make sure things cannot diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index e263ab5eeccf..36bd14e93a75 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -706,8 +706,39 @@ static inline bool iio_device_claim_direct(struct iio_dev *indio_dev) */ #define iio_device_release_direct(indio_dev) __iio_dev_mode_unlock(indio_dev) -int iio_device_claim_buffer_mode(struct iio_dev *indio_dev); -void iio_device_release_buffer_mode(struct iio_dev *indio_dev); +/** + * iio_device_try_claim_buffer_mode() - Keep device in buffer mode + * @indio_dev: the iio_dev associated with the device + * + * If the device is in buffer mode it is guaranteed to stay + * that way until iio_device_release_buffer_mode() is called. + * + * Use with iio_device_release_buffer_mode(). + * + * Returns: true on success, false on failure. + */ +static inline bool iio_device_try_claim_buffer_mode(struct iio_dev *indio_dev) +{ + __iio_dev_mode_lock(indio_dev); + + if (!iio_buffer_enabled(indio_dev)) { + __iio_dev_mode_unlock(indio_dev); + return false; + } + + return true; +} + +/** + * iio_device_release_buffer_mode() - releases claim on buffer mode + * @indio_dev: the iio_dev associated with the device + * + * Release the claim. Device is no longer guaranteed to stay + * in buffer mode. + * + * Use with iio_device_try_claim_buffer_mode(). + */ +#define iio_device_release_buffer_mode(indio_dev) __iio_dev_mode_unlock(indio_dev) extern const struct bus_type iio_bus_type; -- cgit v1.2.3 From 7a38b75da1dc38a8cd9563c41b87367b2475b975 Mon Sep 17 00:00:00 2001 From: Kurt Borja Date: Tue, 20 Jan 2026 01:20:44 -0500 Subject: iio: core: Add cleanup.h support for iio_device_claim_*() MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Add guard classes for iio_device_claim_*() conditional locks. This will aid drivers write safer and cleaner code when dealing with some common patterns. These classes are not meant to be used directly by drivers (hence the __priv__ prefix). Instead, documented wrapper macros are provided to enforce the use of ACQUIRE() or guard() semantics and avoid the problematic scoped guard. Suggested-by: Andy Shevchenko Reviewed-by: David Lechner Reviewed-by: Nuno Sá Acked-by: Andy Shevchenko Signed-off-by: Kurt Borja Signed-off-by: Jonathan Cameron --- include/linux/iio/iio.h | 65 +++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 65 insertions(+) (limited to 'include/linux') diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index 36bd14e93a75..a9ecff191bd9 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -10,6 +10,7 @@ #include #include #include +#include #include #include #include @@ -740,6 +741,70 @@ static inline bool iio_device_try_claim_buffer_mode(struct iio_dev *indio_dev) */ #define iio_device_release_buffer_mode(indio_dev) __iio_dev_mode_unlock(indio_dev) +/* + * These classes are not meant to be used directly by drivers (hence the + * __priv__ prefix). Instead, documented wrapper macros are provided below to + * enforce the use of ACQUIRE() or guard() semantics and avoid the problematic + * scoped guard variants. + */ +DEFINE_GUARD(__priv__iio_dev_mode_lock, struct iio_dev *, + __iio_dev_mode_lock(_T), __iio_dev_mode_unlock(_T)); +DEFINE_GUARD_COND(__priv__iio_dev_mode_lock, _try_direct, + iio_device_claim_direct(_T)); + +/** + * IIO_DEV_ACQUIRE_DIRECT_MODE() - Tries to acquire the direct mode lock with + * automatic release + * @dev: IIO device instance + * @claim: Variable identifier to store acquire result + * + * Tries to acquire the direct mode lock with cleanup ACQUIRE() semantics and + * automatically releases it at the end of the scope. It most be always paired + * with IIO_DEV_ACQUIRE_ERR(), for example (notice the scope braces):: + * + * switch() { + * case IIO_CHAN_INFO_RAW: { + * IIO_DEV_ACQUIRE_DIRECT_MODE(indio_dev, claim); + * if (IIO_DEV_ACQUIRE_FAILED(claim)) + * return -EBUSY; + * + * ... + * } + * case IIO_CHAN_INFO_SCALE: + * ... + * ... + * } + * + * Context: Can sleep + */ +#define IIO_DEV_ACQUIRE_DIRECT_MODE(dev, claim) \ + ACQUIRE(__priv__iio_dev_mode_lock_try_direct, claim)(dev) + +/** + * IIO_DEV_ACQUIRE_FAILED() - ACQUIRE_ERR() wrapper + * @claim: The claim variable passed to IIO_DEV_ACQUIRE_*_MODE() + * + * Return: true if failed to acquire the mode, otherwise false. + */ +#define IIO_DEV_ACQUIRE_FAILED(claim) \ + ACQUIRE_ERR(__priv__iio_dev_mode_lock_try_direct, &(claim)) + +/** + * IIO_DEV_GUARD_CURRENT_MODE() - Acquires the mode lock with automatic release + * @dev: IIO device instance + * + * Acquires the mode lock with cleanup guard() semantics. It is usually paired + * with iio_buffer_enabled(). + * + * This should *not* be used to protect internal driver state and it's use in + * general is *strongly* discouraged. Use any of the IIO_DEV_ACQUIRE_*_MODE() + * variants. + * + * Context: Can sleep + */ +#define IIO_DEV_GUARD_CURRENT_MODE(dev) \ + guard(__priv__iio_dev_mode_lock)(dev) + extern const struct bus_type iio_bus_type; /** -- cgit v1.2.3 From 404a3b4c36f2e1987ff28d1d61d309292d2e33cb Mon Sep 17 00:00:00 2001 From: Jonathan Santos Date: Wed, 14 Jan 2026 06:27:03 -0300 Subject: units: add conversion macros for percentage related units Add macros to convert between ratio and percentage related units, including percent (1/100), permille (1/1,000), permyriad (1/10,000, also equivalent to one Basis point) and per cent mille (1/100,000). Those are Used for precise fractional calculations in engineering, finance, and measurement applications. Signed-off-by: Jonathan Santos Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- include/linux/units.h | 19 +++++++++++++++++++ 1 file changed, 19 insertions(+) (limited to 'include/linux') diff --git a/include/linux/units.h b/include/linux/units.h index 00e15de33eca..3471c5a38dcf 100644 --- a/include/linux/units.h +++ b/include/linux/units.h @@ -21,6 +21,25 @@ #define PICO 1000000000000ULL #define FEMTO 1000000000000000ULL +/* + * Percentage and related scaling units + * + * These macros define scaling factors used to convert between ratio and + * percentage-based representations with different decimal resolutions. + * They are used for precise fractional calculations in engineering, finance, + * and measurement applications. + * + * Examples: + * 1% = 0.01 = 1 / PERCENT + * 0.1% = 0.001 = 1 / PERMILLE + * 0.01% = 0.0001 = 1 / PERMYRIAD (1 basis point) + * 0.001% = 0.00001 = 1 / PERCENTMILLE + */ +#define PERCENT 100 +#define PERMILLE 1000 +#define PERMYRIAD 10000 +#define PERCENTMILLE 100000 + #define NANOHZ_PER_HZ 1000000000UL #define MICROHZ_PER_HZ 1000000UL #define MILLIHZ_PER_HZ 1000UL -- cgit v1.2.3 From dfd7b082c0a22ff0d697605a2a6bb62d9a4730a6 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 19 Jan 2026 13:18:18 +0000 Subject: usb: typec: Add mode_control field to port property This new field in the port properties dictates whether the Platform Policy Manager (PPM) allows the OS Policy Manager (OPM) to change the currently active, negotiated alternate mode. Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Reviewed-by: Benson Leung Link: https://patch.msgid.link/20260119131824.2529334-2-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/class.c | 9 ++++++--- drivers/usb/typec/class.h | 1 + include/linux/usb/typec.h | 2 ++ 3 files changed, 9 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index c4ff4310ff58..0f12d6120511 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -457,11 +457,13 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj, struct attribute *attr, int n) { struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj)); + struct typec_port *port = typec_altmode2port(adev); if (attr == &dev_attr_active.attr) - if (!is_typec_port(adev->dev.parent) && - (!adev->ops || !adev->ops->activate)) - return 0444; + if (!is_typec_port(adev->dev.parent)) { + if (!port->mode_control || !adev->ops || !adev->ops->activate) + return 0444; + } return attr->mode; } @@ -2708,6 +2710,7 @@ struct typec_port *typec_register_port(struct device *parent, } port->pd = cap->pd; + port->mode_control = !cap->no_mode_control; ret = device_add(&port->dev); if (ret) { diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index db2fe96c48ff..2e89a83c2eb7 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -62,6 +62,7 @@ struct typec_port { struct mutex partner_link_lock; enum typec_orientation orientation; + bool mode_control; struct typec_switch *sw; struct typec_mux *mux; struct typec_retimer *retimer; diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index c6fd46902fce..dbb259d88526 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -290,6 +290,7 @@ enum usb_pd_svdm_ver { * @prefer_role: Initial role preference (DRP ports). * @accessory: Supported Accessory Modes * @usb_capability: Supported USB Modes + * @no_mode_control: Ability to manage Alternate Modes * @fwnode: Optional fwnode of the port * @driver_data: Private pointer for driver specific info * @pd: Optional USB Power Delivery Support @@ -307,6 +308,7 @@ struct typec_capability { enum typec_accessory accessory[TYPEC_MAX_ACCESSORY]; unsigned int orientation_aware:1; u8 usb_capability; + bool no_mode_control; struct fwnode_handle *fwnode; void *driver_data; -- cgit v1.2.3 From 027b304ca3f6989f6946b5b3bcc727cf3f54774f Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 19 Jan 2026 13:18:20 +0000 Subject: usb: typec: Expose alternate mode priority via sysfs This patch introduces a priority sysfs attribute to the USB Type-C alternate mode port interface. This new attribute allows user-space to configure the numeric priority of alternate modes managing their preferred order of operation. If a new priority value conflicts with an existing mode's priority, the priorities of the conflicting mode and all subsequent modes are automatically incremented to ensure uniqueness. Signed-off-by: Andrei Kuchynski Reviewed-by: Benson Leung Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260119131824.2529334-4-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- Documentation/ABI/testing/sysfs-class-typec | 11 ++++ drivers/usb/typec/class.c | 90 ++++++++++++++++++++++++++++- include/linux/usb/typec_altmode.h | 1 + 3 files changed, 101 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-class-typec b/Documentation/ABI/testing/sysfs-class-typec index 38e101c17a00..737b76828b50 100644 --- a/Documentation/ABI/testing/sysfs-class-typec +++ b/Documentation/ABI/testing/sysfs-class-typec @@ -162,6 +162,17 @@ Description: Lists the supported USB Modes. The default USB mode that is used - usb3 (USB 3.2) - usb4 (USB4) +What: /sys/class/typec///priority +Date: July 2025 +Contact: Andrei Kuchynski +Description: + Displays and allows setting the priority for a specific alternate mode. + The priority is an integer in the range 0-255. A lower numerical value + indicates a higher priority (0 is the highest). + If the new value is already in use by another mode, the priority of the + conflicting mode and any subsequent modes will be incremented until they + are all unique. + USB Type-C partner devices (eg. /sys/class/typec/port0-partner/) What: /sys/class/typec/-partner/accessory_mode diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index 0f12d6120511..a48c44712518 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -445,11 +445,88 @@ svid_show(struct device *dev, struct device_attribute *attr, char *buf) } static DEVICE_ATTR_RO(svid); +static int increment_duplicated_priority(struct device *dev, void *data) +{ + if (is_typec_port_altmode(dev)) { + struct typec_altmode **alt_target = (struct typec_altmode **)data; + struct typec_altmode *alt = to_typec_altmode(dev); + + if (alt != *alt_target && alt->priority == (*alt_target)->priority) { + alt->priority++; + *alt_target = alt; + return 1; + } + } + return 0; +} + +static int find_duplicated_priority(struct device *dev, void *data) +{ + if (is_typec_port_altmode(dev)) { + struct typec_altmode **alt_target = (struct typec_altmode **)data; + struct typec_altmode *alt = to_typec_altmode(dev); + + if (alt != *alt_target && alt->priority == (*alt_target)->priority) + return 1; + } + return 0; +} + +static int typec_mode_set_priority(struct typec_altmode *alt, const u8 priority) +{ + struct typec_port *port = to_typec_port(alt->dev.parent); + const u8 old_priority = alt->priority; + int res = 1; + + alt->priority = priority; + while (res) { + res = device_for_each_child(&port->dev, &alt, find_duplicated_priority); + if (res) { + alt->priority++; + if (alt->priority == 0) { + alt->priority = old_priority; + return -EOVERFLOW; + } + } + } + + res = 1; + alt->priority = priority; + while (res) + res = device_for_each_child(&port->dev, &alt, + increment_duplicated_priority); + + return 0; +} + +static ssize_t priority_store(struct device *dev, + struct device_attribute *attr, + const char *buf, size_t size) +{ + u8 val; + int err = kstrtou8(buf, 10, &val); + + if (!err) + err = typec_mode_set_priority(to_typec_altmode(dev), val); + + if (!err) + return size; + return err; +} + +static ssize_t priority_show(struct device *dev, + struct device_attribute *attr, char *buf) +{ + return sysfs_emit(buf, "%u\n", to_typec_altmode(dev)->priority); +} +static DEVICE_ATTR_RW(priority); + static struct attribute *typec_altmode_attrs[] = { &dev_attr_active.attr, &dev_attr_mode.attr, &dev_attr_svid.attr, &dev_attr_vdo.attr, + &dev_attr_priority.attr, NULL }; @@ -459,11 +536,15 @@ static umode_t typec_altmode_attr_is_visible(struct kobject *kobj, struct typec_altmode *adev = to_typec_altmode(kobj_to_dev(kobj)); struct typec_port *port = typec_altmode2port(adev); - if (attr == &dev_attr_active.attr) + if (attr == &dev_attr_active.attr) { if (!is_typec_port(adev->dev.parent)) { if (!port->mode_control || !adev->ops || !adev->ops->activate) return 0444; } + } else if (attr == &dev_attr_priority.attr) { + if (!is_typec_port(adev->dev.parent) || !port->mode_control) + return 0; + } return attr->mode; } @@ -2498,6 +2579,7 @@ typec_port_register_altmode(struct typec_port *port, struct typec_altmode *adev; struct typec_mux *mux; struct typec_retimer *retimer; + int ret; mux = typec_mux_get(&port->dev); if (IS_ERR(mux)) @@ -2516,6 +2598,12 @@ typec_port_register_altmode(struct typec_port *port, } else { to_altmode(adev)->mux = mux; to_altmode(adev)->retimer = retimer; + + ret = typec_mode_set_priority(adev, 0); + if (ret) { + typec_unregister_altmode(adev); + return ERR_PTR(ret); + } } return adev; diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 9197a4637a93..7e6c02d74b54 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -36,6 +36,7 @@ struct typec_altmode { int mode; u32 vdo; unsigned int active:1; + u8 priority; char *desc; const struct typec_altmode_ops *ops; -- cgit v1.2.3 From fb2abc754672f769ca0bc36bdeb3c2f81927f446 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 19 Jan 2026 13:18:21 +0000 Subject: usb: typec: Implement mode selection The mode selection process is controlled by the following API functions, which allow to initiate and complete mode entry based on the priority of each mode: `typec_mode_selection_start` function compiles a priority list of supported Alternate Modes. `typec_altmode_state_update` function is invoked by the port driver to communicate the current mode of the Type-C connector. `typec_mode_selection_delete` function stops the currently running mode selection process and releases all associated system resources. `mode_selection_work_fn` task attempts to activate modes. The process stops on success; otherwise, it proceeds to the next mode after a timeout or error. Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260119131824.2529334-5-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/Makefile | 2 +- drivers/usb/typec/class.h | 2 + drivers/usb/typec/mode_selection.c | 283 +++++++++++++++++++++++++++++++++++++ include/linux/usb/typec_altmode.h | 40 ++++++ 4 files changed, 326 insertions(+), 1 deletion(-) create mode 100644 drivers/usb/typec/mode_selection.c (limited to 'include/linux') diff --git a/drivers/usb/typec/Makefile b/drivers/usb/typec/Makefile index 7a368fea61bc..8a6a1c663eb6 100644 --- a/drivers/usb/typec/Makefile +++ b/drivers/usb/typec/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0 obj-$(CONFIG_TYPEC) += typec.o -typec-y := class.o mux.o bus.o pd.o retimer.o +typec-y := class.o mux.o bus.o pd.o retimer.o mode_selection.o typec-$(CONFIG_ACPI) += port-mapper.o obj-$(CONFIG_TYPEC) += altmodes/ obj-$(CONFIG_TYPEC_TCPM) += tcpm/ diff --git a/drivers/usb/typec/class.h b/drivers/usb/typec/class.h index 2e89a83c2eb7..d3435936ee7c 100644 --- a/drivers/usb/typec/class.h +++ b/drivers/usb/typec/class.h @@ -9,6 +9,7 @@ struct typec_mux; struct typec_switch; struct usb_device; +struct mode_selection; struct typec_plug { struct device dev; @@ -39,6 +40,7 @@ struct typec_partner { u8 usb_capability; struct usb_power_delivery *pd; + struct mode_selection *sel; void (*attach)(struct typec_partner *partner, struct device *dev); void (*deattach)(struct typec_partner *partner, struct device *dev); diff --git a/drivers/usb/typec/mode_selection.c b/drivers/usb/typec/mode_selection.c new file mode 100644 index 000000000000..a95b31e21b52 --- /dev/null +++ b/drivers/usb/typec/mode_selection.c @@ -0,0 +1,283 @@ +// SPDX-License-Identifier: GPL-2.0-only +/* + * Copyright 2025 Google LLC. + */ + +#include +#include +#include +#include +#include +#include + +#include "class.h" + +/** + * struct mode_state - State tracking for a specific Type-C alternate mode + * @svid: Standard or Vendor ID of the Alternate Mode + * @priority: Mode priority + * @error: Outcome of the last attempt to enter the mode + * @list: List head to link this mode state into a prioritized list + */ +struct mode_state { + u16 svid; + u8 priority; + int error; + struct list_head list; +}; + +/** + * struct mode_selection - Manages the selection and state of Alternate Modes + * @mode_list: Prioritized list of available Alternate Modes + * @lock: Mutex to protect mode_list + * @work: Work structure + * @partner: Handle to the Type-C partner device + * @active_svid: svid of currently active mode + * @timeout: Timeout for a mode entry attempt, ms + * @delay: Delay between mode entry/exit attempts, ms + */ +struct mode_selection { + struct list_head mode_list; + /* Protects the mode_list*/ + struct mutex lock; + struct delayed_work work; + struct typec_partner *partner; + u16 active_svid; + unsigned int timeout; + unsigned int delay; +}; + +/** + * struct mode_order - Mode activation tracking + * @svid: Standard or Vendor ID of the Alternate Mode + * @enter: Flag indicating if the driver is currently attempting to enter or + * exit the mode + * @result: Outcome of the attempt to activate the mode + */ +struct mode_order { + u16 svid; + int enter; + int result; +}; + +static int activate_altmode(struct device *dev, void *data) +{ + if (is_typec_partner_altmode(dev)) { + struct typec_altmode *alt = to_typec_altmode(dev); + struct mode_order *order = (struct mode_order *)data; + + if (order->svid == alt->svid) { + if (alt->ops && alt->ops->activate) + order->result = alt->ops->activate(alt, order->enter); + else + order->result = -EOPNOTSUPP; + return 1; + } + } + return 0; +} + +static int mode_selection_activate(struct mode_selection *sel, + const u16 svid, const int enter) + + __must_hold(&sel->lock) +{ + struct mode_order order = {.svid = svid, .enter = enter, .result = -ENODEV}; + + /* + * The port driver may acquire its internal mutex during alternate mode + * activation. Since this is the same mutex that may be held during the + * execution of typec_altmode_state_update(), it is crucial to release + * sel->mutex before activation to avoid potential deadlock. + * Note that sel->mode_list must remain invariant throughout this unlocked + * interval. + */ + mutex_unlock(&sel->lock); + device_for_each_child(&sel->partner->dev, &order, activate_altmode); + mutex_lock(&sel->lock); + + return order.result; +} + +static void mode_list_clean(struct mode_selection *sel) +{ + struct mode_state *ms, *tmp; + + list_for_each_entry_safe(ms, tmp, &sel->mode_list, list) { + list_del(&ms->list); + kfree(ms); + } +} + +/** + * mode_selection_work_fn() - Alternate mode activation task + * @work: work structure + * + * - If the Alternate Mode currently prioritized at the top of the list is already + * active, the entire selection process is considered finished. + * - If a different Alternate Mode is currently active, the system must exit that + * active mode first before attempting any new entry. + * + * The function then checks the result of the attempt to entre the current mode, + * stored in the `ms->error` field: + * - if the attempt FAILED, the mode is deactivated and removed from the list. + * - `ms->error` value of 0 signifies that the mode has not yet been activated. + * + * Once successfully activated, the task is scheduled for subsequent entry after + * a timeout period. The alternate mode driver is expected to call back with the + * actual mode entry result via `typec_altmode_state_update()`. + */ +static void mode_selection_work_fn(struct work_struct *work) +{ + struct mode_selection *sel = container_of(work, + struct mode_selection, work.work); + struct mode_state *ms; + unsigned int delay = sel->delay; + int result; + + guard(mutex)(&sel->lock); + + ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list); + if (!ms) + return; + + if (sel->active_svid == ms->svid) { + dev_dbg(&sel->partner->dev, "%x altmode is active\n", ms->svid); + mode_list_clean(sel); + } else if (sel->active_svid != 0) { + result = mode_selection_activate(sel, sel->active_svid, 0); + if (result) + mode_list_clean(sel); + else + sel->active_svid = 0; + } else if (ms->error) { + dev_err(&sel->partner->dev, "%x: entry error %pe\n", + ms->svid, ERR_PTR(ms->error)); + mode_selection_activate(sel, ms->svid, 0); + list_del(&ms->list); + kfree(ms); + } else { + result = mode_selection_activate(sel, ms->svid, 1); + if (result) { + dev_err(&sel->partner->dev, "%x: activation error %pe\n", + ms->svid, ERR_PTR(result)); + list_del(&ms->list); + kfree(ms); + } else { + delay = sel->timeout; + ms->error = -ETIMEDOUT; + } + } + + if (!list_empty(&sel->mode_list)) + schedule_delayed_work(&sel->work, msecs_to_jiffies(delay)); +} + +void typec_altmode_state_update(struct typec_partner *partner, const u16 svid, + const int error) +{ + struct mode_selection *sel = partner->sel; + struct mode_state *ms; + + if (sel) { + mutex_lock(&sel->lock); + ms = list_first_entry_or_null(&sel->mode_list, struct mode_state, list); + if (ms && ms->svid == svid) { + ms->error = error; + if (cancel_delayed_work(&sel->work)) + schedule_delayed_work(&sel->work, 0); + } + if (!error) + sel->active_svid = svid; + else + sel->active_svid = 0; + mutex_unlock(&sel->lock); + } +} +EXPORT_SYMBOL_GPL(typec_altmode_state_update); + +static int compare_priorities(void *priv, + const struct list_head *a, const struct list_head *b) +{ + const struct mode_state *msa = container_of(a, struct mode_state, list); + const struct mode_state *msb = container_of(b, struct mode_state, list); + + if (msa->priority < msb->priority) + return -1; + return 1; +} + +static int altmode_add_to_list(struct device *dev, void *data) +{ + if (is_typec_partner_altmode(dev)) { + struct list_head *list = (struct list_head *)data; + struct typec_altmode *altmode = to_typec_altmode(dev); + const struct typec_altmode *pdev = typec_altmode_get_partner(altmode); + struct mode_state *ms; + + if (pdev && altmode->ops && altmode->ops->activate) { + ms = kzalloc(sizeof(*ms), GFP_KERNEL); + if (!ms) + return -ENOMEM; + ms->svid = pdev->svid; + ms->priority = pdev->priority; + INIT_LIST_HEAD(&ms->list); + list_add_tail(&ms->list, list); + } + } + return 0; +} + +int typec_mode_selection_start(struct typec_partner *partner, + const unsigned int delay, const unsigned int timeout) +{ + struct mode_selection *sel; + int ret; + + if (partner->usb_mode == USB_MODE_USB4) + return -EBUSY; + + if (partner->sel) + return -EALREADY; + + sel = kzalloc(sizeof(*sel), GFP_KERNEL); + if (!sel) + return -ENOMEM; + + INIT_LIST_HEAD(&sel->mode_list); + + ret = device_for_each_child(&partner->dev, &sel->mode_list, + altmode_add_to_list); + + if (ret || list_empty(&sel->mode_list)) { + mode_list_clean(sel); + kfree(sel); + return ret; + } + + list_sort(NULL, &sel->mode_list, compare_priorities); + sel->partner = partner; + sel->delay = delay; + sel->timeout = timeout; + mutex_init(&sel->lock); + INIT_DELAYED_WORK(&sel->work, mode_selection_work_fn); + schedule_delayed_work(&sel->work, msecs_to_jiffies(delay)); + partner->sel = sel; + + return 0; +} +EXPORT_SYMBOL_GPL(typec_mode_selection_start); + +void typec_mode_selection_delete(struct typec_partner *partner) +{ + struct mode_selection *sel = partner->sel; + + if (sel) { + partner->sel = NULL; + cancel_delayed_work_sync(&sel->work); + mode_list_clean(sel); + mutex_destroy(&sel->lock); + kfree(sel); + } +} +EXPORT_SYMBOL_GPL(typec_mode_selection_delete); diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 7e6c02d74b54..70026f5f8f99 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -240,4 +240,44 @@ void typec_altmode_unregister_driver(struct typec_altmode_driver *drv); module_driver(__typec_altmode_driver, typec_altmode_register_driver, \ typec_altmode_unregister_driver) +/** + * typec_mode_selection_start - Start an alternate mode selection process + * @partner: Handle to the Type-C partner device + * @delay: Delay between mode entry/exit attempts, ms + * @timeout: Timeout for a mode entry attempt, ms + * + * This function initiates the process of attempting to enter an Alternate Mode + * supported by the connected Type-C partner. + * Returns 0 on success, or a negative error code on failure. + */ +int typec_mode_selection_start(struct typec_partner *partner, + const unsigned int delay, const unsigned int timeout); + +/** + * typec_altmode_state_update - Report the current status of an Alternate Mode + * negotiation + * @partner: Handle to the Type-C partner device + * @svid: Standard or Vendor ID of the Alternate Mode. A value of 0 should be + * passed if no mode is currently active + * @result: Result of the entry operation. This should be 0 on success, or a + * negative error code if the negotiation failed + * + * This function should be called by an Alternate Mode driver to report the + * result of an asynchronous alternate mode entry request. It signals what the + * current active SVID is (or 0 if none) and the success or failure status of + * the last attempt. + */ +void typec_altmode_state_update(struct typec_partner *partner, const u16 svid, + const int result); + +/** + * typec_mode_selection_delete - Delete an alternate mode selection instance + * @partner: Handle to the Type-C partner device. + * + * This function cancels a pending alternate mode selection request that was + * previously started with typec_mode_selection_start(). + * This is typically called when the partner disconnects. + */ +void typec_mode_selection_delete(struct typec_partner *partner); + #endif /* __USB_TYPEC_ALTMODE_H */ -- cgit v1.2.3 From be727d4000669a64a3ce6697669150d78bb2d492 Mon Sep 17 00:00:00 2001 From: Andrei Kuchynski Date: Mon, 19 Jan 2026 13:18:22 +0000 Subject: usb: typec: Introduce mode_selection bit The port driver sets this bit for an alternate mode description to indicate support for the mode selection feature. Once set, individual Alt Mode drivers will no longer attempt to activate their respective modes within their probe functions. This prevents race conditions and non-prioritized activation. The bit is not set by default. If left unset, the system retains the current behavior where Alt Mode drivers manage their own activation logic. Signed-off-by: Andrei Kuchynski Reviewed-by: Heikki Krogerus Link: https://patch.msgid.link/20260119131824.2529334-6-akuchynski@chromium.org Signed-off-by: Greg Kroah-Hartman --- drivers/usb/typec/altmodes/displayport.c | 6 ++++-- drivers/usb/typec/altmodes/thunderbolt.c | 2 +- drivers/usb/typec/class.c | 1 + include/linux/usb/typec.h | 1 + include/linux/usb/typec_altmode.h | 1 + 5 files changed, 8 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/typec/altmodes/displayport.c b/drivers/usb/typec/altmodes/displayport.c index d96ab106a980..d185688a16b1 100644 --- a/drivers/usb/typec/altmodes/displayport.c +++ b/drivers/usb/typec/altmodes/displayport.c @@ -804,8 +804,10 @@ int dp_altmode_probe(struct typec_altmode *alt) if (plug) typec_altmode_set_drvdata(plug, dp); - dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; - schedule_work(&dp->work); + if (!alt->mode_selection) { + dp->state = plug ? DP_STATE_ENTER_PRIME : DP_STATE_ENTER; + schedule_work(&dp->work); + } return 0; } diff --git a/drivers/usb/typec/altmodes/thunderbolt.c b/drivers/usb/typec/altmodes/thunderbolt.c index 6eadf7835f8f..c4c5da6154da 100644 --- a/drivers/usb/typec/altmodes/thunderbolt.c +++ b/drivers/usb/typec/altmodes/thunderbolt.c @@ -307,7 +307,7 @@ static int tbt_altmode_probe(struct typec_altmode *alt) typec_altmode_set_drvdata(alt, tbt); typec_altmode_set_ops(alt, &tbt_altmode_ops); - if (tbt_ready(alt)) { + if (!alt->mode_selection && tbt_ready(alt)) { if (tbt->plug[TYPEC_PLUG_SOP_P]) tbt->state = TBT_STATE_SOP_P_ENTER; else if (tbt->plug[TYPEC_PLUG_SOP_PP]) diff --git a/drivers/usb/typec/class.c b/drivers/usb/typec/class.c index a48c44712518..dbba53f02497 100644 --- a/drivers/usb/typec/class.c +++ b/drivers/usb/typec/class.c @@ -655,6 +655,7 @@ typec_register_altmode(struct device *parent, alt->adev.svid = desc->svid; alt->adev.mode = desc->mode; alt->adev.vdo = desc->vdo; + alt->adev.mode_selection = desc->mode_selection; alt->roles = desc->roles; alt->id = id; diff --git a/include/linux/usb/typec.h b/include/linux/usb/typec.h index dbb259d88526..d61ec38216fa 100644 --- a/include/linux/usb/typec.h +++ b/include/linux/usb/typec.h @@ -155,6 +155,7 @@ struct typec_altmode_desc { /* Only used with ports */ enum typec_port_data roles; bool inactive; + bool mode_selection; }; void typec_partner_set_pd_revision(struct typec_partner *partner, u16 pd_revision); diff --git a/include/linux/usb/typec_altmode.h b/include/linux/usb/typec_altmode.h index 70026f5f8f99..0513d333b797 100644 --- a/include/linux/usb/typec_altmode.h +++ b/include/linux/usb/typec_altmode.h @@ -37,6 +37,7 @@ struct typec_altmode { u32 vdo; unsigned int active:1; u8 priority; + bool mode_selection; char *desc; const struct typec_altmode_ops *ops; -- cgit v1.2.3 From 53cc2d90542cfee42bf73627c26318372e7ea50e Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Thu, 22 Jan 2026 17:11:24 +0200 Subject: usb: phy: tegra: use phy type directly Refactor to directly use enum usb_phy_interface to determine the PHY mode. This change is in preparation for adding support for HSIC mode. Signed-off-by: Svyatoslav Ryhel Link: https://patch.msgid.link/20260122151125.7367-2-clamor95@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 51 ++++++++++++++++++++++++--------------- include/linux/usb/tegra_usb_phy.h | 2 +- 2 files changed, 33 insertions(+), 20 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index fb9031628d39..3fb082c98d99 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -805,15 +805,24 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) { - int err; + int err = 0; if (phy->powered_on) return 0; - if (phy->is_ulpi_phy) - err = ulpi_phy_power_on(phy); - else + switch (phy->phy_type) { + case USBPHY_INTERFACE_MODE_UTMI: err = utmi_phy_power_on(phy); + break; + + case USBPHY_INTERFACE_MODE_ULPI: + err = ulpi_phy_power_on(phy); + break; + + default: + break; + } + if (err) return err; @@ -827,15 +836,24 @@ static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) { - int err; + int err = 0; if (!phy->powered_on) return 0; - if (phy->is_ulpi_phy) - err = ulpi_phy_power_off(phy); - else + switch (phy->phy_type) { + case USBPHY_INTERFACE_MODE_UTMI: err = utmi_phy_power_off(phy); + break; + + case USBPHY_INTERFACE_MODE_ULPI: + err = ulpi_phy_power_off(phy); + break; + + default: + break; + } + if (err) return err; @@ -854,7 +872,7 @@ static void tegra_usb_phy_shutdown(struct usb_phy *u_phy) usb_phy_set_wakeup(u_phy, false); tegra_usb_phy_power_off(phy); - if (!phy->is_ulpi_phy) + if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) utmip_pad_close(phy); regulator_disable(phy->vbus); @@ -1040,7 +1058,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy) goto disable_clk; } - if (!phy->is_ulpi_phy) { + if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) { err = utmip_pad_open(phy); if (err) goto disable_vbus; @@ -1057,7 +1075,7 @@ static int tegra_usb_phy_init(struct usb_phy *u_phy) return 0; close_phy: - if (!phy->is_ulpi_phy) + if (phy->phy_type == USBPHY_INTERFACE_MODE_UTMI) utmip_pad_close(phy); disable_vbus: @@ -1095,8 +1113,6 @@ static int utmi_phy_probe(struct tegra_usb_phy *tegra_phy, struct resource *res; int err; - tegra_phy->is_ulpi_phy = false; - res = platform_get_resource(pdev, IORESOURCE_MEM, 1); if (!res) { dev_err(&pdev->dev, "Failed to get UTMI pad regs\n"); @@ -1252,7 +1268,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) { struct device_node *np = pdev->dev.of_node; struct tegra_usb_phy *tegra_phy; - enum usb_phy_interface phy_type; struct reset_control *reset; struct gpio_desc *gpiod; struct resource *res; @@ -1314,8 +1329,8 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) return err; } - phy_type = of_usb_get_phy_mode(np); - switch (phy_type) { + tegra_phy->phy_type = of_usb_get_phy_mode(np); + switch (tegra_phy->phy_type) { case USBPHY_INTERFACE_MODE_UTMI: err = utmi_phy_probe(tegra_phy, pdev); if (err) @@ -1341,8 +1356,6 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) break; case USBPHY_INTERFACE_MODE_ULPI: - tegra_phy->is_ulpi_phy = true; - tegra_phy->clk = devm_clk_get(&pdev->dev, "ulpi-link"); err = PTR_ERR_OR_ZERO(tegra_phy->clk); if (err) { @@ -1382,7 +1395,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) default: dev_err(&pdev->dev, "phy_type %u is invalid or unsupported\n", - phy_type); + tegra_phy->phy_type); return -EINVAL; } diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 40afcee8b4f5..fb5227353d3b 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -72,7 +72,7 @@ struct tegra_usb_phy { struct usb_phy *ulpi; struct usb_phy u_phy; bool is_legacy_phy; - bool is_ulpi_phy; + enum usb_phy_interface phy_type; struct gpio_desc *reset_gpio; struct reset_control *pad_rst; bool wakeup_enabled; -- cgit v1.2.3 From 8acc379b664ec987dcc7eca25a5f5c4a9a4eb9c4 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Thu, 22 Jan 2026 17:11:25 +0200 Subject: usb: phy: tegra: add HSIC support Add support for HSIC USB mode, which can be set for second USB controller and PHY on Tegra SoC along with already supported UTMI or ULPI. Signed-off-by: Svyatoslav Ryhel Link: https://patch.msgid.link/20260122151125.7367-3-clamor95@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 249 ++++++++++++++++++++++++++++++++++++-- include/linux/usb/tegra_usb_phy.h | 5 + 2 files changed, 243 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 3fb082c98d99..effa767ec019 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -29,17 +29,26 @@ #include #include +#define USB_TXFILLTUNING 0x154 +#define USB_FIFO_TXFILL_THRES(x) (((x) & 0x1f) << 16) +#define USB_FIFO_TXFILL_MASK 0x1f0000 + #define ULPI_VIEWPORT 0x170 /* PORTSC PTS/PHCD bits, Tegra20 only */ #define TEGRA_USB_PORTSC1 0x184 -#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) -#define TEGRA_USB_PORTSC1_PHCD BIT(23) +#define TEGRA_USB_PORTSC1_PTS(x) (((x) & 0x3) << 30) +#define TEGRA_USB_PORTSC1_PHCD BIT(23) +#define TEGRA_USB_PORTSC1_WKOC BIT(22) +#define TEGRA_USB_PORTSC1_WKDS BIT(21) +#define TEGRA_USB_PORTSC1_WKCN BIT(20) /* HOSTPC1 PTS/PHCD bits, Tegra30 and above */ +#define TEGRA30_USB_PORTSC1 0x174 #define TEGRA_USB_HOSTPC1_DEVLC 0x1b4 -#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) -#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22) +#define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) +#define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22) +#define TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC BIT(2) /* Bits of PORTSC1, which will get cleared by writing 1 into them */ #define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) @@ -51,11 +60,12 @@ #define USB_SUSP_CLR BIT(5) #define USB_PHY_CLK_VALID BIT(7) #define UTMIP_RESET BIT(11) -#define UHSIC_RESET BIT(11) #define UTMIP_PHY_ENABLE BIT(12) #define ULPI_PHY_ENABLE BIT(13) #define USB_SUSP_SET BIT(14) +#define UHSIC_RESET BIT(14) #define USB_WAKEUP_DEBOUNCE_COUNT(x) (((x) & 0x7) << 16) +#define UHSIC_PHY_ENABLE BIT(19) #define USB_PHY_VBUS_SENSORS 0x404 #define B_SESS_VLD_WAKEUP_EN BIT(14) @@ -156,6 +166,58 @@ #define UTMIP_BIAS_CFG1 0x83c #define UTMIP_BIAS_PDTRK_COUNT(x) (((x) & 0x1f) << 3) +/* + * Tegra20 has no UTMIP registers on PHY2 and UHSIC registers start from 0x800 + * just where UTMIP registers should have been. This is the case only with Tegra20 + * Tegra30+ have UTMIP registers at 0x800 and UHSIC registers shifter by 0x400 + * to 0xc00, but register layout is preserved. + */ +#define UHSIC_PLL_CFG1 0x804 +#define UHSIC_XTAL_FREQ_COUNT(x) (((x) & 0xfff) << 0) +#define UHSIC_PLLU_ENABLE_DLY_COUNT(x) (((x) & 0x1f) << 14) + +#define UHSIC_HSRX_CFG0 0x808 +#define UHSIC_ELASTIC_UNDERRUN_LIMIT(x) (((x) & 0x1f) << 2) +#define UHSIC_ELASTIC_OVERRUN_LIMIT(x) (((x) & 0x1f) << 8) +#define UHSIC_IDLE_WAIT(x) (((x) & 0x1f) << 13) + +#define UHSIC_HSRX_CFG1 0x80c +#define UHSIC_HS_SYNC_START_DLY(x) (((x) & 0x1f) << 1) + +#define UHSIC_TX_CFG0 0x810 +#define UHSIC_HS_READY_WAIT_FOR_VALID BIT(9) + +#define UHSIC_MISC_CFG0 0x814 +#define UHSIC_SUSPEND_EXIT_ON_EDGE BIT(7) +#define UHSIC_DETECT_SHORT_CONNECT BIT(8) +#define UHSIC_FORCE_XCVR_MODE BIT(15) +#define UHSIC_DISABLE_BUSRESET BIT(20) + +#define UHSIC_MISC_CFG1 0x818 +#define UHSIC_PLLU_STABLE_COUNT(x) (((x) & 0xfff) << 2) + +#define UHSIC_PADS_CFG0 0x81c +#define UHSIC_TX_RTUNEN 0xf000 +#define UHSIC_TX_RTUNE(x) (((x) & 0xf) << 12) + +#define UHSIC_PADS_CFG1 0x820 +#define UHSIC_PD_BG BIT(2) +#define UHSIC_PD_TX BIT(3) +#define UHSIC_PD_TRK BIT(4) +#define UHSIC_PD_RX BIT(5) +#define UHSIC_PD_ZI BIT(6) +#define UHSIC_RX_SEL BIT(7) +#define UHSIC_RPD_DATA BIT(9) +#define UHSIC_RPD_STROBE BIT(10) +#define UHSIC_RPU_DATA BIT(11) +#define UHSIC_RPU_STROBE BIT(12) + +#define UHSIC_CMD_CFG0 0x824 +#define UHSIC_PRETEND_CONNECT_DETECT BIT(5) + +#define UHSIC_STAT_CFG0 0x828 +#define UHSIC_CONNECT_DETECT BIT(0) + /* For Tegra30 and above only, the address is different in Tegra20 */ #define USB_USBMODE 0x1f8 #define USB_USBMODE_MASK (3 << 0) @@ -174,7 +236,8 @@ struct tegra_xtal_freq { u8 enable_delay; u8 stable_count; u8 active_delay; - u8 xtal_freq_count; + u8 utmi_xtal_freq_count; + u16 hsic_xtal_freq_count; u16 debounce; }; @@ -184,7 +247,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .enable_delay = 0x02, .stable_count = 0x2F, .active_delay = 0x04, - .xtal_freq_count = 0x76, + .utmi_xtal_freq_count = 0x76, + .hsic_xtal_freq_count = 0x1CA, .debounce = 0x7530, }, { @@ -192,7 +256,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .enable_delay = 0x02, .stable_count = 0x33, .active_delay = 0x05, - .xtal_freq_count = 0x7F, + .utmi_xtal_freq_count = 0x7F, + .hsic_xtal_freq_count = 0x1F0, .debounce = 0x7EF4, }, { @@ -200,7 +265,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .enable_delay = 0x03, .stable_count = 0x4B, .active_delay = 0x06, - .xtal_freq_count = 0xBB, + .utmi_xtal_freq_count = 0xBB, + .hsic_xtal_freq_count = 0x2DD, .debounce = 0xBB80, }, { @@ -208,7 +274,8 @@ static const struct tegra_xtal_freq tegra_freq_table[] = { .enable_delay = 0x04, .stable_count = 0x66, .active_delay = 0x09, - .xtal_freq_count = 0xFE, + .utmi_xtal_freq_count = 0xFE, + .hsic_xtal_freq_count = 0x3E0, .debounce = 0xFDE8, }, }; @@ -532,7 +599,7 @@ static int utmi_phy_power_on(struct tegra_usb_phy *phy) val = readl_relaxed(base + UTMIP_PLL_CFG1); val &= ~(UTMIP_XTAL_FREQ_COUNT(~0) | UTMIP_PLLU_ENABLE_DLY_COUNT(~0)); - val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->xtal_freq_count) | + val |= UTMIP_XTAL_FREQ_COUNT(phy->freq->utmi_xtal_freq_count) | UTMIP_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); writel_relaxed(val, base + UTMIP_PLL_CFG1); } @@ -803,6 +870,153 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) return 0; } +static u32 tegra_hsic_readl(struct tegra_usb_phy *phy, u32 reg) +{ + void __iomem *base = phy->regs; + u32 shift = phy->soc_config->uhsic_registers_shift; + + return readl_relaxed(base + shift + reg); +} + +static void tegra_hsic_writel(struct tegra_usb_phy *phy, u32 reg, u32 value) +{ + void __iomem *base = phy->regs; + u32 shift = phy->soc_config->uhsic_registers_shift; + + writel_relaxed(value, base + shift + reg); +} + +static int uhsic_phy_power_on(struct tegra_usb_phy *phy) +{ + struct tegra_utmip_config *config = phy->config; + void __iomem *base = phy->regs; + u32 val; + + val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1); + val &= ~(UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX | + UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE); + val |= UHSIC_RX_SEL; + tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val); + + udelay(2); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel_relaxed(val, base + USB_SUSP_CTRL); + + udelay(30); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val |= UHSIC_PHY_ENABLE; + writel_relaxed(val, base + USB_SUSP_CTRL); + + val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG0); + val &= ~(UHSIC_IDLE_WAIT(~0) | + UHSIC_ELASTIC_UNDERRUN_LIMIT(~0) | + UHSIC_ELASTIC_OVERRUN_LIMIT(~0)); + val |= UHSIC_IDLE_WAIT(config->idle_wait_delay) | + UHSIC_ELASTIC_UNDERRUN_LIMIT(config->elastic_limit) | + UHSIC_ELASTIC_OVERRUN_LIMIT(config->elastic_limit); + tegra_hsic_writel(phy, UHSIC_HSRX_CFG0, val); + + val = tegra_hsic_readl(phy, UHSIC_HSRX_CFG1); + val &= ~UHSIC_HS_SYNC_START_DLY(~0); + val |= UHSIC_HS_SYNC_START_DLY(config->hssync_start_delay); + tegra_hsic_writel(phy, UHSIC_HSRX_CFG1, val); + + val = tegra_hsic_readl(phy, UHSIC_MISC_CFG0); + val |= UHSIC_SUSPEND_EXIT_ON_EDGE; + tegra_hsic_writel(phy, UHSIC_MISC_CFG0, val); + + val = tegra_hsic_readl(phy, UHSIC_MISC_CFG1); + val &= ~UHSIC_PLLU_STABLE_COUNT(~0); + val |= UHSIC_PLLU_STABLE_COUNT(phy->freq->stable_count); + tegra_hsic_writel(phy, UHSIC_MISC_CFG1, val); + + val = tegra_hsic_readl(phy, UHSIC_PLL_CFG1); + val &= ~(UHSIC_XTAL_FREQ_COUNT(~0) | + UHSIC_PLLU_ENABLE_DLY_COUNT(~0)); + val |= UHSIC_XTAL_FREQ_COUNT(phy->freq->hsic_xtal_freq_count) | + UHSIC_PLLU_ENABLE_DLY_COUNT(phy->freq->enable_delay); + tegra_hsic_writel(phy, UHSIC_PLL_CFG1, val); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val &= ~UHSIC_RESET; + writel_relaxed(val, base + USB_SUSP_CTRL); + + udelay(2); + + if (phy->soc_config->requires_usbmode_setup) { + val = readl_relaxed(base + USB_USBMODE); + val &= ~USB_USBMODE_MASK; + if (phy->mode == USB_DR_MODE_HOST) + val |= USB_USBMODE_HOST; + else + val |= USB_USBMODE_DEVICE; + writel_relaxed(val, base + USB_USBMODE); + } + + if (phy->soc_config->has_hostpc) + set_pts(phy, TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC); + else + set_pts(phy, 0); + + val = readl_relaxed(base + USB_TXFILLTUNING); + if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) { + val = USB_FIFO_TXFILL_THRES(0x10); + writel_relaxed(val, base + USB_TXFILLTUNING); + } + + if (phy->soc_config->has_hostpc) { + val = readl_relaxed(base + TEGRA30_USB_PORTSC1); + val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS | + TEGRA_USB_PORTSC1_WKCN); + writel_relaxed(val, base + TEGRA30_USB_PORTSC1); + } else { + val = readl_relaxed(base + TEGRA_USB_PORTSC1); + val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS | + TEGRA_USB_PORTSC1_WKCN); + writel_relaxed(val, base + TEGRA_USB_PORTSC1); + } + + val = tegra_hsic_readl(phy, UHSIC_PADS_CFG0); + val &= ~UHSIC_TX_RTUNEN; + val |= UHSIC_TX_RTUNE(phy->soc_config->uhsic_tx_rtune); + tegra_hsic_writel(phy, UHSIC_PADS_CFG0, val); + + if (utmi_wait_register(base + USB_SUSP_CTRL, USB_PHY_CLK_VALID, + USB_PHY_CLK_VALID)) + dev_err(phy->u_phy.dev, + "Timeout waiting for PHY to stabilize on enable (HSIC)\n"); + + return 0; +} + +static int uhsic_phy_power_off(struct tegra_usb_phy *phy) +{ + void __iomem *base = phy->regs; + u32 val; + + set_phcd(phy, true); + + val = tegra_hsic_readl(phy, UHSIC_PADS_CFG1); + val |= (UHSIC_PD_BG | UHSIC_PD_TX | UHSIC_PD_TRK | UHSIC_PD_RX | + UHSIC_PD_ZI | UHSIC_RPD_DATA | UHSIC_RPD_STROBE); + tegra_hsic_writel(phy, UHSIC_PADS_CFG1, val); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val |= UHSIC_RESET; + writel_relaxed(val, base + USB_SUSP_CTRL); + + udelay(30); + + val = readl_relaxed(base + USB_SUSP_CTRL); + val &= ~UHSIC_PHY_ENABLE; + writel_relaxed(val, base + USB_SUSP_CTRL); + + return 0; +} + static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) { int err = 0; @@ -819,6 +1033,10 @@ static int tegra_usb_phy_power_on(struct tegra_usb_phy *phy) err = ulpi_phy_power_on(phy); break; + case USBPHY_INTERFACE_MODE_HSIC: + err = uhsic_phy_power_on(phy); + break; + default: break; } @@ -850,6 +1068,10 @@ static int tegra_usb_phy_power_off(struct tegra_usb_phy *phy) err = ulpi_phy_power_off(phy); break; + case USBPHY_INTERFACE_MODE_HSIC: + err = uhsic_phy_power_off(phy); + break; + default: break; } @@ -1247,6 +1469,8 @@ static const struct tegra_phy_soc_config tegra20_soc_config = { .requires_usbmode_setup = false, .requires_extra_tuning_parameters = false, .requires_pmc_ao_power_up = false, + .uhsic_registers_shift = 0, + .uhsic_tx_rtune = 0, /* 40 ohm */ }; static const struct tegra_phy_soc_config tegra30_soc_config = { @@ -1255,6 +1479,8 @@ static const struct tegra_phy_soc_config tegra30_soc_config = { .requires_usbmode_setup = true, .requires_extra_tuning_parameters = true, .requires_pmc_ao_power_up = true, + .uhsic_registers_shift = 0x400, + .uhsic_tx_rtune = 8, /* 50 ohm */ }; static const struct of_device_id tegra_usb_phy_id_table[] = { @@ -1332,6 +1558,7 @@ static int tegra_usb_phy_probe(struct platform_device *pdev) tegra_phy->phy_type = of_usb_get_phy_mode(np); switch (tegra_phy->phy_type) { case USBPHY_INTERFACE_MODE_UTMI: + case USBPHY_INTERFACE_MODE_HSIC: err = utmi_phy_probe(tegra_phy, pdev); if (err) return err; diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index fb5227353d3b..6d57da13d395 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -23,6 +23,9 @@ struct gpio_desc; * requires_extra_tuning_parameters: true if xcvr_hsslew, hssquelch_level * and hsdiscon_level should be set for adequate signal quality * requires_pmc_ao_power_up: true if USB AO is powered down by default + * uhsic_registers_shift: for Tegra30+ where HSIC registers were shifted + * comparing to Tegra20 by 0x400, since Tegra20 has no UTMIP on PHY2 + * uhsic_tx_rtune: fine tuned 50 Ohm termination resistor for NMOS/PMOS driver */ struct tegra_phy_soc_config { @@ -31,6 +34,8 @@ struct tegra_phy_soc_config { bool requires_usbmode_setup; bool requires_extra_tuning_parameters; bool requires_pmc_ao_power_up; + u32 uhsic_registers_shift; + u32 uhsic_tx_rtune; }; struct tegra_utmip_config { -- cgit v1.2.3 From d000422a46aad32217cf1be747eb61d641baae2f Mon Sep 17 00:00:00 2001 From: Xin Zhao Date: Tue, 23 Dec 2025 11:48:36 +0800 Subject: tty: tty_port: add workqueue to flip TTY buffer On the embedded platform, certain critical data, such as IMU data, is transmitted through UART. The tty_flip_buffer_push() interface in the TTY layer uses system_dfl_wq to handle the flipping of the TTY buffer. Although the unbound workqueue can create new threads on demand and wake up the kworker thread on an idle CPU, it may be preempted by real-time tasks or other high-prio tasks. flush_to_ldisc() needs to wake up the relevant data handle thread. When executing __wake_up_common_lock(), it calls spin_lock_irqsave(), which does not disable preemption but disables migration in RT-Linux. This prevents the kworker thread from being migrated to other cores by CPU's balancing logic, resulting in long delays. The call trace is as follows: __wake_up_common_lock __wake_up ep_poll_callback __wake_up_common __wake_up_common_lock __wake_up n_tty_receive_buf_common n_tty_receive_buf2 tty_ldisc_receive_buf tty_port_default_receive_buf flush_to_ldisc In our system, the processing interval for each frame of IMU data transmitted via UART can experience significant jitter due to this issue. Instead of the expected 10 to 15 ms frame processing interval, we see spikes up to 30 to 35 ms. Moreover, in just one or two hours, there can be 2 to 3 occurrences of such high jitter, which is quite frequent. This jitter exceeds the software's tolerable limit of 20 ms. Introduce flip_wq in tty_port which can be set by tty_port_link_wq() or as default linked to default workqueue allocated when tty_register_driver(). The default workqueue is allocated with flag WQ_SYSFS, so that cpumask and nice can be set dynamically. The execution timing of tty_port_link_wq() is not clearly restricted. The newly added function tty_port_link_driver_wq() checks whether the flip_wq of the tty_port has already been assigned when linking the default tty_driver's workqueue to the port. After the user has set a custom workqueue for a certain tty_port using tty_port_link_wq(), the system will only use this custom workqueue, even if tty_driver does not have %TTY_DRIVER_CUSTOM_WORKQUEUE flag. Introduce %TTY_DRIVER_CUSTOM_WORKQUEUE flag meaning not to create the default single tty_driver workqueue. Two reasons why need to introduce the %TTY_DRIVER_CUSTOM_WORKQUEUE flag: 1. If the WQ_SYSFS parameter is enabled, workqueue_sysfs_register() will fail when trying to create a workqueue with the same name. The pty is an example of this; if both CONFIG_LEGACY_PTYS and CONFIG_UNIX98_PTYS are enabled, the call to tty_register_driver() in unix98_pty_init() will fail. 2. Different tty ports may be used for different tasks, which may require separate core binding control via workqueues. In this case, the workqueue created by default in the tty driver is unnecessary. Enabling this flag prevents the creation of this redundant workqueue. After applying this patch, we can set the related UART TTY flip buffer workqueue by sysfs. We set the cpumask to CPU cores associated with the IMU tasks, and set the nice to -20. Testing has shown significant improvement in the previously described issue, with almost no stuttering occurring anymore. Signed-off-by: Xin Zhao Reviewed-by: Jiri Slaby Link: https://patch.msgid.link/20251223034836.2625547-1-jackzxcui1989@163.com Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 14 ++++++++++---- drivers/tty/tty_buffer.c | 8 ++++---- drivers/tty/tty_io.c | 21 ++++++++++++++++++++- drivers/tty/tty_port.c | 23 +++++++++++++++++++++++ include/linux/tty_buffer.h | 1 + include/linux/tty_driver.h | 7 +++++++ include/linux/tty_port.h | 13 +++++++++++++ 7 files changed, 78 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 6120d827a797..1f17575f8fe0 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -403,6 +403,8 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, o_tty->link = tty; tty_port_init(ports[0]); tty_port_init(ports[1]); + tty_port_link_wq(ports[0], system_dfl_wq); + tty_port_link_wq(ports[1], system_dfl_wq); tty_buffer_set_limit(ports[0], 8192); tty_buffer_set_limit(ports[1], 8192); o_tty->port = ports[0]; @@ -532,14 +534,16 @@ static void __init legacy_pty_init(void) pty_driver = tty_alloc_driver(legacy_count, TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_CUSTOM_WORKQUEUE); if (IS_ERR(pty_driver)) panic("Couldn't allocate pty driver"); pty_slave_driver = tty_alloc_driver(legacy_count, TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_CUSTOM_WORKQUEUE); if (IS_ERR(pty_slave_driver)) panic("Couldn't allocate pty slave driver"); @@ -849,7 +853,8 @@ static void __init unix98_pty_init(void) TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_CUSTOM_WORKQUEUE); if (IS_ERR(ptm_driver)) panic("Couldn't allocate Unix98 ptm driver"); pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX, @@ -857,7 +862,8 @@ static void __init unix98_pty_init(void) TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM | - TTY_DRIVER_DYNAMIC_ALLOC); + TTY_DRIVER_DYNAMIC_ALLOC | + TTY_DRIVER_CUSTOM_WORKQUEUE); if (IS_ERR(pts_driver)) panic("Couldn't allocate Unix98 pts driver"); diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 1a5673acd9b1..86e1e7178e90 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -76,7 +76,7 @@ void tty_buffer_unlock_exclusive(struct tty_port *port) mutex_unlock(&buf->lock); if (restart) - queue_work(system_dfl_wq, &buf->work); + queue_work(buf->flip_wq, &buf->work); } EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive); @@ -530,7 +530,7 @@ void tty_flip_buffer_push(struct tty_port *port) struct tty_bufhead *buf = &port->buf; tty_flip_buffer_commit(buf->tail); - queue_work(system_dfl_wq, &buf->work); + queue_work(buf->flip_wq, &buf->work); } EXPORT_SYMBOL(tty_flip_buffer_push); @@ -560,7 +560,7 @@ int tty_insert_flip_string_and_push_buffer(struct tty_port *port, tty_flip_buffer_commit(buf->tail); spin_unlock_irqrestore(&port->lock, flags); - queue_work(system_dfl_wq, &buf->work); + queue_work(buf->flip_wq, &buf->work); return size; } @@ -613,7 +613,7 @@ void tty_buffer_set_lock_subclass(struct tty_port *port) bool tty_buffer_restart_work(struct tty_port *port) { - return queue_work(system_dfl_wq, &port->buf.work); + return queue_work(port->buf.flip_wq, &port->buf.work); } bool tty_buffer_cancel_work(struct tty_port *port) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index e2d92cf70eb7..d64fb08baa17 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3446,10 +3446,23 @@ int tty_register_driver(struct tty_driver *driver) if (error < 0) goto err; + if (!(driver->flags & TTY_DRIVER_CUSTOM_WORKQUEUE)) { + driver->flip_wq = alloc_workqueue("%s-flip-wq", WQ_UNBOUND | WQ_SYSFS, + 0, driver->name); + if (!driver->flip_wq) { + error = -ENOMEM; + goto err_unreg_char; + } + for (i = 0; i < driver->num; i++) { + if (driver->ports[i]) + tty_port_link_driver_wq(driver->ports[i], driver); + } + } + if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) { error = tty_cdev_add(driver, dev, 0, driver->num); if (error) - goto err_unreg_char; + goto err_destroy_wq; } scoped_guard(mutex, &tty_mutex) @@ -3475,6 +3488,10 @@ err_unreg_devs: scoped_guard(mutex, &tty_mutex) list_del(&driver->tty_drivers); +err_destroy_wq: + if (!(driver->flags & TTY_DRIVER_CUSTOM_WORKQUEUE)) + destroy_workqueue(driver->flip_wq); + err_unreg_char: unregister_chrdev_region(dev, driver->num); err: @@ -3494,6 +3511,8 @@ void tty_unregister_driver(struct tty_driver *driver) driver->num); scoped_guard(mutex, &tty_mutex) list_del(&driver->tty_drivers); + if (!(driver->flags & TTY_DRIVER_CUSTOM_WORKQUEUE)) + destroy_workqueue(driver->flip_wq); } EXPORT_SYMBOL(tty_unregister_driver); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index fe67c5cb0a3f..611f878149f8 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -99,6 +99,26 @@ void tty_port_init(struct tty_port *port) } EXPORT_SYMBOL(tty_port_init); +/** + * tty_port_link_wq - link tty_port and flip workqueue + * @port: tty_port of the device + * @flip_wq: workqueue to queue flip buffer work on + * + * When %TTY_DRIVER_CUSTOM_WORKQUEUE is used, every tty_port shall be linked to + * a workqueue manually by this function, otherwise tty_flip_buffer_push() will + * see %NULL flip_wq pointer on queue_work. + * When %TTY_DRIVER_CUSTOM_WORKQUEUE is NOT used, the function can be used to + * link a certain port to a specific workqueue, instead of using the workqueue + * allocated in tty_register_driver(). + * + * Note that TTY port API will NOT destroy the workqueue. + */ +void tty_port_link_wq(struct tty_port *port, struct workqueue_struct *flip_wq) +{ + port->buf.flip_wq = flip_wq; +} +EXPORT_SYMBOL_GPL(tty_port_link_wq); + /** * tty_port_link_device - link tty and tty_port * @port: tty_port of the device @@ -157,6 +177,7 @@ struct device *tty_port_register_device_attr(struct tty_port *port, const struct attribute_group **attr_grp) { tty_port_link_device(port, driver, index); + tty_port_link_driver_wq(port, driver); return tty_register_device_attr(driver, index, device, drvdata, attr_grp); } @@ -183,6 +204,7 @@ struct device *tty_port_register_device_attr_serdev(struct tty_port *port, struct device *dev; tty_port_link_device(port, driver, index); + tty_port_link_driver_wq(port, driver); dev = serdev_tty_port_register(port, host, parent, driver, index); if (PTR_ERR(dev) != -ENODEV) { @@ -703,6 +725,7 @@ int tty_port_install(struct tty_port *port, struct tty_driver *driver, struct tty_struct *tty) { tty->port = port; + tty_port_link_driver_wq(port, driver); return tty_standard_install(driver, tty); } EXPORT_SYMBOL_GPL(tty_port_install); diff --git a/include/linux/tty_buffer.h b/include/linux/tty_buffer.h index 31125e3be3c5..48adcb0e8ff3 100644 --- a/include/linux/tty_buffer.h +++ b/include/linux/tty_buffer.h @@ -34,6 +34,7 @@ static inline u8 *flag_buf_ptr(struct tty_buffer *b, unsigned int ofs) struct tty_bufhead { struct tty_buffer *head; /* Queue head */ + struct workqueue_struct *flip_wq; struct work_struct work; struct mutex lock; atomic_t priority; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 188ee9b768eb..9c65854f7d94 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -69,6 +69,10 @@ struct serial_struct; * Do not create numbered ``/dev`` nodes. For example, create * ``/dev/ttyprintk`` and not ``/dev/ttyprintk0``. Applicable only when a * driver for a single tty device is being allocated. + * + * @TTY_DRIVER_CUSTOM_WORKQUEUE: + * Do not create workqueue when tty_register_driver(). When set, flip + * buffer workqueue shall be set by tty_port_link_wq() for every port. */ enum tty_driver_flag { TTY_DRIVER_INSTALLED = BIT(0), @@ -79,6 +83,7 @@ enum tty_driver_flag { TTY_DRIVER_HARDWARE_BREAK = BIT(5), TTY_DRIVER_DYNAMIC_ALLOC = BIT(6), TTY_DRIVER_UNNUMBERED_NODE = BIT(7), + TTY_DRIVER_CUSTOM_WORKQUEUE = BIT(8), }; enum tty_driver_type { @@ -506,6 +511,7 @@ struct tty_operations { * @flags: tty driver flags (%TTY_DRIVER_) * @proc_entry: proc fs entry, used internally * @other: driver of the linked tty; only used for the PTY driver + * @flip_wq: workqueue to queue flip buffer work on * @ttys: array of active &struct tty_struct, set by tty_standard_install() * @ports: array of &struct tty_port; can be set during initialization by * tty_port_link_device() and similar @@ -539,6 +545,7 @@ struct tty_driver { unsigned long flags; struct proc_dir_entry *proc_entry; struct tty_driver *other; + struct workqueue_struct *flip_wq; /* * Pointer to the tty data structures diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index 660c254f1efe..c1b87f3c5603 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -138,6 +138,7 @@ struct tty_port { kernel */ void tty_port_init(struct tty_port *port); +void tty_port_link_wq(struct tty_port *port, struct workqueue_struct *flip_wq); void tty_port_link_device(struct tty_port *port, struct tty_driver *driver, unsigned index); struct device *tty_port_register_device(struct tty_port *port, @@ -165,6 +166,18 @@ static inline struct tty_port *tty_port_get(struct tty_port *port) return NULL; } +/* + * Never overwrite the workqueue set by tty_port_link_wq(). + * No effect when %TTY_DRIVER_CUSTOM_WORKQUEUE is set, as driver->flip_wq is + * %NULL. + */ +static inline void tty_port_link_driver_wq(struct tty_port *port, + struct tty_driver *driver) +{ + if (!port->buf.flip_wq) + port->buf.flip_wq = driver->flip_wq; +} + /* If the cts flow control is enabled, return true. */ static inline bool tty_port_cts_enabled(const struct tty_port *port) { -- cgit v1.2.3 From 2cddfc2e8fc78c13b0f5286ea5dd48cdf527ad41 Mon Sep 17 00:00:00 2001 From: Aaron Tomlin Date: Fri, 26 Dec 2025 11:07:24 -0500 Subject: tracing: Add bitmask-list option for human-readable bitmask display Add support for displaying bitmasks in human-readable list format (e.g., 0,2-5,7) in addition to the default hexadecimal bitmap representation. This is particularly useful when tracing CPU masks and other large bitmasks where individual bit positions are more meaningful than their hexadecimal encoding. When the "bitmask-list" option is enabled, the printk "%*pbl" format specifier is used to render bitmasks as comma-separated ranges, making trace output easier to interpret for complex CPU configurations and large bitmask values. Link: https://patch.msgid.link/20251226160724.2246493-2-atomlin@atomlin.com Signed-off-by: Aaron Tomlin Signed-off-by: Steven Rostedt (Google) --- Documentation/trace/ftrace.rst | 9 +++++++++ include/linux/trace_events.h | 8 ++++---- include/linux/trace_seq.h | 12 +++++++++++- include/trace/stages/stage3_trace_output.h | 4 ++-- kernel/trace/trace.h | 1 + kernel/trace/trace_output.c | 30 +++++++++++++++++++++++++++--- kernel/trace/trace_seq.c | 29 ++++++++++++++++++++++++++++- 7 files changed, 82 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/Documentation/trace/ftrace.rst b/Documentation/trace/ftrace.rst index d1f313a5f4ad..639f4d95732f 100644 --- a/Documentation/trace/ftrace.rst +++ b/Documentation/trace/ftrace.rst @@ -1290,6 +1290,15 @@ Here are the available options: This will be useful if you want to find out which hashed value is corresponding to the real value in trace log. + bitmask-list + When enabled, bitmasks are displayed as a human-readable list of + ranges (e.g., 0,2-5,7) using the printk "%*pbl" format specifier. + When disabled (the default), bitmasks are displayed in the + traditional hexadecimal bitmap representation. The list format is + particularly useful for tracing CPU masks and other large bitmasks + where individual bit positions are more meaningful than their + hexadecimal encoding. + record-cmd When any event or tracer is enabled, a hook is enabled in the sched_switch trace point to fill comm cache diff --git a/include/linux/trace_events.h b/include/linux/trace_events.h index 3690221ba3d8..0a2b8229b999 100644 --- a/include/linux/trace_events.h +++ b/include/linux/trace_events.h @@ -38,7 +38,10 @@ const char *trace_print_symbols_seq_u64(struct trace_seq *p, *symbol_array); #endif -const char *trace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr, +struct trace_iterator; +struct trace_event; + +const char *trace_print_bitmask_seq(struct trace_iterator *iter, void *bitmask_ptr, unsigned int bitmask_size); const char *trace_print_hex_seq(struct trace_seq *p, @@ -54,9 +57,6 @@ trace_print_hex_dump_seq(struct trace_seq *p, const char *prefix_str, int prefix_type, int rowsize, int groupsize, const void *buf, size_t len, bool ascii); -struct trace_iterator; -struct trace_event; - int trace_raw_output_prep(struct trace_iterator *iter, struct trace_event *event); extern __printf(2, 3) diff --git a/include/linux/trace_seq.h b/include/linux/trace_seq.h index 4a0b8c172d27..697d619aafdc 100644 --- a/include/linux/trace_seq.h +++ b/include/linux/trace_seq.h @@ -114,7 +114,11 @@ extern void trace_seq_putmem_hex(struct trace_seq *s, const void *mem, extern int trace_seq_path(struct trace_seq *s, const struct path *path); extern void trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, - int nmaskbits); + int nmaskbits); + +extern void trace_seq_bitmask_list(struct trace_seq *s, + const unsigned long *maskp, + int nmaskbits); extern int trace_seq_hex_dump(struct trace_seq *s, const char *prefix_str, int prefix_type, int rowsize, int groupsize, @@ -137,6 +141,12 @@ trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, { } +static inline void +trace_seq_bitmask_list(struct trace_seq *s, const unsigned long *maskp, + int nmaskbits) +{ +} + static inline int trace_print_seq(struct seq_file *m, struct trace_seq *s) { return 0; diff --git a/include/trace/stages/stage3_trace_output.h b/include/trace/stages/stage3_trace_output.h index 1e7b0bef95f5..fce85ea2df1c 100644 --- a/include/trace/stages/stage3_trace_output.h +++ b/include/trace/stages/stage3_trace_output.h @@ -39,7 +39,7 @@ void *__bitmask = __get_dynamic_array(field); \ unsigned int __bitmask_size; \ __bitmask_size = __get_dynamic_array_len(field); \ - trace_print_bitmask_seq(p, __bitmask, __bitmask_size); \ + trace_print_bitmask_seq(iter, __bitmask, __bitmask_size); \ }) #undef __get_cpumask @@ -51,7 +51,7 @@ void *__bitmask = __get_rel_dynamic_array(field); \ unsigned int __bitmask_size; \ __bitmask_size = __get_rel_dynamic_array_len(field); \ - trace_print_bitmask_seq(p, __bitmask, __bitmask_size); \ + trace_print_bitmask_seq(iter, __bitmask, __bitmask_size); \ }) #undef __get_rel_cpumask diff --git a/kernel/trace/trace.h b/kernel/trace/trace.h index b6d42fe06115..8888fc9335b6 100644 --- a/kernel/trace/trace.h +++ b/kernel/trace/trace.h @@ -1411,6 +1411,7 @@ extern int trace_get_user(struct trace_parser *parser, const char __user *ubuf, C(COPY_MARKER, "copy_trace_marker"), \ C(PAUSE_ON_TRACE, "pause-on-trace"), \ C(HASH_PTR, "hash-ptr"), /* Print hashed pointer */ \ + C(BITMASK_LIST, "bitmask-list"), \ FUNCTION_FLAGS \ FGRAPH_FLAGS \ STACK_FLAGS \ diff --git a/kernel/trace/trace_output.c b/kernel/trace/trace_output.c index cc2d3306bb60..1996d7aba038 100644 --- a/kernel/trace/trace_output.c +++ b/kernel/trace/trace_output.c @@ -194,13 +194,37 @@ trace_print_symbols_seq_u64(struct trace_seq *p, unsigned long long val, EXPORT_SYMBOL(trace_print_symbols_seq_u64); #endif +/** + * trace_print_bitmask_seq - print a bitmask to a sequence buffer + * @iter: The trace iterator for the current event instance + * @bitmask_ptr: The pointer to the bitmask data + * @bitmask_size: The size of the bitmask in bytes + * + * Prints a bitmask into a sequence buffer as either a hex string or a + * human-readable range list, depending on the instance's "bitmask-list" + * trace option. The bitmask is formatted into the iterator's temporary + * scratchpad rather than the primary sequence buffer. This avoids + * duplication and pointer-collision issues when the returned string is + * processed by a "%s" specifier in a TP_printk() macro. + * + * Returns a pointer to the formatted string within the temporary buffer. + */ const char * -trace_print_bitmask_seq(struct trace_seq *p, void *bitmask_ptr, +trace_print_bitmask_seq(struct trace_iterator *iter, void *bitmask_ptr, unsigned int bitmask_size) { - const char *ret = trace_seq_buffer_ptr(p); + struct trace_seq *p = &iter->tmp_seq; + const struct trace_array *tr = iter->tr; + const char *ret; + + trace_seq_init(p); + ret = trace_seq_buffer_ptr(p); + + if (tr->trace_flags & TRACE_ITER(BITMASK_LIST)) + trace_seq_bitmask_list(p, bitmask_ptr, bitmask_size * 8); + else + trace_seq_bitmask(p, bitmask_ptr, bitmask_size * 8); - trace_seq_bitmask(p, bitmask_ptr, bitmask_size * 8); trace_seq_putc(p, 0); return ret; diff --git a/kernel/trace/trace_seq.c b/kernel/trace/trace_seq.c index 32684ef4fb9d..85f6f10d107f 100644 --- a/kernel/trace/trace_seq.c +++ b/kernel/trace/trace_seq.c @@ -106,7 +106,7 @@ EXPORT_SYMBOL_GPL(trace_seq_printf); * Writes a ASCII representation of a bitmask string into @s. */ void trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, - int nmaskbits) + int nmaskbits) { unsigned int save_len = s->seq.len; @@ -124,6 +124,33 @@ void trace_seq_bitmask(struct trace_seq *s, const unsigned long *maskp, } EXPORT_SYMBOL_GPL(trace_seq_bitmask); +/** + * trace_seq_bitmask_list - write a bitmask array in its list representation + * @s: trace sequence descriptor + * @maskp: points to an array of unsigned longs that represent a bitmask + * @nmaskbits: The number of bits that are valid in @maskp + * + * Writes a list representation (e.g., 0-3,5-7) of a bitmask string into @s. + */ +void trace_seq_bitmask_list(struct trace_seq *s, const unsigned long *maskp, + int nmaskbits) +{ + unsigned int save_len = s->seq.len; + + if (s->full) + return; + + __trace_seq_init(s); + + seq_buf_printf(&s->seq, "%*pbl", nmaskbits, maskp); + + if (unlikely(seq_buf_has_overflowed(&s->seq))) { + s->seq.len = save_len; + s->full = 1; + } +} +EXPORT_SYMBOL_GPL(trace_seq_bitmask_list); + /** * trace_seq_vprintf - sequence printing of trace information * @s: trace sequence descriptor -- cgit v1.2.3 From 0a15f43b92ddaa2fdb476891a12ac2e207c7fcd2 Mon Sep 17 00:00:00 2001 From: Greg Kroah-Hartman Date: Tue, 27 Jan 2026 13:58:21 +0100 Subject: Revert "tty: tty_port: add workqueue to flip TTY buffer" This reverts commit d000422a46aad32217cf1be747eb61d641baae2f. It is reported by many to cause boot failures, so must be reverted. Cc: Xin Zhao Cc: Jiri Slaby Link: https://lore.kernel.org/r/d1942304-ee30-478d-90fb-279519f3ae81@samsung.com Reported-by: Marek Szyprowski Reported-by: Tommaso Merciai Reported-by: Geert Uytterhoeven Signed-off-by: Greg Kroah-Hartman --- drivers/tty/pty.c | 14 ++++---------- drivers/tty/tty_buffer.c | 8 ++++---- drivers/tty/tty_io.c | 21 +-------------------- drivers/tty/tty_port.c | 23 ----------------------- include/linux/tty_buffer.h | 1 - include/linux/tty_driver.h | 7 ------- include/linux/tty_port.h | 13 ------------- 7 files changed, 9 insertions(+), 78 deletions(-) (limited to 'include/linux') diff --git a/drivers/tty/pty.c b/drivers/tty/pty.c index 1f17575f8fe0..6120d827a797 100644 --- a/drivers/tty/pty.c +++ b/drivers/tty/pty.c @@ -403,8 +403,6 @@ static int pty_common_install(struct tty_driver *driver, struct tty_struct *tty, o_tty->link = tty; tty_port_init(ports[0]); tty_port_init(ports[1]); - tty_port_link_wq(ports[0], system_dfl_wq); - tty_port_link_wq(ports[1], system_dfl_wq); tty_buffer_set_limit(ports[0], 8192); tty_buffer_set_limit(ports[1], 8192); o_tty->port = ports[0]; @@ -534,16 +532,14 @@ static void __init legacy_pty_init(void) pty_driver = tty_alloc_driver(legacy_count, TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_ALLOC | - TTY_DRIVER_CUSTOM_WORKQUEUE); + TTY_DRIVER_DYNAMIC_ALLOC); if (IS_ERR(pty_driver)) panic("Couldn't allocate pty driver"); pty_slave_driver = tty_alloc_driver(legacy_count, TTY_DRIVER_RESET_TERMIOS | TTY_DRIVER_REAL_RAW | - TTY_DRIVER_DYNAMIC_ALLOC | - TTY_DRIVER_CUSTOM_WORKQUEUE); + TTY_DRIVER_DYNAMIC_ALLOC); if (IS_ERR(pty_slave_driver)) panic("Couldn't allocate pty slave driver"); @@ -853,8 +849,7 @@ static void __init unix98_pty_init(void) TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM | - TTY_DRIVER_DYNAMIC_ALLOC | - TTY_DRIVER_CUSTOM_WORKQUEUE); + TTY_DRIVER_DYNAMIC_ALLOC); if (IS_ERR(ptm_driver)) panic("Couldn't allocate Unix98 ptm driver"); pts_driver = tty_alloc_driver(NR_UNIX98_PTY_MAX, @@ -862,8 +857,7 @@ static void __init unix98_pty_init(void) TTY_DRIVER_REAL_RAW | TTY_DRIVER_DYNAMIC_DEV | TTY_DRIVER_DEVPTS_MEM | - TTY_DRIVER_DYNAMIC_ALLOC | - TTY_DRIVER_CUSTOM_WORKQUEUE); + TTY_DRIVER_DYNAMIC_ALLOC); if (IS_ERR(pts_driver)) panic("Couldn't allocate Unix98 pts driver"); diff --git a/drivers/tty/tty_buffer.c b/drivers/tty/tty_buffer.c index 86e1e7178e90..1a5673acd9b1 100644 --- a/drivers/tty/tty_buffer.c +++ b/drivers/tty/tty_buffer.c @@ -76,7 +76,7 @@ void tty_buffer_unlock_exclusive(struct tty_port *port) mutex_unlock(&buf->lock); if (restart) - queue_work(buf->flip_wq, &buf->work); + queue_work(system_dfl_wq, &buf->work); } EXPORT_SYMBOL_GPL(tty_buffer_unlock_exclusive); @@ -530,7 +530,7 @@ void tty_flip_buffer_push(struct tty_port *port) struct tty_bufhead *buf = &port->buf; tty_flip_buffer_commit(buf->tail); - queue_work(buf->flip_wq, &buf->work); + queue_work(system_dfl_wq, &buf->work); } EXPORT_SYMBOL(tty_flip_buffer_push); @@ -560,7 +560,7 @@ int tty_insert_flip_string_and_push_buffer(struct tty_port *port, tty_flip_buffer_commit(buf->tail); spin_unlock_irqrestore(&port->lock, flags); - queue_work(buf->flip_wq, &buf->work); + queue_work(system_dfl_wq, &buf->work); return size; } @@ -613,7 +613,7 @@ void tty_buffer_set_lock_subclass(struct tty_port *port) bool tty_buffer_restart_work(struct tty_port *port) { - return queue_work(port->buf.flip_wq, &port->buf.work); + return queue_work(system_dfl_wq, &port->buf.work); } bool tty_buffer_cancel_work(struct tty_port *port) diff --git a/drivers/tty/tty_io.c b/drivers/tty/tty_io.c index d64fb08baa17..e2d92cf70eb7 100644 --- a/drivers/tty/tty_io.c +++ b/drivers/tty/tty_io.c @@ -3446,23 +3446,10 @@ int tty_register_driver(struct tty_driver *driver) if (error < 0) goto err; - if (!(driver->flags & TTY_DRIVER_CUSTOM_WORKQUEUE)) { - driver->flip_wq = alloc_workqueue("%s-flip-wq", WQ_UNBOUND | WQ_SYSFS, - 0, driver->name); - if (!driver->flip_wq) { - error = -ENOMEM; - goto err_unreg_char; - } - for (i = 0; i < driver->num; i++) { - if (driver->ports[i]) - tty_port_link_driver_wq(driver->ports[i], driver); - } - } - if (driver->flags & TTY_DRIVER_DYNAMIC_ALLOC) { error = tty_cdev_add(driver, dev, 0, driver->num); if (error) - goto err_destroy_wq; + goto err_unreg_char; } scoped_guard(mutex, &tty_mutex) @@ -3488,10 +3475,6 @@ err_unreg_devs: scoped_guard(mutex, &tty_mutex) list_del(&driver->tty_drivers); -err_destroy_wq: - if (!(driver->flags & TTY_DRIVER_CUSTOM_WORKQUEUE)) - destroy_workqueue(driver->flip_wq); - err_unreg_char: unregister_chrdev_region(dev, driver->num); err: @@ -3511,8 +3494,6 @@ void tty_unregister_driver(struct tty_driver *driver) driver->num); scoped_guard(mutex, &tty_mutex) list_del(&driver->tty_drivers); - if (!(driver->flags & TTY_DRIVER_CUSTOM_WORKQUEUE)) - destroy_workqueue(driver->flip_wq); } EXPORT_SYMBOL(tty_unregister_driver); diff --git a/drivers/tty/tty_port.c b/drivers/tty/tty_port.c index 611f878149f8..fe67c5cb0a3f 100644 --- a/drivers/tty/tty_port.c +++ b/drivers/tty/tty_port.c @@ -99,26 +99,6 @@ void tty_port_init(struct tty_port *port) } EXPORT_SYMBOL(tty_port_init); -/** - * tty_port_link_wq - link tty_port and flip workqueue - * @port: tty_port of the device - * @flip_wq: workqueue to queue flip buffer work on - * - * When %TTY_DRIVER_CUSTOM_WORKQUEUE is used, every tty_port shall be linked to - * a workqueue manually by this function, otherwise tty_flip_buffer_push() will - * see %NULL flip_wq pointer on queue_work. - * When %TTY_DRIVER_CUSTOM_WORKQUEUE is NOT used, the function can be used to - * link a certain port to a specific workqueue, instead of using the workqueue - * allocated in tty_register_driver(). - * - * Note that TTY port API will NOT destroy the workqueue. - */ -void tty_port_link_wq(struct tty_port *port, struct workqueue_struct *flip_wq) -{ - port->buf.flip_wq = flip_wq; -} -EXPORT_SYMBOL_GPL(tty_port_link_wq); - /** * tty_port_link_device - link tty and tty_port * @port: tty_port of the device @@ -177,7 +157,6 @@ struct device *tty_port_register_device_attr(struct tty_port *port, const struct attribute_group **attr_grp) { tty_port_link_device(port, driver, index); - tty_port_link_driver_wq(port, driver); return tty_register_device_attr(driver, index, device, drvdata, attr_grp); } @@ -204,7 +183,6 @@ struct device *tty_port_register_device_attr_serdev(struct tty_port *port, struct device *dev; tty_port_link_device(port, driver, index); - tty_port_link_driver_wq(port, driver); dev = serdev_tty_port_register(port, host, parent, driver, index); if (PTR_ERR(dev) != -ENODEV) { @@ -725,7 +703,6 @@ int tty_port_install(struct tty_port *port, struct tty_driver *driver, struct tty_struct *tty) { tty->port = port; - tty_port_link_driver_wq(port, driver); return tty_standard_install(driver, tty); } EXPORT_SYMBOL_GPL(tty_port_install); diff --git a/include/linux/tty_buffer.h b/include/linux/tty_buffer.h index 48adcb0e8ff3..31125e3be3c5 100644 --- a/include/linux/tty_buffer.h +++ b/include/linux/tty_buffer.h @@ -34,7 +34,6 @@ static inline u8 *flag_buf_ptr(struct tty_buffer *b, unsigned int ofs) struct tty_bufhead { struct tty_buffer *head; /* Queue head */ - struct workqueue_struct *flip_wq; struct work_struct work; struct mutex lock; atomic_t priority; diff --git a/include/linux/tty_driver.h b/include/linux/tty_driver.h index 9c65854f7d94..188ee9b768eb 100644 --- a/include/linux/tty_driver.h +++ b/include/linux/tty_driver.h @@ -69,10 +69,6 @@ struct serial_struct; * Do not create numbered ``/dev`` nodes. For example, create * ``/dev/ttyprintk`` and not ``/dev/ttyprintk0``. Applicable only when a * driver for a single tty device is being allocated. - * - * @TTY_DRIVER_CUSTOM_WORKQUEUE: - * Do not create workqueue when tty_register_driver(). When set, flip - * buffer workqueue shall be set by tty_port_link_wq() for every port. */ enum tty_driver_flag { TTY_DRIVER_INSTALLED = BIT(0), @@ -83,7 +79,6 @@ enum tty_driver_flag { TTY_DRIVER_HARDWARE_BREAK = BIT(5), TTY_DRIVER_DYNAMIC_ALLOC = BIT(6), TTY_DRIVER_UNNUMBERED_NODE = BIT(7), - TTY_DRIVER_CUSTOM_WORKQUEUE = BIT(8), }; enum tty_driver_type { @@ -511,7 +506,6 @@ struct tty_operations { * @flags: tty driver flags (%TTY_DRIVER_) * @proc_entry: proc fs entry, used internally * @other: driver of the linked tty; only used for the PTY driver - * @flip_wq: workqueue to queue flip buffer work on * @ttys: array of active &struct tty_struct, set by tty_standard_install() * @ports: array of &struct tty_port; can be set during initialization by * tty_port_link_device() and similar @@ -545,7 +539,6 @@ struct tty_driver { unsigned long flags; struct proc_dir_entry *proc_entry; struct tty_driver *other; - struct workqueue_struct *flip_wq; /* * Pointer to the tty data structures diff --git a/include/linux/tty_port.h b/include/linux/tty_port.h index c1b87f3c5603..660c254f1efe 100644 --- a/include/linux/tty_port.h +++ b/include/linux/tty_port.h @@ -138,7 +138,6 @@ struct tty_port { kernel */ void tty_port_init(struct tty_port *port); -void tty_port_link_wq(struct tty_port *port, struct workqueue_struct *flip_wq); void tty_port_link_device(struct tty_port *port, struct tty_driver *driver, unsigned index); struct device *tty_port_register_device(struct tty_port *port, @@ -166,18 +165,6 @@ static inline struct tty_port *tty_port_get(struct tty_port *port) return NULL; } -/* - * Never overwrite the workqueue set by tty_port_link_wq(). - * No effect when %TTY_DRIVER_CUSTOM_WORKQUEUE is set, as driver->flip_wq is - * %NULL. - */ -static inline void tty_port_link_driver_wq(struct tty_port *port, - struct tty_driver *driver) -{ - if (!port->buf.flip_wq) - port->buf.flip_wq = driver->flip_wq; -} - /* If the cts flow control is enabled, return true. */ static inline bool tty_port_cts_enabled(const struct tty_port *port) { -- cgit v1.2.3 From c86d39d6805474ab879c00ca6b938c6dd7e4d33f Mon Sep 17 00:00:00 2001 From: Tim Bird Date: Sat, 17 Jan 2026 13:00:38 -0700 Subject: kernel: debug: Add SPDX license ids to kdb files Add GPL-2.0 license id to some files related to kdb and kgdb, replacing references to GPL or COPYING. These files were introduced into the kernel in 2008 and 2010. Signed-off-by: Tim Bird Signed-off-by: Greg Kroah-Hartman --- include/linux/kdb.h | 5 +---- include/linux/kgdb.h | 4 +--- kernel/debug/debug_core.h | 5 +---- kernel/debug/kdb/kdb_bp.c | 5 +---- kernel/debug/kdb/kdb_bt.c | 5 +---- kernel/debug/kdb/kdb_debugger.c | 1 + kernel/debug/kdb/kdb_io.c | 5 +---- kernel/debug/kdb/kdb_keyboard.c | 4 +--- kernel/debug/kdb/kdb_main.c | 5 +---- kernel/debug/kdb/kdb_private.h | 1 + kernel/debug/kdb/kdb_support.c | 5 +---- 11 files changed, 11 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/include/linux/kdb.h b/include/linux/kdb.h index 741c58e86431..26fe4ab81b42 100644 --- a/include/linux/kdb.h +++ b/include/linux/kdb.h @@ -1,13 +1,10 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _KDB_H #define _KDB_H /* * Kernel Debugger Architecture Independent Global Headers * - * 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) 2000-2007 Silicon Graphics, Inc. All Rights Reserved. * Copyright (C) 2000 Stephane Eranian * Copyright (C) 2009 Jason Wessel diff --git a/include/linux/kgdb.h b/include/linux/kgdb.h index 5eebbe7a3545..22b3f3839f30 100644 --- a/include/linux/kgdb.h +++ b/include/linux/kgdb.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * This provides the callbacks and functions that KGDB needs to share between * the core, I/O and arch-specific portions. @@ -6,9 +7,6 @@ * Tom Rini * * 2001-2004 (c) Amit S. Kale and 2003-2005 (c) MontaVista Software, Inc. - * 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. */ #ifndef _KGDB_H_ #define _KGDB_H_ diff --git a/kernel/debug/debug_core.h b/kernel/debug/debug_core.h index cd22b5f68831..fa1226158b45 100644 --- a/kernel/debug/debug_core.h +++ b/kernel/debug/debug_core.h @@ -1,11 +1,8 @@ +/* SPDX-License-Identifier: GPL-2.0 */ /* * Created by: Jason Wessel * * Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved. - * - * 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. */ #ifndef _DEBUG_CORE_H_ diff --git a/kernel/debug/kdb/kdb_bp.c b/kernel/debug/kdb/kdb_bp.c index c0c2072f5452..eb8d851d620f 100644 --- a/kernel/debug/kdb/kdb_bp.c +++ b/kernel/debug/kdb/kdb_bp.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Kernel Debugger Architecture Independent Breakpoint Handler * - * 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) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved. */ diff --git a/kernel/debug/kdb/kdb_bt.c b/kernel/debug/kdb/kdb_bt.c index 137ba73f56fc..c561aa076970 100644 --- a/kernel/debug/kdb/kdb_bt.c +++ b/kernel/debug/kdb/kdb_bt.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Kernel Debugger Architecture Independent Stack Traceback * - * 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) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved. */ diff --git a/kernel/debug/kdb/kdb_debugger.c b/kernel/debug/kdb/kdb_debugger.c index e91fc3e4edd5..59b81032bbab 100644 --- a/kernel/debug/kdb/kdb_debugger.c +++ b/kernel/debug/kdb/kdb_debugger.c @@ -1,3 +1,4 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Created by: Jason Wessel * diff --git a/kernel/debug/kdb/kdb_io.c b/kernel/debug/kdb/kdb_io.c index 61c1690058ed..c399f11740ef 100644 --- a/kernel/debug/kdb/kdb_io.c +++ b/kernel/debug/kdb/kdb_io.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Kernel Debugger Architecture Independent Console I/O handler * - * 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) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved. */ diff --git a/kernel/debug/kdb/kdb_keyboard.c b/kernel/debug/kdb/kdb_keyboard.c index 386d30e530b7..c7ebcb9e9d8f 100644 --- a/kernel/debug/kdb/kdb_keyboard.c +++ b/kernel/debug/kdb/kdb_keyboard.c @@ -1,9 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Kernel Debugger Architecture Dependent Console I/O handler * - * This file is subject to the terms and conditions of the GNU General Public - * License. - * * Copyright (c) 1999-2006 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved. */ diff --git a/kernel/debug/kdb/kdb_main.c b/kernel/debug/kdb/kdb_main.c index dddf2b5aad57..314787fb8ce7 100644 --- a/kernel/debug/kdb/kdb_main.c +++ b/kernel/debug/kdb/kdb_main.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Kernel Debugger Architecture Independent Main Code * - * 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) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. * Copyright (C) 2000 Stephane Eranian * Xscale (R) modifications copyright (C) 2003 Intel Corporation. diff --git a/kernel/debug/kdb/kdb_private.h b/kernel/debug/kdb/kdb_private.h index a2fc7d2bc9fc..92a28b8ab604 100644 --- a/kernel/debug/kdb/kdb_private.h +++ b/kernel/debug/kdb/kdb_private.h @@ -1,3 +1,4 @@ +/* SPDX-License-Identifier: GPL-2.0 */ #ifndef _KDBPRIVATE_H #define _KDBPRIVATE_H diff --git a/kernel/debug/kdb/kdb_support.c b/kernel/debug/kdb/kdb_support.c index 56f7b906e7cc..0a2e54e77ce6 100644 --- a/kernel/debug/kdb/kdb_support.c +++ b/kernel/debug/kdb/kdb_support.c @@ -1,10 +1,7 @@ +// SPDX-License-Identifier: GPL-2.0 /* * Kernel Debugger Architecture Independent Support Functions * - * 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) 1999-2004 Silicon Graphics, Inc. All Rights Reserved. * Copyright (c) 2009 Wind River Systems, Inc. All Rights Reserved. * 03/02/13 added new 2.5 kallsyms -- cgit v1.2.3 From 18fa479b90c29a48481ee0aae98779278c51b96b Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 9 Dec 2025 12:40:27 +0100 Subject: fsi: Provide thin wrappers around dev_[gs]et_data() for fsi devices MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Similar to wrappers for other subsystems provide inline functions for fsi devices to store driver data. Signed-off-by: Uwe Kleine-König Acked-by: Eddie James Link: https://patch.msgid.link/5de7a7cbb30918b3503235130bd8aa1a9a63d71c.1765279318.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- include/linux/fsi.h | 10 ++++++++++ 1 file changed, 10 insertions(+) (limited to 'include/linux') diff --git a/include/linux/fsi.h b/include/linux/fsi.h index adea1b432f2d..05be75869a69 100644 --- a/include/linux/fsi.h +++ b/include/linux/fsi.h @@ -19,6 +19,16 @@ struct fsi_device { uint32_t size; }; +static inline void *fsi_get_drvdata(struct fsi_device *fsi_dev) +{ + return dev_get_drvdata(&fsi_dev->dev); +} + +static inline void fsi_set_drvdata(struct fsi_device *fsi_dev, void *data) +{ + dev_set_drvdata(&fsi_dev->dev, data); +} + extern int fsi_device_read(struct fsi_device *dev, uint32_t addr, void *val, size_t size); extern int fsi_device_write(struct fsi_device *dev, uint32_t addr, -- cgit v1.2.3 From 68cc6588b52359ff9b48516525b463551a4bb315 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 9 Dec 2025 12:40:30 +0100 Subject: fsi: Make fsi_bus_type a private variable to the core MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There are no users of fsi_bus_type outside of fsi-core.c, so make that variable static, don't export it and drop the declaration from the public header file. As there is a usage of fsi_bus_type in fsi_create_device() the definition of that variable must happen further up in the file to not have to add a local declaration. Signed-off-by: Uwe Kleine-König Acked-by: Eddie James Link: https://patch.msgid.link/bfd83034dec04d5a6b01a234988377fc6224614d.1765279318.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/fsi-core.c | 67 +++++++++++++++++++++++++------------------------- include/linux/fsi.h | 1 - 2 files changed, 33 insertions(+), 35 deletions(-) (limited to 'include/linux') diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index e1ea1124282e..4e60d4b17c11 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -100,6 +100,39 @@ static int fsi_master_write(struct fsi_master *master, int link, uint8_t slave_id, uint32_t addr, const void *val, size_t size); static int fsi_master_break(struct fsi_master *master, int link); +/* FSI core & Linux bus type definitions */ + +static int fsi_bus_match(struct device *dev, const struct device_driver *drv) +{ + struct fsi_device *fsi_dev = to_fsi_dev(dev); + const struct fsi_driver *fsi_drv = to_fsi_drv(drv); + const struct fsi_device_id *id; + + if (!fsi_drv->id_table) + return 0; + + for (id = fsi_drv->id_table; id->engine_type; id++) { + if (id->engine_type != fsi_dev->engine_type) + continue; + if (id->version == FSI_VERSION_ANY || + id->version == fsi_dev->version) { + if (drv->of_match_table) { + if (of_driver_match_device(dev, drv)) + return 1; + } else { + return 1; + } + } + } + + return 0; +} + +static const struct bus_type fsi_bus_type = { + .name = "fsi", + .match = fsi_bus_match, +}; + /* * fsi_device_read() / fsi_device_write() / fsi_device_peek() * @@ -1359,34 +1392,6 @@ void fsi_master_unregister(struct fsi_master *master) } EXPORT_SYMBOL_GPL(fsi_master_unregister); -/* FSI core & Linux bus type definitions */ - -static int fsi_bus_match(struct device *dev, const struct device_driver *drv) -{ - struct fsi_device *fsi_dev = to_fsi_dev(dev); - const struct fsi_driver *fsi_drv = to_fsi_drv(drv); - const struct fsi_device_id *id; - - if (!fsi_drv->id_table) - return 0; - - for (id = fsi_drv->id_table; id->engine_type; id++) { - if (id->engine_type != fsi_dev->engine_type) - continue; - if (id->version == FSI_VERSION_ANY || - id->version == fsi_dev->version) { - if (drv->of_match_table) { - if (of_driver_match_device(dev, drv)) - return 1; - } else { - return 1; - } - } - } - - return 0; -} - int fsi_driver_register(struct fsi_driver *fsi_drv) { if (!fsi_drv) @@ -1406,12 +1411,6 @@ void fsi_driver_unregister(struct fsi_driver *fsi_drv) } EXPORT_SYMBOL_GPL(fsi_driver_unregister); -const struct bus_type fsi_bus_type = { - .name = "fsi", - .match = fsi_bus_match, -}; -EXPORT_SYMBOL_GPL(fsi_bus_type); - static int __init fsi_init(void) { int rc; diff --git a/include/linux/fsi.h b/include/linux/fsi.h index 05be75869a69..3e3a8f3adac3 100644 --- a/include/linux/fsi.h +++ b/include/linux/fsi.h @@ -78,7 +78,6 @@ extern int fsi_slave_read(struct fsi_slave *slave, uint32_t addr, extern int fsi_slave_write(struct fsi_slave *slave, uint32_t addr, const void *val, size_t size); -extern const struct bus_type fsi_bus_type; extern const struct device_type fsi_cdev_type; enum fsi_dev_type { -- cgit v1.2.3 From 69d4ca009005c14068fe358196c38281ec5ee316 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Tue, 9 Dec 2025 12:40:31 +0100 Subject: fsi: Create bus specific probe and remove functions MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Introduce a bus specific probe and remove function. For now this only allows to get rid of a cast of the generic device to an fsi device in the drivers and changes the remove prototype to return void---a non-zero return value is ignored anyhow. The objective is to get rid of users of struct device callbacks .probe(), .remove() and .shutdown() to eventually remove these. Until all fsi drivers are converted this results in a runtime warning about the drivers needing an update because there is a bus probe function and a driver probe function. Signed-off-by: Uwe Kleine-König Acked-by: Eddie James Link: https://patch.msgid.link/3b53adb75a5ae7894736d46cb6eb85f5ef36520e.1765279318.git.u.kleine-koenig@baylibre.com Signed-off-by: Greg Kroah-Hartman --- drivers/fsi/fsi-core.c | 50 ++++++++++++++++++++++++++++++++++++++++++++++++++ include/linux/fsi.h | 2 ++ 2 files changed, 52 insertions(+) (limited to 'include/linux') diff --git a/drivers/fsi/fsi-core.c b/drivers/fsi/fsi-core.c index 4e60d4b17c11..83599a1c548b 100644 --- a/drivers/fsi/fsi-core.c +++ b/drivers/fsi/fsi-core.c @@ -128,9 +128,31 @@ static int fsi_bus_match(struct device *dev, const struct device_driver *drv) return 0; } +static int fsi_probe(struct device *dev) +{ + struct fsi_device *fsidev = to_fsi_dev(dev); + struct fsi_driver *fsidrv = to_fsi_drv(dev->driver); + + if (fsidrv->probe) + return fsidrv->probe(fsidev); + else + return 0; +} + +static void fsi_remove(struct device *dev) +{ + struct fsi_device *fsidev = to_fsi_dev(dev); + struct fsi_driver *fsidrv = to_fsi_drv(dev->driver); + + if (fsidrv->remove) + fsidrv->remove(fsidev); +} + static const struct bus_type fsi_bus_type = { .name = "fsi", .match = fsi_bus_match, + .probe = fsi_probe, + .remove = fsi_remove, }; /* @@ -1392,6 +1414,25 @@ void fsi_master_unregister(struct fsi_master *master) } EXPORT_SYMBOL_GPL(fsi_master_unregister); +static int fsi_legacy_probe(struct fsi_device *fsidev) +{ + struct device *dev = &fsidev->dev; + struct device_driver *driver = dev->driver; + + return driver->probe(dev); +} + +static void fsi_legacy_remove(struct fsi_device *fsidev) +{ + struct device *dev = &fsidev->dev; + struct device_driver *driver = dev->driver; + int ret; + + ret = driver->remove(dev); + if (unlikely(ret)) + dev_warn(dev, "Ignoring return value of remove callback (%pe)\n", ERR_PTR(ret)); +} + int fsi_driver_register(struct fsi_driver *fsi_drv) { if (!fsi_drv) @@ -1401,6 +1442,15 @@ int fsi_driver_register(struct fsi_driver *fsi_drv) fsi_drv->drv.bus = &fsi_bus_type; + /* + * This driver needs updating. Note that driver_register() warns about + * this, so we're not adding another warning here. + */ + if (!fsi_drv->probe && fsi_drv->drv.probe) + fsi_drv->probe = fsi_legacy_probe; + if (!fsi_drv->remove && fsi_drv->drv.remove) + fsi_drv->remove = fsi_legacy_remove; + return driver_register(&fsi_drv->drv); } EXPORT_SYMBOL_GPL(fsi_driver_register); diff --git a/include/linux/fsi.h b/include/linux/fsi.h index 3e3a8f3adac3..9c67c43f9e6c 100644 --- a/include/linux/fsi.h +++ b/include/linux/fsi.h @@ -49,6 +49,8 @@ struct fsi_device_id { .engine_type = (t), .version = (v), struct fsi_driver { + int (*probe)(struct fsi_device *fsidev); + void (*remove)(struct fsi_device *fsidev); struct device_driver drv; const struct fsi_device_id *id_table; }; -- cgit v1.2.3 From fac55d29581fcd4c3b66b9c2b9f7995c459c0064 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Thu, 22 Jan 2026 08:50:40 +0100 Subject: platform/x86: asus-wmi: Add support for multiple kbd led handlers MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some devices, such as the Z13 have multiple Aura devices connected to them by USB. In addition, they might have a WMI interface for RGB. In Windows, Armoury Crate exposes a unified brightness slider for all of them, with 3 brightness levels. Therefore, to be synergistic in Linux, and support existing tooling such as UPower, allow adding listeners to the RGB device of the WMI interface. If WMI does not exist, lazy initialize the interface. Since hid-asus and asus-wmi can both interact with the led objects including from an atomic context, protect the brightness access with a spinlock and update the values from a workqueue. Use this workqueue to also process WMI keyboard events, so they are handled asynchronously. Acked-by: Benjamin Tissoires Signed-off-by: Antheas Kapenekakis Reviewed-by: Denis Benato Link: https://patch.msgid.link/20260122075044.5070-8-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 183 +++++++++++++++++++++++++---- include/linux/platform_data/x86/asus-wmi.h | 15 +++ 2 files changed, 173 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index 4aec7ec69250..c45846be3f99 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -31,13 +31,13 @@ #include #include #include -#include #include #include #include #include #include #include +#include #include #include @@ -256,6 +256,9 @@ struct asus_wmi { int tpd_led_wk; struct led_classdev kbd_led; int kbd_led_wk; + bool kbd_led_notify; + bool kbd_led_avail; + bool kbd_led_registered; struct led_classdev lightbar_led; int lightbar_led_wk; struct led_classdev micmute_led; @@ -264,6 +267,7 @@ struct asus_wmi { struct work_struct tpd_led_work; struct work_struct wlan_led_work; struct work_struct lightbar_led_work; + struct work_struct kbd_led_work; struct asus_rfkill wlan; struct asus_rfkill bluetooth; @@ -1615,6 +1619,106 @@ static void asus_wmi_battery_exit(struct asus_wmi *asus) /* LEDs ***********************************************************************/ +struct asus_hid_ref { + struct list_head listeners; + struct asus_wmi *asus; + /* Protects concurrent access from hid-asus and asus-wmi to leds */ + spinlock_t lock; +}; + +static struct asus_hid_ref asus_ref = { + .listeners = LIST_HEAD_INIT(asus_ref.listeners), + .asus = NULL, + /* + * Protects .asus, .asus.kbd_led_{wk,notify}, and .listener refs. Other + * asus variables are read-only after .asus is set. + * + * The led cdev device is not protected because it calls backlight_get + * during initialization, which would result in a nested lock attempt. + * + * The led cdev is safe to access without a lock because if + * kbd_led_avail is true it is initialized before .asus is set and never + * changed until .asus is dropped. If kbd_led_avail is false, the led + * cdev is registered by the workqueue, which is single-threaded and + * cancelled before asus-wmi would access the led cdev to unregister it. + * + * A spinlock is used, because the protected variables can be accessed + * from an IRQ context from asus-hid. + */ + .lock = __SPIN_LOCK_UNLOCKED(asus_ref.lock), +}; + +/* + * Allows registering hid-asus listeners that want to be notified of + * keyboard backlight changes. + */ +int asus_hid_register_listener(struct asus_hid_listener *bdev) +{ + struct asus_wmi *asus; + + guard(spinlock_irqsave)(&asus_ref.lock); + list_add_tail(&bdev->list, &asus_ref.listeners); + asus = asus_ref.asus; + if (asus) + queue_work(asus->led_workqueue, &asus->kbd_led_work); + return 0; +} +EXPORT_SYMBOL_GPL(asus_hid_register_listener); + +/* + * Allows unregistering hid-asus listeners that were added with + * asus_hid_register_listener(). + */ +void asus_hid_unregister_listener(struct asus_hid_listener *bdev) +{ + guard(spinlock_irqsave)(&asus_ref.lock); + list_del(&bdev->list); +} +EXPORT_SYMBOL_GPL(asus_hid_unregister_listener); + +static void do_kbd_led_set(struct led_classdev *led_cdev, int value); + +static void kbd_led_update_all(struct work_struct *work) +{ + struct asus_wmi *asus; + bool registered, notify; + int ret, value; + + asus = container_of(work, struct asus_wmi, kbd_led_work); + + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + registered = asus->kbd_led_registered; + value = asus->kbd_led_wk; + notify = asus->kbd_led_notify; + } + + if (!registered) { + /* + * This workqueue runs under asus-wmi, which means probe has + * completed and asus-wmi will keep running until it finishes. + * Therefore, we can safely register the LED without holding + * a spinlock. + */ + ret = devm_led_classdev_register(&asus->platform_device->dev, + &asus->kbd_led); + if (!ret) { + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus->kbd_led_registered = true; + } else { + pr_warn("Failed to register keyboard backlight LED: %d\n", ret); + return; + } + } + + if (value >= 0) + do_kbd_led_set(&asus->kbd_led, value); + if (notify) { + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus->kbd_led_notify = false; + led_classdev_notify_brightness_hw_changed(&asus->kbd_led, value); + } +} + /* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -1661,7 +1765,8 @@ static void kbd_led_update(struct asus_wmi *asus) { int ctrl_param = 0; - ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); + scoped_guard(spinlock_irqsave, &asus_ref.lock) + ctrl_param = 0x80 | (asus->kbd_led_wk & 0x7F); asus_wmi_set_devstate(ASUS_WMI_DEVID_KBD_BACKLIGHT, ctrl_param, NULL); } @@ -1694,14 +1799,23 @@ static int kbd_led_read(struct asus_wmi *asus, int *level, int *env) static void do_kbd_led_set(struct led_classdev *led_cdev, int value) { + struct asus_hid_listener *listener; struct asus_wmi *asus; int max_level; asus = container_of(led_cdev, struct asus_wmi, kbd_led); max_level = asus->kbd_led.max_brightness; - asus->kbd_led_wk = clamp_val(value, 0, max_level); - kbd_led_update(asus); + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus->kbd_led_wk = clamp_val(value, 0, max_level); + + if (asus->kbd_led_avail) + kbd_led_update(asus); + + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + list_for_each_entry(listener, &asus_ref.listeners, list) + listener->brightness_set(listener, asus->kbd_led_wk); + } } static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) @@ -1716,10 +1830,11 @@ static int kbd_led_set(struct led_classdev *led_cdev, enum led_brightness value) static void kbd_led_set_by_kbd(struct asus_wmi *asus, enum led_brightness value) { - struct led_classdev *led_cdev = &asus->kbd_led; - - do_kbd_led_set(led_cdev, value); - led_classdev_notify_brightness_hw_changed(led_cdev, asus->kbd_led_wk); + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + asus->kbd_led_wk = value; + asus->kbd_led_notify = true; + } + queue_work(asus->led_workqueue, &asus->kbd_led_work); } static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) @@ -1729,10 +1844,18 @@ static enum led_brightness kbd_led_get(struct led_classdev *led_cdev) asus = container_of(led_cdev, struct asus_wmi, kbd_led); + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + if (!asus->kbd_led_avail) + return asus->kbd_led_wk; + } + retval = kbd_led_read(asus, &value, NULL); if (retval < 0) return retval; + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus->kbd_led_wk = value; + return value; } @@ -1844,7 +1967,9 @@ static int camera_led_set(struct led_classdev *led_cdev, static void asus_wmi_led_exit(struct asus_wmi *asus) { - led_classdev_unregister(&asus->kbd_led); + scoped_guard(spinlock_irqsave, &asus_ref.lock) + asus_ref.asus = NULL; + led_classdev_unregister(&asus->tpd_led); led_classdev_unregister(&asus->wlan_led); led_classdev_unregister(&asus->lightbar_led); @@ -1882,22 +2007,26 @@ static int asus_wmi_led_init(struct asus_wmi *asus) goto error; } - if (!kbd_led_read(asus, &led_val, NULL) && !dmi_check_system(asus_use_hid_led_dmi_ids)) { - pr_info("using asus-wmi for asus::kbd_backlight\n"); - asus->kbd_led_wk = led_val; - asus->kbd_led.name = "asus::kbd_backlight"; - asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; - asus->kbd_led.brightness_set_blocking = kbd_led_set; - asus->kbd_led.brightness_get = kbd_led_get; - asus->kbd_led.max_brightness = 3; + asus->kbd_led.name = "asus::kbd_backlight"; + asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; + asus->kbd_led.brightness_set_blocking = kbd_led_set; + asus->kbd_led.brightness_get = kbd_led_get; + asus->kbd_led.max_brightness = 3; + asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL); + INIT_WORK(&asus->kbd_led_work, kbd_led_update_all); + if (asus->kbd_led_avail) { + asus->kbd_led_wk = led_val; if (num_rgb_groups != 0) asus->kbd_led.groups = kbd_rgb_mode_groups; + } else { + asus->kbd_led_wk = -1; + } - rv = led_classdev_register(&asus->platform_device->dev, - &asus->kbd_led); - if (rv) - goto error; + scoped_guard(spinlock_irqsave, &asus_ref.lock) { + asus_ref.asus = asus; + if (asus->kbd_led_avail || !list_empty(&asus_ref.listeners)) + queue_work(asus->led_workqueue, &asus->kbd_led_work); } if (asus_wmi_dev_is_present(asus, ASUS_WMI_DEVID_WIRELESS_LED) @@ -4372,6 +4501,7 @@ static int asus_wmi_get_event_code(union acpi_object *obj) static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) { + enum led_brightness led_value; unsigned int key_value = 1; bool autorelease = 1; @@ -4388,19 +4518,22 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } + scoped_guard(spinlock_irqsave, &asus_ref.lock) + led_value = asus->kbd_led_wk; + if (code == NOTIFY_KBD_BRTUP) { - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); + kbd_led_set_by_kbd(asus, led_value + 1); return; } if (code == NOTIFY_KBD_BRTDWN) { - kbd_led_set_by_kbd(asus, asus->kbd_led_wk - 1); + kbd_led_set_by_kbd(asus, led_value - 1); return; } if (code == NOTIFY_KBD_BRTTOGGLE) { - if (asus->kbd_led_wk == asus->kbd_led.max_brightness) + if (led_value >= asus->kbd_led.max_brightness) kbd_led_set_by_kbd(asus, 0); else - kbd_led_set_by_kbd(asus, asus->kbd_led_wk + 1); + kbd_led_set_by_kbd(asus, led_value + 1); return; } diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index 419491d4abca..d347cffd05d5 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -172,12 +172,20 @@ enum asus_ally_mcu_hack { ASUS_WMI_ALLY_MCU_HACK_DISABLED, }; +/* Used to notify hid-asus when asus-wmi changes keyboard backlight */ +struct asus_hid_listener { + struct list_head list; + void (*brightness_set)(struct asus_hid_listener *listener, int brightness); +}; + #if IS_REACHABLE(CONFIG_ASUS_WMI) void set_ally_mcu_hack(enum asus_ally_mcu_hack status); void set_ally_mcu_powersave(bool enabled); int asus_wmi_get_devstate_dsts(u32 dev_id, u32 *retval); int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval); int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); +int asus_hid_register_listener(struct asus_hid_listener *cdev); +void asus_hid_unregister_listener(struct asus_hid_listener *cdev); #else static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status) { @@ -198,6 +206,13 @@ static inline int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, { return -ENODEV; } +static inline int asus_hid_register_listener(struct asus_hid_listener *bdev) +{ + return -ENODEV; +} +static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev) +{ +} #endif #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */ -- cgit v1.2.3 From d3133ccaf53694498b9827c5f9dbae52fc199d28 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Thu, 22 Jan 2026 08:50:42 +0100 Subject: platform/x86: asus-wmi: remove unused keyboard backlight quirk MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The quirk for selecting whether keyboard backlight should be controlled by HID or WMI is not needed anymore, so remove the file containing it. Reviewed-by: Denis Benato Acked-by: Benjamin Tissoires Signed-off-by: Antheas Kapenekakis Link: https://patch.msgid.link/20260122075044.5070-10-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- .../linux/platform_data/x86/asus-wmi-leds-ids.h | 50 ---------------------- 1 file changed, 50 deletions(-) delete mode 100644 include/linux/platform_data/x86/asus-wmi-leds-ids.h (limited to 'include/linux') diff --git a/include/linux/platform_data/x86/asus-wmi-leds-ids.h b/include/linux/platform_data/x86/asus-wmi-leds-ids.h deleted file mode 100644 index 034a039c4e37..000000000000 --- a/include/linux/platform_data/x86/asus-wmi-leds-ids.h +++ /dev/null @@ -1,50 +0,0 @@ -/* SPDX-License-Identifier: GPL-2.0 */ -#ifndef __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H -#define __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H - -#include -#include - -/* To be used by both hid-asus and asus-wmi to determine which controls kbd_brightness */ -#if IS_REACHABLE(CONFIG_ASUS_WMI) || IS_REACHABLE(CONFIG_HID_ASUS) -static const struct dmi_system_id asus_use_hid_led_dmi_ids[] = { - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Zephyrus"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Strix"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ROG Flow"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_PRODUCT_FAMILY, "ProArt P16"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "GA403U"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "GU605M"), - }, - }, - { - .matches = { - DMI_MATCH(DMI_BOARD_NAME, "RC71L"), - }, - }, - { }, -}; -#endif - -#endif /* __PLATFORM_DATA_X86_ASUS_WMI_LEDS_IDS_H */ -- cgit v1.2.3 From 7525566abd360ca425e43dd3f7f09fa3445dae17 Mon Sep 17 00:00:00 2001 From: Antheas Kapenekakis Date: Thu, 22 Jan 2026 08:50:43 +0100 Subject: platform/x86: asus-wmi: add keyboard brightness event handler MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The keyboard brightness control of Asus WMI keyboards is handled in kernel, which leads to the shortcut going from brightness 0, to 1, to 2, and 3. However, for HID keyboards it is exposed as a key and handled by the user's desktop environment. For the toggle button, this means that brightness control becomes on/off. In addition, in the absence of a DE, the keyboard brightness does not work. Therefore, expose an event handler for the keyboard brightness control which can then be used by hid-asus. Since this handler is called from an interrupt context, defer the actual work to a workqueue. In the process, introduce ASUS_EV_MAX_BRIGHTNESS to hold the constant for maximum brightness since it is shared between hid-asus/asus-wmi. Reviewed-by: Luke D. Jones Tested-by: Luke D. Jones Acked-by: Benjamin Tissoires Reviewed-by: Denis Benato Signed-off-by: Antheas Kapenekakis Link: https://patch.msgid.link/20260122075044.5070-11-lkml@antheas.dev Reviewed-by: Ilpo Järvinen Signed-off-by: Ilpo Järvinen --- drivers/platform/x86/asus-wmi.c | 46 ++++++++++++++++++++++++++---- include/linux/platform_data/x86/asus-wmi.h | 13 +++++++++ 2 files changed, 54 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/platform/x86/asus-wmi.c b/drivers/platform/x86/asus-wmi.c index c45846be3f99..4cbd7b9806dc 100644 --- a/drivers/platform/x86/asus-wmi.c +++ b/drivers/platform/x86/asus-wmi.c @@ -1719,6 +1719,44 @@ static void kbd_led_update_all(struct work_struct *work) } } +/* + * This function is called from hid-asus to inform asus-wmi of brightness + * changes initiated by the keyboard backlight keys. + */ +int asus_hid_event(enum asus_hid_event event) +{ + struct asus_wmi *asus; + int brightness; + + guard(spinlock_irqsave)(&asus_ref.lock); + asus = asus_ref.asus; + if (!asus || !asus->kbd_led_registered) + return -EBUSY; + + brightness = asus->kbd_led_wk; + + switch (event) { + case ASUS_EV_BRTUP: + brightness += 1; + break; + case ASUS_EV_BRTDOWN: + brightness -= 1; + break; + case ASUS_EV_BRTTOGGLE: + if (brightness >= ASUS_EV_MAX_BRIGHTNESS) + brightness = 0; + else + brightness += 1; + break; + } + + asus->kbd_led_wk = clamp_val(brightness, 0, ASUS_EV_MAX_BRIGHTNESS); + asus->kbd_led_notify = true; + queue_work(asus->led_workqueue, &asus->kbd_led_work); + return 0; +} +EXPORT_SYMBOL_GPL(asus_hid_event); + /* * These functions actually update the LED's, and are called from a * workqueue. By doing this as separate work rather than when the LED @@ -1801,13 +1839,11 @@ static void do_kbd_led_set(struct led_classdev *led_cdev, int value) { struct asus_hid_listener *listener; struct asus_wmi *asus; - int max_level; asus = container_of(led_cdev, struct asus_wmi, kbd_led); - max_level = asus->kbd_led.max_brightness; scoped_guard(spinlock_irqsave, &asus_ref.lock) - asus->kbd_led_wk = clamp_val(value, 0, max_level); + asus->kbd_led_wk = clamp_val(value, 0, ASUS_EV_MAX_BRIGHTNESS); if (asus->kbd_led_avail) kbd_led_update(asus); @@ -2011,7 +2047,7 @@ static int asus_wmi_led_init(struct asus_wmi *asus) asus->kbd_led.flags = LED_BRIGHT_HW_CHANGED; asus->kbd_led.brightness_set_blocking = kbd_led_set; asus->kbd_led.brightness_get = kbd_led_get; - asus->kbd_led.max_brightness = 3; + asus->kbd_led.max_brightness = ASUS_EV_MAX_BRIGHTNESS; asus->kbd_led_avail = !kbd_led_read(asus, &led_val, NULL); INIT_WORK(&asus->kbd_led_work, kbd_led_update_all); @@ -4530,7 +4566,7 @@ static void asus_wmi_handle_event_code(int code, struct asus_wmi *asus) return; } if (code == NOTIFY_KBD_BRTTOGGLE) { - if (led_value >= asus->kbd_led.max_brightness) + if (led_value >= ASUS_EV_MAX_BRIGHTNESS) kbd_led_set_by_kbd(asus, 0); else kbd_led_set_by_kbd(asus, led_value + 1); diff --git a/include/linux/platform_data/x86/asus-wmi.h b/include/linux/platform_data/x86/asus-wmi.h index d347cffd05d5..7b872b5d0960 100644 --- a/include/linux/platform_data/x86/asus-wmi.h +++ b/include/linux/platform_data/x86/asus-wmi.h @@ -178,6 +178,14 @@ struct asus_hid_listener { void (*brightness_set)(struct asus_hid_listener *listener, int brightness); }; +enum asus_hid_event { + ASUS_EV_BRTUP, + ASUS_EV_BRTDOWN, + ASUS_EV_BRTTOGGLE, +}; + +#define ASUS_EV_MAX_BRIGHTNESS 3 + #if IS_REACHABLE(CONFIG_ASUS_WMI) void set_ally_mcu_hack(enum asus_ally_mcu_hack status); void set_ally_mcu_powersave(bool enabled); @@ -186,6 +194,7 @@ int asus_wmi_set_devstate(u32 dev_id, u32 ctrl_param, u32 *retval); int asus_wmi_evaluate_method(u32 method_id, u32 arg0, u32 arg1, u32 *retval); int asus_hid_register_listener(struct asus_hid_listener *cdev); void asus_hid_unregister_listener(struct asus_hid_listener *cdev); +int asus_hid_event(enum asus_hid_event event); #else static inline void set_ally_mcu_hack(enum asus_ally_mcu_hack status) { @@ -213,6 +222,10 @@ static inline int asus_hid_register_listener(struct asus_hid_listener *bdev) static inline void asus_hid_unregister_listener(struct asus_hid_listener *bdev) { } +static inline int asus_hid_event(enum asus_hid_event event) +{ + return -ENODEV; +} #endif #endif /* __PLATFORM_DATA_X86_ASUS_WMI_H */ -- cgit v1.2.3 From ca136b2553084120a1f864f14f4bc70f08cb6420 Mon Sep 17 00:00:00 2001 From: Remi Pommarel Date: Fri, 23 Jan 2026 15:48:07 +0100 Subject: wait: Introduce io_wait_event_killable() Add io_wait_event_killable(), a variant of wait_event_killable() that uses io_schedule() instead of schedule(). This is to be used in situation where waiting time is to be accounted as IO wait time. Signed-off-by: Remi Pommarel Acked-by: Peter Zijlstra (Intel) Message-ID: <1b2870001ecd34fe6c05be2ddfefb3c798b11701.1769179462.git.repk@triplefau.lt> Signed-off-by: Dominique Martinet --- include/linux/wait.h | 15 +++++++++++++++ 1 file changed, 15 insertions(+) (limited to 'include/linux') diff --git a/include/linux/wait.h b/include/linux/wait.h index f648044466d5..dce055e6add3 100644 --- a/include/linux/wait.h +++ b/include/linux/wait.h @@ -937,6 +937,21 @@ extern int do_wait_intr_irq(wait_queue_head_t *, wait_queue_entry_t *); __ret; \ }) +#define __io_wait_event_killable(wq, condition) \ + ___wait_event(wq, condition, TASK_KILLABLE, 0, 0, io_schedule()) + +/* + * wait_event_killable() - link wait_event_killable but with io_schedule() + */ +#define io_wait_event_killable(wq_head, condition) \ +({ \ + int __ret = 0; \ + might_sleep(); \ + if (!(condition)) \ + __ret = __io_wait_event_killable(wq_head, condition); \ + __ret; \ +}) + #define __wait_event_state(wq, condition, state) \ ___wait_event(wq, condition, state, 0, 0, schedule()) -- cgit v1.2.3 From 55fb177d3a0346106974749374ae2191ba250825 Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Wed, 28 Jan 2026 14:24:05 +0100 Subject: fs: add helpers name_is_dot{,dot,_dotdot} Rename the helper is_dot_dotdot() into the name_ namespace and add complementary helpers to check for dot and dotdot names individually. Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260128132406.23768-3-amir73il@gmail.com Reviewed-by: Eric Biggers Signed-off-by: Christian Brauner --- fs/crypto/fname.c | 2 +- fs/ecryptfs/crypto.c | 2 +- fs/exportfs/expfs.c | 3 ++- fs/f2fs/dir.c | 2 +- fs/f2fs/hash.c | 2 +- fs/namei.c | 2 +- fs/overlayfs/readdir.c | 3 ++- fs/smb/server/vfs.c | 2 +- include/linux/fs.h | 14 ++++++++++++-- 9 files changed, 22 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/fs/crypto/fname.c b/fs/crypto/fname.c index a9a4432d12ba..629eb0d72e86 100644 --- a/fs/crypto/fname.c +++ b/fs/crypto/fname.c @@ -76,7 +76,7 @@ struct fscrypt_nokey_name { static inline bool fscrypt_is_dot_dotdot(const struct qstr *str) { - return is_dot_dotdot(str->name, str->len); + return name_is_dot_dotdot(str->name, str->len); } /** diff --git a/fs/ecryptfs/crypto.c b/fs/ecryptfs/crypto.c index 260f8a4938b0..3c89f06c7453 100644 --- a/fs/ecryptfs/crypto.c +++ b/fs/ecryptfs/crypto.c @@ -1904,7 +1904,7 @@ int ecryptfs_decode_and_decrypt_filename(char **plaintext_name, if ((mount_crypt_stat->flags & ECRYPTFS_GLOBAL_ENCRYPT_FILENAMES) && !(mount_crypt_stat->flags & ECRYPTFS_ENCRYPTED_VIEW_ENABLED)) { - if (is_dot_dotdot(name, name_size)) { + if (name_is_dot_dotdot(name, name_size)) { rc = ecryptfs_copy_filename(plaintext_name, plaintext_name_size, name, name_size); diff --git a/fs/exportfs/expfs.c b/fs/exportfs/expfs.c index d3e55de4a2a2..6c9be60a3e48 100644 --- a/fs/exportfs/expfs.c +++ b/fs/exportfs/expfs.c @@ -253,7 +253,8 @@ static bool filldir_one(struct dir_context *ctx, const char *name, int len, container_of(ctx, struct getdents_callback, ctx); buf->sequence++; - if (buf->ino == ino && len <= NAME_MAX && !is_dot_dotdot(name, len)) { + if (buf->ino == ino && len <= NAME_MAX && + !name_is_dot_dotdot(name, len)) { memcpy(buf->name, name, len); buf->name[len] = '\0'; buf->found = 1; diff --git a/fs/f2fs/dir.c b/fs/f2fs/dir.c index 48f4f98afb01..29412e6e078d 100644 --- a/fs/f2fs/dir.c +++ b/fs/f2fs/dir.c @@ -67,7 +67,7 @@ int f2fs_init_casefolded_name(const struct inode *dir, int len; if (IS_CASEFOLDED(dir) && - !is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) { + !name_is_dot_dotdot(fname->usr_fname->name, fname->usr_fname->len)) { buf = f2fs_kmem_cache_alloc(f2fs_cf_name_slab, GFP_NOFS, false, F2FS_SB(sb)); if (!buf) diff --git a/fs/f2fs/hash.c b/fs/f2fs/hash.c index 049ce50cec9b..14082fe5e6b2 100644 --- a/fs/f2fs/hash.c +++ b/fs/f2fs/hash.c @@ -100,7 +100,7 @@ void f2fs_hash_filename(const struct inode *dir, struct f2fs_filename *fname) WARN_ON_ONCE(!name); - if (is_dot_dotdot(name, len)) { + if (name_is_dot_dotdot(name, len)) { fname->hash = 0; return; } diff --git a/fs/namei.c b/fs/namei.c index 4e3a5fd370a8..aa8fbca81686 100644 --- a/fs/namei.c +++ b/fs/namei.c @@ -3042,7 +3042,7 @@ int lookup_noperm_common(struct qstr *qname, struct dentry *base) if (!len) return -EACCES; - if (is_dot_dotdot(name, len)) + if (name_is_dot_dotdot(name, len)) return -EACCES; while (len--) { diff --git a/fs/overlayfs/readdir.c b/fs/overlayfs/readdir.c index 724ec9d93fc8..9f6b36f3d4cf 100644 --- a/fs/overlayfs/readdir.c +++ b/fs/overlayfs/readdir.c @@ -76,7 +76,8 @@ static int ovl_casefold(struct ovl_readdir_data *rdd, const char *str, int len, char *cf_name; int cf_len; - if (!IS_ENABLED(CONFIG_UNICODE) || !rdd->map || is_dot_dotdot(str, len)) + if (!IS_ENABLED(CONFIG_UNICODE) || !rdd->map || + name_is_dot_dotdot(str, len)) return 0; cf_name = kmalloc(NAME_MAX, GFP_KERNEL); diff --git a/fs/smb/server/vfs.c b/fs/smb/server/vfs.c index 98b0eb966d91..e73e968f664d 100644 --- a/fs/smb/server/vfs.c +++ b/fs/smb/server/vfs.c @@ -1052,7 +1052,7 @@ static bool __dir_empty(struct dir_context *ctx, const char *name, int namlen, struct ksmbd_readdir_data *buf; buf = container_of(ctx, struct ksmbd_readdir_data, ctx); - if (!is_dot_dotdot(name, namlen)) + if (!name_is_dot_dotdot(name, namlen)) buf->dirent_count++; return !buf->dirent_count; diff --git a/include/linux/fs.h b/include/linux/fs.h index 094b0adcb035..95bb9a15e109 100644 --- a/include/linux/fs.h +++ b/include/linux/fs.h @@ -2844,12 +2844,22 @@ u64 vfsmount_to_propagation_flags(struct vfsmount *mnt); extern char *file_path(struct file *, char *, int); +static inline bool name_is_dot(const char *name, size_t len) +{ + return unlikely(len == 1 && name[0] == '.'); +} + +static inline bool name_is_dotdot(const char *name, size_t len) +{ + return unlikely(len == 2 && name[0] == '.' && name[1] == '.'); +} + /** - * is_dot_dotdot - returns true only if @name is "." or ".." + * name_is_dot_dotdot - returns true only if @name is "." or ".." * @name: file name to check * @len: length of file name, in bytes */ -static inline bool is_dot_dotdot(const char *name, size_t len) +static inline bool name_is_dot_dotdot(const char *name, size_t len) { return len && unlikely(name[0] == '.') && (len == 1 || (len == 2 && name[1] == '.')); -- cgit v1.2.3 From 0e6b7eae1fded85f94a357d6132f07d64c614cfa Mon Sep 17 00:00:00 2001 From: Andrey Albershteyn Date: Mon, 26 Jan 2026 12:56:57 +0100 Subject: fs: add FS_XFLAG_VERITY for fs-verity files fs-verity introduced inode flag for inodes with enabled fs-verity on them. This patch adds FS_XFLAG_VERITY file attribute which can be retrieved with FS_IOC_FSGETXATTR ioctl() and file_getattr() syscall. This flag is read-only and can not be set with corresponding set ioctl() and file_setattr(). The FS_IOC_SETFLAGS requires file to be opened for writing which is not allowed for verity files. The FS_IOC_FSSETXATTR and file_setattr() clears this flag from the user input. As this is now common flag for both flag interfaces (flags/xflags) add it to overlapping flags list to exclude it from overwrite. Signed-off-by: Andrey Albershteyn Link: https://patch.msgid.link/20260126115658.27656-2-aalbersh@kernel.org Reviewed-by: Darrick J. Wong Signed-off-by: Christian Brauner --- Documentation/filesystems/fsverity.rst | 16 ++++++++++++++++ fs/file_attr.c | 4 ++++ include/linux/fileattr.h | 6 +++--- include/uapi/linux/fs.h | 1 + 4 files changed, 24 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/Documentation/filesystems/fsverity.rst b/Documentation/filesystems/fsverity.rst index 412cf11e3298..22b49b295d1f 100644 --- a/Documentation/filesystems/fsverity.rst +++ b/Documentation/filesystems/fsverity.rst @@ -341,6 +341,22 @@ the file has fs-verity enabled. This can perform better than FS_IOC_GETFLAGS and FS_IOC_MEASURE_VERITY because it doesn't require opening the file, and opening verity files can be expensive. +FS_IOC_FSGETXATTR +----------------- + +Since Linux v7.0, the FS_IOC_FSGETXATTR ioctl sets FS_XFLAG_VERITY (0x00020000) +in the returned flags when the file has verity enabled. Note that this attribute +cannot be set with FS_IOC_FSSETXATTR as enabling verity requires input +parameters. See FS_IOC_ENABLE_VERITY. + +file_getattr +------------ + +Since Linux v7.0, the file_getattr() syscall sets FS_XFLAG_VERITY (0x00020000) +in the returned flags when the file has verity enabled. Note that this attribute +cannot be set with file_setattr() as enabling verity requires input parameters. +See FS_IOC_ENABLE_VERITY. + .. _accessing_verity_files: Accessing verity files diff --git a/fs/file_attr.c b/fs/file_attr.c index f3704881c126..dfde87401817 100644 --- a/fs/file_attr.c +++ b/fs/file_attr.c @@ -36,6 +36,8 @@ void fileattr_fill_xflags(struct file_kattr *fa, u32 xflags) fa->flags |= FS_DAX_FL; if (fa->fsx_xflags & FS_XFLAG_PROJINHERIT) fa->flags |= FS_PROJINHERIT_FL; + if (fa->fsx_xflags & FS_XFLAG_VERITY) + fa->flags |= FS_VERITY_FL; } EXPORT_SYMBOL(fileattr_fill_xflags); @@ -66,6 +68,8 @@ void fileattr_fill_flags(struct file_kattr *fa, u32 flags) fa->fsx_xflags |= FS_XFLAG_DAX; if (fa->flags & FS_PROJINHERIT_FL) fa->fsx_xflags |= FS_XFLAG_PROJINHERIT; + if (fa->flags & FS_VERITY_FL) + fa->fsx_xflags |= FS_XFLAG_VERITY; } EXPORT_SYMBOL(fileattr_fill_flags); diff --git a/include/linux/fileattr.h b/include/linux/fileattr.h index f89dcfad3f8f..3780904a63a6 100644 --- a/include/linux/fileattr.h +++ b/include/linux/fileattr.h @@ -7,16 +7,16 @@ #define FS_COMMON_FL \ (FS_SYNC_FL | FS_IMMUTABLE_FL | FS_APPEND_FL | \ FS_NODUMP_FL | FS_NOATIME_FL | FS_DAX_FL | \ - FS_PROJINHERIT_FL) + FS_PROJINHERIT_FL | FS_VERITY_FL) #define FS_XFLAG_COMMON \ (FS_XFLAG_SYNC | FS_XFLAG_IMMUTABLE | FS_XFLAG_APPEND | \ FS_XFLAG_NODUMP | FS_XFLAG_NOATIME | FS_XFLAG_DAX | \ - FS_XFLAG_PROJINHERIT) + FS_XFLAG_PROJINHERIT | FS_XFLAG_VERITY) /* Read-only inode flags */ #define FS_XFLAG_RDONLY_MASK \ - (FS_XFLAG_PREALLOC | FS_XFLAG_HASATTR) + (FS_XFLAG_PREALLOC | FS_XFLAG_HASATTR | FS_XFLAG_VERITY) /* Flags to indicate valid value of fsx_ fields */ #define FS_XFLAG_VALUES_MASK \ diff --git a/include/uapi/linux/fs.h b/include/uapi/linux/fs.h index 66ca526cf786..70b2b661f42c 100644 --- a/include/uapi/linux/fs.h +++ b/include/uapi/linux/fs.h @@ -253,6 +253,7 @@ struct file_attr { #define FS_XFLAG_FILESTREAM 0x00004000 /* use filestream allocator */ #define FS_XFLAG_DAX 0x00008000 /* use DAX for IO */ #define FS_XFLAG_COWEXTSIZE 0x00010000 /* CoW extent size allocator hint */ +#define FS_XFLAG_VERITY 0x00020000 /* fs-verity enabled */ #define FS_XFLAG_HASATTR 0x80000000 /* no DIFLAG for this */ /* the read-only stuff doesn't really belong here, but any other place is -- cgit v1.2.3 From a39162f77f49b618df5a721a1e48d8b903280fbd Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Thu, 29 Jan 2026 11:02:11 +0100 Subject: exportfs: clarify the documentation of open()/permission() expotrfs ops pidfs and nsfs recently gained support for encode/decode of file handles via name_to_handle_at(2)/open_by_handle_at(2). These special kernel filesystems have custom ->open() and ->permission() export methods, which nfsd does not respect and it was never meant to be used for exporting those filesystems by nfsd. Update kernel-doc comments to express the fact the those methods are for open_by_handle(2) system only and not compatible with nfsd. Reviewed-by: Jeff Layton Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260129100212.49727-2-amir73il@gmail.com Reviewed-by: Chuck Lever Signed-off-by: Christian Brauner --- include/linux/exportfs.h | 12 ++++++++++-- 1 file changed, 10 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index 262e24d83313..0660953c3fb7 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h @@ -200,6 +200,10 @@ struct handle_to_path_ctx { * @get_parent: find the parent of a given directory * @commit_metadata: commit metadata changes to stable storage * + * Methods for open_by_handle(2) syscall with special kernel file systems: + * @permission: custom permission for opening a file by handle + * @open: custom open routine for opening file by handle + * * See Documentation/filesystems/nfs/exporting.rst for details on how to use * this interface correctly and the definition of the flags. * @@ -244,10 +248,14 @@ struct handle_to_path_ctx { * space cannot be allocated, a %ERR_PTR should be returned. * * @permission: - * Allow filesystems to specify a custom permission function. + * Allow filesystems to specify a custom permission function for the + * open_by_handle_at(2) syscall instead of the default permission check. + * This custom permission function is not respected by nfsd. * * @open: - * Allow filesystems to specify a custom open function. + * Allow filesystems to specify a custom open function for the + * open_by_handle_at(2) syscall instead of the default file_open_root(). + * This custom open function is not respected by nfsd. * * @commit_metadata: * @commit_metadata should commit metadata changes to stable storage. -- cgit v1.2.3 From b3c78bc53630d14a5770451ede3a30e7052f3b8b Mon Sep 17 00:00:00 2001 From: Amir Goldstein Date: Thu, 29 Jan 2026 11:02:12 +0100 Subject: nfsd: do not allow exporting of special kernel filesystems pidfs and nsfs recently gained support for encode/decode of file handles via name_to_handle_at(2)/open_by_handle_at(2). These special kernel filesystems have custom ->open() and ->permission() export methods, which nfsd does not respect and it was never meant to be used for exporting those filesystems by nfsd. Therefore, do not allow nfsd to export filesystems with custom ->open() or ->permission() methods. Fixes: b3caba8f7a34a ("pidfs: implement file handle support") Fixes: 5222470b2fbb3 ("nsfs: support file handles") Reviewed-by: Jeff Layton Signed-off-by: Amir Goldstein Link: https://patch.msgid.link/20260129100212.49727-3-amir73il@gmail.com Reviewed-by: Chuck Lever Signed-off-by: Christian Brauner --- fs/nfsd/export.c | 8 +++++--- include/linux/exportfs.h | 9 +++++++++ 2 files changed, 14 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/fs/nfsd/export.c b/fs/nfsd/export.c index 9d55512d0cc9..baaa18b878d7 100644 --- a/fs/nfsd/export.c +++ b/fs/nfsd/export.c @@ -427,7 +427,8 @@ static int check_export(const struct path *path, int *flags, unsigned char *uuid * either a device number (so FS_REQUIRES_DEV needed) * or an FSID number (so NFSEXP_FSID or ->uuid is needed). * 2: We must be able to find an inode from a filehandle. - * This means that s_export_op must be set. + * This means that s_export_op must be set and comply with + * the requirements for remote filesystem export. * 3: We must not currently be on an idmapped mount. */ if (!(inode->i_sb->s_type->fs_flags & FS_REQUIRES_DEV) && @@ -437,8 +438,9 @@ static int check_export(const struct path *path, int *flags, unsigned char *uuid return -EINVAL; } - if (!exportfs_can_decode_fh(inode->i_sb->s_export_op)) { - dprintk("exp_export: export of invalid fs type.\n"); + if (!exportfs_may_export(inode->i_sb->s_export_op)) { + dprintk("exp_export: export of invalid fs type (%s).\n", + inode->i_sb->s_type->name); return -EINVAL; } diff --git a/include/linux/exportfs.h b/include/linux/exportfs.h index 0660953c3fb7..8bcdba28b406 100644 --- a/include/linux/exportfs.h +++ b/include/linux/exportfs.h @@ -338,6 +338,15 @@ static inline bool exportfs_can_decode_fh(const struct export_operations *nop) return nop && nop->fh_to_dentry; } +static inline bool exportfs_may_export(const struct export_operations *nop) +{ + /* + * Do not allow nfs export for filesystems with custom ->open() or + * ->permission() ops, which nfsd does not respect (e.g. pidfs, nsfs). + */ + return exportfs_can_decode_fh(nop) && !nop->open && !nop->permission; +} + static inline bool exportfs_can_encode_fh(const struct export_operations *nop, int fh_flags) { -- cgit v1.2.3 From bf870c97ce2b9b48f02184d317dab4cf5691ca0e Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Wed, 28 Jan 2026 12:07:20 -0800 Subject: iio: buffer: buffer_impl.h: fix kernel-doc warnings Resolve all kernel-doc warnings in buffer_impl.h: Warning: include/linux/iio/buffer_impl.h:172 struct member 'direction' not described in 'iio_buffer' Warning: include/linux/iio/buffer_impl.h:184 No description found for return value of 'iio_update_buffers' Also correct one typo (word order switch) and remove one stray space in a kernel-doc comment. Signed-off-by: Randy Dunlap Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- include/linux/iio/buffer_impl.h | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/iio/buffer_impl.h b/include/linux/iio/buffer_impl.h index c0b0e0992a85..9442c165561a 100644 --- a/include/linux/iio/buffer_impl.h +++ b/include/linux/iio/buffer_impl.h @@ -113,10 +113,10 @@ struct iio_buffer { /** @flags: File ops flags including busy flag. */ unsigned long flags; - /** @bytes_per_datum: Size of individual datum including timestamp. */ + /** @bytes_per_datum: Size of individual datum including timestamp. */ size_t bytes_per_datum; - /* @direction: Direction of the data stream (in/out). */ + /** @direction: Direction of the data stream (in/out). */ enum iio_buffer_direction direction; /** @@ -178,7 +178,9 @@ struct iio_buffer { * @insert_buffer: buffer to insert * @remove_buffer: buffer_to_remove * - * Note this will tear down the all buffering and build it up again + * Note this will tear down all the buffering and build it up again + * + * Returns: 0 on success or -errno on error */ int iio_update_buffers(struct iio_dev *indio_dev, struct iio_buffer *insert_buffer, -- cgit v1.2.3 From 2693ca2e02793fde06ff9d64175aed6f144f2287 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Tue, 27 Jan 2026 22:25:37 -0800 Subject: iio: frequency: ad9523: correct kernel-doc bad line warning Insert a "*" in the kernel-doc line to resolve a warning: Warning: include/linux/iio/frequency/ad9523.h:47 bad line: LSB = 1/2 of a period of the divider input clock. Signed-off-by: Randy Dunlap Reviewed-by: Andy Shevchenko Signed-off-by: Jonathan Cameron --- include/linux/iio/frequency/ad9523.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/iio/frequency/ad9523.h b/include/linux/iio/frequency/ad9523.h index ff22a0ac15f5..236437a226b2 100644 --- a/include/linux/iio/frequency/ad9523.h +++ b/include/linux/iio/frequency/ad9523.h @@ -45,7 +45,7 @@ enum ref_sel_mode { * @output_dis: Disables, powers down the entire channel. * @driver_mode: Output driver mode (logic level family). * @divider_phase: Divider initial phase after a SYNC. Range 0..63 - LSB = 1/2 of a period of the divider input clock. + * LSB = 1/2 of a period of the divider input clock. * @channel_divider: 10-bit channel divider. * @extended_name: Optional descriptive channel name. */ -- cgit v1.2.3 From 0077e9b985482e5c020468c6257f8508f68aa0b2 Mon Sep 17 00:00:00 2001 From: Gwendal Grignou Date: Tue, 27 Jan 2026 19:27:02 -0800 Subject: iio: cros_ec: Allow enabling/disabling calibration mode 'calibrate' was a one-shot event sent to the sensor to calibrate itself. It is used on Bosch sensors (BMI160, BMA254). Light sensors work differently: They are first put in calibration mode, tests are run to collect information and calculate the calibration values to apply. Once done, the sensors are put back in normal mode. Accept boolean true and false (not just true) to enter/exit calibration state. Check "echo 0 > calibrate" is supported. Signed-off-by: Gwendal Grignou Reviewed-by: Nick Vaccaro Reviewed-by: Tzung-Bi Shih Signed-off-by: Jonathan Cameron --- Documentation/ABI/testing/sysfs-bus-iio-cros-ec | 9 ++++++--- drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c | 6 +++--- include/linux/platform_data/cros_ec_commands.h | 12 +++++++++--- 3 files changed, 18 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/Documentation/ABI/testing/sysfs-bus-iio-cros-ec b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec index 9e3926243797..3de1dfc98389 100644 --- a/Documentation/ABI/testing/sysfs-bus-iio-cros-ec +++ b/Documentation/ABI/testing/sysfs-bus-iio-cros-ec @@ -3,9 +3,12 @@ Date: July 2015 KernelVersion: 4.7 Contact: linux-iio@vger.kernel.org Description: - Writing '1' will perform a FOC (Fast Online Calibration). The - corresponding calibration offsets can be read from `*_calibbias` - entries. + Writing '1' either perform a FOC (Fast Online Calibration) or + enter calibration mode. + Writing '0` exits calibration mode. It is a NOP for FOC enabled + sensors. + The corresponding calibration offsets can be read from `*_calibbias` + entries. What: /sys/bus/iio/devices/iio:deviceX/id Date: September 2017 diff --git a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c index ef53066b1735..5133755c2ea6 100644 --- a/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c +++ b/drivers/iio/common/cros_ec_sensors/cros_ec_sensors_core.c @@ -441,14 +441,14 @@ static ssize_t cros_ec_sensors_calibrate(struct iio_dev *indio_dev, ret = kstrtobool(buf, &calibrate); if (ret < 0) return ret; - if (!calibrate) - return -EINVAL; mutex_lock(&st->cmd_lock); st->param.cmd = MOTIONSENSE_CMD_PERFORM_CALIB; + st->param.perform_calib.enable = calibrate; ret = cros_ec_motion_send_host_cmd(st, 0); if (ret != 0) { - dev_warn(&indio_dev->dev, "Unable to calibrate sensor\n"); + dev_warn(&indio_dev->dev, "Unable to calibrate sensor: %d\n", + ret); } else { /* Save values */ for (i = CROS_EC_SENSOR_X; i < CROS_EC_SENSOR_MAX_AXIS; i++) diff --git a/include/linux/platform_data/cros_ec_commands.h b/include/linux/platform_data/cros_ec_commands.h index 69294f79cc88..d363d60bb8a3 100644 --- a/include/linux/platform_data/cros_ec_commands.h +++ b/include/linux/platform_data/cros_ec_commands.h @@ -2598,13 +2598,19 @@ struct ec_params_motion_sense { /* * Used for MOTIONSENSE_CMD_INFO, MOTIONSENSE_CMD_DATA - * and MOTIONSENSE_CMD_PERFORM_CALIB. */ struct __ec_todo_unpacked { uint8_t sensor_num; - } info, info_3, data, fifo_flush, perform_calib, - list_activities; + } info, info_3, data, fifo_flush, list_activities; + /* + * Used for MOTIONSENSE_CMD_PERFORM_CALIB: + * Allow entering/exiting the calibration mode. + */ + struct __ec_todo_unpacked { + uint8_t sensor_num; + uint8_t enable; + } perform_calib; /* * Used for MOTIONSENSE_CMD_EC_RATE, MOTIONSENSE_CMD_SENSOR_ODR * and MOTIONSENSE_CMD_SENSOR_RANGE. -- cgit v1.2.3 From 28c3edc43cb9ab6ebe43439d48a662a095409b03 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:00 +0100 Subject: spi: spi-mem: Create a repeated address operation In octal DTR mode addresses may either be long enough to cover at least two bytes (in which case the existing macro works), or otherwise for single byte addresses, the byte must also be duplicated and sent twice: on each front of the clock. Create a macro for this common case. Signed-off-by: Miquel Raynal --- include/linux/spi/spi-mem.h | 8 ++++++++ 1 file changed, 8 insertions(+) (limited to 'include/linux') diff --git a/include/linux/spi/spi-mem.h b/include/linux/spi/spi-mem.h index e4db0924898c..5774e554c0f0 100644 --- a/include/linux/spi/spi-mem.h +++ b/include/linux/spi/spi-mem.h @@ -51,6 +51,14 @@ .dtr = true, \ } +#define SPI_MEM_DTR_OP_RPT_ADDR(__val, __buswidth) \ + { \ + .nbytes = 2, \ + .val = __val | __val << 8, \ + .buswidth = __buswidth, \ + .dtr = true, \ + } + #define SPI_MEM_OP_NO_ADDR { } #define SPI_MEM_OP_DUMMY(__nbytes, __buswidth) \ -- cgit v1.2.3 From a57b1f07d2d35843a7ada30c8cf9a215c0931868 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:02 +0100 Subject: mtd: spinand: Fix kernel doc The @data buffer is 5 bytes, not 4, it has been extended for the need of devices with an extra ID bytes. Fixes: 34a956739d29 ("mtd: spinand: Add support for 5-byte IDs") Reviewed-by: Tudor Ambarus Signed-off-by: Miquel Raynal --- include/linux/mtd/spinand.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index c50a43b447d2..3de22f0f79d7 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -287,7 +287,7 @@ struct spinand_device; /** * struct spinand_id - SPI NAND id structure - * @data: buffer containing the id bytes. Currently 4 bytes large, but can + * @data: buffer containing the id bytes. Currently 5 bytes large, but can * be extended if required * @len: ID length */ -- cgit v1.2.3 From d48db8ca47662f5f4e1b7e173687d853f017ae3a Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:04 +0100 Subject: mtd: spinand: Remove stale definitions SPI NAND command values are directly included in the macros defining the ops. These are stale definitions, they are unused so drop them. Reviewed-by: Tudor Ambarus Signed-off-by: Miquel Raynal --- include/linux/mtd/spinand.h | 6 ------ 1 file changed, 6 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 3de22f0f79d7..cafbd0fa8db0 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -232,12 +232,6 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(len, buf, 8)) -/** - * Standard SPI NAND flash commands - */ -#define SPINAND_CMD_PROG_LOAD_X4 0x32 -#define SPINAND_CMD_PROG_LOAD_RDM_DATA_X4 0x34 - /* feature register */ #define REG_BLOCK_LOCK 0xa0 #define BL_ALL_UNLOCKED 0x00 -- cgit v1.2.3 From c0ba929cf7a960c796cc9946b3f79d8405e9b805 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:06 +0100 Subject: mtd: spinand: Decouple write enable and write disable operations In order to introduce templates for all operations and not only for page helpers (in order to introduce octal DDR support), decouple the WR_EN and WR_DIS operations into two separate macros. Adapt the callers accordingly. There is no functional change. Reviewed-by: Tudor Ambarus Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 2 +- drivers/mtd/nand/spi/esmt.c | 2 +- drivers/mtd/nand/spi/micron.c | 2 +- include/linux/mtd/spinand.h | 10 ++++++++-- 4 files changed, 11 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 2c587685e02a..30a8f2894d02 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -362,7 +362,7 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status) int spinand_write_enable_op(struct spinand_device *spinand) { - struct spi_mem_op op = SPINAND_WR_EN_DIS_1S_0_0_OP(true); + struct spi_mem_op op = SPINAND_WR_EN_1S_0_0_OP; return spi_mem_exec_op(spinand->spimem, &op); } diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c index e60e4ac1fd6f..adadc01e8f2f 100644 --- a/drivers/mtd/nand/spi/esmt.c +++ b/drivers/mtd/nand/spi/esmt.c @@ -138,7 +138,7 @@ static int f50l1g41lb_user_otp_info(struct spinand_device *spinand, size_t len, static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from, size_t len) { - struct spi_mem_op write_op = SPINAND_WR_EN_DIS_1S_0_0_OP(true); + struct spi_mem_op write_op = SPINAND_WR_EN_1S_0_0_OP; struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0); u8 status; int ret; diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index a49d7cb6a96d..b8130e04e8e7 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -251,7 +251,7 @@ static int mt29f2g01abagd_user_otp_info(struct spinand_device *spinand, static int mt29f2g01abagd_otp_lock(struct spinand_device *spinand, loff_t from, size_t len) { - struct spi_mem_op write_op = SPINAND_WR_EN_DIS_1S_0_0_OP(true); + struct spi_mem_op write_op = SPINAND_WR_EN_1S_0_0_OP; struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0); u8 status; int ret; diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index cafbd0fa8db0..4715083aa8e5 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -26,8 +26,14 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -#define SPINAND_WR_EN_DIS_1S_0_0_OP(enable) \ - SPI_MEM_OP(SPI_MEM_OP_CMD((enable) ? 0x06 : 0x04, 1), \ +#define SPINAND_WR_EN_1S_0_0_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x06, 1), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_WR_DIS_1S_0_0_OP \ + SPI_MEM_OP(SPI_MEM_OP_CMD(0x04, 1), \ SPI_MEM_OP_NO_ADDR, \ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_NO_DATA) -- cgit v1.2.3 From 408015023294958407925bc50cdd85718d12a335 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:07 +0100 Subject: mtd: spinand: Create an array of operation templates Currently, the SPI NAND core implementation directly calls macros to get the various operations in shape. These macros are specific to the bus interface, currently only supporting the single SDR interface (any command following the 1S-XX-XX pattern). Introducing support for other bus interfaces (such as octal DTR) would mean that every user of these macros should become aware of the current bus interface and act accordingly, picking up and adapting to the current configuration. This would add quite a bit of boilerplate, be repetitive as well as error prone in case we miss one occurrence. Instead, let's create a table with all SPI NAND memory operations that are currently supported. We initialize them with the same single SDR _OP macros as before. This opens the possibility for users of the individual macros to make use of these templates instead. This way, when we will add another bus interface, we can just switch to another set of templates and all users will magically fill in their spi_mem_op structures with the correct ops. The existing read, write and update cache variants are also moved in this template array, which is barely noticeable by callers as we also add a structure member pointing to it. Reviewed-by: Tudor Ambarus Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 38 +++++++++++++++++++++++++++---------- drivers/mtd/nand/spi/winbond.c | 4 ++-- include/linux/mtd/spinand.h | 43 +++++++++++++++++++++++++++++++++--------- 3 files changed, 64 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 30a8f2894d02..4ab63d7fc461 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -184,9 +184,9 @@ static int spinand_init_quad_enable(struct spinand_device *spinand) if (!(spinand->flags & SPINAND_HAS_QE_BIT)) return 0; - if (spinand->op_templates.read_cache->data.buswidth == 4 || - spinand->op_templates.write_cache->data.buswidth == 4 || - spinand->op_templates.update_cache->data.buswidth == 4) + if (spinand->op_templates->read_cache->data.buswidth == 4 || + spinand->op_templates->write_cache->data.buswidth == 4 || + spinand->op_templates->update_cache->data.buswidth == 4) enable = true; return spinand_upd_cfg(spinand, CFG_QUAD_ENABLE, @@ -1154,7 +1154,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand, info.offset = plane << fls(nand->memorg.pagesize); info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); - info.op_tmpl = *spinand->op_templates.update_cache; + info.op_tmpl = *spinand->op_templates->update_cache; desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, spinand->spimem, &info); if (IS_ERR(desc)) @@ -1162,7 +1162,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand, spinand->dirmaps[plane].wdesc = desc; - info.op_tmpl = *spinand->op_templates.read_cache; + info.op_tmpl = *spinand->op_templates->read_cache; desc = spinand_create_rdesc(spinand, &info); if (IS_ERR(desc)) return PTR_ERR(desc); @@ -1177,7 +1177,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand, } info.length = nanddev_page_size(nand) + nanddev_per_page_oobsize(nand); - info.op_tmpl = *spinand->op_templates.update_cache; + info.op_tmpl = *spinand->op_templates->update_cache; info.op_tmpl.data.ecc = true; desc = devm_spi_mem_dirmap_create(&spinand->spimem->spi->dev, spinand->spimem, &info); @@ -1186,7 +1186,7 @@ static int spinand_create_dirmap(struct spinand_device *spinand, spinand->dirmaps[plane].wdesc_ecc = desc; - info.op_tmpl = *spinand->op_templates.read_cache; + info.op_tmpl = *spinand->op_templates->read_cache; info.op_tmpl.data.ecc = true; desc = spinand_create_rdesc(spinand, &info); if (IS_ERR(desc)) @@ -1324,6 +1324,22 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand) return spinand->manufacturer->ops->cleanup(spinand); } +static void spinand_init_ssdr_templates(struct spinand_device *spinand) +{ + struct spinand_mem_ops *tmpl = &spinand->ssdr_op_templates; + + tmpl->reset = (struct spi_mem_op)SPINAND_RESET_1S_0_0_OP; + tmpl->readid = (struct spi_mem_op)SPINAND_READID_1S_1S_1S_OP(0, 0, NULL, 0); + tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_1S_0_0_OP; + tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_1S_0_0_OP; + tmpl->set_feature = (struct spi_mem_op)SPINAND_SET_FEATURE_1S_1S_1S_OP(0, NULL); + tmpl->get_feature = (struct spi_mem_op)SPINAND_GET_FEATURE_1S_1S_1S_OP(0, NULL); + tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_1S_1S_0_OP(0); + tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_1S_1S_0_OP(0); + tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_1S_1S_0_OP(0); + spinand->op_templates = &spinand->ssdr_op_templates; +} + static const struct spi_mem_op * spinand_select_op_variant(struct spinand_device *spinand, const struct spinand_op_variants *variants) @@ -1419,21 +1435,21 @@ int spinand_match_and_init(struct spinand_device *spinand, if (!op) return -EOPNOTSUPP; - spinand->op_templates.read_cache = op; + spinand->ssdr_op_templates.read_cache = op; op = spinand_select_op_variant(spinand, info->op_variants.write_cache); if (!op) return -EOPNOTSUPP; - spinand->op_templates.write_cache = op; + spinand->ssdr_op_templates.write_cache = op; op = spinand_select_op_variant(spinand, info->op_variants.update_cache); if (!op) return -EOPNOTSUPP; - spinand->op_templates.update_cache = op; + spinand->ssdr_op_templates.update_cache = op; return 0; } @@ -1548,6 +1564,8 @@ static int spinand_init(struct spinand_device *spinand) if (!spinand->scratchbuf) return -ENOMEM; + spinand_init_ssdr_templates(spinand); + ret = spinand_detect(spinand); if (ret) goto err_free_bufs; diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 4870b2d5edb2..d5799c2df065 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -291,7 +291,7 @@ static int w25n0xjw_hs_cfg(struct spinand_device *spinand) u8 sr4; int ret; - op = spinand->op_templates.read_cache; + op = spinand->op_templates->read_cache; if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) hs = false; else if (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && @@ -355,7 +355,7 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand) u8 io_mode; int ret; - op = spinand->op_templates.read_cache; + op = spinand->op_templates->read_cache; single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && op->data.buswidth == 1); dtr = (op->cmd.dtr || op->addr.dtr || op->data.dtr); diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 4715083aa8e5..a458617fb375 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -600,6 +600,36 @@ struct spinand_dirmap { struct spi_mem_dirmap_desc *rdesc_ecc; }; +/** + * struct spinand_mem_ops - SPI NAND memory operations + * @reset: reset op template + * @readid: read ID op template + * @wr_en: write enable op template + * @wr_dis: write disable op template + * @set_feature: set feature op template + * @get_feature: get feature op template + * @blk_erase: blk erase op template + * @page_read: page read op template + * @prog_exec: prog exec op template + * @read_cache: read cache op template + * @write_cache: write cache op template + * @update_cache: update cache op template + */ +struct spinand_mem_ops { + struct spi_mem_op reset; + struct spi_mem_op readid; + struct spi_mem_op wr_en; + struct spi_mem_op wr_dis; + struct spi_mem_op set_feature; + struct spi_mem_op get_feature; + struct spi_mem_op blk_erase; + struct spi_mem_op page_read; + struct spi_mem_op prog_exec; + const struct spi_mem_op *read_cache; + const struct spi_mem_op *write_cache; + const struct spi_mem_op *update_cache; +}; + /** * struct spinand_device - SPI NAND device instance * @base: NAND device instance @@ -607,10 +637,8 @@ struct spinand_dirmap { * @lock: lock used to serialize accesses to the NAND * @id: NAND ID as returned by READ_ID * @flags: NAND flags - * @op_templates: various SPI mem op templates - * @op_templates.read_cache: read cache op template - * @op_templates.write_cache: write cache op template - * @op_templates.update_cache: update cache op template + * @ssdr_op_templates: Templates for all single SDR SPI mem operations + * @op_templates: Templates for all SPI mem operations * @select_target: select a specific target/die. Usually called before sending * a command addressing a page or an eraseblock embedded in * this die. Only required if your chip exposes several dies @@ -644,11 +672,8 @@ struct spinand_device { struct spinand_id id; u32 flags; - struct { - const struct spi_mem_op *read_cache; - const struct spi_mem_op *write_cache; - const struct spi_mem_op *update_cache; - } op_templates; + struct spinand_mem_ops ssdr_op_templates; + struct spinand_mem_ops *op_templates; struct spinand_dirmap *dirmaps; -- cgit v1.2.3 From 88b0e3584acb905c41252b7917013ecf7c0518bc Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:08 +0100 Subject: mtd: spinand: Make use of the operation templates through SPINAND_OP() Create a SPINAND_OP() macro to which we give the name of the operation we want. This macro retrieves the correct operation template based on the current bus interface (currently only single SDR, will soon be extended to octal DTR) and fills it with the usual parameters. This macro makes the transition from calling directly the low-level macros into using the (bus interface dependent) templates very smooth. Use it in all places that can be trivially converted. At this stage there is no functional change expected, until octal DTR support gets added. Reviewed-by: Tudor Ambarus Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 110 +++++++++++++++++++++++++++++++++----- drivers/mtd/nand/spi/esmt.c | 4 +- drivers/mtd/nand/spi/gigadevice.c | 8 +-- drivers/mtd/nand/spi/macronix.c | 4 +- drivers/mtd/nand/spi/micron.c | 8 +-- drivers/mtd/nand/spi/toshiba.c | 3 +- drivers/mtd/nand/spi/winbond.c | 3 +- include/linux/mtd/spinand.h | 8 +++ 8 files changed, 121 insertions(+), 27 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 4ab63d7fc461..7146eec51afa 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -20,10 +20,94 @@ #include #include +static struct spi_mem_op +spinand_fill_reset_op(struct spinand_device *spinand) +{ + return spinand->op_templates->reset; +} + +static struct spi_mem_op +spinand_fill_readid_op(struct spinand_device *spinand, + u8 naddr, u8 ndummy, void *buf, unsigned int len) +{ + struct spi_mem_op op = spinand->op_templates->readid; + + op.addr.nbytes = naddr; + op.dummy.nbytes = ndummy; + op.data.buf.in = buf; + op.data.nbytes = len; + + return op; +} + +struct spi_mem_op +spinand_fill_wr_en_op(struct spinand_device *spinand) +{ + return spinand->op_templates->wr_en; +} + +static __maybe_unused struct spi_mem_op +spinand_fill_wr_dis_op(struct spinand_device *spinand) +{ + return spinand->op_templates->wr_dis; +} + +struct spi_mem_op +spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const void *valptr) +{ + struct spi_mem_op op = spinand->op_templates->set_feature; + + op.addr.val = reg; + op.data.buf.out = valptr; + + return op; +} + +struct spi_mem_op +spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void *valptr) +{ + struct spi_mem_op op = spinand->op_templates->get_feature; + + op.addr.val = reg; + op.data.buf.in = valptr; + + return op; +} + +static struct spi_mem_op +spinand_fill_blk_erase_op(struct spinand_device *spinand, u64 addr) +{ + struct spi_mem_op op = spinand->op_templates->blk_erase; + + op.addr.val = addr; + + return op; +} + +static struct spi_mem_op +spinand_fill_page_read_op(struct spinand_device *spinand, u64 addr) +{ + struct spi_mem_op op = spinand->op_templates->page_read; + + op.addr.val = addr; + + return op; +} + +struct spi_mem_op +spinand_fill_prog_exec_op(struct spinand_device *spinand, u64 addr) +{ + struct spi_mem_op op = spinand->op_templates->prog_exec; + + op.addr.val = addr; + + return op; +} + int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) { - struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(reg, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + reg, spinand->scratchbuf); int ret; ret = spi_mem_exec_op(spinand->spimem, &op); @@ -36,8 +120,8 @@ int spinand_read_reg_op(struct spinand_device *spinand, u8 reg, u8 *val) int spinand_write_reg_op(struct spinand_device *spinand, u8 reg, u8 val) { - struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(reg, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, set_feature, + reg, spinand->scratchbuf); *spinand->scratchbuf = val; return spi_mem_exec_op(spinand->spimem, &op); @@ -362,7 +446,7 @@ static void spinand_ondie_ecc_save_status(struct nand_device *nand, u8 status) int spinand_write_enable_op(struct spinand_device *spinand) { - struct spi_mem_op op = SPINAND_WR_EN_1S_0_0_OP; + struct spi_mem_op op = SPINAND_OP(spinand, wr_en); return spi_mem_exec_op(spinand->spimem, &op); } @@ -372,7 +456,7 @@ static int spinand_load_page_op(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); unsigned int row = nanddev_pos_to_row(nand, &req->pos); - struct spi_mem_op op = SPINAND_PAGE_READ_1S_1S_0_OP(row); + struct spi_mem_op op = SPINAND_OP(spinand, page_read, row); return spi_mem_exec_op(spinand->spimem, &op); } @@ -527,7 +611,7 @@ static int spinand_program_op(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); unsigned int row = nanddev_pos_to_row(nand, &req->pos); - struct spi_mem_op op = SPINAND_PROG_EXEC_1S_1S_0_OP(row); + struct spi_mem_op op = SPINAND_OP(spinand, prog_exec, row); return spi_mem_exec_op(spinand->spimem, &op); } @@ -537,7 +621,7 @@ static int spinand_erase_op(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); unsigned int row = nanddev_pos_to_row(nand, pos); - struct spi_mem_op op = SPINAND_BLK_ERASE_1S_1S_0_OP(row); + struct spi_mem_op op = SPINAND_OP(spinand, blk_erase, row); return spi_mem_exec_op(spinand->spimem, &op); } @@ -557,8 +641,8 @@ static int spinand_erase_op(struct spinand_device *spinand, int spinand_wait(struct spinand_device *spinand, unsigned long initial_delay_us, unsigned long poll_delay_us, u8 *s) { - struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(REG_STATUS, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + REG_STATUS, spinand->scratchbuf); u8 status; int ret; @@ -591,8 +675,8 @@ out: static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, u8 ndummy, u8 *buf) { - struct spi_mem_op op = SPINAND_READID_1S_1S_1S_OP( - naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN); + struct spi_mem_op op = SPINAND_OP(spinand, readid, + naddr, ndummy, spinand->scratchbuf, SPINAND_MAX_ID_LEN); int ret; ret = spi_mem_exec_op(spinand->spimem, &op); @@ -604,7 +688,7 @@ static int spinand_read_id_op(struct spinand_device *spinand, u8 naddr, static int spinand_reset_op(struct spinand_device *spinand) { - struct spi_mem_op op = SPINAND_RESET_1S_0_0_OP; + struct spi_mem_op op = SPINAND_OP(spinand, reset); int ret; ret = spi_mem_exec_op(spinand->spimem, &op); diff --git a/drivers/mtd/nand/spi/esmt.c b/drivers/mtd/nand/spi/esmt.c index adadc01e8f2f..3020aa89a495 100644 --- a/drivers/mtd/nand/spi/esmt.c +++ b/drivers/mtd/nand/spi/esmt.c @@ -138,8 +138,8 @@ static int f50l1g41lb_user_otp_info(struct spinand_device *spinand, size_t len, static int f50l1g41lb_otp_lock(struct spinand_device *spinand, loff_t from, size_t len) { - struct spi_mem_op write_op = SPINAND_WR_EN_1S_0_0_OP; - struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0); + struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en); + struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0); u8 status; int ret; diff --git a/drivers/mtd/nand/spi/gigadevice.c b/drivers/mtd/nand/spi/gigadevice.c index 72ad36c9a126..e4380208edd0 100644 --- a/drivers/mtd/nand/spi/gigadevice.c +++ b/drivers/mtd/nand/spi/gigadevice.c @@ -266,8 +266,8 @@ static int gd5fxgq4uexxg_ecc_get_status(struct spinand_device *spinand, u8 status) { u8 status2; - struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(GD5FXGQXXEXXG_REG_STATUS2, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + GD5FXGQXXEXXG_REG_STATUS2, spinand->scratchbuf); int ret; switch (status & STATUS_ECC_MASK) { @@ -309,8 +309,8 @@ static int gd5fxgq5xexxg_ecc_get_status(struct spinand_device *spinand, u8 status) { u8 status2; - struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(GD5FXGQXXEXXG_REG_STATUS2, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + GD5FXGQXXEXXG_REG_STATUS2, spinand->scratchbuf); int ret; switch (status & STATUS_ECC_MASK) { diff --git a/drivers/mtd/nand/spi/macronix.c b/drivers/mtd/nand/spi/macronix.c index edf63b9996cf..143cc120bdec 100644 --- a/drivers/mtd/nand/spi/macronix.c +++ b/drivers/mtd/nand/spi/macronix.c @@ -148,8 +148,8 @@ static int macronix_set_cont_read(struct spinand_device *spinand, bool enable) static int macronix_set_read_retry(struct spinand_device *spinand, unsigned int retry_mode) { - struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(MACRONIX_FEATURE_ADDR_READ_RETRY, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, set_feature, + MACRONIX_FEATURE_ADDR_READ_RETRY, spinand->scratchbuf); *spinand->scratchbuf = retry_mode; return spi_mem_exec_op(spinand->spimem, &op); diff --git a/drivers/mtd/nand/spi/micron.c b/drivers/mtd/nand/spi/micron.c index b8130e04e8e7..36f6cbbd7462 100644 --- a/drivers/mtd/nand/spi/micron.c +++ b/drivers/mtd/nand/spi/micron.c @@ -137,8 +137,8 @@ static const struct mtd_ooblayout_ops micron_4_ooblayout = { static int micron_select_target(struct spinand_device *spinand, unsigned int target) { - struct spi_mem_op op = SPINAND_SET_FEATURE_1S_1S_1S_OP(MICRON_DIE_SELECT_REG, - spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, set_feature, + MICRON_DIE_SELECT_REG, spinand->scratchbuf); if (target > 1) return -EINVAL; @@ -251,8 +251,8 @@ static int mt29f2g01abagd_user_otp_info(struct spinand_device *spinand, static int mt29f2g01abagd_otp_lock(struct spinand_device *spinand, loff_t from, size_t len) { - struct spi_mem_op write_op = SPINAND_WR_EN_1S_0_0_OP; - struct spi_mem_op exec_op = SPINAND_PROG_EXEC_1S_1S_0_OP(0); + struct spi_mem_op write_op = SPINAND_OP(spinand, wr_en); + struct spi_mem_op exec_op = SPINAND_OP(spinand, prog_exec, 0); u8 status; int ret; diff --git a/drivers/mtd/nand/spi/toshiba.c b/drivers/mtd/nand/spi/toshiba.c index 6530257ac0be..ef649162ee68 100644 --- a/drivers/mtd/nand/spi/toshiba.c +++ b/drivers/mtd/nand/spi/toshiba.c @@ -73,7 +73,8 @@ static int tx58cxgxsxraix_ecc_get_status(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); u8 mbf = 0; - struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(0x30, spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + 0x30, spinand->scratchbuf); switch (status & STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index d5799c2df065..bfec5d037f25 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -251,7 +251,8 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand, { struct nand_device *nand = spinand_to_nand(spinand); u8 mbf = 0; - struct spi_mem_op op = SPINAND_GET_FEATURE_1S_1S_1S_OP(0x30, spinand->scratchbuf); + struct spi_mem_op op = SPINAND_OP(spinand, get_feature, + 0x30, spinand->scratchbuf); switch (status & STATUS_ECC_MASK) { case STATUS_ECC_NO_BITFLIPS: diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index a458617fb375..553c56a389d2 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -703,6 +703,14 @@ struct spinand_device { unsigned int retry_mode); }; +struct spi_mem_op spinand_fill_wr_en_op(struct spinand_device *spinand); +struct spi_mem_op spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const void *valptr); +struct spi_mem_op spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void *valptr); +struct spi_mem_op spinand_fill_prog_exec_op(struct spinand_device *spinand, u64 addr); + +#define SPINAND_OP(spinand, op_name, ...) \ + spinand_fill_ ## op_name ## _op(spinand, ##__VA_ARGS__) + /** * mtd_to_spinand() - Get the SPI NAND device attached to an MTD instance * @mtd: MTD instance -- cgit v1.2.3 From fbc7538782f8e7df4737dcec7d854cf4d53bfc67 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:12 +0100 Subject: mtd: spinand: List vendor specific operations and make sure they are supported It is probably safe to expect that all SPI controller drivers will ever support all the most basic SPI NAND operations, such as write enable, register reads, page program, block erases, etc. However, what about vendor specific operations? So far nobody complained about it, but as we are about to introduce octal DTR support, and as none of the SPI NAND instruction set is defined in any standard, we must remain careful about these extra operations. One way to make sure we do not blindly get ourselves in strange situations with vendor commands failing silently is to make the check once for all, while probing the chip. However at this stage we have no such list, so let's add the necessary infrastructure to allow: - registering vendor operations, - checking they are actually supported when appropriate. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 26 ++++++++++++++++++++++++++ include/linux/mtd/spinand.h | 5 +++++ 2 files changed, 31 insertions(+) (limited to 'include/linux') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 7146eec51afa..cdf45d054082 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1424,6 +1424,27 @@ static void spinand_init_ssdr_templates(struct spinand_device *spinand) spinand->op_templates = &spinand->ssdr_op_templates; } +static int spinand_support_vendor_ops(struct spinand_device *spinand, + const struct spinand_info *info) +{ + int i; + + /* + * The vendor ops array is only used in order to verify this chip and all its memory + * operations are supported. If we see patterns emerging, we could ideally name these + * operations and define them at the SPI NAND core level instead. + * For now, this only serves as a sanity check. + */ + for (i = 0; i < info->vendor_ops->nops; i++) { + const struct spi_mem_op *op = &info->vendor_ops->ops[i]; + + if (!spi_mem_supports_op(spinand->spimem, op)) + return -EOPNOTSUPP; + } + + return 0; +} + static const struct spi_mem_op * spinand_select_op_variant(struct spinand_device *spinand, const struct spinand_op_variants *variants) @@ -1490,6 +1511,7 @@ int spinand_match_and_init(struct spinand_device *spinand, u8 *id = spinand->id.data; struct nand_device *nand = spinand_to_nand(spinand); unsigned int i; + int ret; for (i = 0; i < table_size; i++) { const struct spinand_info *info = &table[i]; @@ -1535,6 +1557,10 @@ int spinand_match_and_init(struct spinand_device *spinand, spinand->ssdr_op_templates.update_cache = op; + ret = spinand_support_vendor_ops(spinand, info); + if (ret) + return ret; + return 0; } diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 553c56a389d2..b020c119a15d 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -494,6 +494,7 @@ struct spinand_user_otp { * @op_variants.read_cache: variants of the read-cache operation * @op_variants.write_cache: variants of the write-cache operation * @op_variants.update_cache: variants of the update-cache operation + * @vendor_ops: vendor specific operations * @select_target: function used to select a target/die. Required only for * multi-die chips * @configure_chip: Align the chip configuration with the core settings @@ -518,6 +519,7 @@ struct spinand_info { const struct spinand_op_variants *write_cache; const struct spinand_op_variants *update_cache; } op_variants; + const struct spinand_op_variants *vendor_ops; int (*select_target)(struct spinand_device *spinand, unsigned int target); int (*configure_chip)(struct spinand_device *spinand); @@ -544,6 +546,9 @@ struct spinand_info { .update_cache = __update, \ } +#define SPINAND_INFO_VENDOR_OPS(__ops) \ + .vendor_ops = __ops + #define SPINAND_ECCINFO(__ooblayout, __get_status) \ .eccinfo = { \ .ooblayout = __ooblayout, \ -- cgit v1.2.3 From 20387f2fe509eba46ecf758da052786d7b1203fb Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:20 +0100 Subject: mtd: spinand: Add support for setting a bus interface Create a bus interface enumeration, currently only containing the one we support: SSDR, for single SDR, so any operation whose command is sent over a single data line in SDR mode, ie. any operation matching 1S-XX-XX. The main spinand_device structure gets a new parameter to store this enumeration, for now unused. Of course it is set to SSDR during the SSDR templates initialization to further clarify the state we are in at the moment. This member is subject to be used to know in which bus configuration we and be updated by the core when we switch to faster mode(s). Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 1 + include/linux/mtd/spinand.h | 10 ++++++++++ 2 files changed, 11 insertions(+) (limited to 'include/linux') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index e52d76afb5b5..b2993262b3f1 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1407,6 +1407,7 @@ static void spinand_init_ssdr_templates(struct spinand_device *spinand) tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_1S_1S_0_OP(0); tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_1S_1S_0_OP(0); spinand->op_templates = &spinand->ssdr_op_templates; + spinand->bus_iface = SSDR; } static int spinand_support_vendor_ops(struct spinand_device *spinand, diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index b020c119a15d..154037749a6c 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -482,6 +482,14 @@ struct spinand_user_otp { const struct spinand_user_otp_ops *ops; }; +/** + * enum spinand_bus_interface - SPI NAND bus interface types + * @SSDR: Bus configuration supporting all 1S-XX-XX operations, including dual and quad + */ +enum spinand_bus_interface { + SSDR, +}; + /** * struct spinand_info - Structure used to describe SPI NAND chips * @model: model name @@ -644,6 +652,7 @@ struct spinand_mem_ops { * @flags: NAND flags * @ssdr_op_templates: Templates for all single SDR SPI mem operations * @op_templates: Templates for all SPI mem operations + * @bus_iface: Current bus interface * @select_target: select a specific target/die. Usually called before sending * a command addressing a page or an eraseblock embedded in * this die. Only required if your chip exposes several dies @@ -679,6 +688,7 @@ struct spinand_device { struct spinand_mem_ops ssdr_op_templates; struct spinand_mem_ops *op_templates; + enum spinand_bus_interface bus_iface; struct spinand_dirmap *dirmaps; -- cgit v1.2.3 From 0a331a1851aedd670b95a2d16c6a82496137378d Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:22 +0100 Subject: mtd: spinand: Give the bus interface to the configuration helper The chip configuration hook is the one responsible to actually switch the switch between bus interfaces. It is natural to give it the bus interface we expect with a new parameter. For now the only value we can give is SSDR, but this is subject to change in the future, so add a bit of extra logic in the implementations of this callback to make sure both the core and the chip driver are aligned on the request. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 2 +- drivers/mtd/nand/spi/winbond.c | 28 +++++++++++++++++++++------- include/linux/mtd/spinand.h | 6 ++++-- 3 files changed, 26 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index 1aff2d368339..bbda60b39788 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -1604,7 +1604,7 @@ static int spinand_configure_chip(struct spinand_device *spinand) return ret; if (spinand->configure_chip) { - ret = spinand->configure_chip(spinand); + ret = spinand->configure_chip(spinand, SSDR); if (ret) return ret; } diff --git a/drivers/mtd/nand/spi/winbond.c b/drivers/mtd/nand/spi/winbond.c index 1d79a8ae7920..419f4303a0dc 100644 --- a/drivers/mtd/nand/spi/winbond.c +++ b/drivers/mtd/nand/spi/winbond.c @@ -311,13 +311,17 @@ static int w25n02kv_ecc_get_status(struct spinand_device *spinand, return -EINVAL; } -static int w25n0xjw_hs_cfg(struct spinand_device *spinand) +static int w25n0xjw_hs_cfg(struct spinand_device *spinand, + enum spinand_bus_interface iface) { const struct spi_mem_op *op; bool hs; u8 sr4; int ret; + if (iface != SSDR) + return -EOPNOTSUPP; + op = spinand->op_templates->read_cache; if (op->cmd.dtr || op->addr.dtr || op->dummy.dtr || op->data.dtr) hs = false; @@ -371,17 +375,25 @@ static int w35n0xjw_write_vcr(struct spinand_device *spinand, u8 reg, u8 val) return 0; } -static int w35n0xjw_vcr_cfg(struct spinand_device *spinand) +static int w35n0xjw_vcr_cfg(struct spinand_device *spinand, + enum spinand_bus_interface iface) { - const struct spi_mem_op *op; + const struct spi_mem_op *ref_op; unsigned int dummy_cycles; bool dtr, single; u8 io_mode; int ret; - op = spinand->op_templates->read_cache; + switch (iface) { + case SSDR: + ref_op = spinand->ssdr_op_templates.read_cache; + break; + default: + return -EOPNOTSUPP; + }; - dummy_cycles = ((op->dummy.nbytes * 8) / op->dummy.buswidth) / (op->dummy.dtr ? 2 : 1); + dummy_cycles = ((ref_op->dummy.nbytes * 8) / ref_op->dummy.buswidth) / + (ref_op->dummy.dtr ? 2 : 1); switch (dummy_cycles) { case 8: case 12: @@ -398,8 +410,10 @@ static int w35n0xjw_vcr_cfg(struct spinand_device *spinand) if (ret) return ret; - single = (op->cmd.buswidth == 1 && op->addr.buswidth == 1 && op->data.buswidth == 1); - dtr = (op->cmd.dtr && op->addr.dtr && op->data.dtr); + single = (ref_op->cmd.buswidth == 1 && + ref_op->addr.buswidth == 1 && + ref_op->data.buswidth == 1); + dtr = (ref_op->cmd.dtr && ref_op->addr.dtr && ref_op->data.dtr); if (single && !dtr) io_mode = W35N01JW_VCR_IO_MODE_SINGLE_SDR; else if (!single && !dtr) diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 154037749a6c..20643d1c395e 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -530,7 +530,8 @@ struct spinand_info { const struct spinand_op_variants *vendor_ops; int (*select_target)(struct spinand_device *spinand, unsigned int target); - int (*configure_chip)(struct spinand_device *spinand); + int (*configure_chip)(struct spinand_device *spinand, + enum spinand_bus_interface iface); int (*set_cont_read)(struct spinand_device *spinand, bool enable); struct spinand_fact_otp fact_otp; @@ -705,7 +706,8 @@ struct spinand_device { const struct spinand_manufacturer *manufacturer; void *priv; - int (*configure_chip)(struct spinand_device *spinand); + int (*configure_chip)(struct spinand_device *spinand, + enum spinand_bus_interface iface); bool cont_read_possible; int (*set_cont_read)(struct spinand_device *spinand, bool enable); -- cgit v1.2.3 From 76b7dc76dd0e1af5a538e977e015ac95271b3b12 Mon Sep 17 00:00:00 2001 From: Miquel Raynal Date: Fri, 9 Jan 2026 18:18:24 +0100 Subject: mtd: spinand: Add octal DTR support Create a new bus interface named ODTR for "octal DTR", which matches the following pattern: 8D-8D-8D. Add octal DTR support for all the existing core operations. Add a second set of templates for this bus interface. Give the possibility for drivers to register their read, write and update cache variants as well as their vendor specific operations. Check the SPI controller driver supports all the octal DTR commands that we might need before switching to the ODTR bus interface. Make the switch by calling ->configure_chip() with the ODTR parameter. Fallback in case this step fails. If someone ever attempts to suspend a chip in octal DTR mode, there are changes that it will loose its configuration at resume. Prevent any problem by explicitly switching back to SSDR while suspending. Note: there is a limitation in the current approach, page I/Os are not available as the dirmaps will be created for the ODTR bus interface if that option is supported and not switched back to SSDR during suspend. Switching them is possible but would be costly and would not bring anything as right after resuming we will switch again to ODTR. In case this capability is used for debug, developpers should mind to destroy and recreate suitable direct mappings. Finally, as a side effect, we increase the buffer for reading IDs to 6. No device at this point returns 6 bytes, but we support 5 bytes IDs, which means in octal DTR mode we have no other choice than reading an even number of bytes, hence 6. Signed-off-by: Miquel Raynal --- drivers/mtd/nand/spi/core.c | 140 +++++++++++++++++++++++++++++++++++++++++++- include/linux/mtd/spinand.h | 79 ++++++++++++++++++++++++- 2 files changed, 216 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/mtd/nand/spi/core.c b/drivers/mtd/nand/spi/core.c index bbda60b39788..21a980e626eb 100644 --- a/drivers/mtd/nand/spi/core.c +++ b/drivers/mtd/nand/spi/core.c @@ -57,6 +57,9 @@ spinand_fill_set_feature_op(struct spinand_device *spinand, u64 reg, const void { struct spi_mem_op op = spinand->op_templates->set_feature; + if (op.cmd.dtr && op.cmd.buswidth == 8) + reg |= reg << 8; + op.addr.val = reg; op.data.buf.out = valptr; @@ -68,6 +71,9 @@ spinand_fill_get_feature_op(struct spinand_device *spinand, u64 reg, void *valpt { struct spi_mem_op op = spinand->op_templates->get_feature; + if (op.cmd.dtr && op.cmd.buswidth == 8) + reg |= reg << 8; + op.addr.val = reg; op.data.buf.in = valptr; @@ -1393,6 +1399,11 @@ static void spinand_manufacturer_cleanup(struct spinand_device *spinand) return spinand->manufacturer->ops->cleanup(spinand); } +static bool spinand_op_is_odtr(const struct spi_mem_op *op) +{ + return op->cmd.dtr && op->cmd.buswidth == 8; +} + static void spinand_init_ssdr_templates(struct spinand_device *spinand) { struct spinand_mem_ops *tmpl = &spinand->ssdr_op_templates; @@ -1425,6 +1436,10 @@ static int spinand_support_vendor_ops(struct spinand_device *spinand, for (i = 0; i < info->vendor_ops->nops; i++) { const struct spi_mem_op *op = &info->vendor_ops->ops[i]; + if ((iface == SSDR && spinand_op_is_odtr(op)) || + (iface == ODTR && !spinand_op_is_odtr(op))) + continue; + if (!spi_mem_supports_op(spinand->spimem, op)) return -EOPNOTSUPP; } @@ -1432,6 +1447,49 @@ static int spinand_support_vendor_ops(struct spinand_device *spinand, return 0; } +static int spinand_init_odtr_instruction_set(struct spinand_device *spinand) +{ + struct spinand_mem_ops *tmpl = &spinand->odtr_op_templates; + + tmpl->reset = (struct spi_mem_op)SPINAND_RESET_8D_0_0_OP; + if (!spi_mem_supports_op(spinand->spimem, &tmpl->reset)) + return -EOPNOTSUPP; + + tmpl->readid = (struct spi_mem_op)SPINAND_READID_8D_8D_8D_OP(0, 0, NULL, 0); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->readid)) + return -EOPNOTSUPP; + + tmpl->wr_en = (struct spi_mem_op)SPINAND_WR_EN_8D_0_0_OP; + if (!spi_mem_supports_op(spinand->spimem, &tmpl->wr_en)) + return -EOPNOTSUPP; + + tmpl->wr_dis = (struct spi_mem_op)SPINAND_WR_DIS_8D_0_0_OP; + if (!spi_mem_supports_op(spinand->spimem, &tmpl->wr_dis)) + return -EOPNOTSUPP; + + tmpl->set_feature = (struct spi_mem_op)SPINAND_SET_FEATURE_8D_8D_8D_OP(0, NULL); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->set_feature)) + return -EOPNOTSUPP; + + tmpl->get_feature = (struct spi_mem_op)SPINAND_GET_FEATURE_8D_8D_8D_OP(0, NULL); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->get_feature)) + return -EOPNOTSUPP; + + tmpl->blk_erase = (struct spi_mem_op)SPINAND_BLK_ERASE_8D_8D_0_OP(0); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->blk_erase)) + return -EOPNOTSUPP; + + tmpl->page_read = (struct spi_mem_op)SPINAND_PAGE_READ_8D_8D_0_OP(0); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->page_read)) + return -EOPNOTSUPP; + + tmpl->prog_exec = (struct spi_mem_op)SPINAND_PROG_EXEC_8D_8D_0_OP(0); + if (!spi_mem_supports_op(spinand->spimem, &tmpl->prog_exec)) + return -EOPNOTSUPP; + + return 0; +} + static const struct spi_mem_op * spinand_select_op_variant(struct spinand_device *spinand, enum spinand_bus_interface iface, const struct spinand_op_variants *variants) @@ -1447,6 +1505,10 @@ spinand_select_op_variant(struct spinand_device *spinand, enum spinand_bus_inter unsigned int nbytes; int ret; + if ((iface == SSDR && spinand_op_is_odtr(&op)) || + (iface == ODTR && !spinand_op_is_odtr(&op))) + continue; + nbytes = nanddev_per_page_oobsize(nand) + nanddev_page_size(nand); @@ -1523,6 +1585,8 @@ int spinand_match_and_init(struct spinand_device *spinand, spinand->read_retries = table[i].read_retries; spinand->set_read_retry = table[i].set_read_retry; + /* I/O variants selection with single-spi SDR commands */ + op = spinand_select_op_variant(spinand, SSDR, info->op_variants.read_cache); if (!op) @@ -1548,6 +1612,28 @@ int spinand_match_and_init(struct spinand_device *spinand, if (ret) return ret; + /* I/O variants selection with octo-spi DDR commands (optional) */ + + ret = spinand_init_odtr_instruction_set(spinand); + if (ret) + return 0; + + ret = spinand_support_vendor_ops(spinand, info, ODTR); + if (ret) + return 0; + + op = spinand_select_op_variant(spinand, ODTR, + info->op_variants.read_cache); + spinand->odtr_op_templates.read_cache = op; + + op = spinand_select_op_variant(spinand, ODTR, + info->op_variants.write_cache); + spinand->odtr_op_templates.write_cache = op; + + op = spinand_select_op_variant(spinand, ODTR, + info->op_variants.update_cache); + spinand->odtr_op_templates.update_cache = op; + return 0; } @@ -1589,9 +1675,34 @@ static int spinand_detect(struct spinand_device *spinand) static int spinand_configure_chip(struct spinand_device *spinand) { - bool quad_enable = false; + bool odtr = false, quad_enable = false; int ret; + if (spinand->odtr_op_templates.read_cache && + spinand->odtr_op_templates.write_cache && + spinand->odtr_op_templates.update_cache) + odtr = true; + + if (odtr) { + if (!spinand->configure_chip) + goto try_ssdr; + + /* ODTR bus interface configuration happens here */ + ret = spinand->configure_chip(spinand, ODTR); + if (ret) { + spinand->odtr_op_templates.read_cache = NULL; + spinand->odtr_op_templates.write_cache = NULL; + spinand->odtr_op_templates.update_cache = NULL; + goto try_ssdr; + } + + spinand->op_templates = &spinand->odtr_op_templates; + spinand->bus_iface = ODTR; + + return 0; + } + +try_ssdr: if (spinand->flags & SPINAND_HAS_QE_BIT) { if (spinand->ssdr_op_templates.read_cache->data.buswidth == 4 || spinand->ssdr_op_templates.write_cache->data.buswidth == 4 || @@ -1673,6 +1784,32 @@ static void spinand_mtd_resume(struct mtd_info *mtd) spinand_ecc_enable(spinand, false); } +static int spinand_mtd_suspend(struct mtd_info *mtd) +{ + struct spinand_device *spinand = mtd_to_spinand(mtd); + int ret; + + /* + * Return to SSDR interface in the suspend path to make sure the + * reset operation is correctly processed upon resume. + * + * Note: Once back in SSDR mode, every operation but the page helpers + * (dirmap based I/O accessors) will work. Page accesses would require + * destroying and recreating the dirmaps twice to work, which would be + * impacting for no reason, as this is just a transitional state. + */ + if (spinand->bus_iface == ODTR) { + ret = spinand->configure_chip(spinand, SSDR); + if (ret) + return ret; + + spinand->op_templates = &spinand->ssdr_op_templates; + spinand->bus_iface = SSDR; + } + + return 0; +} + static int spinand_init(struct spinand_device *spinand) { struct device *dev = &spinand->spimem->spi->dev; @@ -1742,6 +1879,7 @@ static int spinand_init(struct spinand_device *spinand) mtd->_block_isreserved = spinand_mtd_block_isreserved; mtd->_erase = spinand_mtd_erase; mtd->_max_bad_blocks = nanddev_mtd_max_bad_blocks; + mtd->_suspend = spinand_mtd_suspend; mtd->_resume = spinand_mtd_resume; if (spinand_user_otp_size(spinand) || spinand_fact_otp_size(spinand)) { diff --git a/include/linux/mtd/spinand.h b/include/linux/mtd/spinand.h index 20643d1c395e..6a024cf1c53a 100644 --- a/include/linux/mtd/spinand.h +++ b/include/linux/mtd/spinand.h @@ -238,6 +238,77 @@ SPI_MEM_OP_NO_DUMMY, \ SPI_MEM_OP_DATA_OUT(len, buf, 8)) +/** + * Octal DDR SPI NAND flash operations + */ + +#define SPINAND_RESET_8D_0_0_OP \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0xff, 8), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_READID_8D_8D_8D_OP(naddr, ndummy, buf, len) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9f, 8), \ + SPI_MEM_DTR_OP_ADDR(naddr, 0, 8), \ + SPI_MEM_DTR_OP_DUMMY(ndummy, 8), \ + SPI_MEM_DTR_OP_DATA_IN(len, buf, 8)) + +#define SPINAND_WR_EN_8D_0_0_OP \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x06, 8), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_WR_DIS_8D_0_0_OP \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x04, 8), \ + SPI_MEM_OP_NO_ADDR, \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_SET_FEATURE_8D_8D_8D_OP(reg, valptr) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x1f, 8), \ + SPI_MEM_DTR_OP_RPT_ADDR(reg, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_DTR_OP_DATA_OUT(2, valptr, 8)) + +#define SPINAND_GET_FEATURE_8D_8D_8D_OP(reg, valptr) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x0f, 8), \ + SPI_MEM_DTR_OP_RPT_ADDR(reg, 8), \ + SPI_MEM_DTR_OP_DUMMY(14, 8), \ + SPI_MEM_DTR_OP_DATA_IN(2, valptr, 8)) + +#define SPINAND_BLK_ERASE_8D_8D_0_OP(addr) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0xd8, 8), \ + SPI_MEM_DTR_OP_ADDR(2, addr, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PAGE_READ_8D_8D_0_OP(addr) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x13, 8), \ + SPI_MEM_DTR_OP_ADDR(2, addr, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PAGE_READ_FROM_CACHE_8D_8D_8D_OP(addr, ndummy, buf, len, freq) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x9d, 8), \ + SPI_MEM_DTR_OP_ADDR(2, addr, 8), \ + SPI_MEM_DTR_OP_DUMMY(ndummy, 8), \ + SPI_MEM_DTR_OP_DATA_IN(len, buf, 8), \ + SPI_MEM_OP_MAX_FREQ(freq)) + +#define SPINAND_PROG_EXEC_8D_8D_0_OP(addr) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD(0x10, 8), \ + SPI_MEM_DTR_OP_ADDR(2, addr, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_OP_NO_DATA) + +#define SPINAND_PROG_LOAD_8D_8D_8D_OP(reset, addr, buf, len) \ + SPI_MEM_OP(SPI_MEM_DTR_OP_RPT_CMD((reset ? 0xc2 : 0xc4), 8), \ + SPI_MEM_DTR_OP_ADDR(2, addr, 8), \ + SPI_MEM_OP_NO_DUMMY, \ + SPI_MEM_DTR_OP_DATA_OUT(len, buf, 8)) + /* feature register */ #define REG_BLOCK_LOCK 0xa0 #define BL_ALL_UNLOCKED 0x00 @@ -261,7 +332,7 @@ struct spinand_op; struct spinand_device; -#define SPINAND_MAX_ID_LEN 5 +#define SPINAND_MAX_ID_LEN 6 /* * For erase, write and read operation, we got the following timings : * tBERS (erase) 1ms to 4ms @@ -287,7 +358,7 @@ struct spinand_device; /** * struct spinand_id - SPI NAND id structure - * @data: buffer containing the id bytes. Currently 5 bytes large, but can + * @data: buffer containing the id bytes. Currently 6 bytes large, but can * be extended if required * @len: ID length */ @@ -485,9 +556,11 @@ struct spinand_user_otp { /** * enum spinand_bus_interface - SPI NAND bus interface types * @SSDR: Bus configuration supporting all 1S-XX-XX operations, including dual and quad + * @ODTR: Bus configuration supporting only 8D-8D-8D operations */ enum spinand_bus_interface { SSDR, + ODTR, }; /** @@ -652,6 +725,7 @@ struct spinand_mem_ops { * @id: NAND ID as returned by READ_ID * @flags: NAND flags * @ssdr_op_templates: Templates for all single SDR SPI mem operations + * @odtr_op_templates: Templates for all octal DTR SPI mem operations * @op_templates: Templates for all SPI mem operations * @bus_iface: Current bus interface * @select_target: select a specific target/die. Usually called before sending @@ -688,6 +762,7 @@ struct spinand_device { u32 flags; struct spinand_mem_ops ssdr_op_templates; + struct spinand_mem_ops odtr_op_templates; struct spinand_mem_ops *op_templates; enum spinand_bus_interface bus_iface; -- cgit v1.2.3 From d095d2dbcc8df60ede1674e28ce0f0a478d37535 Mon Sep 17 00:00:00 2001 From: Brian Masney Date: Mon, 12 Jan 2026 17:47:58 -0500 Subject: MIPS: copy pic32.h header file from asm/mach-pic32/ to include/platform-data/ There are currently some pic32 MIPS drivers that are in tree, and are only configured to be compiled on the pic32 platform. There's a risk of breaking some of these drivers when migrating drivers away from legacy APIs. It happened to me with a pic32 clk driver. Let's go ahead and copy the MIPS pic32.h header to include/linux/platform_data/, and make a minor update to allow compiling this on other architectures. This will make it easier, and cleaner to enable COMPILE_TEST for some of these pic32 drivers. The asm variant of the header file will be dropped once all drivers have been updated. Link: https://lore.kernel.org/linux-clk/CABx5tq+eOocJ41X-GSgkGy6S+s+Am1yCS099wqP695NtwALTmg@mail.gmail.com/T/ Signed-off-by: Brian Masney Signed-off-by: Thomas Bogendoerfer --- include/linux/platform_data/pic32.h | 39 +++++++++++++++++++++++++++++++++++++ 1 file changed, 39 insertions(+) create mode 100644 include/linux/platform_data/pic32.h (limited to 'include/linux') diff --git a/include/linux/platform_data/pic32.h b/include/linux/platform_data/pic32.h new file mode 100644 index 000000000000..f0b395fdb784 --- /dev/null +++ b/include/linux/platform_data/pic32.h @@ -0,0 +1,39 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Joshua Henderson + * Copyright (C) 2015 Microchip Technology Inc. All rights reserved. + */ +#ifndef __LINUX_PLATFORM_DATA_PIC32_H +#define __LINUX_PLATFORM_DATA_PIC32_H + +#include + +/* + * PIC32 register offsets for SET/CLR/INV where supported. + */ +#define PIC32_CLR(_reg) ((_reg) + 0x04) +#define PIC32_SET(_reg) ((_reg) + 0x08) +#define PIC32_INV(_reg) ((_reg) + 0x0C) + +/* + * PIC32 Base Register Offsets + */ +#define PIC32_BASE_CONFIG 0x1f800000 +#define PIC32_BASE_OSC 0x1f801200 +#define PIC32_BASE_RESET 0x1f801240 +#define PIC32_BASE_PPS 0x1f801400 +#define PIC32_BASE_UART 0x1f822000 +#define PIC32_BASE_PORT 0x1f860000 +#define PIC32_BASE_DEVCFG2 0x1fc4ff44 + +#if defined(CONFIG_MACH_PIC32) +/* Register unlock sequence required for some register access. */ +void pic32_syskey_unlock_debug(const char *fn, const ulong ln); +#define pic32_syskey_unlock() \ + pic32_syskey_unlock_debug(__func__, __LINE__) +#else +/* COMPILE_TEST on all other architectures */ +#define pic32_syskey_unlock() +#endif + +#endif /* __LINUX_PLATFORM_DATA_PIC32_H */ -- cgit v1.2.3 From a46023d5616ed3ed781e56ca93400eb9490e3646 Mon Sep 17 00:00:00 2001 From: Steven Rostedt Date: Mon, 26 Jan 2026 18:11:48 -0500 Subject: tracing: Guard __DECLARE_TRACE() use of __DO_TRACE_CALL() with SRCU-fast The current use of guard(preempt_notrace)() within __DECLARE_TRACE() to protect invocation of __DO_TRACE_CALL() means that BPF programs attached to tracepoints are non-preemptible. This is unhelpful in real-time systems, whose users apparently wish to use BPF while also achieving low latencies. (Who knew?) One option would be to use preemptible RCU, but this introduces many opportunities for infinite recursion, which many consider to be counterproductive, especially given the relatively small stacks provided by the Linux kernel. These opportunities could be shut down by sufficiently energetic duplication of code, but this sort of thing is considered impolite in some circles. Therefore, use the shiny new SRCU-fast API, which provides somewhat faster readers than those of preemptible RCU, at least on Paul E. McKenney's laptop, where task_struct access is more expensive than access to per-CPU variables. And SRCU-fast provides way faster readers than does SRCU, courtesy of being able to avoid the read-side use of smp_mb(). Also, it is quite straightforward to create srcu_read_{,un}lock_fast_notrace() functions. Link: https://lore.kernel.org/all/20250613152218.1924093-1-bigeasy@linutronix.de/ Cc: Masami Hiramatsu Cc: Mark Rutland Cc: Mathieu Desnoyers Cc: Andrew Morton Cc: Sebastian Andrzej Siewior Cc: Alexei Starovoitov Link: https://patch.msgid.link/20260126231256.499701982@kernel.org Co-developed-by: Paul E. McKenney Signed-off-by: Paul E. McKenney Signed-off-by: Steven Rostedt (Google) --- include/linux/tracepoint.h | 9 +++++---- include/trace/trace_events.h | 4 ++-- kernel/tracepoint.c | 18 ++++++++++++++---- 3 files changed, 21 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/tracepoint.h b/include/linux/tracepoint.h index 8a56f3278b1b..22ca1c8b54f3 100644 --- a/include/linux/tracepoint.h +++ b/include/linux/tracepoint.h @@ -108,14 +108,15 @@ void for_each_tracepoint_in_module(struct module *mod, * An alternative is to use the following for batch reclaim associated * with a given tracepoint: * - * - tracepoint_is_faultable() == false: call_rcu() + * - tracepoint_is_faultable() == false: call_srcu() * - tracepoint_is_faultable() == true: call_rcu_tasks_trace() */ #ifdef CONFIG_TRACEPOINTS +extern struct srcu_struct tracepoint_srcu; static inline void tracepoint_synchronize_unregister(void) { synchronize_rcu_tasks_trace(); - synchronize_rcu(); + synchronize_srcu(&tracepoint_srcu); } static inline bool tracepoint_is_faultable(struct tracepoint *tp) { @@ -275,13 +276,13 @@ static inline struct tracepoint *tracepoint_ptr_deref(tracepoint_ptr_t *p) return static_branch_unlikely(&__tracepoint_##name.key);\ } -#define __DECLARE_TRACE(name, proto, args, cond, data_proto) \ +#define __DECLARE_TRACE(name, proto, args, cond, data_proto) \ __DECLARE_TRACE_COMMON(name, PARAMS(proto), PARAMS(args), PARAMS(data_proto)) \ static inline void __do_trace_##name(proto) \ { \ TRACEPOINT_CHECK(name) \ if (cond) { \ - guard(preempt_notrace)(); \ + guard(srcu_fast_notrace)(&tracepoint_srcu); \ __DO_TRACE_CALL(name, TP_ARGS(args)); \ } \ } \ diff --git a/include/trace/trace_events.h b/include/trace/trace_events.h index 4f22136fd465..fbc07d353be6 100644 --- a/include/trace/trace_events.h +++ b/include/trace/trace_events.h @@ -436,6 +436,7 @@ __DECLARE_EVENT_CLASS(call, PARAMS(proto), PARAMS(args), PARAMS(tstruct), \ static notrace void \ trace_event_raw_event_##call(void *__data, proto) \ { \ + guard(preempt_notrace)(); \ do_trace_event_raw_event_##call(__data, args); \ } @@ -447,9 +448,8 @@ static notrace void \ trace_event_raw_event_##call(void *__data, proto) \ { \ might_fault(); \ - preempt_disable_notrace(); \ + guard(preempt_notrace)(); \ do_trace_event_raw_event_##call(__data, args); \ - preempt_enable_notrace(); \ } /* diff --git a/kernel/tracepoint.c b/kernel/tracepoint.c index 62719d2941c9..fd2ee879815c 100644 --- a/kernel/tracepoint.c +++ b/kernel/tracepoint.c @@ -34,9 +34,13 @@ enum tp_transition_sync { struct tp_transition_snapshot { unsigned long rcu; + unsigned long srcu_gp; bool ongoing; }; +DEFINE_SRCU_FAST(tracepoint_srcu); +EXPORT_SYMBOL_GPL(tracepoint_srcu); + /* Protected by tracepoints_mutex */ static struct tp_transition_snapshot tp_transition_snapshot[_NR_TP_TRANSITION_SYNC]; @@ -46,6 +50,7 @@ static void tp_rcu_get_state(enum tp_transition_sync sync) /* Keep the latest get_state snapshot. */ snapshot->rcu = get_state_synchronize_rcu(); + snapshot->srcu_gp = start_poll_synchronize_srcu(&tracepoint_srcu); snapshot->ongoing = true; } @@ -56,6 +61,8 @@ static void tp_rcu_cond_sync(enum tp_transition_sync sync) if (!snapshot->ongoing) return; cond_synchronize_rcu(snapshot->rcu); + if (!poll_state_synchronize_srcu(&tracepoint_srcu, snapshot->srcu_gp)) + synchronize_srcu(&tracepoint_srcu); snapshot->ongoing = false; } @@ -112,10 +119,13 @@ static inline void release_probes(struct tracepoint *tp, struct tracepoint_func struct tp_probes *tp_probes = container_of(old, struct tp_probes, probes[0]); - if (tracepoint_is_faultable(tp)) - call_rcu_tasks_trace(&tp_probes->rcu, rcu_free_old_probes); - else - call_rcu(&tp_probes->rcu, rcu_free_old_probes); + if (tracepoint_is_faultable(tp)) { + call_rcu_tasks_trace(&tp_probes->rcu, + rcu_free_old_probes); + } else { + call_srcu(&tracepoint_srcu, &tp_probes->rcu, + rcu_free_old_probes); + } } } -- cgit v1.2.3 From 17926aa1b62c6ddb1e2ddbda8b2ac46913c5311c Mon Sep 17 00:00:00 2001 From: Andy Shevchenko Date: Tue, 27 Jan 2026 11:32:06 +0100 Subject: pinctrl: core: Remove unused devm_pinctrl_unregister() There are no users, drop it for good. Signed-off-by: Andy Shevchenko Reviewed-by: Bartosz Golaszewski Signed-off-by: Linus Walleij --- Documentation/driver-api/driver-model/devres.rst | 1 - drivers/pinctrl/core.c | 22 ---------------------- include/linux/pinctrl/pinctrl.h | 3 --- 3 files changed, 26 deletions(-) (limited to 'include/linux') diff --git a/Documentation/driver-api/driver-model/devres.rst b/Documentation/driver-api/driver-model/devres.rst index 0198ac65e874..7d2b897d66fa 100644 --- a/Documentation/driver-api/driver-model/devres.rst +++ b/Documentation/driver-api/driver-model/devres.rst @@ -408,7 +408,6 @@ PINCTRL devm_pinctrl_get_select() devm_pinctrl_register() devm_pinctrl_register_and_init() - devm_pinctrl_unregister() POWER devm_reboot_mode_register() diff --git a/drivers/pinctrl/core.c b/drivers/pinctrl/core.c index 83254a95ef17..b2b86c4d8475 100644 --- a/drivers/pinctrl/core.c +++ b/drivers/pinctrl/core.c @@ -2323,16 +2323,6 @@ static void devm_pinctrl_dev_release(struct device *dev, void *res) pinctrl_unregister(pctldev); } -static int devm_pinctrl_dev_match(struct device *dev, void *res, void *data) -{ - struct pctldev **r = res; - - if (WARN_ON(!r || !*r)) - return 0; - - return *r == data; -} - /** * devm_pinctrl_register() - Resource managed version of pinctrl_register(). * @dev: parent device for this pin controller @@ -2403,18 +2393,6 @@ int devm_pinctrl_register_and_init(struct device *dev, } EXPORT_SYMBOL_GPL(devm_pinctrl_register_and_init); -/** - * devm_pinctrl_unregister() - Resource managed version of pinctrl_unregister(). - * @dev: device for which resource was allocated - * @pctldev: the pinctrl device to unregister. - */ -void devm_pinctrl_unregister(struct device *dev, struct pinctrl_dev *pctldev) -{ - WARN_ON(devres_release(dev, devm_pinctrl_dev_release, - devm_pinctrl_dev_match, pctldev)); -} -EXPORT_SYMBOL_GPL(devm_pinctrl_unregister); - static int __init pinctrl_init(void) { pr_debug("initialized pinctrl subsystem\n"); diff --git a/include/linux/pinctrl/pinctrl.h b/include/linux/pinctrl/pinctrl.h index 1a8084e29405..c329cc693139 100644 --- a/include/linux/pinctrl/pinctrl.h +++ b/include/linux/pinctrl/pinctrl.h @@ -187,9 +187,6 @@ extern struct pinctrl_dev *devm_pinctrl_register(struct device *dev, const struct pinctrl_desc *pctldesc, void *driver_data); -extern void devm_pinctrl_unregister(struct device *dev, - struct pinctrl_dev *pctldev); - extern void pinctrl_add_gpio_range(struct pinctrl_dev *pctldev, struct pinctrl_gpio_range *range); extern void pinctrl_add_gpio_ranges(struct pinctrl_dev *pctldev, -- cgit v1.2.3 From f4b830a5371914239756b0599e5dc9d4c328e387 Mon Sep 17 00:00:00 2001 From: Duje Mihanović Date: Wed, 17 Dec 2025 19:14:23 +0100 Subject: leds: expresswire: Fix chip state breakage MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit It is possible to put the KTD2801 chip in an unknown/undefined state by changing the brightness very rapidly (for example, with a brightness slider). When this happens, the brightness is stuck on max and cannot be changed until the chip is power cycled. Fix this by disabling interrupts while talking to the chip. While at it, make expresswire_power_off() use fsleep() and also unexport some functions meant to be internal. Fixes: 1368d06dd2c9 ("leds: Introduce ExpressWire library") Tested-by: Karel Balej Signed-off-by: Duje Mihanović Link: https://patch.msgid.link/20251217-expresswire-fix-v2-1-4a02b10acd96@dujemihanovic.xyz Signed-off-by: Lee Jones --- drivers/leds/leds-expresswire.c | 24 +++++++++++++++++------- include/linux/leds-expresswire.h | 3 --- 2 files changed, 17 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/drivers/leds/leds-expresswire.c b/drivers/leds/leds-expresswire.c index bb69be228a6d..25c6b159a6ee 100644 --- a/drivers/leds/leds-expresswire.c +++ b/drivers/leds/leds-expresswire.c @@ -9,6 +9,7 @@ #include #include #include +#include #include #include @@ -16,37 +17,41 @@ void expresswire_power_off(struct expresswire_common_props *props) { gpiod_set_value_cansleep(props->ctrl_gpio, 0); - usleep_range(props->timing.poweroff_us, props->timing.poweroff_us * 2); + fsleep(props->timing.poweroff_us); } EXPORT_SYMBOL_NS_GPL(expresswire_power_off, "EXPRESSWIRE"); void expresswire_enable(struct expresswire_common_props *props) { + unsigned long flags; + + local_irq_save(flags); + gpiod_set_value(props->ctrl_gpio, 1); udelay(props->timing.detect_delay_us); gpiod_set_value(props->ctrl_gpio, 0); udelay(props->timing.detect_us); gpiod_set_value(props->ctrl_gpio, 1); + + local_irq_restore(flags); } EXPORT_SYMBOL_NS_GPL(expresswire_enable, "EXPRESSWIRE"); -void expresswire_start(struct expresswire_common_props *props) +static void expresswire_start(struct expresswire_common_props *props) { gpiod_set_value(props->ctrl_gpio, 1); udelay(props->timing.data_start_us); } -EXPORT_SYMBOL_NS_GPL(expresswire_start, "EXPRESSWIRE"); -void expresswire_end(struct expresswire_common_props *props) +static void expresswire_end(struct expresswire_common_props *props) { gpiod_set_value(props->ctrl_gpio, 0); udelay(props->timing.end_of_data_low_us); gpiod_set_value(props->ctrl_gpio, 1); udelay(props->timing.end_of_data_high_us); } -EXPORT_SYMBOL_NS_GPL(expresswire_end, "EXPRESSWIRE"); -void expresswire_set_bit(struct expresswire_common_props *props, bool bit) +static void expresswire_set_bit(struct expresswire_common_props *props, bool bit) { if (bit) { gpiod_set_value(props->ctrl_gpio, 0); @@ -60,13 +65,18 @@ void expresswire_set_bit(struct expresswire_common_props *props, bool bit) udelay(props->timing.short_bitset_us); } } -EXPORT_SYMBOL_NS_GPL(expresswire_set_bit, "EXPRESSWIRE"); void expresswire_write_u8(struct expresswire_common_props *props, u8 val) { + unsigned long flags; + + local_irq_save(flags); + expresswire_start(props); for (int i = 7; i >= 0; i--) expresswire_set_bit(props, val & BIT(i)); expresswire_end(props); + + local_irq_restore(flags); } EXPORT_SYMBOL_NS_GPL(expresswire_write_u8, "EXPRESSWIRE"); diff --git a/include/linux/leds-expresswire.h b/include/linux/leds-expresswire.h index a422921f4159..7f8c4795f69f 100644 --- a/include/linux/leds-expresswire.h +++ b/include/linux/leds-expresswire.h @@ -30,9 +30,6 @@ struct expresswire_common_props { void expresswire_power_off(struct expresswire_common_props *props); void expresswire_enable(struct expresswire_common_props *props); -void expresswire_start(struct expresswire_common_props *props); -void expresswire_end(struct expresswire_common_props *props); -void expresswire_set_bit(struct expresswire_common_props *props, bool bit); void expresswire_write_u8(struct expresswire_common_props *props, u8 val); #endif /* _LEDS_EXPRESSWIRE_H */ -- cgit v1.2.3 From aacad391440ecc4f25e8d2db0d944a74fd874dd2 Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 2 Feb 2026 10:05:23 +0200 Subject: usb: phy: tegra: cosmetic fixes Change TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC to its literal value instead of using the BIT macro, as it is an enumeration. Correct the spelling in the comment and rename uhsic_registers_shift to uhsic_registers_offset. These changes are cosmetic and do not affect code behavior. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Link: https://patch.msgid.link/20260202080526.23487-2-clamor95@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 12 ++++++------ include/linux/usb/tegra_usb_phy.h | 4 ++-- 2 files changed, 8 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index effa767ec019..3a7a74f01d1c 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -48,7 +48,7 @@ #define TEGRA_USB_HOSTPC1_DEVLC 0x1b4 #define TEGRA_USB_HOSTPC1_DEVLC_PTS(x) (((x) & 0x7) << 29) #define TEGRA_USB_HOSTPC1_DEVLC_PHCD BIT(22) -#define TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC BIT(2) +#define TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC 4 /* Bits of PORTSC1, which will get cleared by writing 1 into them */ #define TEGRA_PORTSC1_RWC_BITS (PORT_CSC | PORT_PEC | PORT_OCC) @@ -169,7 +169,7 @@ /* * Tegra20 has no UTMIP registers on PHY2 and UHSIC registers start from 0x800 * just where UTMIP registers should have been. This is the case only with Tegra20 - * Tegra30+ have UTMIP registers at 0x800 and UHSIC registers shifter by 0x400 + * Tegra30+ have UTMIP registers at 0x800 and UHSIC registers are shifted by 0x400 * to 0xc00, but register layout is preserved. */ #define UHSIC_PLL_CFG1 0x804 @@ -873,7 +873,7 @@ static int ulpi_phy_power_off(struct tegra_usb_phy *phy) static u32 tegra_hsic_readl(struct tegra_usb_phy *phy, u32 reg) { void __iomem *base = phy->regs; - u32 shift = phy->soc_config->uhsic_registers_shift; + u32 shift = phy->soc_config->uhsic_registers_offset; return readl_relaxed(base + shift + reg); } @@ -881,7 +881,7 @@ static u32 tegra_hsic_readl(struct tegra_usb_phy *phy, u32 reg) static void tegra_hsic_writel(struct tegra_usb_phy *phy, u32 reg, u32 value) { void __iomem *base = phy->regs; - u32 shift = phy->soc_config->uhsic_registers_shift; + u32 shift = phy->soc_config->uhsic_registers_offset; writel_relaxed(value, base + shift + reg); } @@ -1469,7 +1469,7 @@ static const struct tegra_phy_soc_config tegra20_soc_config = { .requires_usbmode_setup = false, .requires_extra_tuning_parameters = false, .requires_pmc_ao_power_up = false, - .uhsic_registers_shift = 0, + .uhsic_registers_offset = 0, .uhsic_tx_rtune = 0, /* 40 ohm */ }; @@ -1479,7 +1479,7 @@ static const struct tegra_phy_soc_config tegra30_soc_config = { .requires_usbmode_setup = true, .requires_extra_tuning_parameters = true, .requires_pmc_ao_power_up = true, - .uhsic_registers_shift = 0x400, + .uhsic_registers_offset = 0x400, .uhsic_tx_rtune = 8, /* 50 ohm */ }; diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 6d57da13d395..91420df25627 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -23,7 +23,7 @@ struct gpio_desc; * requires_extra_tuning_parameters: true if xcvr_hsslew, hssquelch_level * and hsdiscon_level should be set for adequate signal quality * requires_pmc_ao_power_up: true if USB AO is powered down by default - * uhsic_registers_shift: for Tegra30+ where HSIC registers were shifted + * uhsic_registers_offset: for Tegra30+ where HSIC registers were offset * comparing to Tegra20 by 0x400, since Tegra20 has no UTMIP on PHY2 * uhsic_tx_rtune: fine tuned 50 Ohm termination resistor for NMOS/PMOS driver */ @@ -34,7 +34,7 @@ struct tegra_phy_soc_config { bool requires_usbmode_setup; bool requires_extra_tuning_parameters; bool requires_pmc_ao_power_up; - u32 uhsic_registers_shift; + u32 uhsic_registers_offset; u32 uhsic_tx_rtune; }; -- cgit v1.2.3 From 8dc7ab65bd15e3c774f60ca073158bcb9a26ee5b Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 2 Feb 2026 10:05:25 +0200 Subject: usb: phy: tegra: parametrize HSIC PTS value The parallel transceiver select used in HSIC mode differs on Tegra20, where it uses the UTMI value (0), whereas Tegra30+ uses a dedicated HSIC value. Reflect this in the SoC config. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Link: https://patch.msgid.link/20260202080526.23487-4-clamor95@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 7 +++---- include/linux/usb/tegra_usb_phy.h | 2 ++ 2 files changed, 5 insertions(+), 4 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 6173b240c3ea..812d99443180 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -957,10 +957,7 @@ static int uhsic_phy_power_on(struct tegra_usb_phy *phy) writel_relaxed(val, base + USB_USBMODE); } - if (phy->soc_config->has_hostpc) - set_pts(phy, TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC); - else - set_pts(phy, 0); + set_pts(phy, phy->soc_config->uhsic_pts_value); val = readl_relaxed(base + USB_TXFILLTUNING); if ((val & USB_FIFO_TXFILL_MASK) != USB_FIFO_TXFILL_THRES(0x10)) { @@ -1474,6 +1471,7 @@ static const struct tegra_phy_soc_config tegra20_soc_config = { .requires_pmc_ao_power_up = false, .uhsic_registers_offset = 0, .uhsic_tx_rtune = 0, /* 40 ohm */ + .uhsic_pts_value = 0, /* UTMI */ }; static const struct tegra_phy_soc_config tegra30_soc_config = { @@ -1484,6 +1482,7 @@ static const struct tegra_phy_soc_config tegra30_soc_config = { .requires_pmc_ao_power_up = true, .uhsic_registers_offset = 0x400, .uhsic_tx_rtune = 8, /* 50 ohm */ + .uhsic_pts_value = TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC, }; static const struct of_device_id tegra_usb_phy_id_table[] = { diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 91420df25627..7209b7731c29 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -26,6 +26,7 @@ struct gpio_desc; * uhsic_registers_offset: for Tegra30+ where HSIC registers were offset * comparing to Tegra20 by 0x400, since Tegra20 has no UTMIP on PHY2 * uhsic_tx_rtune: fine tuned 50 Ohm termination resistor for NMOS/PMOS driver + * uhsic_pts_value: parallel transceiver select enumeration value */ struct tegra_phy_soc_config { @@ -36,6 +37,7 @@ struct tegra_phy_soc_config { bool requires_pmc_ao_power_up; u32 uhsic_registers_offset; u32 uhsic_tx_rtune; + u32 uhsic_pts_value; }; struct tegra_utmip_config { -- cgit v1.2.3 From e5b250214aa402e079de566e10f6e01223fd26bd Mon Sep 17 00:00:00 2001 From: Svyatoslav Ryhel Date: Mon, 2 Feb 2026 10:05:26 +0200 Subject: usb: phy: tegra: parametrize PORTSC1 register offset The PORTSC1 register has a different offset in Tegra20 compared to Tegra30+, yet they share a crucial set of registers required for HSIC functionality. Reflect this register offset change in the SoC config. Signed-off-by: Svyatoslav Ryhel Reviewed-by: Mikko Perttunen Link: https://patch.msgid.link/20260202080526.23487-5-clamor95@gmail.com Signed-off-by: Greg Kroah-Hartman --- drivers/usb/phy/phy-tegra-usb.c | 17 ++++++----------- include/linux/usb/tegra_usb_phy.h | 2 ++ 2 files changed, 8 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/usb/phy/phy-tegra-usb.c b/drivers/usb/phy/phy-tegra-usb.c index 812d99443180..00443a7beaeb 100644 --- a/drivers/usb/phy/phy-tegra-usb.c +++ b/drivers/usb/phy/phy-tegra-usb.c @@ -965,17 +965,10 @@ static int uhsic_phy_power_on(struct tegra_usb_phy *phy) writel_relaxed(val, base + USB_TXFILLTUNING); } - if (phy->soc_config->has_hostpc) { - val = readl_relaxed(base + TEGRA30_USB_PORTSC1); - val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS | - TEGRA_USB_PORTSC1_WKCN); - writel_relaxed(val, base + TEGRA30_USB_PORTSC1); - } else { - val = readl_relaxed(base + TEGRA_USB_PORTSC1); - val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS | - TEGRA_USB_PORTSC1_WKCN); - writel_relaxed(val, base + TEGRA_USB_PORTSC1); - } + val = readl_relaxed(base + phy->soc_config->portsc1_offset); + val &= ~(TEGRA_USB_PORTSC1_WKOC | TEGRA_USB_PORTSC1_WKDS | + TEGRA_USB_PORTSC1_WKCN); + writel_relaxed(val, base + phy->soc_config->portsc1_offset); val = tegra_hsic_readl(phy, UHSIC_PADS_CFG0); val &= ~UHSIC_TX_RTUNEN; @@ -1472,6 +1465,7 @@ static const struct tegra_phy_soc_config tegra20_soc_config = { .uhsic_registers_offset = 0, .uhsic_tx_rtune = 0, /* 40 ohm */ .uhsic_pts_value = 0, /* UTMI */ + .portsc1_offset = TEGRA_USB_PORTSC1, }; static const struct tegra_phy_soc_config tegra30_soc_config = { @@ -1483,6 +1477,7 @@ static const struct tegra_phy_soc_config tegra30_soc_config = { .uhsic_registers_offset = 0x400, .uhsic_tx_rtune = 8, /* 50 ohm */ .uhsic_pts_value = TEGRA_USB_HOSTPC1_DEVLC_PTS_HSIC, + .portsc1_offset = TEGRA30_USB_PORTSC1, }; static const struct of_device_id tegra_usb_phy_id_table[] = { diff --git a/include/linux/usb/tegra_usb_phy.h b/include/linux/usb/tegra_usb_phy.h index 7209b7731c29..6343f88df5de 100644 --- a/include/linux/usb/tegra_usb_phy.h +++ b/include/linux/usb/tegra_usb_phy.h @@ -27,6 +27,7 @@ struct gpio_desc; * comparing to Tegra20 by 0x400, since Tegra20 has no UTMIP on PHY2 * uhsic_tx_rtune: fine tuned 50 Ohm termination resistor for NMOS/PMOS driver * uhsic_pts_value: parallel transceiver select enumeration value + * portsc1_offset: register offset of PORTSC1 */ struct tegra_phy_soc_config { @@ -38,6 +39,7 @@ struct tegra_phy_soc_config { u32 uhsic_registers_offset; u32 uhsic_tx_rtune; u32 uhsic_pts_value; + u32 portsc1_offset; }; struct tegra_utmip_config { -- cgit v1.2.3 From 9c5a40f2922a5a6d6b42e7b3d4c8e253918c07a1 Mon Sep 17 00:00:00 2001 From: Conor Dooley Date: Tue, 3 Feb 2026 16:17:07 +0000 Subject: pinctrl: generic: move function to amlogic-am4 driver pinconf_generic_dt_node_to_map_pinmux() is not actually a generic function, and really belongs in the amlogic-am4 driver. There are three reasons why. First, and least, of the reasons is that this function behaves differently to the other dt_node_to_map functions in a way that is not obvious from a first glance. This difference stems for the devicetree properties that the function is intended for use with, and how they are typically used. The other generic dt_node_to_map functions support platforms where the pins, groups and functions are described statically in the driver and require a function that will produce a mapping from dt nodes to these pre-established descriptions. No other code in the driver is require to be executed at runtime. pinconf_generic_dt_node_to_map_pinmux() on the other hand is intended for use with the pinmux property, where groups and functions are determined entirely from the devicetree. As a result, there are no statically defined groups and functions in the driver for this function to perform a mapping to. Other drivers that use the pinmux property (e.g. the k1) their dt_node_to_map function creates the groups and functions as the devicetree is parsed. Instead of that, pinconf_generic_dt_node_to_map_pinmux() requires that the devicetree is parsed twice, once by it and once at probe, so that the driver dynamically creates the groups and functions before the dt_node_to_map callback is executed. I don't believe this double parsing requirement is how developers would expect this to work and is not necessary given there are drivers that do not have this behaviour. Secondly and thirdly, the function bakes in some assumptions that only really match the amlogic platform about how the devicetree is constructed. These, to me, are problematic for something that claims to be generic. The other dt_node_to_map implementations accept a being called for either a node containing pin configuration properties or a node containing child nodes that each contain the configuration properties. IOW, they support the following two devicetree configurations: | cfg { | label: group { | pinmux = ; | config-item1; | }; | }; | label: cfg { | group1 { | pinmux = ; | config-item2; | }; | group2 { | pinmux = ; | config-item1; | }; | }; pinconf_generic_dt_node_to_map_pinmux() only supports the latter. The other assumption about devicetree configuration that the function makes is that the labeled node's parent is a "function node". The amlogic driver uses these "function nodes" to create the functions at probe time, and pinconf_generic_dt_node_to_map_pinmux() finds the parent of the node it is operating on's name as part of the mapping. IOW, it requires that the devicetree look like: | pinctrl@bla { | | func-foo { | label: group-default { | pinmuxes = ; | }; | }; | }; and couldn't be used if the nodes containing the pinmux and configuration properties are children of the pinctrl node itself: | pinctrl@bla { | | label: group-default { | pinmuxes = ; | }; | }; These final two reasons are mainly why I believe this is not suitable as a generic function, and should be moved into the driver that is the sole user and originator of the "generic" function. Signed-off-by: Conor Dooley Acked-by: Andy Shevchenko Signed-off-by: Linus Walleij --- drivers/pinctrl/meson/pinctrl-amlogic-a4.c | 71 +++++++++++++++++++++++++++++- drivers/pinctrl/pinconf-generic.c | 69 ----------------------------- include/linux/pinctrl/pinconf-generic.h | 5 --- 3 files changed, 70 insertions(+), 75 deletions(-) (limited to 'include/linux') diff --git a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c index 40542edd557e..dfa32b11555c 100644 --- a/drivers/pinctrl/meson/pinctrl-amlogic-a4.c +++ b/drivers/pinctrl/meson/pinctrl-amlogic-a4.c @@ -24,6 +24,7 @@ #include #include "../core.h" +#include "../pinctrl-utils.h" #include "../pinconf.h" #define gpio_chip_to_bank(chip) \ @@ -672,11 +673,79 @@ static void aml_pin_dbg_show(struct pinctrl_dev *pcdev, struct seq_file *s, seq_printf(s, " %s", dev_name(pcdev->dev)); } +static int aml_dt_node_to_map_pinmux(struct pinctrl_dev *pctldev, + struct device_node *np, + struct pinctrl_map **map, + unsigned int *num_maps) +{ + struct device *dev = pctldev->dev; + struct device_node *pnode; + unsigned long *configs = NULL; + unsigned int num_configs = 0; + struct property *prop; + unsigned int reserved_maps; + int reserve; + int ret; + + prop = of_find_property(np, "pinmux", NULL); + if (!prop) { + dev_info(dev, "Missing pinmux property\n"); + return -ENOENT; + } + + pnode = of_get_parent(np); + if (!pnode) { + dev_info(dev, "Missing function node\n"); + return -EINVAL; + } + + reserved_maps = 0; + *map = NULL; + *num_maps = 0; + + ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, + &num_configs); + if (ret < 0) { + dev_err(dev, "%pOF: could not parse node property\n", np); + return ret; + } + + reserve = 1; + if (num_configs) + reserve++; + + ret = pinctrl_utils_reserve_map(pctldev, map, &reserved_maps, + num_maps, reserve); + if (ret < 0) + goto exit; + + ret = pinctrl_utils_add_map_mux(pctldev, map, + &reserved_maps, num_maps, np->name, + pnode->name); + if (ret < 0) + goto exit; + + if (num_configs) { + ret = pinctrl_utils_add_map_configs(pctldev, map, &reserved_maps, + num_maps, np->name, configs, + num_configs, PIN_MAP_TYPE_CONFIGS_GROUP); + if (ret < 0) + goto exit; + } + +exit: + kfree(configs); + if (ret) + pinctrl_utils_free_map(pctldev, *map, *num_maps); + + return ret; +} + static const struct pinctrl_ops aml_pctrl_ops = { .get_groups_count = aml_get_groups_count, .get_group_name = aml_get_group_name, .get_group_pins = aml_get_group_pins, - .dt_node_to_map = pinconf_generic_dt_node_to_map_pinmux, + .dt_node_to_map = aml_dt_node_to_map_pinmux, .dt_free_map = pinconf_generic_dt_free_map, .pin_dbg_show = aml_pin_dbg_show, }; diff --git a/drivers/pinctrl/pinconf-generic.c b/drivers/pinctrl/pinconf-generic.c index 366775841c63..94b1d057197c 100644 --- a/drivers/pinctrl/pinconf-generic.c +++ b/drivers/pinctrl/pinconf-generic.c @@ -385,75 +385,6 @@ out: } EXPORT_SYMBOL_GPL(pinconf_generic_parse_dt_config); -int pinconf_generic_dt_node_to_map_pinmux(struct pinctrl_dev *pctldev, - struct device_node *np, - struct pinctrl_map **map, - unsigned int *num_maps) -{ - struct device *dev = pctldev->dev; - struct device_node *pnode; - unsigned long *configs = NULL; - unsigned int num_configs = 0; - struct property *prop; - unsigned int reserved_maps; - int reserve; - int ret; - - prop = of_find_property(np, "pinmux", NULL); - if (!prop) { - dev_info(dev, "Missing pinmux property\n"); - return -ENOENT; - } - - pnode = of_get_parent(np); - if (!pnode) { - dev_info(dev, "Missing function node\n"); - return -EINVAL; - } - - reserved_maps = 0; - *map = NULL; - *num_maps = 0; - - ret = pinconf_generic_parse_dt_config(np, pctldev, &configs, - &num_configs); - if (ret < 0) { - dev_err(dev, "%pOF: could not parse node property\n", np); - return ret; - } - - reserve = 1; - if (num_configs) - reserve++; - - ret = pinctrl_utils_reserve_map(pctldev, map, &reserved_maps, - num_maps, reserve); - if (ret < 0) - goto exit; - - ret = pinctrl_utils_add_map_mux(pctldev, map, - &reserved_maps, num_maps, np->name, - pnode->name); - if (ret < 0) - goto exit; - - if (num_configs) { - ret = pinctrl_utils_add_map_configs(pctldev, map, &reserved_maps, - num_maps, np->name, configs, - num_configs, PIN_MAP_TYPE_CONFIGS_GROUP); - if (ret < 0) - goto exit; - } - -exit: - kfree(configs); - if (ret) - pinctrl_utils_free_map(pctldev, *map, *num_maps); - - return ret; -} -EXPORT_SYMBOL_GPL(pinconf_generic_dt_node_to_map_pinmux); - int pinconf_generic_dt_subnode_to_map(struct pinctrl_dev *pctldev, struct device_node *np, struct pinctrl_map **map, unsigned int *reserved_maps, unsigned int *num_maps, diff --git a/include/linux/pinctrl/pinconf-generic.h b/include/linux/pinctrl/pinconf-generic.h index 1be4032071c2..89277808ea61 100644 --- a/include/linux/pinctrl/pinconf-generic.h +++ b/include/linux/pinctrl/pinconf-generic.h @@ -250,9 +250,4 @@ static inline int pinconf_generic_dt_node_to_map_all(struct pinctrl_dev *pctldev return pinconf_generic_dt_node_to_map(pctldev, np_config, map, num_maps, PIN_MAP_TYPE_INVALID); } - -int pinconf_generic_dt_node_to_map_pinmux(struct pinctrl_dev *pctldev, - struct device_node *np, - struct pinctrl_map **map, - unsigned int *num_maps); #endif /* __LINUX_PINCTRL_PINCONF_GENERIC_H */ -- cgit v1.2.3 From b7cc142dbafeaf6c053284ca9121b9f70b6d6d06 Mon Sep 17 00:00:00 2001 From: Ilya Dryomov Date: Mon, 22 Dec 2025 20:41:27 +0100 Subject: libceph: add support for CEPH_CRYPTO_AES256KRB5 This is based on AES256-CTS-HMAC384-192 crypto algorithm per RFC 8009 (i.e. Kerberos 5, hence the name) with custom-defined key usage numbers. The implementation allows a given key to have/be linked to between one and three usage numbers. The existing CEPH_CRYPTO_AES remains in place and unchanged. The usage_slot parameter that needed to be added to ceph_crypt() and its wrappers is simply ignored there. Signed-off-by: Ilya Dryomov --- include/linux/ceph/ceph_fs.h | 5 +- net/ceph/Kconfig | 1 + net/ceph/auth_x.c | 88 ++++++++++++++++-------- net/ceph/auth_x_protocol.h | 38 +++++++++++ net/ceph/crypto.c | 157 ++++++++++++++++++++++++++++++++++++++----- net/ceph/crypto.h | 16 +++-- 6 files changed, 257 insertions(+), 48 deletions(-) (limited to 'include/linux') diff --git a/include/linux/ceph/ceph_fs.h b/include/linux/ceph/ceph_fs.h index 08e5dbe15ca4..69ac3e55a3fe 100644 --- a/include/linux/ceph/ceph_fs.h +++ b/include/linux/ceph/ceph_fs.h @@ -89,8 +89,9 @@ struct ceph_dir_layout { } __attribute__ ((packed)); /* crypto algorithms */ -#define CEPH_CRYPTO_NONE 0x0 -#define CEPH_CRYPTO_AES 0x1 +#define CEPH_CRYPTO_NONE 0x0 +#define CEPH_CRYPTO_AES 0x1 +#define CEPH_CRYPTO_AES256KRB5 0x2 /* AES256-CTS-HMAC384-192 */ #define CEPH_AES_IV "cephsageyudagreg" diff --git a/net/ceph/Kconfig b/net/ceph/Kconfig index ea60e3ef0834..7e2528cde4b9 100644 --- a/net/ceph/Kconfig +++ b/net/ceph/Kconfig @@ -6,6 +6,7 @@ config CEPH_LIB select CRYPTO_AES select CRYPTO_CBC select CRYPTO_GCM + select CRYPTO_KRB5 select CRYPTO_LIB_SHA256 select CRYPTO select KEYS diff --git a/net/ceph/auth_x.c b/net/ceph/auth_x.c index abdd35be263a..decd2867f8f1 100644 --- a/net/ceph/auth_x.c +++ b/net/ceph/auth_x.c @@ -17,6 +17,22 @@ #include "auth_x.h" #include "auth_x_protocol.h" +static const u32 ticket_key_usages[] = { + CEPHX_KEY_USAGE_TICKET_SESSION_KEY, + CEPHX_KEY_USAGE_TICKET_BLOB, + CEPHX_KEY_USAGE_AUTH_CONNECTION_SECRET +}; + +static const u32 authorizer_key_usages[] = { + CEPHX_KEY_USAGE_AUTHORIZE, + CEPHX_KEY_USAGE_AUTHORIZE_CHALLENGE, + CEPHX_KEY_USAGE_AUTHORIZE_REPLY +}; + +static const u32 client_key_usages[] = { + CEPHX_KEY_USAGE_TICKET_SESSION_KEY +}; + static void ceph_x_validate_tickets(struct ceph_auth_client *ac, int *pneed); static int ceph_x_is_authenticated(struct ceph_auth_client *ac) @@ -57,6 +73,7 @@ static int ceph_x_encrypt_offset(const struct ceph_crypto_key *key) /* * AES: ciphertext_len | hdr | data... | padding + * AES256KRB5: ciphertext_len | confounder | hdr | data... | hmac */ static int ceph_x_encrypt_buflen(const struct ceph_crypto_key *key, int data_len) @@ -65,19 +82,19 @@ static int ceph_x_encrypt_buflen(const struct ceph_crypto_key *key, return sizeof(u32) + ceph_crypt_buflen(key, encrypt_len); } -static int ceph_x_encrypt(struct ceph_crypto_key *secret, void *buf, - int buf_len, int plaintext_len) +static int ceph_x_encrypt(const struct ceph_crypto_key *key, int usage_slot, + void *buf, int buf_len, int plaintext_len) { struct ceph_x_encrypt_header *hdr; int ciphertext_len; int ret; - hdr = buf + sizeof(u32) + ceph_crypt_data_offset(secret); + hdr = buf + sizeof(u32) + ceph_crypt_data_offset(key); hdr->struct_v = 1; hdr->magic = cpu_to_le64(CEPHX_ENC_MAGIC); - ret = ceph_crypt(secret, true, buf + sizeof(u32), buf_len - sizeof(u32), - plaintext_len + sizeof(struct ceph_x_encrypt_header), + ret = ceph_crypt(key, usage_slot, true, buf + sizeof(u32), + buf_len - sizeof(u32), plaintext_len + sizeof(*hdr), &ciphertext_len); if (ret) return ret; @@ -86,19 +103,19 @@ static int ceph_x_encrypt(struct ceph_crypto_key *secret, void *buf, return sizeof(u32) + ciphertext_len; } -static int __ceph_x_decrypt(struct ceph_crypto_key *secret, void *p, - int ciphertext_len) +static int __ceph_x_decrypt(const struct ceph_crypto_key *key, int usage_slot, + void *p, int ciphertext_len) { struct ceph_x_encrypt_header *hdr; int plaintext_len; int ret; - ret = ceph_crypt(secret, false, p, ciphertext_len, ciphertext_len, - &plaintext_len); + ret = ceph_crypt(key, usage_slot, false, p, ciphertext_len, + ciphertext_len, &plaintext_len); if (ret) return ret; - hdr = p + ceph_crypt_data_offset(secret); + hdr = p + ceph_crypt_data_offset(key); if (le64_to_cpu(hdr->magic) != CEPHX_ENC_MAGIC) { pr_err("%s bad magic\n", __func__); return -EINVAL; @@ -107,7 +124,8 @@ static int __ceph_x_decrypt(struct ceph_crypto_key *secret, void *p, return plaintext_len - sizeof(*hdr); } -static int ceph_x_decrypt(struct ceph_crypto_key *secret, void **p, void *end) +static int ceph_x_decrypt(const struct ceph_crypto_key *key, int usage_slot, + void **p, void *end) { int ciphertext_len; int ret; @@ -115,7 +133,7 @@ static int ceph_x_decrypt(struct ceph_crypto_key *secret, void **p, void *end) ceph_decode_32_safe(p, end, ciphertext_len, e_inval); ceph_decode_need(p, end, ciphertext_len, e_inval); - ret = __ceph_x_decrypt(secret, *p, ciphertext_len); + ret = __ceph_x_decrypt(key, usage_slot, *p, ciphertext_len); if (ret < 0) return ret; @@ -207,7 +225,9 @@ static int process_one_ticket(struct ceph_auth_client *ac, /* blob for me */ dp = *p + ceph_x_encrypt_offset(secret); - ret = ceph_x_decrypt(secret, p, end); + ret = ceph_x_decrypt(secret, + 0 /* CEPHX_KEY_USAGE_TICKET_SESSION_KEY */, + p, end); if (ret < 0) goto out; dout(" decrypted %d bytes\n", ret); @@ -221,7 +241,8 @@ static int process_one_ticket(struct ceph_auth_client *ac, if (ret) goto out; - ret = ceph_crypto_key_prepare(&new_session_key); + ret = ceph_crypto_key_prepare(&new_session_key, ticket_key_usages, + ARRAY_SIZE(ticket_key_usages)); if (ret) goto out; @@ -238,7 +259,9 @@ static int process_one_ticket(struct ceph_auth_client *ac, if (is_enc) { /* encrypted */ tp = *p + ceph_x_encrypt_offset(&th->session_key); - ret = ceph_x_decrypt(&th->session_key, p, end); + ret = ceph_x_decrypt(&th->session_key, + 1 /* CEPHX_KEY_USAGE_TICKET_BLOB */, + p, end); if (ret < 0) goto out; dout(" encrypted ticket, decrypted %d bytes\n", ret); @@ -341,7 +364,9 @@ static int encrypt_authorizer(struct ceph_x_authorizer *au, msg_b->server_challenge_plus_one = 0; } - ret = ceph_x_encrypt(&au->session_key, p, end - p, sizeof(*msg_b)); + ret = ceph_x_encrypt(&au->session_key, + 0 /* CEPHX_KEY_USAGE_AUTHORIZE */, + p, end - p, sizeof(*msg_b)); if (ret < 0) return ret; @@ -384,7 +409,8 @@ static int ceph_x_build_authorizer(struct ceph_auth_client *ac, if (ret) goto out_au; - ret = ceph_crypto_key_prepare(&au->session_key); + ret = ceph_crypto_key_prepare(&au->session_key, authorizer_key_usages, + ARRAY_SIZE(authorizer_key_usages)); if (ret) goto out_au; @@ -542,7 +568,8 @@ static int ceph_x_build_request(struct ceph_auth_client *ac, get_random_bytes(&auth->client_challenge, sizeof(u64)); blob->client_challenge = auth->client_challenge; blob->server_challenge = cpu_to_le64(xi->server_challenge); - ret = ceph_x_encrypt(&xi->secret, enc_buf, CEPHX_AU_ENC_BUF_LEN, + ret = ceph_x_encrypt(&xi->secret, 0 /* dummy */, + enc_buf, CEPHX_AU_ENC_BUF_LEN, sizeof(*blob)); if (ret < 0) return ret; @@ -656,7 +683,9 @@ static int handle_auth_session_key(struct ceph_auth_client *ac, u64 global_id, dout("%s connection secret blob len %d\n", __func__, len); if (len > 0) { dp = *p + ceph_x_encrypt_offset(&th->session_key); - ret = ceph_x_decrypt(&th->session_key, p, *p + len); + ret = ceph_x_decrypt(&th->session_key, + 2 /* CEPHX_KEY_USAGE_AUTH_CONNECTION_SECRET */, + p, *p + len); if (ret < 0) return ret; @@ -820,7 +849,9 @@ static int decrypt_authorizer_challenge(struct ceph_crypto_key *secret, int ret; /* no leading len */ - ret = __ceph_x_decrypt(secret, challenge, challenge_len); + ret = __ceph_x_decrypt(secret, + 1 /* CEPHX_KEY_USAGE_AUTHORIZE_CHALLENGE */, + challenge, challenge_len); if (ret < 0) return ret; @@ -873,7 +904,8 @@ static int decrypt_authorizer_reply(struct ceph_crypto_key *secret, int ret; dp = *p + ceph_x_encrypt_offset(secret); - ret = ceph_x_decrypt(secret, p, end); + ret = ceph_x_decrypt(secret, 2 /* CEPHX_KEY_USAGE_AUTHORIZE_REPLY */, + p, end); if (ret < 0) return ret; @@ -1004,8 +1036,9 @@ static int calc_signature(struct ceph_x_authorizer *au, struct ceph_msg *msg, sigblock->middle_crc = msg->footer.middle_crc; sigblock->data_crc = msg->footer.data_crc; - ret = ceph_x_encrypt(&au->session_key, enc_buf, - CEPHX_AU_ENC_BUF_LEN, sizeof(*sigblock)); + ret = ceph_x_encrypt(&au->session_key, 0 /* dummy */, + enc_buf, CEPHX_AU_ENC_BUF_LEN, + sizeof(*sigblock)); if (ret < 0) return ret; @@ -1036,9 +1069,9 @@ static int calc_signature(struct ceph_x_authorizer *au, struct ceph_msg *msg, sigblock->seq_lower_word = *(__le32 *)&msg->hdr.seq; /* no leading len, no ceph_x_encrypt_header */ - ret = ceph_crypt(&au->session_key, true, enc_buf, - CEPHX_AU_ENC_BUF_LEN, sizeof(*sigblock), - &ciphertext_len); + ret = ceph_crypt(&au->session_key, 0 /* dummy */, + true, enc_buf, CEPHX_AU_ENC_BUF_LEN, + sizeof(*sigblock), &ciphertext_len); if (ret) return ret; @@ -1130,7 +1163,8 @@ int ceph_x_init(struct ceph_auth_client *ac) goto err_xi; } - ret = ceph_crypto_key_prepare(&xi->secret); + ret = ceph_crypto_key_prepare(&xi->secret, client_key_usages, + ARRAY_SIZE(client_key_usages)); if (ret) { pr_err("cannot prepare key: %d\n", ret); goto err_secret; diff --git a/net/ceph/auth_x_protocol.h b/net/ceph/auth_x_protocol.h index 9c60feeb1bcb..d097b3651c99 100644 --- a/net/ceph/auth_x_protocol.h +++ b/net/ceph/auth_x_protocol.h @@ -6,6 +6,44 @@ #define CEPHX_GET_PRINCIPAL_SESSION_KEY 0x0200 #define CEPHX_GET_ROTATING_KEY 0x0400 +/* Client <-> AuthMonitor */ +/* + * The AUTH session's connection secret: encrypted with the AUTH + * ticket session key + */ +#define CEPHX_KEY_USAGE_AUTH_CONNECTION_SECRET 0x03 +/* + * The ticket's blob for the client ("blob for me", contains the + * session key): encrypted with the client's secret key in case of + * the AUTH ticket and the AUTH ticket session key in case of other + * service tickets + */ +#define CEPHX_KEY_USAGE_TICKET_SESSION_KEY 0x04 +/* + * The ticket's blob for the service (ceph_x_ticket_blob): possibly + * encrypted with the old AUTH ticket session key in case of the AUTH + * ticket and not encrypted in case of other service tickets + */ +#define CEPHX_KEY_USAGE_TICKET_BLOB 0x05 + +/* Client <-> Service */ +/* + * The client's authorization request (ceph_x_authorize_b): + * encrypted with the service ticket session key + */ +#define CEPHX_KEY_USAGE_AUTHORIZE 0x10 +/* + * The service's challenge (ceph_x_authorize_challenge): + * encrypted with the service ticket session key + */ +#define CEPHX_KEY_USAGE_AUTHORIZE_CHALLENGE 0x11 +/* + * The service's final reply (ceph_x_authorize_reply + the service + * session's connection secret): encrypted with the service ticket + * session key + */ +#define CEPHX_KEY_USAGE_AUTHORIZE_REPLY 0x12 + /* common bits */ struct ceph_x_ticket_blob { __u8 struct_v; diff --git a/net/ceph/crypto.c b/net/ceph/crypto.c index 3453dc303315..b54085d8d5f0 100644 --- a/net/ceph/crypto.c +++ b/net/ceph/crypto.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -22,28 +23,68 @@ static int set_aes_tfm(struct ceph_crypto_key *key) int ret; noio_flag = memalloc_noio_save(); - key->tfm = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0); + key->aes_tfm = crypto_alloc_sync_skcipher("cbc(aes)", 0, 0); memalloc_noio_restore(noio_flag); - if (IS_ERR(key->tfm)) { - ret = PTR_ERR(key->tfm); - key->tfm = NULL; + if (IS_ERR(key->aes_tfm)) { + ret = PTR_ERR(key->aes_tfm); + key->aes_tfm = NULL; return ret; } - ret = crypto_sync_skcipher_setkey(key->tfm, key->key, key->len); + ret = crypto_sync_skcipher_setkey(key->aes_tfm, key->key, key->len); if (ret) return ret; return 0; } -int ceph_crypto_key_prepare(struct ceph_crypto_key *key) +static int set_krb5_tfms(struct ceph_crypto_key *key, const u32 *key_usages, + int key_usage_cnt) +{ + struct krb5_buffer TK = { .len = key->len, .data = key->key }; + unsigned int noio_flag; + int ret = 0; + int i; + + if (WARN_ON_ONCE(key_usage_cnt > ARRAY_SIZE(key->krb5_tfms))) + return -EINVAL; + + key->krb5_type = crypto_krb5_find_enctype( + KRB5_ENCTYPE_AES256_CTS_HMAC_SHA384_192); + if (!key->krb5_type) + return -ENOPKG; + + /* + * Despite crypto_krb5_prepare_encryption() taking a gfp mask, + * crypto_alloc_aead() inside of it allocates with GFP_KERNEL. + */ + noio_flag = memalloc_noio_save(); + for (i = 0; i < key_usage_cnt; i++) { + key->krb5_tfms[i] = crypto_krb5_prepare_encryption( + key->krb5_type, &TK, key_usages[i], + GFP_NOIO); + if (IS_ERR(key->krb5_tfms[i])) { + ret = PTR_ERR(key->krb5_tfms[i]); + key->krb5_tfms[i] = NULL; + goto out_flag; + } + } + +out_flag: + memalloc_noio_restore(noio_flag); + return ret; +} + +int ceph_crypto_key_prepare(struct ceph_crypto_key *key, + const u32 *key_usages, int key_usage_cnt) { switch (key->type) { case CEPH_CRYPTO_NONE: return 0; /* nothing to do */ case CEPH_CRYPTO_AES: return set_aes_tfm(key); + case CEPH_CRYPTO_AES256KRB5: + return set_krb5_tfms(key, key_usages, key_usage_cnt); default: return -ENOTSUPP; } @@ -123,12 +164,25 @@ int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *inkey) void ceph_crypto_key_destroy(struct ceph_crypto_key *key) { - if (key) { - kfree_sensitive(key->key); - key->key = NULL; - if (key->tfm) { - crypto_free_sync_skcipher(key->tfm); - key->tfm = NULL; + int i; + + if (!key) + return; + + kfree_sensitive(key->key); + key->key = NULL; + + if (key->type == CEPH_CRYPTO_AES) { + if (key->aes_tfm) { + crypto_free_sync_skcipher(key->aes_tfm); + key->aes_tfm = NULL; + } + } else if (key->type == CEPH_CRYPTO_AES256KRB5) { + for (i = 0; i < ARRAY_SIZE(key->krb5_tfms); i++) { + if (key->krb5_tfms[i]) { + crypto_free_aead(key->krb5_tfms[i]); + key->krb5_tfms[i] = NULL; + } } } } @@ -208,7 +262,7 @@ static void teardown_sgtable(struct sg_table *sgt) static int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt, void *buf, int buf_len, int in_len, int *pout_len) { - SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->tfm); + SYNC_SKCIPHER_REQUEST_ON_STACK(req, key->aes_tfm); struct sg_table sgt; struct scatterlist prealloc_sg; char iv[AES_BLOCK_SIZE] __aligned(8); @@ -224,7 +278,7 @@ static int ceph_aes_crypt(const struct ceph_crypto_key *key, bool encrypt, return ret; memcpy(iv, aes_iv, AES_BLOCK_SIZE); - skcipher_request_set_sync_tfm(req, key->tfm); + skcipher_request_set_sync_tfm(req, key->aes_tfm); skcipher_request_set_callback(req, 0, NULL, NULL); skcipher_request_set_crypt(req, sgt.sgl, sgt.sgl, crypt_len, iv); @@ -269,7 +323,68 @@ out_sgt: return ret; } -int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt, +static int ceph_krb5_encrypt(const struct ceph_crypto_key *key, int usage_slot, + void *buf, int buf_len, int in_len, int *pout_len) +{ + struct sg_table sgt; + struct scatterlist prealloc_sg; + int ret; + + if (WARN_ON_ONCE(usage_slot >= ARRAY_SIZE(key->krb5_tfms))) + return -EINVAL; + + ret = setup_sgtable(&sgt, &prealloc_sg, buf, buf_len); + if (ret) + return ret; + + ret = crypto_krb5_encrypt(key->krb5_type, key->krb5_tfms[usage_slot], + sgt.sgl, sgt.nents, buf_len, AES_BLOCK_SIZE, + in_len, false); + if (ret < 0) { + pr_err("%s encrypt failed: %d\n", __func__, ret); + goto out_sgt; + } + + *pout_len = ret; + ret = 0; + +out_sgt: + teardown_sgtable(&sgt); + return ret; +} + +static int ceph_krb5_decrypt(const struct ceph_crypto_key *key, int usage_slot, + void *buf, int buf_len, int in_len, int *pout_len) +{ + struct sg_table sgt; + struct scatterlist prealloc_sg; + size_t data_off = 0; + size_t data_len = in_len; + int ret; + + if (WARN_ON_ONCE(usage_slot >= ARRAY_SIZE(key->krb5_tfms))) + return -EINVAL; + + ret = setup_sgtable(&sgt, &prealloc_sg, buf, in_len); + if (ret) + return ret; + + ret = crypto_krb5_decrypt(key->krb5_type, key->krb5_tfms[usage_slot], + sgt.sgl, sgt.nents, &data_off, &data_len); + if (ret) { + pr_err("%s decrypt failed: %d\n", __func__, ret); + goto out_sgt; + } + + WARN_ON(data_off != AES_BLOCK_SIZE); + *pout_len = data_len; + +out_sgt: + teardown_sgtable(&sgt); + return ret; +} + +int ceph_crypt(const struct ceph_crypto_key *key, int usage_slot, bool encrypt, void *buf, int buf_len, int in_len, int *pout_len) { switch (key->type) { @@ -279,6 +394,12 @@ int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt, case CEPH_CRYPTO_AES: return ceph_aes_crypt(key, encrypt, buf, buf_len, in_len, pout_len); + case CEPH_CRYPTO_AES256KRB5: + return encrypt ? + ceph_krb5_encrypt(key, usage_slot, buf, buf_len, in_len, + pout_len) : + ceph_krb5_decrypt(key, usage_slot, buf, buf_len, in_len, + pout_len); default: return -ENOTSUPP; } @@ -290,6 +411,9 @@ int ceph_crypt_data_offset(const struct ceph_crypto_key *key) case CEPH_CRYPTO_NONE: case CEPH_CRYPTO_AES: return 0; + case CEPH_CRYPTO_AES256KRB5: + /* confounder */ + return AES_BLOCK_SIZE; default: BUG(); } @@ -304,6 +428,9 @@ int ceph_crypt_buflen(const struct ceph_crypto_key *key, int data_len) /* PKCS#7 padding at the end */ return data_len + AES_BLOCK_SIZE - (data_len & (AES_BLOCK_SIZE - 1)); + case CEPH_CRYPTO_AES256KRB5: + /* confounder at the beginning and 192-bit HMAC at the end */ + return AES_BLOCK_SIZE + data_len + 24; default: BUG(); } diff --git a/net/ceph/crypto.h b/net/ceph/crypto.h index 2b8f8f68ff7a..2c37c54d0f56 100644 --- a/net/ceph/crypto.h +++ b/net/ceph/crypto.h @@ -5,7 +5,7 @@ #include #include -#define CEPH_MAX_KEY_LEN 16 +#define CEPH_MAX_KEY_LEN 32 #define CEPH_MAX_CON_SECRET_LEN 64 /* @@ -16,10 +16,18 @@ struct ceph_crypto_key { struct ceph_timespec created; int len; void *key; - struct crypto_sync_skcipher *tfm; + + union { + struct crypto_sync_skcipher *aes_tfm; + struct { + const struct krb5_enctype *krb5_type; + struct crypto_aead *krb5_tfms[3]; + }; + }; }; -int ceph_crypto_key_prepare(struct ceph_crypto_key *key); +int ceph_crypto_key_prepare(struct ceph_crypto_key *key, + const u32 *key_usages, int key_usage_cnt); int ceph_crypto_key_clone(struct ceph_crypto_key *dst, const struct ceph_crypto_key *src); int ceph_crypto_key_decode(struct ceph_crypto_key *key, void **p, void *end); @@ -27,7 +35,7 @@ int ceph_crypto_key_unarmor(struct ceph_crypto_key *key, const char *in); void ceph_crypto_key_destroy(struct ceph_crypto_key *key); /* crypto.c */ -int ceph_crypt(const struct ceph_crypto_key *key, bool encrypt, +int ceph_crypt(const struct ceph_crypto_key *key, int usage_slot, bool encrypt, void *buf, int buf_len, int in_len, int *pout_len); int ceph_crypt_data_offset(const struct ceph_crypto_key *key); int ceph_crypt_buflen(const struct ceph_crypto_key *key, int data_len); -- cgit v1.2.3 From 802182490445f6bcf5de0e0518fb967c2afb6da1 Mon Sep 17 00:00:00 2001 From: Christian Brauner Date: Tue, 20 Jan 2026 15:52:35 +0100 Subject: pidfs: convert rb-tree to rhashtable Mateusz reported performance penalties [1] during task creation because pidfs uses pidmap_lock to add elements into the rbtree. Switch to an rhashtable to have separate fine-grained locking and to decouple from pidmap_lock moving all heavy manipulations outside of it. Convert the pidfs inode-to-pid mapping from an rb-tree with seqcount protection to an rhashtable. This removes the global pidmap_lock contention from pidfs_ino_get_pid() lookups and allows the hashtable insert to happen outside the pidmap_lock. pidfs_add_pid() is split. pidfs_prepare_pid() allocates inode number and initializes pid fields and is called inside pidmap_lock. pidfs_add_pid() inserts pid into rhashtable and is called outside pidmap_lock. Insertion into the rhashtable can fail and memory allocation may happen so we need to drop the spinlock. To guard against accidently opening an already reaped task pidfs_ino_get_pid() uses additional checks beyond pid_vnr(). If pid->attr is PIDFS_PID_DEAD or NULL the pid either never had a pidfd or it already went through pidfs_exit() aka the process as already reaped. If pid->attr is valid check PIDFS_ATTR_BIT_EXIT to figure out whether the task has exited. This slightly changes visibility semantics: pidfd creation is denied after pidfs_exit() runs, which is just before the pid number is removed from the via free_pid(). That should not be an issue though. Link: https://lore.kernel.org/20251206131955.780557-1-mjguzik@gmail.com [1] Link: https://patch.msgid.link/20260120-work-pidfs-rhashtable-v2-1-d593c4d0f576@kernel.org Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- fs/pidfs.c | 81 +++++++++++++++++++++------------------------------ include/linux/pid.h | 4 +-- include/linux/pidfs.h | 3 +- kernel/pid.c | 13 ++++++--- 4 files changed, 46 insertions(+), 55 deletions(-) (limited to 'include/linux') diff --git a/fs/pidfs.c b/fs/pidfs.c index dba703d4ce4a..ee0e36dd29d2 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -21,6 +21,7 @@ #include #include #include +#include #include #include "internal.h" @@ -55,7 +56,14 @@ struct pidfs_attr { __u32 coredump_signal; }; -static struct rb_root pidfs_ino_tree = RB_ROOT; +static struct rhashtable pidfs_ino_ht; + +static const struct rhashtable_params pidfs_ino_ht_params = { + .key_offset = offsetof(struct pid, ino), + .key_len = sizeof(u64), + .head_offset = offsetof(struct pid, pidfs_hash), + .automatic_shrinking = true, +}; #if BITS_PER_LONG == 32 static inline unsigned long pidfs_ino(u64 ino) @@ -84,21 +92,11 @@ static inline u32 pidfs_gen(u64 ino) } #endif -static int pidfs_ino_cmp(struct rb_node *a, const struct rb_node *b) -{ - struct pid *pid_a = rb_entry(a, struct pid, pidfs_node); - struct pid *pid_b = rb_entry(b, struct pid, pidfs_node); - u64 pid_ino_a = pid_a->ino; - u64 pid_ino_b = pid_b->ino; - - if (pid_ino_a < pid_ino_b) - return -1; - if (pid_ino_a > pid_ino_b) - return 1; - return 0; -} - -void pidfs_add_pid(struct pid *pid) +/* + * Allocate inode number and initialize pidfs fields. + * Called with pidmap_lock held. + */ +void pidfs_prepare_pid(struct pid *pid) { static u64 pidfs_ino_nr = 2; @@ -131,20 +129,22 @@ void pidfs_add_pid(struct pid *pid) pidfs_ino_nr += 2; pid->ino = pidfs_ino_nr; + pid->pidfs_hash.next = NULL; pid->stashed = NULL; pid->attr = NULL; pidfs_ino_nr++; +} - write_seqcount_begin(&pidmap_lock_seq); - rb_find_add_rcu(&pid->pidfs_node, &pidfs_ino_tree, pidfs_ino_cmp); - write_seqcount_end(&pidmap_lock_seq); +int pidfs_add_pid(struct pid *pid) +{ + return rhashtable_insert_fast(&pidfs_ino_ht, &pid->pidfs_hash, + pidfs_ino_ht_params); } void pidfs_remove_pid(struct pid *pid) { - write_seqcount_begin(&pidmap_lock_seq); - rb_erase(&pid->pidfs_node, &pidfs_ino_tree); - write_seqcount_end(&pidmap_lock_seq); + rhashtable_remove_fast(&pidfs_ino_ht, &pid->pidfs_hash, + pidfs_ino_ht_params); } void pidfs_free_pid(struct pid *pid) @@ -773,42 +773,24 @@ static int pidfs_encode_fh(struct inode *inode, u32 *fh, int *max_len, return FILEID_KERNFS; } -static int pidfs_ino_find(const void *key, const struct rb_node *node) -{ - const u64 pid_ino = *(u64 *)key; - const struct pid *pid = rb_entry(node, struct pid, pidfs_node); - - if (pid_ino < pid->ino) - return -1; - if (pid_ino > pid->ino) - return 1; - return 0; -} - /* Find a struct pid based on the inode number. */ static struct pid *pidfs_ino_get_pid(u64 ino) { struct pid *pid; - struct rb_node *node; - unsigned int seq; + struct pidfs_attr *attr; guard(rcu)(); - do { - seq = read_seqcount_begin(&pidmap_lock_seq); - node = rb_find_rcu(&ino, &pidfs_ino_tree, pidfs_ino_find); - if (node) - break; - } while (read_seqcount_retry(&pidmap_lock_seq, seq)); - - if (!node) + pid = rhashtable_lookup(&pidfs_ino_ht, &ino, pidfs_ino_ht_params); + if (!pid) + return NULL; + attr = READ_ONCE(pid->attr); + if (IS_ERR_OR_NULL(attr)) + return NULL; + if (test_bit(PIDFS_ATTR_BIT_EXIT, &attr->attr_mask)) return NULL; - - pid = rb_entry(node, struct pid, pidfs_node); - /* Within our pid namespace hierarchy? */ if (pid_vnr(pid) == 0) return NULL; - return get_pid(pid); } @@ -1086,6 +1068,9 @@ struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags) void __init pidfs_init(void) { + if (rhashtable_init(&pidfs_ino_ht, &pidfs_ino_ht_params)) + panic("Failed to initialize pidfs hashtable"); + pidfs_attr_cachep = kmem_cache_create("pidfs_attr_cache", sizeof(struct pidfs_attr), 0, (SLAB_HWCACHE_ALIGN | SLAB_RECLAIM_ACCOUNT | SLAB_ACCOUNT | SLAB_PANIC), NULL); diff --git a/include/linux/pid.h b/include/linux/pid.h index 003a1027d219..ce9b5cb7560b 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -6,6 +6,7 @@ #include #include #include +#include #include #include @@ -60,7 +61,7 @@ struct pid { spinlock_t lock; struct { u64 ino; - struct rb_node pidfs_node; + struct rhash_head pidfs_hash; struct dentry *stashed; struct pidfs_attr *attr; }; @@ -73,7 +74,6 @@ struct pid { struct upid numbers[]; }; -extern seqcount_spinlock_t pidmap_lock_seq; extern struct pid init_struct_pid; struct file; diff --git a/include/linux/pidfs.h b/include/linux/pidfs.h index 3e08c33da2df..416bdff4d6ce 100644 --- a/include/linux/pidfs.h +++ b/include/linux/pidfs.h @@ -6,7 +6,8 @@ struct coredump_params; struct file *pidfs_alloc_file(struct pid *pid, unsigned int flags); void __init pidfs_init(void); -void pidfs_add_pid(struct pid *pid); +void pidfs_prepare_pid(struct pid *pid); +int pidfs_add_pid(struct pid *pid); void pidfs_remove_pid(struct pid *pid); void pidfs_exit(struct task_struct *tsk); #ifdef CONFIG_COREDUMP diff --git a/kernel/pid.c b/kernel/pid.c index f45ae56db7da..06356e40ac00 100644 --- a/kernel/pid.c +++ b/kernel/pid.c @@ -43,7 +43,6 @@ #include #include #include -#include #include #include @@ -85,7 +84,6 @@ struct pid_namespace init_pid_ns = { EXPORT_SYMBOL_GPL(init_pid_ns); static __cacheline_aligned_in_smp DEFINE_SPINLOCK(pidmap_lock); -seqcount_spinlock_t pidmap_lock_seq = SEQCNT_SPINLOCK_ZERO(pidmap_lock_seq, &pidmap_lock); void put_pid(struct pid *pid) { @@ -141,9 +139,9 @@ void free_pid(struct pid *pid) idr_remove(&ns->idr, upid->nr); } - pidfs_remove_pid(pid); spin_unlock(&pidmap_lock); + pidfs_remove_pid(pid); call_rcu(&pid->rcu, delayed_put_pid); } @@ -316,7 +314,8 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid, retval = -ENOMEM; if (unlikely(!(ns->pid_allocated & PIDNS_ADDING))) goto out_free; - pidfs_add_pid(pid); + pidfs_prepare_pid(pid); + for (upid = pid->numbers + ns->level; upid >= pid->numbers; --upid) { /* Make the PID visible to find_pid_ns. */ idr_replace(&upid->ns->idr, pid, upid->nr); @@ -326,6 +325,12 @@ struct pid *alloc_pid(struct pid_namespace *ns, pid_t *arg_set_tid, idr_preload_end(); ns_ref_active_get(ns); + retval = pidfs_add_pid(pid); + if (unlikely(retval)) { + free_pid(pid); + pid = ERR_PTR(-ENOMEM); + } + return pid; out_free: -- cgit v1.2.3 From 03aef0602f22f30aab0e42e7f3169b0a5920c461 Mon Sep 17 00:00:00 2001 From: Mateusz Guzik Date: Tue, 20 Jan 2026 21:48:20 +0100 Subject: pid: reorder fields in pid_namespace to reduce false sharing alloc_pid() loads pid_cachep, level and pid_max prior to taking the lock. It dirties idr and pid_allocated with the lock. Some of these fields share the cacheline as is, split them up. No change in the size of the struct. Signed-off-by: Mateusz Guzik Link: https://patch.msgid.link/20260120204820.1497002-1-mjguzik@gmail.com Reviewed-by: Jan Kara Signed-off-by: Christian Brauner --- include/linux/pid_namespace.h | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pid_namespace.h b/include/linux/pid_namespace.h index 0e7ae12c96d2..b20baaa7e62b 100644 --- a/include/linux/pid_namespace.h +++ b/include/linux/pid_namespace.h @@ -27,6 +27,13 @@ struct pid_namespace { struct idr idr; struct rcu_head rcu; unsigned int pid_allocated; +#ifdef CONFIG_SYSCTL +#if defined(CONFIG_MEMFD_CREATE) + int memfd_noexec_scope; +#endif + struct ctl_table_set set; + struct ctl_table_header *sysctls; +#endif struct task_struct *child_reaper; struct kmem_cache *pid_cachep; unsigned int level; @@ -40,13 +47,6 @@ struct pid_namespace { int reboot; /* group exit code if this pidns was rebooted */ struct ns_common ns; struct work_struct work; -#ifdef CONFIG_SYSCTL - struct ctl_table_set set; - struct ctl_table_header *sysctls; -#if defined(CONFIG_MEMFD_CREATE) - int memfd_noexec_scope; -#endif -#endif } __randomize_layout; extern struct pid_namespace init_pid_ns; -- cgit v1.2.3 From c4d6d7829817f762dfdce829ffd0c14ea3bad7fe Mon Sep 17 00:00:00 2001 From: Harry Yoo Date: Tue, 10 Feb 2026 13:46:41 +0900 Subject: mm/slab: allow freeing kmalloc_nolock()'d objects using kfree[_rcu]() Slab objects that are allocated with kmalloc_nolock() must be freed using kfree_nolock() because only a subset of alloc hooks are called, since kmalloc_nolock() can't spin on a lock during allocation. This imposes a limitation: such objects cannot be freed with kfree_rcu(), forcing users to work around this limitation by calling call_rcu() with a callback that frees the object using kfree_nolock(). Remove this limitation by teaching kmemleak to gracefully ignore cases when kmemleak_free() or kmemleak_ignore() is called without a prior kmemleak_alloc(). Unlike kmemleak, kfence already handles this case, because, due to its design, only a subset of allocations are served from kfence. With this change, kfree() and kfree_rcu() can be used to free objects that are allocated using kmalloc_nolock(). Suggested-by: Alexei Starovoitov Acked-by: Alexei Starovoitov Reviewed-by: Catalin Marinas Signed-off-by: Harry Yoo Link: https://patch.msgid.link/20260210044642.139482-2-harry.yoo@oracle.com Signed-off-by: Vlastimil Babka --- include/linux/rcupdate.h | 4 ++-- mm/kmemleak.c | 22 ++++++++++------------ mm/slub.c | 21 ++++++++++++++++++++- 3 files changed, 32 insertions(+), 15 deletions(-) (limited to 'include/linux') diff --git a/include/linux/rcupdate.h b/include/linux/rcupdate.h index c5b30054cd01..72ba681360ad 100644 --- a/include/linux/rcupdate.h +++ b/include/linux/rcupdate.h @@ -1076,8 +1076,8 @@ static inline void rcu_read_unlock_migrate(void) * either fall back to use of call_rcu() or rearrange the structure to * position the rcu_head structure into the first 4096 bytes. * - * The object to be freed can be allocated either by kmalloc() or - * kmem_cache_alloc(). + * The object to be freed can be allocated either by kmalloc(), + * kmalloc_nolock(), or kmem_cache_alloc(). * * Note that the allowable offset might decrease in the future. * diff --git a/mm/kmemleak.c b/mm/kmemleak.c index 1ac56ceb29b6..95ad827fcd69 100644 --- a/mm/kmemleak.c +++ b/mm/kmemleak.c @@ -837,13 +837,12 @@ static void delete_object_full(unsigned long ptr, unsigned int objflags) struct kmemleak_object *object; object = find_and_remove_object(ptr, 0, objflags); - if (!object) { -#ifdef DEBUG - kmemleak_warn("Freeing unknown object at 0x%08lx\n", - ptr); -#endif + if (!object) + /* + * kmalloc_nolock() -> kfree() calls kmemleak_free() + * without kmemleak_alloc(). + */ return; - } __delete_object(object); } @@ -926,13 +925,12 @@ static void paint_ptr(unsigned long ptr, int color, unsigned int objflags) struct kmemleak_object *object; object = __find_and_get_object(ptr, 0, objflags); - if (!object) { - kmemleak_warn("Trying to color unknown object at 0x%08lx as %s\n", - ptr, - (color == KMEMLEAK_GREY) ? "Grey" : - (color == KMEMLEAK_BLACK) ? "Black" : "Unknown"); + if (!object) + /* + * kmalloc_nolock() -> kfree_rcu() calls kmemleak_ignore() + * without kmemleak_alloc(). + */ return; - } paint_it(object, color); put_object(object); } diff --git a/mm/slub.c b/mm/slub.c index 591e41e5acc4..3f64a6b94571 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2585,6 +2585,24 @@ struct rcu_delayed_free { * Returns true if freeing of the object can proceed, false if its reuse * was delayed by CONFIG_SLUB_RCU_DEBUG or KASAN quarantine, or it was returned * to KFENCE. + * + * For objects allocated via kmalloc_nolock(), only a subset of alloc hooks + * are invoked, so some free hooks must handle asymmetric hook calls. + * + * Alloc hooks called for kmalloc_nolock(): + * - kmsan_slab_alloc() + * - kasan_slab_alloc() + * - memcg_slab_post_alloc_hook() + * - alloc_tagging_slab_alloc_hook() + * + * Free hooks that must handle missing corresponding alloc hooks: + * - kmemleak_free_recursive() + * - kfence_free() + * + * Free hooks that have no alloc hook counterpart, and thus safe to call: + * - debug_check_no_locks_freed() + * - debug_check_no_obj_freed() + * - __kcsan_check_access() */ static __always_inline bool slab_free_hook(struct kmem_cache *s, void *x, bool init, @@ -6394,7 +6412,7 @@ void kvfree_rcu_cb(struct rcu_head *head) /** * kfree - free previously allocated memory - * @object: pointer returned by kmalloc() or kmem_cache_alloc() + * @object: pointer returned by kmalloc(), kmalloc_nolock(), or kmem_cache_alloc() * * If @object is NULL, no operation is performed. */ @@ -6413,6 +6431,7 @@ void kfree(const void *object) page = virt_to_page(object); slab = page_slab(page); if (!slab) { + /* kmalloc_nolock() doesn't support large kmalloc */ free_large_kmalloc(page, (void *)object); return; } -- cgit v1.2.3 From 27125df9a5d3b4cfd03bce3a8ec405a368cc9aae Mon Sep 17 00:00:00 2001 From: Harry Yoo Date: Tue, 10 Feb 2026 13:46:42 +0900 Subject: mm/slab: drop the OBJEXTS_NOSPIN_ALLOC flag from enum objext_flags OBJEXTS_NOSPIN_ALLOC was used to remember whether a slabobj_ext vector was allocated via kmalloc_nolock(), so that free_slab_obj_exts() could call kfree_nolock() instead of kfree(). Now that kfree() supports freeing kmalloc_nolock() objects, this flag is no longer needed. Instead, pass the allow_spin parameter down to free_slab_obj_exts() to determine whether kfree_nolock() or kfree() should be called in the free path, and free one bit in enum objext_flags. Acked-by: Alexei Starovoitov Signed-off-by: Harry Yoo Reviewed-by: Hao Li Link: https://patch.msgid.link/20260210044642.139482-3-harry.yoo@oracle.com Signed-off-by: Vlastimil Babka --- include/linux/memcontrol.h | 3 +-- mm/slub.c | 18 ++++++++---------- 2 files changed, 9 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index 0651865a4564..bb789ec4a2a2 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -359,8 +359,7 @@ enum objext_flags { * MEMCG_DATA_OBJEXTS. */ OBJEXTS_ALLOC_FAIL = __OBJEXTS_ALLOC_FAIL, - /* slabobj_ext vector allocated with kmalloc_nolock() */ - OBJEXTS_NOSPIN_ALLOC = __FIRST_OBJEXT_FLAG, + __OBJEXTS_FLAG_UNUSED = __FIRST_OBJEXT_FLAG, /* the next bit after the last actual flag */ __NR_OBJEXTS_FLAGS = (__FIRST_OBJEXT_FLAG << 1), }; diff --git a/mm/slub.c b/mm/slub.c index 3f64a6b94571..18c30872d196 100644 --- a/mm/slub.c +++ b/mm/slub.c @@ -2190,8 +2190,6 @@ int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, virt_to_slab(vec)->slab_cache == s); new_exts = (unsigned long)vec; - if (unlikely(!allow_spin)) - new_exts |= OBJEXTS_NOSPIN_ALLOC; #ifdef CONFIG_MEMCG new_exts |= MEMCG_DATA_OBJEXTS; #endif @@ -2229,7 +2227,7 @@ retry: return 0; } -static inline void free_slab_obj_exts(struct slab *slab) +static inline void free_slab_obj_exts(struct slab *slab, bool allow_spin) { struct slabobj_ext *obj_exts; @@ -2257,10 +2255,10 @@ static inline void free_slab_obj_exts(struct slab *slab) * the extension for obj_exts is expected to be NULL. */ mark_objexts_empty(obj_exts); - if (unlikely(READ_ONCE(slab->obj_exts) & OBJEXTS_NOSPIN_ALLOC)) - kfree_nolock(obj_exts); - else + if (allow_spin) kfree(obj_exts); + else + kfree_nolock(obj_exts); slab->obj_exts = 0; } @@ -2324,7 +2322,7 @@ static int alloc_slab_obj_exts(struct slab *slab, struct kmem_cache *s, return 0; } -static inline void free_slab_obj_exts(struct slab *slab) +static inline void free_slab_obj_exts(struct slab *slab, bool allow_spin) { } @@ -3404,14 +3402,14 @@ static __always_inline void account_slab(struct slab *slab, int order, } static __always_inline void unaccount_slab(struct slab *slab, int order, - struct kmem_cache *s) + struct kmem_cache *s, bool allow_spin) { /* * The slab object extensions should now be freed regardless of * whether mem_alloc_profiling_enabled() or not because profiling * might have been disabled after slab->obj_exts got allocated. */ - free_slab_obj_exts(slab); + free_slab_obj_exts(slab, allow_spin); mod_node_page_state(slab_pgdat(slab), cache_vmstat_idx(s), -(PAGE_SIZE << order)); @@ -3515,7 +3513,7 @@ static void __free_slab(struct kmem_cache *s, struct slab *slab, bool allow_spin page->mapping = NULL; __ClearPageSlab(page); mm_account_reclaimed_pages(pages); - unaccount_slab(slab, order, s); + unaccount_slab(slab, order, s, allow_spin); if (allow_spin) free_frozen_pages(page, order); else -- cgit v1.2.3 From 84f90ab5d3e859cced1d7b080adc4ea562ca2eaa Mon Sep 17 00:00:00 2001 From: Oleg Nesterov Date: Wed, 15 Oct 2025 14:36:33 +0200 Subject: pid: introduce task_ppid_vnr() helper Cosmetic change. Unlike all other similar helpers task_ppid_nr_ns() doesn't have a _vnr() version; add one for consistency. Signed-off-by: Oleg Nesterov Link: https://patch.msgid.link/20251015123633.GB9456@redhat.com Signed-off-by: Christian Brauner --- fs/pidfs.c | 2 +- include/linux/pid.h | 5 +++++ 2 files changed, 6 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/fs/pidfs.c b/fs/pidfs.c index b984d0e95734..e21205c2fd12 100644 --- a/fs/pidfs.c +++ b/fs/pidfs.c @@ -446,7 +446,7 @@ static long pidfd_info(struct file *file, unsigned int cmd, unsigned long arg) * the fields are set correctly, or return ESRCH to avoid providing * incomplete information. */ - kinfo.ppid = task_ppid_nr_ns(task, NULL); + kinfo.ppid = task_ppid_vnr(task); kinfo.tgid = task_tgid_vnr(task); kinfo.pid = task_pid_vnr(task); kinfo.mask |= PIDFD_INFO_PID; diff --git a/include/linux/pid.h b/include/linux/pid.h index ce9b5cb7560b..ddaef0bbc8ba 100644 --- a/include/linux/pid.h +++ b/include/linux/pid.h @@ -310,6 +310,11 @@ static inline pid_t task_ppid_nr_ns(const struct task_struct *tsk, struct pid_na return pid; } +static inline pid_t task_ppid_vnr(const struct task_struct *tsk) +{ + return task_ppid_nr_ns(tsk, NULL); +} + static inline pid_t task_ppid_nr(const struct task_struct *tsk) { return task_ppid_nr_ns(tsk, &init_pid_ns); -- cgit v1.2.3 From 9342bf3d670b1b3d3cfc77a9dc1cd0d6574e5cc6 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Tue, 10 Feb 2026 16:17:11 -0800 Subject: configfs-tsm-report: Increase TSM_REPORT_OUTBLOB_MAX to 16MB Confidential Computing (CoCo) attestation is evolving toward standardized models such as DICE (Device Identifier Composition Engine) and Post-Quantum Cryptography (PQC), which rely on layered certificate chains and larger cryptographic signatures. A typical PQC certificate can range from 5KB to 15KB, and DICE-based architectures accumulate these certificates across multiple boot stages. In such configurations, the total attestation evidence can reach several megabytes, exceeding the current 32KB limit. Increase TSM_REPORT_OUTBLOB_MAX to 16MB to accommodate these larger certificate chains. This provides sufficient headroom to handle evolving requirements without requiring frequent updates to the limit. TSM_REPORT_OUTBLOB_MAX is used by the configfs read interface to cap the maximum allowed binary blob size for outblob, auxblob and manifestblob attributes. Hence, the per-open-file worst case memory allocation increases from 32KB to 16MB. Multiple concurrent readers multiply this cost (e.g., N readers of an M-byte blob incur NxM bytes of vmalloc-backed memory). However, allocations are performed on demand and remain proportional to the actual blob length, not the configured maximum. Reviewed-by: Fang Peter Signed-off-by: Kuppuswamy Sathyanarayanan Link: https://patch.msgid.link/20260211001712.1531955-3-sathyanarayanan.kuppuswamy@linux.intel.com Signed-off-by: Dan Williams --- include/linux/tsm.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/tsm.h b/include/linux/tsm.h index a3b7ab668eff..7f72a154b6b2 100644 --- a/include/linux/tsm.h +++ b/include/linux/tsm.h @@ -8,7 +8,7 @@ #include #define TSM_REPORT_INBLOB_MAX 64 -#define TSM_REPORT_OUTBLOB_MAX SZ_32K +#define TSM_REPORT_OUTBLOB_MAX SZ_16M /* * Privilege level is a nested permission concept to allow confidential -- cgit v1.2.3 From 7537bae8b6eb635583e0e6260f61d13ddbd52087 Mon Sep 17 00:00:00 2001 From: Kuppuswamy Sathyanarayanan Date: Mon, 9 Feb 2026 15:43:09 -0800 Subject: powercap: intel_rapl: Remove incorrect CPU check in PMU context The RAPL MSR read path incorrectly validates CPU context when called from the PMU subsystem: if (atomic) { if (unlikely(smp_processor_id() != cpu)) return -EIO; rdmsrq(ra->reg.msr, ra->value); } This check fails for package-scoped MSRs like RAPL energy counters, which are readable from any CPU within the package. The perf tool avoids hitting this check by validating against /sys/bus/event_source/devices/power/cpumask before opening events. However, turbostat does not perform this validation and may attempt reads from non-lead CPUs, causing the check to fail and return zero power values. Since package-scoped MSRs are architecturally accessible from any CPU in the package, remove the CPU matching check. Also rename 'atomic' to 'pmu_ctx' to clarify this indicates PMU context where rdmsrq() can be used directly instead of rdmsrl_safe_on_cpu(). Fixes: 748d6ba43afd ("powercap: intel_rapl: Enable MSR-based RAPL PMU support") Signed-off-by: Kuppuswamy Sathyanarayanan Tested-by: Furquim Ulisses Reviewed-by: Srinivas Pandruvada Link: https://patch.msgid.link/20260209234310.1440722-2-sathyanarayanan.kuppuswamy@linux.intel.com Signed-off-by: Rafael J. Wysocki --- drivers/powercap/intel_rapl_common.c | 6 +++--- drivers/powercap/intel_rapl_msr.c | 12 +++++------- include/linux/intel_rapl.h | 2 +- 3 files changed, 9 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/powercap/intel_rapl_common.c b/drivers/powercap/intel_rapl_common.c index 3ff6da3bf4e6..3705d0608a0f 100644 --- a/drivers/powercap/intel_rapl_common.c +++ b/drivers/powercap/intel_rapl_common.c @@ -254,7 +254,7 @@ static void rapl_init_domains(struct rapl_package *rp); static int rapl_read_data_raw(struct rapl_domain *rd, enum rapl_primitives prim, bool xlate, u64 *data, - bool atomic); + bool pmu_ctx); static int rapl_write_data_raw(struct rapl_domain *rd, enum rapl_primitives prim, unsigned long long value); @@ -832,7 +832,7 @@ prim_fixups(struct rapl_domain *rd, enum rapl_primitives prim) */ static int rapl_read_data_raw(struct rapl_domain *rd, enum rapl_primitives prim, bool xlate, u64 *data, - bool atomic) + bool pmu_ctx) { u64 value; enum rapl_primitives prim_fixed = prim_fixups(rd, prim); @@ -854,7 +854,7 @@ static int rapl_read_data_raw(struct rapl_domain *rd, ra.mask = rpi->mask; - if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, atomic)) { + if (rd->rp->priv->read_raw(get_rid(rd->rp), &ra, pmu_ctx)) { pr_debug("failed to read reg 0x%llx for %s:%s\n", ra.reg.val, rd->rp->name, rd->name); return -EIO; } diff --git a/drivers/powercap/intel_rapl_msr.c b/drivers/powercap/intel_rapl_msr.c index a2bc0a9c1e10..3d5e7f56d68a 100644 --- a/drivers/powercap/intel_rapl_msr.c +++ b/drivers/powercap/intel_rapl_msr.c @@ -110,16 +110,14 @@ static int rapl_cpu_down_prep(unsigned int cpu) return 0; } -static int rapl_msr_read_raw(int cpu, struct reg_action *ra, bool atomic) +static int rapl_msr_read_raw(int cpu, struct reg_action *ra, bool pmu_ctx) { /* - * When called from atomic-context (eg PMU event handler) - * perform MSR read directly using rdmsrq(). + * When called from PMU context, perform MSR read directly using + * rdmsrq() without IPI overhead. Package-scoped MSRs are readable + * from any CPU in the package. */ - if (atomic) { - if (unlikely(smp_processor_id() != cpu)) - return -EIO; - + if (pmu_ctx) { rdmsrq(ra->reg.msr, ra->value); goto out; } diff --git a/include/linux/intel_rapl.h b/include/linux/intel_rapl.h index f479ef5b3341..fa1f328d6712 100644 --- a/include/linux/intel_rapl.h +++ b/include/linux/intel_rapl.h @@ -152,7 +152,7 @@ struct rapl_if_priv { union rapl_reg reg_unit; union rapl_reg regs[RAPL_DOMAIN_MAX][RAPL_DOMAIN_REG_MAX]; int limits[RAPL_DOMAIN_MAX]; - int (*read_raw)(int id, struct reg_action *ra, bool atomic); + int (*read_raw)(int id, struct reg_action *ra, bool pmu_ctx); int (*write_raw)(int id, struct reg_action *ra); void *defaults; void *rpi; -- cgit v1.2.3 From 453daece381e60df20da16c49ccc6a9bc5c6515a Mon Sep 17 00:00:00 2001 From: Chaitanya Kulkarni Date: Wed, 11 Feb 2026 12:44:38 -0800 Subject: block: change return type to void Now that all the callers of __blkdev_issue_discard() have been changed to ignore its return value, change its return type from int to void. Signed-off-by: Chaitanya Kulkarni Reviewed-by: Christoph Hellwig Signed-off-by: Jens Axboe --- block/blk-lib.c | 3 +-- include/linux/blkdev.h | 2 +- 2 files changed, 2 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/block/blk-lib.c b/block/blk-lib.c index 0be3acdc3eb5..3213afc7f0d5 100644 --- a/block/blk-lib.c +++ b/block/blk-lib.c @@ -60,7 +60,7 @@ struct bio *blk_alloc_discard_bio(struct block_device *bdev, return bio; } -int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, +void __blkdev_issue_discard(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask, struct bio **biop) { struct bio *bio; @@ -68,7 +68,6 @@ int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, while ((bio = blk_alloc_discard_bio(bdev, §or, &nr_sects, gfp_mask))) *biop = bio_chain_and_submit(*biop, bio); - return 0; } EXPORT_SYMBOL(__blkdev_issue_discard); diff --git a/include/linux/blkdev.h b/include/linux/blkdev.h index 99ef8cd7673c..d463b9b5a0a5 100644 --- a/include/linux/blkdev.h +++ b/include/linux/blkdev.h @@ -1259,7 +1259,7 @@ extern void blk_io_schedule(void); int blkdev_issue_discard(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask); -int __blkdev_issue_discard(struct block_device *bdev, sector_t sector, +void __blkdev_issue_discard(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp_mask, struct bio **biop); int blkdev_issue_secure_erase(struct block_device *bdev, sector_t sector, sector_t nr_sects, gfp_t gfp); -- cgit v1.2.3 From 1aceed565ff172fc0331dd1d5e7e65139b711139 Mon Sep 17 00:00:00 2001 From: Bing Jiao Date: Wed, 14 Jan 2026 20:53:02 +0000 Subject: mm/vmscan: fix demotion targets checks in reclaim/demotion Patch series "mm/vmscan: fix demotion targets checks in reclaim/demotion", v9. This patch series addresses two issues in demote_folio_list(), can_demote(), and next_demotion_node() in reclaim/demotion. 1. demote_folio_list() and can_demote() do not correctly check demotion target against cpuset.mems_effective, which will cause (a) pages to be demoted to not-allowed nodes and (b) pages fail demotion even if the system still has allowed demotion nodes. Patch 1 fixes this bug by updating cpuset_node_allowed() and mem_cgroup_node_allowed() to return effective_mems, allowing directly logic-and operation against demotion targets. 2. next_demotion_node() returns a preferred demotion target, but it does not check the node against allowed nodes. Patch 2 ensures that next_demotion_node() filters against the allowed node mask and selects the closest demotion target to the source node. This patch (of 2): Fix two bugs in demote_folio_list() and can_demote() due to incorrect demotion target checks against cpuset.mems_effective in reclaim/demotion. Commit 7d709f49babc ("vmscan,cgroup: apply mems_effective to reclaim") introduces the cpuset.mems_effective check and applies it to can_demote(). However: 1. It does not apply this check in demote_folio_list(), which leads to situations where pages are demoted to nodes that are explicitly excluded from the task's cpuset.mems. 2. It checks only the nodes in the immediate next demotion hierarchy and does not check all allowed demotion targets in can_demote(). This can cause pages to never be demoted if the nodes in the next demotion hierarchy are not set in mems_effective. These bugs break resource isolation provided by cpuset.mems. This is visible from userspace because pages can either fail to be demoted entirely or are demoted to nodes that are not allowed in multi-tier memory systems. To address these bugs, update cpuset_node_allowed() and mem_cgroup_node_allowed() to return effective_mems, allowing directly logic-and operation against demotion targets. Also update can_demote() and demote_folio_list() accordingly. Bug 1 reproduction: Assume a system with 4 nodes, where nodes 0-1 are top-tier and nodes 2-3 are far-tier memory. All nodes have equal capacity. Test script: echo 1 > /sys/kernel/mm/numa/demotion_enabled mkdir /sys/fs/cgroup/test echo +cpuset > /sys/fs/cgroup/cgroup.subtree_control echo "0-2" > /sys/fs/cgroup/test/cpuset.mems echo $$ > /sys/fs/cgroup/test/cgroup.procs swapoff -a # Expectation: Should respect node 0-2 limit. # Observation: Node 3 shows significant allocation (MemFree drops) stress-ng --oomable --vm 1 --vm-bytes 150% --mbind 0,1 Bug 2 reproduction: Assume a system with 6 nodes, where nodes 0-2 are top-tier, node 3 is a far-tier node, and nodes 4-5 are the farthest-tier nodes. All nodes have equal capacity. Test script: echo 1 > /sys/kernel/mm/numa/demotion_enabled mkdir /sys/fs/cgroup/test echo +cpuset > /sys/fs/cgroup/cgroup.subtree_control echo "0-2,4-5" > /sys/fs/cgroup/test/cpuset.mems echo $$ > /sys/fs/cgroup/test/cgroup.procs swapoff -a # Expectation: Pages are demoted to Nodes 4-5 # Observation: No pages are demoted before oom. stress-ng --oomable --vm 1 --vm-bytes 150% --mbind 0,1,2 Link: https://lkml.kernel.org/r/20260114205305.2869796-1-bingjiao@google.com Link: https://lkml.kernel.org/r/20260114205305.2869796-2-bingjiao@google.com Fixes: 7d709f49babc ("vmscan,cgroup: apply mems_effective to reclaim") Signed-off-by: Bing Jiao Acked-by: Shakeel Butt Cc: Axel Rasmussen Cc: David Hildenbrand Cc: Gregory Price Cc: Johannes Weiner Cc: Joshua Hahn Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Muchun Song Cc: Qi Zheng Cc: Roman Gushchin Cc: Suren Baghdasaryan Cc: Tejun Heo Cc: Vlastimil Babka Cc: Waiman Long Cc: Wei Xu Cc: Yuanchu Xie Cc: Signed-off-by: Andrew Morton --- include/linux/cpuset.h | 6 +++--- include/linux/memcontrol.h | 6 +++--- kernel/cgroup/cpuset.c | 54 ++++++++++++++++++++++++++++++---------------- mm/memcontrol.c | 16 ++++++++++++-- mm/vmscan.c | 34 ++++++++++++++++++----------- 5 files changed, 78 insertions(+), 38 deletions(-) (limited to 'include/linux') diff --git a/include/linux/cpuset.h b/include/linux/cpuset.h index a98d3330385c..631577384677 100644 --- a/include/linux/cpuset.h +++ b/include/linux/cpuset.h @@ -174,7 +174,7 @@ static inline void set_mems_allowed(nodemask_t nodemask) task_unlock(current); } -extern bool cpuset_node_allowed(struct cgroup *cgroup, int nid); +extern void cpuset_nodes_allowed(struct cgroup *cgroup, nodemask_t *mask); #else /* !CONFIG_CPUSETS */ static inline bool cpusets_enabled(void) { return false; } @@ -301,9 +301,9 @@ static inline bool read_mems_allowed_retry(unsigned int seq) return false; } -static inline bool cpuset_node_allowed(struct cgroup *cgroup, int nid) +static inline void cpuset_nodes_allowed(struct cgroup *cgroup, nodemask_t *mask) { - return true; + nodes_copy(*mask, node_states[N_MEMORY]); } #endif /* !CONFIG_CPUSETS */ diff --git a/include/linux/memcontrol.h b/include/linux/memcontrol.h index ed4764e1a30e..b6c82c8f73e1 100644 --- a/include/linux/memcontrol.h +++ b/include/linux/memcontrol.h @@ -1736,7 +1736,7 @@ static inline void count_objcg_events(struct obj_cgroup *objcg, rcu_read_unlock(); } -bool mem_cgroup_node_allowed(struct mem_cgroup *memcg, int nid); +void mem_cgroup_node_filter_allowed(struct mem_cgroup *memcg, nodemask_t *mask); void mem_cgroup_show_protected_memory(struct mem_cgroup *memcg); @@ -1807,9 +1807,9 @@ static inline ino_t page_cgroup_ino(struct page *page) return 0; } -static inline bool mem_cgroup_node_allowed(struct mem_cgroup *memcg, int nid) +static inline void mem_cgroup_node_filter_allowed(struct mem_cgroup *memcg, + nodemask_t *mask) { - return true; } static inline void mem_cgroup_show_protected_memory(struct mem_cgroup *memcg) diff --git a/kernel/cgroup/cpuset.c b/kernel/cgroup/cpuset.c index 99cf37e7d491..a5fc8d904dfa 100644 --- a/kernel/cgroup/cpuset.c +++ b/kernel/cgroup/cpuset.c @@ -4423,40 +4423,58 @@ bool cpuset_current_node_allowed(int node, gfp_t gfp_mask) return allowed; } -bool cpuset_node_allowed(struct cgroup *cgroup, int nid) +/** + * cpuset_nodes_allowed - return effective_mems mask from a cgroup cpuset. + * @cgroup: pointer to struct cgroup. + * @mask: pointer to struct nodemask_t to be returned. + * + * Returns effective_mems mask from a cgroup cpuset if it is cgroup v2 and + * has cpuset subsys. Otherwise, returns node_states[N_MEMORY]. + * + * This function intentionally avoids taking the cpuset_mutex or callback_lock + * when accessing effective_mems. This is because the obtained effective_mems + * is stale immediately after the query anyway (e.g., effective_mems is updated + * immediately after releasing the lock but before returning). + * + * As a result, returned @mask may be empty because cs->effective_mems can be + * rebound during this call. Besides, nodes in @mask are not guaranteed to be + * online due to hot plugins. Callers should check the mask for validity on + * return based on its subsequent use. + **/ +void cpuset_nodes_allowed(struct cgroup *cgroup, nodemask_t *mask) { struct cgroup_subsys_state *css; struct cpuset *cs; - bool allowed; /* * In v1, mem_cgroup and cpuset are unlikely in the same hierarchy * and mems_allowed is likely to be empty even if we could get to it, - * so return true to avoid taking a global lock on the empty check. + * so return directly to avoid taking a global lock on the empty check. */ - if (!cpuset_v2()) - return true; + if (!cgroup || !cpuset_v2()) { + nodes_copy(*mask, node_states[N_MEMORY]); + return; + } css = cgroup_get_e_css(cgroup, &cpuset_cgrp_subsys); - if (!css) - return true; + if (!css) { + nodes_copy(*mask, node_states[N_MEMORY]); + return; + } /* - * Normally, accessing effective_mems would require the cpuset_mutex - * or callback_lock - but node_isset is atomic and the reference - * taken via cgroup_get_e_css is sufficient to protect css. - * - * Since this interface is intended for use by migration paths, we - * relax locking here to avoid taking global locks - while accepting - * there may be rare scenarios where the result may be innaccurate. + * The reference taken via cgroup_get_e_css is sufficient to + * protect css, but it does not imply safe accesses to effective_mems. * - * Reclaim and migration are subject to these same race conditions, and - * cannot make strong isolation guarantees, so this is acceptable. + * Normally, accessing effective_mems would require the cpuset_mutex + * or callback_lock - but the correctness of this information is stale + * immediately after the query anyway. We do not acquire the lock + * during this process to save lock contention in exchange for racing + * against mems_allowed rebinds. */ cs = container_of(css, struct cpuset, css); - allowed = node_isset(nid, cs->effective_mems); + nodes_copy(*mask, cs->effective_mems); css_put(css); - return allowed; } /** diff --git a/mm/memcontrol.c b/mm/memcontrol.c index 21d17975c4ac..007413a53b45 100644 --- a/mm/memcontrol.c +++ b/mm/memcontrol.c @@ -5593,9 +5593,21 @@ subsys_initcall(mem_cgroup_swap_init); #endif /* CONFIG_SWAP */ -bool mem_cgroup_node_allowed(struct mem_cgroup *memcg, int nid) +void mem_cgroup_node_filter_allowed(struct mem_cgroup *memcg, nodemask_t *mask) { - return memcg ? cpuset_node_allowed(memcg->css.cgroup, nid) : true; + nodemask_t allowed; + + if (!memcg) + return; + + /* + * Since this interface is intended for use by migration paths, and + * reclaim and migration are subject to race conditions such as changes + * in effective_mems and hot-unpluging of nodes, inaccurate allowed + * mask is acceptable. + */ + cpuset_nodes_allowed(memcg->css.cgroup, &allowed); + nodes_and(*mask, *mask, allowed); } void mem_cgroup_show_protected_memory(struct mem_cgroup *memcg) diff --git a/mm/vmscan.c b/mm/vmscan.c index 3fc4a4461927..911614723689 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -343,19 +343,21 @@ static void flush_reclaim_state(struct scan_control *sc) static bool can_demote(int nid, struct scan_control *sc, struct mem_cgroup *memcg) { - int demotion_nid; + struct pglist_data *pgdat = NODE_DATA(nid); + nodemask_t allowed_mask; - if (!numa_demotion_enabled) + if (!pgdat || !numa_demotion_enabled) return false; if (sc && sc->no_demotion) return false; - demotion_nid = next_demotion_node(nid); - if (demotion_nid == NUMA_NO_NODE) + node_get_allowed_targets(pgdat, &allowed_mask); + if (nodes_empty(allowed_mask)) return false; - /* If demotion node isn't in the cgroup's mems_allowed, fall back */ - return mem_cgroup_node_allowed(memcg, demotion_nid); + /* Filter out nodes that are not in cgroup's mems_allowed. */ + mem_cgroup_node_filter_allowed(memcg, &allowed_mask); + return !nodes_empty(allowed_mask); } static inline bool can_reclaim_anon_pages(struct mem_cgroup *memcg, @@ -1017,9 +1019,10 @@ static struct folio *alloc_demote_folio(struct folio *src, * Folios which are not demoted are left on @demote_folios. */ static unsigned int demote_folio_list(struct list_head *demote_folios, - struct pglist_data *pgdat) + struct pglist_data *pgdat, + struct mem_cgroup *memcg) { - int target_nid = next_demotion_node(pgdat->node_id); + int target_nid; unsigned int nr_succeeded; nodemask_t allowed_mask; @@ -1031,7 +1034,6 @@ static unsigned int demote_folio_list(struct list_head *demote_folios, */ .gfp_mask = (GFP_HIGHUSER_MOVABLE & ~__GFP_RECLAIM) | __GFP_NOMEMALLOC | GFP_NOWAIT, - .nid = target_nid, .nmask = &allowed_mask, .reason = MR_DEMOTION, }; @@ -1039,10 +1041,18 @@ static unsigned int demote_folio_list(struct list_head *demote_folios, if (list_empty(demote_folios)) return 0; - if (target_nid == NUMA_NO_NODE) + node_get_allowed_targets(pgdat, &allowed_mask); + mem_cgroup_node_filter_allowed(memcg, &allowed_mask); + if (nodes_empty(allowed_mask)) return 0; - node_get_allowed_targets(pgdat, &allowed_mask); + target_nid = next_demotion_node(pgdat->node_id); + if (target_nid == NUMA_NO_NODE) + /* No lower-tier nodes or nodes were hot-unplugged. */ + return 0; + if (!node_isset(target_nid, allowed_mask)) + target_nid = node_random(&allowed_mask); + mtc.nid = target_nid; /* Demotion ignores all cpuset and mempolicy settings */ migrate_pages(demote_folios, alloc_demote_folio, NULL, @@ -1564,7 +1574,7 @@ keep: /* 'folio_list' is always empty here */ /* Migrate folios selected for demotion */ - nr_demoted = demote_folio_list(&demote_folios, pgdat); + nr_demoted = demote_folio_list(&demote_folios, pgdat, memcg); nr_reclaimed += nr_demoted; stat->nr_demoted += nr_demoted; /* Folios that could not be demoted are still in @demote_folios */ -- cgit v1.2.3 From 7ec9ecf217f8e565577bde8a47915a51491ef3a3 Mon Sep 17 00:00:00 2001 From: Bing Jiao Date: Wed, 14 Jan 2026 20:53:03 +0000 Subject: mm/vmscan: select the closest preferred node in demote_folio_list() The preferred demotion node (migration_target_control.nid) should be the one closest to the source node to minimize migration latency. Currently, a discrepancy exists where demote_folio_list() randomly selects an allowed node if the preferred node from next_demotion_node() is not set in mems_effective. To address it, update next_demotion_node() to select a preferred target against allowed nodes; and to return the closest demotion target if all preferred nodes are not in mems_effective via next_demotion_node(). It ensures that the preferred demotion target is consistently the closest available node to the source node. [akpm@linux-foundation.org: fix comment typo, per Shakeel] Link: https://lkml.kernel.org/r/20260114205305.2869796-3-bingjiao@google.com Signed-off-by: Bing Jiao Acked-by: Shakeel Butt Cc: Axel Rasmussen Cc: David Hildenbrand Cc: Gregory Price Cc: Johannes Weiner Cc: Joshua Hahn Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Michal Hocko Cc: Mike Rapoport Cc: Muchun Song Cc: Qi Zheng Cc: Roman Gushchin Cc: Suren Baghdasaryan Cc: Tejun Heo Cc: Vlastimil Babka Cc: Waiman Long Cc: Wei Xu Cc: Yuanchu Xie Signed-off-by: Andrew Morton --- include/linux/memory-tiers.h | 6 +++--- mm/memory-tiers.c | 21 ++++++++++++++++----- mm/vmscan.c | 5 ++--- 3 files changed, 21 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/include/linux/memory-tiers.h b/include/linux/memory-tiers.h index 7a805796fcfd..96987d9d95a8 100644 --- a/include/linux/memory-tiers.h +++ b/include/linux/memory-tiers.h @@ -53,11 +53,11 @@ struct memory_dev_type *mt_find_alloc_memory_type(int adist, struct list_head *memory_types); void mt_put_memory_types(struct list_head *memory_types); #ifdef CONFIG_MIGRATION -int next_demotion_node(int node); +int next_demotion_node(int node, const nodemask_t *allowed_mask); void node_get_allowed_targets(pg_data_t *pgdat, nodemask_t *targets); bool node_is_toptier(int node); #else -static inline int next_demotion_node(int node) +static inline int next_demotion_node(int node, const nodemask_t *allowed_mask) { return NUMA_NO_NODE; } @@ -101,7 +101,7 @@ static inline void clear_node_memory_type(int node, struct memory_dev_type *memt } -static inline int next_demotion_node(int node) +static inline int next_demotion_node(int node, const nodemask_t *allowed_mask) { return NUMA_NO_NODE; } diff --git a/mm/memory-tiers.c b/mm/memory-tiers.c index 0ae8bec86346..545e34626df7 100644 --- a/mm/memory-tiers.c +++ b/mm/memory-tiers.c @@ -320,16 +320,17 @@ void node_get_allowed_targets(pg_data_t *pgdat, nodemask_t *targets) /** * next_demotion_node() - Get the next node in the demotion path * @node: The starting node to lookup the next node + * @allowed_mask: The pointer to allowed node mask * * Return: node id for next memory node in the demotion path hierarchy * from @node; NUMA_NO_NODE if @node is terminal. This does not keep * @node online or guarantee that it *continues* to be the next demotion * target. */ -int next_demotion_node(int node) +int next_demotion_node(int node, const nodemask_t *allowed_mask) { struct demotion_nodes *nd; - int target; + nodemask_t mask; if (!node_demotion) return NUMA_NO_NODE; @@ -344,6 +345,10 @@ int next_demotion_node(int node) * node_demotion[] reads need to be consistent. */ rcu_read_lock(); + /* Filter out nodes that are not in allowed_mask. */ + nodes_and(mask, nd->preferred, *allowed_mask); + rcu_read_unlock(); + /* * If there are multiple target nodes, just select one * target node randomly. @@ -356,10 +361,16 @@ int next_demotion_node(int node) * caching issue, which seems more complicated. So selecting * target node randomly seems better until now. */ - target = node_random(&nd->preferred); - rcu_read_unlock(); + if (!nodes_empty(mask)) + return node_random(&mask); - return target; + /* + * Preferred nodes are not in allowed_mask. Flip bits in + * allowed_mask as used node mask. Then, use it to get the + * closest demotion target. + */ + nodes_complement(mask, *allowed_mask); + return find_next_best_node(node, &mask); } static void disable_all_demotion_targets(void) diff --git a/mm/vmscan.c b/mm/vmscan.c index 911614723689..44e4fcd6463c 100644 --- a/mm/vmscan.c +++ b/mm/vmscan.c @@ -1046,12 +1046,11 @@ static unsigned int demote_folio_list(struct list_head *demote_folios, if (nodes_empty(allowed_mask)) return 0; - target_nid = next_demotion_node(pgdat->node_id); + target_nid = next_demotion_node(pgdat->node_id, &allowed_mask); if (target_nid == NUMA_NO_NODE) /* No lower-tier nodes or nodes were hot-unplugged. */ return 0; - if (!node_isset(target_nid, allowed_mask)) - target_nid = node_random(&allowed_mask); + mtc.nid = target_nid; /* Demotion ignores all cpuset and mempolicy settings */ -- cgit v1.2.3 From bed76bec3111c956a9756643d759d5c7a2193b37 Mon Sep 17 00:00:00 2001 From: "Liam R. Howlett" Date: Wed, 21 Jan 2026 11:49:36 -0500 Subject: mm: relocate the page table ceiling and floor definitions Patch series " Remove XA_ZERO from error recovery of dup_mmap()", v3. It is possible that the dup_mmap() call fails on allocating or setting up a vma after the maple tree of the oldmm is copied. Today, that failure point is marked by inserting an XA_ZERO entry over the failure point so that the exact location does not need to be communicated through to exit_mmap(). However, a race exists in the tear down process because the dup_mmap() drops the mmap lock before exit_mmap() can remove the partially set up vma tree. This means that other tasks may get to the mm tree and find the invalid vma pointer (since it's an XA_ZERO entry), even though the mm is marked as MMF_OOM_SKIP and MMF_UNSTABLE. To remove the race fully, the tree must be cleaned up before dropping the lock. This is accomplished by extracting the vma cleanup in exit_mmap() and changing the required functions to pass through the vma search limit. Any other tree modifications would require extra cycles which should be spent on freeing memory. This does run the risk of increasing the possibility of finding no vmas (which is already possible!) in code that isn't careful. The final four patches are to address the excessive argument lists being passed between the functions. Using the struct unmap_desc also allows some special-case code to be removed in favour of the struct setup differences. This patch (of 11): pgtables.h defines a fallback for ceiling and floor of the page tables within the CONFIG_MMU section. Moving the definitions to outside the CONFIG_MMU allows for using them in generic code. [akpm@linux-foundation.org: remove stray newline, per SeongJae] Link: https://lkml.kernel.org/r/20260121164946.2093480-1-Liam.Howlett@oracle.com Link: https://lkml.kernel.org/r/20260121164946.2093480-2-Liam.Howlett@oracle.com Signed-off-by: Liam R. Howlett Suggested-by: Lorenzo Stoakes Suggested-by: SeongJae Park Cc: Baoquan He Cc: Barry Song Cc: Chris Li Cc: David Hildenbrand Cc: Jann Horn Cc: Kairui Song Cc: Kemeng Shi Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nhat Pham Cc: Pedro Falcato Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/pgtable.h | 38 +++++++++++++++++++------------------- mm/vma_internal.h | 1 + 2 files changed, 20 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h index 827dca25c0bc..21b67d937555 100644 --- a/include/linux/pgtable.h +++ b/include/linux/pgtable.h @@ -22,25 +22,6 @@ #error CONFIG_PGTABLE_LEVELS is not consistent with __PAGETABLE_{P4D,PUD,PMD}_FOLDED #endif -/* - * On almost all architectures and configurations, 0 can be used as the - * upper ceiling to free_pgtables(): on many architectures it has the same - * effect as using TASK_SIZE. However, there is one configuration which - * must impose a more careful limit, to avoid freeing kernel pgtables. - */ -#ifndef USER_PGTABLES_CEILING -#define USER_PGTABLES_CEILING 0UL -#endif - -/* - * This defines the first usable user address. Platforms - * can override its value with custom FIRST_USER_ADDRESS - * defined in their respective . - */ -#ifndef FIRST_USER_ADDRESS -#define FIRST_USER_ADDRESS 0UL -#endif - /* * This defines the generic helper for accessing PMD page * table page. Although platforms can still override this @@ -1629,6 +1610,25 @@ void arch_sync_kernel_mappings(unsigned long start, unsigned long end); #endif /* CONFIG_MMU */ +/* + * On almost all architectures and configurations, 0 can be used as the + * upper ceiling to free_pgtables(): on many architectures it has the same + * effect as using TASK_SIZE. However, there is one configuration which + * must impose a more careful limit, to avoid freeing kernel pgtables. + */ +#ifndef USER_PGTABLES_CEILING +#define USER_PGTABLES_CEILING 0UL +#endif + +/* + * This defines the first usable user address. Platforms + * can override its value with custom FIRST_USER_ADDRESS + * defined in their respective . + */ +#ifndef FIRST_USER_ADDRESS +#define FIRST_USER_ADDRESS 0UL +#endif + /* * No-op macros that just return the current protection value. Defined here * because these macros can be used even if CONFIG_MMU is not defined. diff --git a/mm/vma_internal.h b/mm/vma_internal.h index 2f05735ff190..2da6d224c1a8 100644 --- a/mm/vma_internal.h +++ b/mm/vma_internal.h @@ -46,6 +46,7 @@ #include #include #include +#include #include #include -- cgit v1.2.3 From 0df5a8d3948da979b8ab811a692b34635e1b146d Mon Sep 17 00:00:00 2001 From: "Liam R. Howlett" Date: Wed, 21 Jan 2026 11:49:44 -0500 Subject: mm/vma: use unmap_desc in exit_mmap() and vms_clear_ptes() Convert vms_clear_ptes() to use unmap_desc to call unmap_vmas() instead of the large argument list. The UNMAP_STATE() cannot be used because the vma iterator in the vms does not point to the correct maple state (mas_detach), and the tree_end will be set incorrectly. Setting up the arguments manually avoids setting the struct up incorrectly and doing extra work to get the correct pagetable range. exit_mmap() also calls unmap_vmas() with many arguments. Using the unmap_all_init() function to set the unmap descriptor for all vmas makes this a bit easier to read. Update to the vma test code is necessary to ensure testing continues to function. No functional changes intended. Link: https://lkml.kernel.org/r/20260121164946.2093480-10-Liam.Howlett@oracle.com Signed-off-by: Liam R. Howlett Cc: Baoquan He Cc: Barry Song Cc: Chris Li Cc: David Hildenbrand Cc: David Hildenbrand Cc: Jann Horn Cc: Kairui Song Cc: Kemeng Shi Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Nhat Pham Cc: Pedro Falcato Cc: SeongJae Park Cc: Suren Baghdasaryan Cc: Vlastimil Babka Signed-off-by: Andrew Morton --- include/linux/mm.h | 4 ---- mm/internal.h | 3 +++ mm/memory.c | 20 ++++++++------------ mm/mmap.c | 4 +++- mm/vma.c | 27 ++++++++++++++++++++++----- mm/vma.h | 14 ++++++++++++++ tools/testing/vma/vma_internal.h | 6 +++--- 7 files changed, 53 insertions(+), 25 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 2c6c6d00ed73..945902d23d47 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2625,10 +2625,6 @@ static inline void zap_vma_pages(struct vm_area_struct *vma) zap_page_range_single(vma, vma->vm_start, vma->vm_end - vma->vm_start, NULL); } -void unmap_vmas(struct mmu_gather *tlb, struct ma_state *mas, - struct vm_area_struct *start_vma, unsigned long start, - unsigned long end, unsigned long tree_end); - struct mmu_notifier_range; void free_pgd_range(struct mmu_gather *tlb, unsigned long addr, diff --git a/mm/internal.h b/mm/internal.h index 2a0e42e36b48..0f3ad8665d95 100644 --- a/mm/internal.h +++ b/mm/internal.h @@ -197,6 +197,9 @@ static inline void vma_close(struct vm_area_struct *vma) } } +/* unmap_vmas is in mm/memory.c */ +void unmap_vmas(struct mmu_gather *tlb, struct unmap_desc *unmap); + #ifdef CONFIG_MMU static inline void get_anon_vma(struct anon_vma *anon_vma) diff --git a/mm/memory.c b/mm/memory.c index 6033cf6c93de..d68f8f082b1c 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2144,11 +2144,7 @@ static void unmap_single_vma(struct mmu_gather *tlb, /** * unmap_vmas - unmap a range of memory covered by a list of vma's * @tlb: address of the caller's struct mmu_gather - * @mas: the maple state - * @vma: the starting vma - * @start_addr: virtual address at which to start unmapping - * @end_addr: virtual address at which to end unmapping - * @tree_end: The maximum index to check + * @unmap: The unmap_desc * * Unmap all pages in the vma list. * @@ -2161,10 +2157,9 @@ static void unmap_single_vma(struct mmu_gather *tlb, * ensure that any thus-far unmapped pages are flushed before unmap_vmas() * drops the lock and schedules. */ -void unmap_vmas(struct mmu_gather *tlb, struct ma_state *mas, - struct vm_area_struct *vma, unsigned long start_addr, - unsigned long end_addr, unsigned long tree_end) +void unmap_vmas(struct mmu_gather *tlb, struct unmap_desc *unmap) { + struct vm_area_struct *vma; struct mmu_notifier_range range; struct zap_details details = { .zap_flags = ZAP_FLAG_DROP_MARKER | ZAP_FLAG_UNMAP, @@ -2172,16 +2167,17 @@ void unmap_vmas(struct mmu_gather *tlb, struct ma_state *mas, .even_cows = true, }; + vma = unmap->first; mmu_notifier_range_init(&range, MMU_NOTIFY_UNMAP, 0, vma->vm_mm, - start_addr, end_addr); + unmap->vma_start, unmap->vma_end); mmu_notifier_invalidate_range_start(&range); do { - unsigned long start = start_addr; - unsigned long end = end_addr; + unsigned long start = unmap->vma_start; + unsigned long end = unmap->vma_end; hugetlb_zap_begin(vma, &start, &end); unmap_single_vma(tlb, vma, start, end, &details); hugetlb_zap_end(vma, &details); - vma = mas_find(mas, tree_end - 1); + vma = mas_find(unmap->mas, unmap->tree_end - 1); } while (vma); mmu_notifier_invalidate_range_end(&range); } diff --git a/mm/mmap.c b/mm/mmap.c index 4500e61a0d5e..042b6b4b6ab8 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -1277,6 +1277,7 @@ void exit_mmap(struct mm_struct *mm) struct vm_area_struct *vma; unsigned long nr_accounted = 0; VMA_ITERATOR(vmi, mm, 0); + struct unmap_desc unmap; /* mm's last user has gone, and its about to be pulled down */ mmu_notifier_release(mm); @@ -1292,11 +1293,12 @@ void exit_mmap(struct mm_struct *mm) goto destroy; } + unmap_all_init(&unmap, &vmi, vma); flush_cache_mm(mm); tlb_gather_mmu_fullmm(&tlb, mm); /* update_hiwater_rss(mm) here? but nobody should be looking */ /* Use ULONG_MAX here to ensure all VMAs in the mm are unmapped */ - unmap_vmas(&tlb, &vmi.mas, vma, 0, ULONG_MAX, ULONG_MAX); + unmap_vmas(&tlb, &unmap); mmap_read_unlock(mm); /* diff --git a/mm/vma.c b/mm/vma.c index 75c68c74c062..b46c869d4bb0 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -480,8 +480,7 @@ void unmap_region(struct unmap_desc *unmap) tlb_gather_mmu(&tlb, mm); update_hiwater_rss(mm); - unmap_vmas(&tlb, mas, unmap->first, unmap->vma_start, unmap->vma_end, - unmap->vma_end); + unmap_vmas(&tlb, unmap); mas_set(mas, unmap->tree_reset); free_pgtables(&tlb, mas, unmap->first, unmap->pg_start, unmap->pg_end, unmap->tree_end, unmap->mm_wr_locked); @@ -1257,6 +1256,26 @@ static inline void vms_clear_ptes(struct vma_munmap_struct *vms, struct ma_state *mas_detach, bool mm_wr_locked) { struct mmu_gather tlb; + struct unmap_desc unmap = { + .mas = mas_detach, + .first = vms->vma, + /* start and end may be different if there is no prev or next vma. */ + .pg_start = vms->unmap_start, + .pg_end = vms->unmap_end, + .vma_start = vms->start, + .vma_end = vms->end, + /* + * The tree limits and reset differ from the normal case since it's a + * side-tree + */ + .tree_reset = 1, + .tree_end = vms->vma_count, + /* + * We can free page tables without write-locking mmap_lock because VMAs + * were isolated before we downgraded mmap_lock. + */ + .mm_wr_locked = mm_wr_locked, + }; if (!vms->clear_ptes) /* Nothing to do */ return; @@ -1268,9 +1287,7 @@ static inline void vms_clear_ptes(struct vma_munmap_struct *vms, mas_set(mas_detach, 1); tlb_gather_mmu(&tlb, vms->vma->vm_mm); update_hiwater_rss(vms->vma->vm_mm); - unmap_vmas(&tlb, mas_detach, vms->vma, vms->start, vms->end, - vms->vma_count); - + unmap_vmas(&tlb, &unmap); mas_set(mas_detach, 1); /* start and end may be different if there is no prev or next vma. */ free_pgtables(&tlb, mas_detach, vms->vma, vms->unmap_start, diff --git a/mm/vma.h b/mm/vma.h index cca7553c7d64..bb7fa5d2bde2 100644 --- a/mm/vma.h +++ b/mm/vma.h @@ -167,6 +167,20 @@ struct unmap_desc { bool mm_wr_locked; /* If the mmap write lock is held */ }; +static inline void unmap_all_init(struct unmap_desc *unmap, + struct vma_iterator *vmi, struct vm_area_struct *vma) +{ + unmap->mas = &vmi->mas; + unmap->first = vma; + unmap->pg_start = FIRST_USER_ADDRESS; + unmap->pg_end = USER_PGTABLES_CEILING; + unmap->vma_start = 0; + unmap->vma_end = ULONG_MAX; + unmap->tree_end = ULONG_MAX; + unmap->tree_reset = vma->vm_end; + unmap->mm_wr_locked = false; +} + #define UNMAP_STATE(name, _vmi, _vma, _vma_start, _vma_end, _prev, _next) \ struct unmap_desc name = { \ .mas = &(_vmi)->mas, \ diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h index f50b8ddee612..0b4918aac8d6 100644 --- a/tools/testing/vma/vma_internal.h +++ b/tools/testing/vma/vma_internal.h @@ -1131,9 +1131,9 @@ static inline void update_hiwater_vm(struct mm_struct *mm) { } -static inline void unmap_vmas(struct mmu_gather *tlb, struct ma_state *mas, - struct vm_area_struct *vma, unsigned long start_addr, - unsigned long end_addr, unsigned long tree_end) +struct unmap_desc; + +static inline void unmap_vmas(struct mmu_gather *tlb, struct unmap_desc *unmap) { } -- cgit v1.2.3 From a30de4c6b79a83944d0d6a54cd6ae63014b62ef7 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Thu, 22 Jan 2026 16:06:10 +0000 Subject: mm/vma: remove __private sparse decoration from vma_flags_t Patch series "mm: add bitmap VMA flag helpers and convert all mmap_prepare to use them", v2. We introduced the bitmap VMA type vma_flags_t in the aptly named commit 9ea35a25d51b ("mm: introduce VMA flags bitmap type") in order to permit future growth in VMA flags and to prevent the asinine requirement that VMA flags be available to 64-bit kernels only if they happened to use a bit number about 32-bits. This is a long-term project as there are very many users of VMA flags within the kernel that need to be updated in order to utilise this new type. In order to further this aim, this series adds a number of helper functions to enable ordinary interactions with VMA flags - that is testing, setting and clearing them. In order to make working with VMA bit numbers less cumbersome this series introduces the mk_vma_flags() helper macro which generates a vma_flags_t from a variadic parameter list, e.g.: vma_flags_t flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT); It turns out that the compiler optimises this very well to the point that this is just as efficient as using VM_xxx pre-computed bitmap values. This series then introduces the following functions: bool vma_flags_test_mask(vma_flags_t flags, vma_flags_t to_test); bool vma_flags_test_all_mask(vma_flags_t flags, vma_flags_t to_test); void vma_flags_set_mask(vma_flags_t *flags, vma_flags_t to_set); void vma_flags_clear_mask(vma_flags_t *flags, vma_flags_t to_clear); Providing means of testing any flag, testing all flags, setting, and clearing a specific vma_flags_t mask. For convenience, helper macros are provided - vma_flags_test(), vma_flags_set() and vma_flags_clear(), each of which utilise mk_vma_flags() to make these operations easier, as well as an EMPTY_VMA_FLAGS macro to make initialisation of an empty vma_flags_t value easier, e.g.: vma_flags_t flags = EMPTY_VMA_FLAGS; vma_flags_set(&flags, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT); ... if (vma_flags_test(flags, VMA_READ_BIT)) { ... } ... if (vma_flags_test_all_mask(flags, VMA_REMAP_FLAGS)) { ... } ... vma_flags_clear(&flags, VMA_READ_BIT); Since callers are often dealing with a vm_area_struct (VMA) or vm_area_desc (VMA descriptor as used in .mmap_prepare) object, this series further provides helpers for these - firstly vma_set_flags_mask() and vma_set_flags() for a VMA: vma_flags_t flags = EMPTY_VMA_FLAGS: vma_flags_set(&flags, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT); ... vma_set_flags_mask(&vma, flags); ... vma_set_flags(&vma, VMA_DONTDUMP_BIT); Note that these do NOT ensure appropriate locks are taken and assume the callers takes care of this. For VMA descriptors this series adds vma_desc_[test, set, clear]_flags_mask() and vma_desc_[test, set, clear]_flags() for a VMA descriptor, e.g.: static int foo_mmap_prepare(struct vm_area_desc *desc) { ... vma_desc_set_flags(desc, VMA_SEQ_READ_BIT); vma_desc_clear_flags(desc, VMA_RAND_READ_BIT); ... if (vma_desc_test_flags(desc, VMA_SHARED_BIT) { ... } ... } With these helpers introduced, this series then updates all mmap_prepare users to make use of the vma_flags_t vm_area_desc->vma_flags field rather than the legacy vm_flags_t vm_area_desc->vm_flags field. In order to do so, several other related functions need to be updated, with separate patches for larger changes in hugetlbfs, secretmem and shmem before finally removing vm_area_desc->vm_flags altogether. This lays the foundations for future elimination of vm_flags_t and associated defines and functionality altogether in the long run, and elimination of the use of vm_flags_t in f_op->mmap() hooks in the near term as mmap_prepare replaces these. There is a useful synergy between the VMA flags and mmap_prepare work here as with this change in place, converting f_op->mmap() to f_op->mmap_prepare naturally also converts use of vm_flags_t to vma_flags_t in all drivers which declare mmap handlers. This accounts for the majority of the users of the legacy vm_flags_*() helpers and thus a large number of drivers which need to interact with VMA flags in general. This series also updates the userland VMA tests to account for the change, and adds unit tests for these helper functions to assert that they behave as expected. In order to faciliate this change in a sensible way, the series also separates out the VMA unit tests into - code that is duplicated from the kernel that should be kept in sync, code that is customised for test purposes and code that is stubbed out. We also separate out the VMA userland tests into separate files to make it easier to manage and to provide a sensible baseline for adding the userland tests for these helpers. This patch (of 13): We need to pass around these values and access them in a way that sparse does not allow, as __private implies noderef, i.e. disallowing dereference of the value, which manifests as sparse warnings even when passed around benignly. Link: https://lkml.kernel.org/r/cover.1769097829.git.lorenzo.stoakes@oracle.com Link: https://lkml.kernel.org/r/64fa89f416f22a60ae74cfff8fd565e7677be192.1769097829.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes Reviewed-by: Pedro Falcato Reviewed-by: Liam R. Howlett Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: Damien Le Moal Cc: "Darrick J. Wong" Cc: Jarkko Sakkinen Cc: Yury Norov Cc: Chris Mason Signed-off-by: Andrew Morton --- include/linux/mm.h | 4 ++-- include/linux/mm_types.h | 14 ++++++++------ 2 files changed, 10 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 945902d23d47..c27d79f6b8c0 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -943,7 +943,7 @@ static inline void vm_flags_reset_once(struct vm_area_struct *vma, * system word. */ if (NUM_VMA_FLAG_BITS > BITS_PER_LONG) { - unsigned long *bitmap = ACCESS_PRIVATE(&vma->flags, __vma_flags); + unsigned long *bitmap = vma->flags.__vma_flags; bitmap_zero(&bitmap[1], NUM_VMA_FLAG_BITS - BITS_PER_LONG); } @@ -1006,7 +1006,7 @@ static inline bool __vma_flag_atomic_valid(struct vm_area_struct *vma, static inline void vma_flag_set_atomic(struct vm_area_struct *vma, vma_flag_t bit) { - unsigned long *bitmap = ACCESS_PRIVATE(&vma->flags, __vma_flags); + unsigned long *bitmap = vma->flags.__vma_flags; vma_assert_stabilised(vma); if (__vma_flag_atomic_valid(vma, bit)) diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 8731606d8d36..ed0e128361f7 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -866,7 +866,7 @@ struct mmap_action { #define NUM_VMA_FLAG_BITS BITS_PER_LONG typedef struct { DECLARE_BITMAP(__vma_flags, NUM_VMA_FLAG_BITS); -} __private vma_flags_t; +} vma_flags_t; /* * Describes a VMA that is about to be mmap()'ed. Drivers may choose to @@ -1059,7 +1059,7 @@ struct vm_area_struct { /* Clears all bits in the VMA flags bitmap, non-atomically. */ static inline void vma_flags_clear_all(vma_flags_t *flags) { - bitmap_zero(ACCESS_PRIVATE(flags, __vma_flags), NUM_VMA_FLAG_BITS); + bitmap_zero(flags->__vma_flags, NUM_VMA_FLAG_BITS); } /* @@ -1070,7 +1070,9 @@ static inline void vma_flags_clear_all(vma_flags_t *flags) */ static inline void vma_flags_overwrite_word(vma_flags_t *flags, unsigned long value) { - *ACCESS_PRIVATE(flags, __vma_flags) = value; + unsigned long *bitmap = flags->__vma_flags; + + bitmap[0] = value; } /* @@ -1081,7 +1083,7 @@ static inline void vma_flags_overwrite_word(vma_flags_t *flags, unsigned long va */ static inline void vma_flags_overwrite_word_once(vma_flags_t *flags, unsigned long value) { - unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags); + unsigned long *bitmap = flags->__vma_flags; WRITE_ONCE(*bitmap, value); } @@ -1089,7 +1091,7 @@ static inline void vma_flags_overwrite_word_once(vma_flags_t *flags, unsigned lo /* Update the first system word of VMA flags setting bits, non-atomically. */ static inline void vma_flags_set_word(vma_flags_t *flags, unsigned long value) { - unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags); + unsigned long *bitmap = flags->__vma_flags; *bitmap |= value; } @@ -1097,7 +1099,7 @@ static inline void vma_flags_set_word(vma_flags_t *flags, unsigned long value) /* Update the first system word of VMA flags clearing bits, non-atomically. */ static inline void vma_flags_clear_word(vma_flags_t *flags, unsigned long value) { - unsigned long *bitmap = ACCESS_PRIVATE(flags, __vma_flags); + unsigned long *bitmap = flags->__vma_flags; *bitmap &= ~value; } -- cgit v1.2.3 From e388d31257eddc1077a02ed786513d606c9e3266 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Thu, 22 Jan 2026 16:06:11 +0000 Subject: mm: rename vma_flag_test/set_atomic() to vma_test/set_atomic_flag() In order to stay consistent between functions which manipulate a vm_flags_t argument of the form of vma_flags_...() and those which manipulate a VMA (in this case the flags field of a VMA), rename vma_flag_[test/set]_atomic() to vma_[test/set]_atomic_flag(). This lays the groundwork for adding VMA flag manipulation functions in a subsequent commit. Link: https://lkml.kernel.org/r/033dcf12e819dee5064582bced9b12ea346d1607.1769097829.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes Reviewed-by: Pedro Falcato Reviewed-by: Liam R. Howlett Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: Damien Le Moal Cc: "Darrick J. Wong" Cc: Jarkko Sakkinen Cc: Yury Norov Cc: Chris Mason Cc: Pedro Falcato Signed-off-by: Andrew Morton --- include/linux/mm.h | 13 +++++-------- mm/khugepaged.c | 2 +- mm/madvise.c | 2 +- 3 files changed, 7 insertions(+), 10 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index c27d79f6b8c0..67b80f0ea225 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -987,8 +987,7 @@ static inline void vm_flags_mod(struct vm_area_struct *vma, __vm_flags_mod(vma, set, clear); } -static inline bool __vma_flag_atomic_valid(struct vm_area_struct *vma, - vma_flag_t bit) +static inline bool __vma_atomic_valid_flag(struct vm_area_struct *vma, vma_flag_t bit) { const vm_flags_t mask = BIT((__force int)bit); @@ -1003,13 +1002,12 @@ static inline bool __vma_flag_atomic_valid(struct vm_area_struct *vma, * Set VMA flag atomically. Requires only VMA/mmap read lock. Only specific * valid flags are allowed to do this. */ -static inline void vma_flag_set_atomic(struct vm_area_struct *vma, - vma_flag_t bit) +static inline void vma_set_atomic_flag(struct vm_area_struct *vma, vma_flag_t bit) { unsigned long *bitmap = vma->flags.__vma_flags; vma_assert_stabilised(vma); - if (__vma_flag_atomic_valid(vma, bit)) + if (__vma_atomic_valid_flag(vma, bit)) set_bit((__force int)bit, bitmap); } @@ -1020,10 +1018,9 @@ static inline void vma_flag_set_atomic(struct vm_area_struct *vma, * This is necessarily racey, so callers must ensure that serialisation is * achieved through some other means, or that races are permissible. */ -static inline bool vma_flag_test_atomic(struct vm_area_struct *vma, - vma_flag_t bit) +static inline bool vma_test_atomic_flag(struct vm_area_struct *vma, vma_flag_t bit) { - if (__vma_flag_atomic_valid(vma, bit)) + if (__vma_atomic_valid_flag(vma, bit)) return test_bit((__force int)bit, &vma->vm_flags); return false; diff --git a/mm/khugepaged.c b/mm/khugepaged.c index 1b8faae5b448..fa1e57fd2c46 100644 --- a/mm/khugepaged.c +++ b/mm/khugepaged.c @@ -1741,7 +1741,7 @@ static bool file_backed_vma_is_retractable(struct vm_area_struct *vma) * obtained on guard region installation after the flag is set, so this * check being performed under this lock excludes races. */ - if (vma_flag_test_atomic(vma, VMA_MAYBE_GUARD_BIT)) + if (vma_test_atomic_flag(vma, VMA_MAYBE_GUARD_BIT)) return false; return true; diff --git a/mm/madvise.c b/mm/madvise.c index 1f3040688f04..8debb2d434aa 100644 --- a/mm/madvise.c +++ b/mm/madvise.c @@ -1140,7 +1140,7 @@ static long madvise_guard_install(struct madvise_behavior *madv_behavior) * acquire an mmap/VMA write lock to read it. All remaining readers may * or may not see the flag set, but we don't care. */ - vma_flag_set_atomic(vma, VMA_MAYBE_GUARD_BIT); + vma_set_atomic_flag(vma, VMA_MAYBE_GUARD_BIT); /* * If anonymous and we are establishing page tables the VMA ought to -- cgit v1.2.3 From 1c628004e0de0383a5a56facdb0bf28a54441b5f Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Thu, 22 Jan 2026 16:06:12 +0000 Subject: mm: add mk_vma_flags() bitmap flag macro helper This patch introduces the mk_vma_flags() macro helper to allow easy manipulation of VMA flags utilising the new bitmap representation implemented of VMA flags defined by the vma_flags_t type. It is a variadic macro which provides a bitwise-or'd representation of all of each individual VMA flag specified. Note that, while we maintain VM_xxx flags for backwards compatibility until the conversion is complete, we define VMA flags of type vma_flag_t using VMA_xxx_BIT to avoid confusing the two. This helper macro therefore can be used thusly: vma_flags_t flags = mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT); Testing has demonstrated that the compiler optimises this code such that it generates the same assembly utilising this macro as it does if the flags were specified manually, for instance: vma_flags_t get_flags(void) { return mk_vma_flags(VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT); } Generates the same code as: vma_flags_t get_flags(void) { vma_flags_t flags; vma_flags_clear_all(&flags); vma_flag_set(&flags, VMA_READ_BIT); vma_flag_set(&flags, VMA_WRITE_BIT); vma_flag_set(&flags, VMA_EXEC_BIT); return flags; } And: vma_flags_t get_flags(void) { vma_flags_t flags; unsigned long *bitmap = ACCESS_PRIVATE(&flags, __vma_flags); *bitmap = 1UL << (__force int)VMA_READ_BIT; *bitmap |= 1UL << (__force int)VMA_WRITE_BIT; *bitmap |= 1UL << (__force int)VMA_EXEC_BIT; return flags; } That is: get_flags: movl $7, %eax ret Link: https://lkml.kernel.org/r/fde00df6ff7fb8c4b42cc0defa5a4924c7a1943a.1769097829.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes Suggested-by: Jason Gunthorpe Reviewed-by: Pedro Falcato Reviewed-by: Liam R. Howlett Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: Damien Le Moal Cc: "Darrick J. Wong" Cc: Jarkko Sakkinen Cc: Yury Norov Cc: Chris Mason Signed-off-by: Andrew Morton --- include/linux/mm.h | 33 +++++++++++++++++++++++++++++++++ 1 file changed, 33 insertions(+) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 67b80f0ea225..d3d10c769d6f 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -2,6 +2,7 @@ #ifndef _LINUX_MM_H #define _LINUX_MM_H +#include #include #include #include @@ -1026,6 +1027,38 @@ static inline bool vma_test_atomic_flag(struct vm_area_struct *vma, vma_flag_t b return false; } +/* Set an individual VMA flag in flags, non-atomically. */ +static inline void vma_flag_set(vma_flags_t *flags, vma_flag_t bit) +{ + unsigned long *bitmap = flags->__vma_flags; + + __set_bit((__force int)bit, bitmap); +} + +static inline vma_flags_t __mk_vma_flags(size_t count, const vma_flag_t *bits) +{ + vma_flags_t flags; + int i; + + vma_flags_clear_all(&flags); + for (i = 0; i < count; i++) + vma_flag_set(&flags, bits[i]); + return flags; +} + +/* + * Helper macro which bitwise-or combines the specified input flags into a + * vma_flags_t bitmap value. E.g.: + * + * vma_flags_t flags = mk_vma_flags(VMA_IO_BIT, VMA_PFNMAP_BIT, + * VMA_DONTEXPAND_BIT, VMA_DONTDUMP_BIT); + * + * The compiler cleverly optimises away all of the work and this ends up being + * equivalent to aggregating the values manually. + */ +#define mk_vma_flags(...) __mk_vma_flags(COUNT_ARGS(__VA_ARGS__), \ + (const vma_flag_t []){__VA_ARGS__}) + static inline void vma_set_anonymous(struct vm_area_struct *vma) { vma->vm_ops = NULL; -- cgit v1.2.3 From bae0ba7c7c0a022287d8b093da63ebcb794d77ea Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Thu, 22 Jan 2026 16:06:14 +0000 Subject: mm: add basic VMA flag operation helper functions Now we have the mk_vma_flags() macro helper which permits easy specification of any number of VMA flags, add helper functions which operate with vma_flags_t parameters. This patch provides vma_flags_test[_mask](), vma_flags_set[_mask]() and vma_flags_clear[_mask]() respectively testing, setting and clearing flags with the _mask variants accepting vma_flag_t parameters, and the non-mask variants implemented as macros which accept a list of flags. This allows us to trivially test/set/clear aggregate VMA flag values as necessary, for instance: if (vma_flags_test(&flags, VMA_READ_BIT, VMA_WRITE_BIT)) goto readwrite; vma_flags_set(&flags, VMA_READ_BIT, VMA_WRITE_BIT); vma_flags_clear(&flags, VMA_READ_BIT, VMA_WRITE_BIT); We also add a function for testing that ALL flags are set for convenience, e.g.: if (vma_flags_test_all(&flags, VMA_READ_BIT, VMA_MAYREAD_BIT)) { /* Both READ and MAYREAD flags set */ ... } The compiler generates optimal assembly for each such that they behave as if the caller were setting the bitmap flags manually. This is important for e.g. drivers which manipulate flag values rather than a VMA's specific flag values. We also add helpers for testing, setting and clearing flags for VMA's and VMA descriptors to reduce boilerplate. Also add the EMPTY_VMA_FLAGS define to aid initialisation of empty flags. Finally, update the userland VMA tests to add the helpers there so they can be utilised as part of userland testing. Link: https://lkml.kernel.org/r/885d4897d67a6a57c0b07fa182a7055ad752df11.1769097829.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes Reviewed-by: Pedro Falcato Reviewed-by: Liam R. Howlett Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: Damien Le Moal Cc: "Darrick J. Wong" Cc: Jarkko Sakkinen Cc: Yury Norov Cc: Chris Mason Signed-off-by: Andrew Morton --- include/linux/mm.h | 165 +++++++++++++++++++++++++++++++++++++++ include/linux/mm_types.h | 4 +- tools/testing/vma/vma_internal.h | 147 +++++++++++++++++++++++++++++----- 3 files changed, 295 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index d3d10c769d6f..aa99b28e7a8a 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1059,6 +1059,171 @@ static inline vma_flags_t __mk_vma_flags(size_t count, const vma_flag_t *bits) #define mk_vma_flags(...) __mk_vma_flags(COUNT_ARGS(__VA_ARGS__), \ (const vma_flag_t []){__VA_ARGS__}) +/* Test each of to_test flags in flags, non-atomically. */ +static __always_inline bool vma_flags_test_mask(const vma_flags_t *flags, + vma_flags_t to_test) +{ + const unsigned long *bitmap = flags->__vma_flags; + const unsigned long *bitmap_to_test = to_test.__vma_flags; + + return bitmap_intersects(bitmap_to_test, bitmap, NUM_VMA_FLAG_BITS); +} + +/* + * Test whether any specified VMA flag is set, e.g.: + * + * if (vma_flags_test(flags, VMA_READ_BIT, VMA_MAYREAD_BIT)) { ... } + */ +#define vma_flags_test(flags, ...) \ + vma_flags_test_mask(flags, mk_vma_flags(__VA_ARGS__)) + +/* Test that ALL of the to_test flags are set, non-atomically. */ +static __always_inline bool vma_flags_test_all_mask(const vma_flags_t *flags, + vma_flags_t to_test) +{ + const unsigned long *bitmap = flags->__vma_flags; + const unsigned long *bitmap_to_test = to_test.__vma_flags; + + return bitmap_subset(bitmap_to_test, bitmap, NUM_VMA_FLAG_BITS); +} + +/* + * Test whether ALL specified VMA flags are set, e.g.: + * + * if (vma_flags_test_all(flags, VMA_READ_BIT, VMA_MAYREAD_BIT)) { ... } + */ +#define vma_flags_test_all(flags, ...) \ + vma_flags_test_all_mask(flags, mk_vma_flags(__VA_ARGS__)) + +/* Set each of the to_set flags in flags, non-atomically. */ +static __always_inline void vma_flags_set_mask(vma_flags_t *flags, vma_flags_t to_set) +{ + unsigned long *bitmap = flags->__vma_flags; + const unsigned long *bitmap_to_set = to_set.__vma_flags; + + bitmap_or(bitmap, bitmap, bitmap_to_set, NUM_VMA_FLAG_BITS); +} + +/* + * Set all specified VMA flags, e.g.: + * + * vma_flags_set(&flags, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT); + */ +#define vma_flags_set(flags, ...) \ + vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__)) + +/* Clear all of the to-clear flags in flags, non-atomically. */ +static __always_inline void vma_flags_clear_mask(vma_flags_t *flags, vma_flags_t to_clear) +{ + unsigned long *bitmap = flags->__vma_flags; + const unsigned long *bitmap_to_clear = to_clear.__vma_flags; + + bitmap_andnot(bitmap, bitmap, bitmap_to_clear, NUM_VMA_FLAG_BITS); +} + +/* + * Clear all specified individual flags, e.g.: + * + * vma_flags_clear(&flags, VMA_READ_BIT, VMA_WRITE_BIT, VMA_EXEC_BIT); + */ +#define vma_flags_clear(flags, ...) \ + vma_flags_clear_mask(flags, mk_vma_flags(__VA_ARGS__)) + +/* + * Helper to test that ALL specified flags are set in a VMA. + * + * Note: appropriate locks must be held, this function does not acquire them for + * you. + */ +static inline bool vma_test_all_flags_mask(const struct vm_area_struct *vma, + vma_flags_t flags) +{ + return vma_flags_test_all_mask(&vma->flags, flags); +} + +/* + * Helper macro for checking that ALL specified flags are set in a VMA, e.g.: + * + * if (vma_test_all_flags(vma, VMA_READ_BIT, VMA_MAYREAD_BIT) { ... } + */ +#define vma_test_all_flags(vma, ...) \ + vma_test_all_flags_mask(vma, mk_vma_flags(__VA_ARGS__)) + +/* + * Helper to set all VMA flags in a VMA. + * + * Note: appropriate locks must be held, this function does not acquire them for + * you. + */ +static inline void vma_set_flags_mask(struct vm_area_struct *vma, + vma_flags_t flags) +{ + vma_flags_set_mask(&vma->flags, flags); +} + +/* + * Helper macro for specifying VMA flags in a VMA, e.g.: + * + * vma_set_flags(vma, VMA_IO_BIT, VMA_PFNMAP_BIT, VMA_DONTEXPAND_BIT, + * VMA_DONTDUMP_BIT); + * + * Note: appropriate locks must be held, this function does not acquire them for + * you. + */ +#define vma_set_flags(vma, ...) \ + vma_set_flags_mask(vma, mk_vma_flags(__VA_ARGS__)) + +/* Helper to test all VMA flags in a VMA descriptor. */ +static inline bool vma_desc_test_flags_mask(const struct vm_area_desc *desc, + vma_flags_t flags) +{ + return vma_flags_test_mask(&desc->vma_flags, flags); +} + +/* + * Helper macro for testing VMA flags for an input pointer to a struct + * vm_area_desc object describing a proposed VMA, e.g.: + * + * if (vma_desc_test_flags(desc, VMA_IO_BIT, VMA_PFNMAP_BIT, + * VMA_DONTEXPAND_BIT, VMA_DONTDUMP_BIT)) { ... } + */ +#define vma_desc_test_flags(desc, ...) \ + vma_desc_test_flags_mask(desc, mk_vma_flags(__VA_ARGS__)) + +/* Helper to set all VMA flags in a VMA descriptor. */ +static inline void vma_desc_set_flags_mask(struct vm_area_desc *desc, + vma_flags_t flags) +{ + vma_flags_set_mask(&desc->vma_flags, flags); +} + +/* + * Helper macro for specifying VMA flags for an input pointer to a struct + * vm_area_desc object describing a proposed VMA, e.g.: + * + * vma_desc_set_flags(desc, VMA_IO_BIT, VMA_PFNMAP_BIT, VMA_DONTEXPAND_BIT, + * VMA_DONTDUMP_BIT); + */ +#define vma_desc_set_flags(desc, ...) \ + vma_desc_set_flags_mask(desc, mk_vma_flags(__VA_ARGS__)) + +/* Helper to clear all VMA flags in a VMA descriptor. */ +static inline void vma_desc_clear_flags_mask(struct vm_area_desc *desc, + vma_flags_t flags) +{ + vma_flags_clear_mask(&desc->vma_flags, flags); +} + +/* + * Helper macro for clearing VMA flags for an input pointer to a struct + * vm_area_desc object describing a proposed VMA, e.g.: + * + * vma_desc_clear_flags(desc, VMA_IO_BIT, VMA_PFNMAP_BIT, VMA_DONTEXPAND_BIT, + * VMA_DONTDUMP_BIT); + */ +#define vma_desc_clear_flags(desc, ...) \ + vma_desc_clear_flags_mask(desc, mk_vma_flags(__VA_ARGS__)) + static inline void vma_set_anonymous(struct vm_area_struct *vma) { vma->vm_ops = NULL; diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index ed0e128361f7..9b4311cfd5e8 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -844,7 +844,7 @@ struct mmap_action { /* * If specified, this hook is invoked when an error occurred when - * attempting the selection action. + * attempting the selected action. * * The hook can return an error code in order to filter the error, but * it is not valid to clear the error here. @@ -868,6 +868,8 @@ typedef struct { DECLARE_BITMAP(__vma_flags, NUM_VMA_FLAG_BITS); } vma_flags_t; +#define EMPTY_VMA_FLAGS ((vma_flags_t){ }) + /* * Describes a VMA that is about to be mmap()'ed. Drivers may choose to * manipulate mutable fields which will cause those fields to be updated in the diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h index ca4eb563b29b..2b01794cbd61 100644 --- a/tools/testing/vma/vma_internal.h +++ b/tools/testing/vma/vma_internal.h @@ -21,7 +21,13 @@ #include +#ifdef __CONCAT +#undef __CONCAT +#endif + +#include #include +#include #include #include #include @@ -38,6 +44,8 @@ extern unsigned long dac_mmap_min_addr; #define dac_mmap_min_addr 0UL #endif +#define ACCESS_PRIVATE(p, member) ((p)->member) + #define VM_WARN_ON(_expr) (WARN_ON(_expr)) #define VM_WARN_ON_ONCE(_expr) (WARN_ON_ONCE(_expr)) #define VM_WARN_ON_VMG(_expr, _vmg) (WARN_ON(_expr)) @@ -533,6 +541,8 @@ typedef struct { DECLARE_BITMAP(__vma_flags, NUM_VMA_FLAG_BITS); } __private vma_flags_t; +#define EMPTY_VMA_FLAGS ((vma_flags_t){ }) + struct mm_struct { struct maple_tree mm_mt; int map_count; /* number of VMAs */ @@ -882,6 +892,123 @@ static inline pgprot_t vm_get_page_prot(vm_flags_t vm_flags) return __pgprot(vm_flags); } +static inline void vma_flags_clear_all(vma_flags_t *flags) +{ + bitmap_zero(flags->__vma_flags, NUM_VMA_FLAG_BITS); +} + +static inline void vma_flag_set(vma_flags_t *flags, vma_flag_t bit) +{ + unsigned long *bitmap = flags->__vma_flags; + + __set_bit((__force int)bit, bitmap); +} + +static inline vma_flags_t __mk_vma_flags(size_t count, const vma_flag_t *bits) +{ + vma_flags_t flags; + int i; + + vma_flags_clear_all(&flags); + for (i = 0; i < count; i++) + vma_flag_set(&flags, bits[i]); + return flags; +} + +#define mk_vma_flags(...) __mk_vma_flags(COUNT_ARGS(__VA_ARGS__), \ + (const vma_flag_t []){__VA_ARGS__}) + +static __always_inline bool vma_flags_test_mask(const vma_flags_t *flags, + vma_flags_t to_test) +{ + const unsigned long *bitmap = flags->__vma_flags; + const unsigned long *bitmap_to_test = to_test.__vma_flags; + + return bitmap_intersects(bitmap_to_test, bitmap, NUM_VMA_FLAG_BITS); +} + +#define vma_flags_test(flags, ...) \ + vma_flags_test_mask(flags, mk_vma_flags(__VA_ARGS__)) + +static __always_inline bool vma_flags_test_all_mask(const vma_flags_t *flags, + vma_flags_t to_test) +{ + const unsigned long *bitmap = flags->__vma_flags; + const unsigned long *bitmap_to_test = to_test.__vma_flags; + + return bitmap_subset(bitmap_to_test, bitmap, NUM_VMA_FLAG_BITS); +} + +#define vma_flags_test_all(flags, ...) \ + vma_flags_test_all_mask(flags, mk_vma_flags(__VA_ARGS__)) + +static __always_inline void vma_flags_set_mask(vma_flags_t *flags, vma_flags_t to_set) +{ + unsigned long *bitmap = flags->__vma_flags; + const unsigned long *bitmap_to_set = to_set.__vma_flags; + + bitmap_or(bitmap, bitmap, bitmap_to_set, NUM_VMA_FLAG_BITS); +} + +#define vma_flags_set(flags, ...) \ + vma_flags_set_mask(flags, mk_vma_flags(__VA_ARGS__)) + +static __always_inline void vma_flags_clear_mask(vma_flags_t *flags, vma_flags_t to_clear) +{ + unsigned long *bitmap = flags->__vma_flags; + const unsigned long *bitmap_to_clear = to_clear.__vma_flags; + + bitmap_andnot(bitmap, bitmap, bitmap_to_clear, NUM_VMA_FLAG_BITS); +} + +#define vma_flags_clear(flags, ...) \ + vma_flags_clear_mask(flags, mk_vma_flags(__VA_ARGS__)) + +static inline bool vma_test_all_flags_mask(const struct vm_area_struct *vma, + vma_flags_t flags) +{ + return vma_flags_test_all_mask(&vma->flags, flags); +} + +#define vma_test_all_flags(vma, ...) \ + vma_test_all_flags_mask(vma, mk_vma_flags(__VA_ARGS__)) + +static inline void vma_set_flags_mask(struct vm_area_struct *vma, + vma_flags_t flags) +{ + vma_flags_set_mask(&vma->flags, flags); +} + +#define vma_set_flags(vma, ...) \ + vma_set_flags_mask(vma, mk_vma_flags(__VA_ARGS__)) + +static inline bool vma_desc_test_flags_mask(const struct vm_area_desc *desc, + vma_flags_t flags) +{ + return vma_flags_test_mask(&desc->vma_flags, flags); +} + +#define vma_desc_test_flags(desc, ...) \ + vma_desc_test_flags_mask(desc, mk_vma_flags(__VA_ARGS__)) + +static inline void vma_desc_set_flags_mask(struct vm_area_desc *desc, + vma_flags_t flags) +{ + vma_flags_set_mask(&desc->vma_flags, flags); +} + +#define vma_desc_set_flags(desc, ...) \ + vma_desc_set_flags_mask(desc, mk_vma_flags(__VA_ARGS__)) + +static inline void vma_desc_clear_flags_mask(struct vm_area_desc *desc, + vma_flags_t flags) +{ + vma_flags_clear_mask(&desc->vma_flags, flags); +} + +#define vma_desc_clear_flags(desc, ...) \ + vma_desc_clear_flags_mask(desc, mk_vma_flags(__VA_ARGS__)) + static inline bool is_shared_maywrite(vm_flags_t vm_flags) { return (vm_flags & (VM_SHARED | VM_MAYWRITE)) == @@ -1540,31 +1667,11 @@ static inline void userfaultfd_unmap_complete(struct mm_struct *mm, { } -#define ACCESS_PRIVATE(p, member) ((p)->member) - -#define bitmap_size(nbits) (ALIGN(nbits, BITS_PER_LONG) / BITS_PER_BYTE) - -static __always_inline void bitmap_zero(unsigned long *dst, unsigned int nbits) -{ - unsigned int len = bitmap_size(nbits); - - if (small_const_nbits(nbits)) - *dst = 0; - else - memset(dst, 0, len); -} - static inline bool mm_flags_test(int flag, const struct mm_struct *mm) { return test_bit(flag, ACCESS_PRIVATE(&mm->flags, __mm_flags)); } -/* Clears all bits in the VMA flags bitmap, non-atomically. */ -static inline void vma_flags_clear_all(vma_flags_t *flags) -{ - bitmap_zero(ACCESS_PRIVATE(flags, __vma_flags), NUM_VMA_FLAG_BITS); -} - /* * Copy value to the first system word of VMA flags, non-atomically. * -- cgit v1.2.3 From 097e8db5e22b03d6791abc243183f597d0f76a7b Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Thu, 22 Jan 2026 16:06:15 +0000 Subject: mm: update hugetlbfs to use VMA flags on mmap_prepare In order to update all mmap_prepare users to utilising the new VMA flags type vma_flags_t and associated helper functions, we start by updating hugetlbfs which has a lot of additional logic that requires updating to make this change. This is laying the groundwork for eliminating the vm_flags_t from struct vm_area_desc and using vma_flags_t only, which further lays the ground for removing the deprecated vm_flags_t type altogether. No functional changes intended. Link: https://lkml.kernel.org/r/9226bec80c9aa3447cc2b83354f733841dba8a50.1769097829.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes Reviewed-by: Liam R. Howlett Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: Damien Le Moal Cc: "Darrick J. Wong" Cc: Jarkko Sakkinen Cc: Yury Norov Cc: Chris Mason Cc: Pedro Falcato Signed-off-by: Andrew Morton --- fs/hugetlbfs/inode.c | 14 +++++++------- include/linux/hugetlb.h | 6 +++--- include/linux/hugetlb_inline.h | 10 ++++++++++ ipc/shm.c | 12 +++++++----- mm/hugetlb.c | 22 +++++++++++----------- mm/memfd.c | 4 ++-- mm/mmap.c | 2 +- 7 files changed, 41 insertions(+), 29 deletions(-) (limited to 'include/linux') diff --git a/fs/hugetlbfs/inode.c b/fs/hugetlbfs/inode.c index 3b4c152c5c73..95a5b23b4808 100644 --- a/fs/hugetlbfs/inode.c +++ b/fs/hugetlbfs/inode.c @@ -109,7 +109,7 @@ static int hugetlbfs_file_mmap_prepare(struct vm_area_desc *desc) loff_t len, vma_len; int ret; struct hstate *h = hstate_file(file); - vm_flags_t vm_flags; + vma_flags_t vma_flags; /* * vma address alignment (but not the pgoff alignment) has @@ -119,7 +119,7 @@ static int hugetlbfs_file_mmap_prepare(struct vm_area_desc *desc) * way when do_mmap unwinds (may be important on powerpc * and ia64). */ - desc->vm_flags |= VM_HUGETLB | VM_DONTEXPAND; + vma_desc_set_flags(desc, VMA_HUGETLB_BIT, VMA_DONTEXPAND_BIT); desc->vm_ops = &hugetlb_vm_ops; /* @@ -148,23 +148,23 @@ static int hugetlbfs_file_mmap_prepare(struct vm_area_desc *desc) ret = -ENOMEM; - vm_flags = desc->vm_flags; + vma_flags = desc->vma_flags; /* * for SHM_HUGETLB, the pages are reserved in the shmget() call so skip * reserving here. Note: only for SHM hugetlbfs file, the inode * flag S_PRIVATE is set. */ if (inode->i_flags & S_PRIVATE) - vm_flags |= VM_NORESERVE; + vma_flags_set(&vma_flags, VMA_NORESERVE_BIT); if (hugetlb_reserve_pages(inode, desc->pgoff >> huge_page_order(h), len >> huge_page_shift(h), desc, - vm_flags) < 0) + vma_flags) < 0) goto out; ret = 0; - if ((desc->vm_flags & VM_WRITE) && inode->i_size < len) + if (vma_desc_test_flags(desc, VMA_WRITE_BIT) && inode->i_size < len) i_size_write(inode, len); out: inode_unlock(inode); @@ -1527,7 +1527,7 @@ static int get_hstate_idx(int page_size_log) * otherwise hugetlb_reserve_pages reserves one less hugepages than intended. */ struct file *hugetlb_file_setup(const char *name, size_t size, - vm_flags_t acctflag, int creat_flags, + vma_flags_t acctflag, int creat_flags, int page_size_log) { struct inode *inode; diff --git a/include/linux/hugetlb.h b/include/linux/hugetlb.h index 94a03591990c..4e72bf66077e 100644 --- a/include/linux/hugetlb.h +++ b/include/linux/hugetlb.h @@ -150,7 +150,7 @@ int hugetlb_mfill_atomic_pte(pte_t *dst_pte, struct folio **foliop); #endif /* CONFIG_USERFAULTFD */ long hugetlb_reserve_pages(struct inode *inode, long from, long to, - struct vm_area_desc *desc, vm_flags_t vm_flags); + struct vm_area_desc *desc, vma_flags_t vma_flags); long hugetlb_unreserve_pages(struct inode *inode, long start, long end, long freed); bool folio_isolate_hugetlb(struct folio *folio, struct list_head *list); @@ -529,7 +529,7 @@ static inline struct hugetlbfs_inode_info *HUGETLBFS_I(struct inode *inode) } extern const struct vm_operations_struct hugetlb_vm_ops; -struct file *hugetlb_file_setup(const char *name, size_t size, vm_flags_t acct, +struct file *hugetlb_file_setup(const char *name, size_t size, vma_flags_t acct, int creat_flags, int page_size_log); static inline bool is_file_hugepages(const struct file *file) @@ -545,7 +545,7 @@ static inline struct hstate *hstate_inode(struct inode *i) #define is_file_hugepages(file) false static inline struct file * -hugetlb_file_setup(const char *name, size_t size, vm_flags_t acctflag, +hugetlb_file_setup(const char *name, size_t size, vma_flags_t acctflag, int creat_flags, int page_size_log) { return ERR_PTR(-ENOSYS); diff --git a/include/linux/hugetlb_inline.h b/include/linux/hugetlb_inline.h index a27aa0162918..593f5d4e108b 100644 --- a/include/linux/hugetlb_inline.h +++ b/include/linux/hugetlb_inline.h @@ -11,6 +11,11 @@ static inline bool is_vm_hugetlb_flags(vm_flags_t vm_flags) return !!(vm_flags & VM_HUGETLB); } +static inline bool is_vma_hugetlb_flags(const vma_flags_t *flags) +{ + return vma_flags_test(flags, VMA_HUGETLB_BIT); +} + #else static inline bool is_vm_hugetlb_flags(vm_flags_t vm_flags) @@ -18,6 +23,11 @@ static inline bool is_vm_hugetlb_flags(vm_flags_t vm_flags) return false; } +static inline bool is_vma_hugetlb_flags(const vma_flags_t *flags) +{ + return false; +} + #endif static inline bool is_vm_hugetlb_page(struct vm_area_struct *vma) diff --git a/ipc/shm.c b/ipc/shm.c index 3db36773dd10..2c7379c4c647 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -707,9 +707,9 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) int error; struct shmid_kernel *shp; size_t numpages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; + const bool has_no_reserve = shmflg & SHM_NORESERVE; struct file *file; char name[13]; - vm_flags_t acctflag = 0; if (size < SHMMIN || size > ns->shm_ctlmax) return -EINVAL; @@ -738,6 +738,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) sprintf(name, "SYSV%08x", key); if (shmflg & SHM_HUGETLB) { + vma_flags_t acctflag = EMPTY_VMA_FLAGS; struct hstate *hs; size_t hugesize; @@ -749,17 +750,18 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) hugesize = ALIGN(size, huge_page_size(hs)); /* hugetlb_file_setup applies strict accounting */ - if (shmflg & SHM_NORESERVE) - acctflag = VM_NORESERVE; + if (has_no_reserve) + vma_flags_set(&acctflag, VMA_NORESERVE_BIT); file = hugetlb_file_setup(name, hugesize, acctflag, HUGETLB_SHMFS_INODE, (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK); } else { + vm_flags_t acctflag = 0; + /* * Do not allow no accounting for OVERCOMMIT_NEVER, even * if it's asked for. */ - if ((shmflg & SHM_NORESERVE) && - sysctl_overcommit_memory != OVERCOMMIT_NEVER) + if (has_no_reserve && sysctl_overcommit_memory != OVERCOMMIT_NEVER) acctflag = VM_NORESERVE; file = shmem_kernel_file_setup(name, size, acctflag); } diff --git a/mm/hugetlb.c b/mm/hugetlb.c index 0b005e944ee3..4cb3e1c86e3a 100644 --- a/mm/hugetlb.c +++ b/mm/hugetlb.c @@ -1193,16 +1193,16 @@ static void set_vma_resv_flags(struct vm_area_struct *vma, unsigned long flags) static void set_vma_desc_resv_map(struct vm_area_desc *desc, struct resv_map *map) { - VM_WARN_ON_ONCE(!is_vm_hugetlb_flags(desc->vm_flags)); - VM_WARN_ON_ONCE(desc->vm_flags & VM_MAYSHARE); + VM_WARN_ON_ONCE(!is_vma_hugetlb_flags(&desc->vma_flags)); + VM_WARN_ON_ONCE(vma_desc_test_flags(desc, VMA_MAYSHARE_BIT)); desc->private_data = map; } static void set_vma_desc_resv_flags(struct vm_area_desc *desc, unsigned long flags) { - VM_WARN_ON_ONCE(!is_vm_hugetlb_flags(desc->vm_flags)); - VM_WARN_ON_ONCE(desc->vm_flags & VM_MAYSHARE); + VM_WARN_ON_ONCE(!is_vma_hugetlb_flags(&desc->vma_flags)); + VM_WARN_ON_ONCE(vma_desc_test_flags(desc, VMA_MAYSHARE_BIT)); desc->private_data = (void *)((unsigned long)desc->private_data | flags); } @@ -1216,7 +1216,7 @@ static int is_vma_resv_set(struct vm_area_struct *vma, unsigned long flag) static bool is_vma_desc_resv_set(struct vm_area_desc *desc, unsigned long flag) { - VM_WARN_ON_ONCE(!is_vm_hugetlb_flags(desc->vm_flags)); + VM_WARN_ON_ONCE(!is_vma_hugetlb_flags(&desc->vma_flags)); return ((unsigned long)desc->private_data) & flag; } @@ -6571,7 +6571,7 @@ next: long hugetlb_reserve_pages(struct inode *inode, long from, long to, struct vm_area_desc *desc, - vm_flags_t vm_flags) + vma_flags_t vma_flags) { long chg = -1, add = -1, spool_resv, gbl_resv; struct hstate *h = hstate_inode(inode); @@ -6592,7 +6592,7 @@ long hugetlb_reserve_pages(struct inode *inode, * attempt will be made for VM_NORESERVE to allocate a page * without using reserves */ - if (vm_flags & VM_NORESERVE) + if (vma_flags_test(&vma_flags, VMA_NORESERVE_BIT)) return 0; /* @@ -6601,7 +6601,7 @@ long hugetlb_reserve_pages(struct inode *inode, * to reserve the full area even if read-only as mprotect() may be * called to make the mapping read-write. Assume !desc is a shm mapping */ - if (!desc || desc->vm_flags & VM_MAYSHARE) { + if (!desc || vma_desc_test_flags(desc, VMA_MAYSHARE_BIT)) { /* * resv_map can not be NULL as hugetlb_reserve_pages is only * called for inodes for which resv_maps were created (see @@ -6635,7 +6635,7 @@ long hugetlb_reserve_pages(struct inode *inode, if (err < 0) goto out_err; - if (desc && !(desc->vm_flags & VM_MAYSHARE) && h_cg) { + if (desc && !vma_desc_test_flags(desc, VMA_MAYSHARE_BIT) && h_cg) { /* For private mappings, the hugetlb_cgroup uncharge info hangs * of the resv_map. */ @@ -6672,7 +6672,7 @@ long hugetlb_reserve_pages(struct inode *inode, * consumed reservations are stored in the map. Hence, nothing * else has to be done for private mappings here */ - if (!desc || desc->vm_flags & VM_MAYSHARE) { + if (!desc || vma_desc_test_flags(desc, VMA_MAYSHARE_BIT)) { add = region_add(resv_map, from, to, regions_needed, h, h_cg); if (unlikely(add < 0)) { @@ -6727,7 +6727,7 @@ out_uncharge_cgroup: hugetlb_cgroup_uncharge_cgroup_rsvd(hstate_index(h), chg * pages_per_huge_page(h), h_cg); out_err: - if (!desc || desc->vm_flags & VM_MAYSHARE) + if (!desc || vma_desc_test_flags(desc, VMA_MAYSHARE_BIT)) /* Only call region_abort if the region_chg succeeded but the * region_add failed or didn't run. */ diff --git a/mm/memfd.c b/mm/memfd.c index 82a3f38aa30a..3e8f3bc4f72d 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -86,7 +86,7 @@ struct folio *memfd_alloc_folio(struct file *memfd, pgoff_t idx) gfp_mask &= ~(__GFP_HIGHMEM | __GFP_MOVABLE); idx >>= huge_page_order(h); - nr_resv = hugetlb_reserve_pages(inode, idx, idx + 1, NULL, 0); + nr_resv = hugetlb_reserve_pages(inode, idx, idx + 1, NULL, EMPTY_VMA_FLAGS); if (nr_resv < 0) return ERR_PTR(nr_resv); @@ -463,7 +463,7 @@ struct file *memfd_alloc_file(const char *name, unsigned int flags) int err = 0; if (flags & MFD_HUGETLB) { - file = hugetlb_file_setup(name, 0, VM_NORESERVE, + file = hugetlb_file_setup(name, 0, mk_vma_flags(VMA_NORESERVE_BIT), HUGETLB_ANONHUGE_INODE, (flags >> MFD_HUGE_SHIFT) & MFD_HUGE_MASK); diff --git a/mm/mmap.c b/mm/mmap.c index a03b7681e13c..c62abc616485 100644 --- a/mm/mmap.c +++ b/mm/mmap.c @@ -594,7 +594,7 @@ unsigned long ksys_mmap_pgoff(unsigned long addr, unsigned long len, * taken when vm_ops->mmap() is called */ file = hugetlb_file_setup(HUGETLB_ANON_FILE, len, - VM_NORESERVE, + mk_vma_flags(VMA_NORESERVE_BIT), HUGETLB_ANONHUGE_INODE, (flags >> MAP_HUGE_SHIFT) & MAP_HUGE_MASK); if (IS_ERR(file)) -- cgit v1.2.3 From 590d356aa433074ece2b0d02faa5f959b26d54d6 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Thu, 22 Jan 2026 16:06:17 +0000 Subject: mm: update shmem_[kernel]_file_*() functions to use vma_flags_t In order to be able to use only vma_flags_t in vm_area_desc we must adjust shmem file setup functions to operate in terms of vma_flags_t rather than vm_flags_t. This patch makes this change and updates all callers to use the new functions. No functional changes intended. [akpm@linux-foundation.org: comment fixes, per Baolin] Link: https://lkml.kernel.org/r/736febd280eb484d79cef5cf55b8a6f79ad832d2.1769097829.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes Reviewed-by: Baolin Wang Reviewed-by: Jarkko Sakkinen Reviewed-by: Liam R. Howlett Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: "Darrick J. Wong" Cc: Damien Le Moal Cc: Yury Norov Cc: Chris Mason Cc: Pedro Falcato Signed-off-by: Andrew Morton --- arch/x86/kernel/cpu/sgx/ioctl.c | 2 +- drivers/gpu/drm/drm_gem.c | 5 ++- drivers/gpu/drm/i915/gem/i915_gem_shmem.c | 2 +- drivers/gpu/drm/i915/gem/i915_gem_ttm.c | 3 +- drivers/gpu/drm/i915/gt/shmem_utils.c | 3 +- drivers/gpu/drm/ttm/tests/ttm_tt_test.c | 2 +- drivers/gpu/drm/ttm/ttm_backup.c | 3 +- drivers/gpu/drm/ttm/ttm_tt.c | 2 +- fs/xfs/scrub/xfile.c | 3 +- fs/xfs/xfs_buf_mem.c | 2 +- include/linux/shmem_fs.h | 8 ++-- ipc/shm.c | 6 +-- mm/memfd.c | 2 +- mm/shmem.c | 61 +++++++++++++++++-------------- security/keys/big_key.c | 2 +- 15 files changed, 57 insertions(+), 49 deletions(-) (limited to 'include/linux') diff --git a/arch/x86/kernel/cpu/sgx/ioctl.c b/arch/x86/kernel/cpu/sgx/ioctl.c index 9322a9287dc7..0bc36957979d 100644 --- a/arch/x86/kernel/cpu/sgx/ioctl.c +++ b/arch/x86/kernel/cpu/sgx/ioctl.c @@ -83,7 +83,7 @@ static int sgx_encl_create(struct sgx_encl *encl, struct sgx_secs *secs) encl_size = secs->size + PAGE_SIZE; backing = shmem_file_setup("SGX backing", encl_size + (encl_size >> 5), - VM_NORESERVE); + mk_vma_flags(VMA_NORESERVE_BIT)); if (IS_ERR(backing)) { ret = PTR_ERR(backing); goto err_out_shrink; diff --git a/drivers/gpu/drm/drm_gem.c b/drivers/gpu/drm/drm_gem.c index e4df43427394..be4dca2bc34e 100644 --- a/drivers/gpu/drm/drm_gem.c +++ b/drivers/gpu/drm/drm_gem.c @@ -130,14 +130,15 @@ int drm_gem_object_init_with_mnt(struct drm_device *dev, struct vfsmount *gemfs) { struct file *filp; + const vma_flags_t flags = mk_vma_flags(VMA_NORESERVE_BIT); drm_gem_private_object_init(dev, obj, size); if (gemfs) filp = shmem_file_setup_with_mnt(gemfs, "drm mm object", size, - VM_NORESERVE); + flags); else - filp = shmem_file_setup("drm mm object", size, VM_NORESERVE); + filp = shmem_file_setup("drm mm object", size, flags); if (IS_ERR(filp)) return PTR_ERR(filp); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c index 26dda55a07ff..fe1843497b27 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_shmem.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_shmem.c @@ -496,7 +496,7 @@ static int __create_shmem(struct drm_i915_private *i915, struct drm_gem_object *obj, resource_size_t size) { - unsigned long flags = VM_NORESERVE; + const vma_flags_t flags = mk_vma_flags(VMA_NORESERVE_BIT); struct file *filp; drm_gem_private_object_init(&i915->drm, obj, size); diff --git a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c index f65fe86c02b5..7b1a7d01db2b 100644 --- a/drivers/gpu/drm/i915/gem/i915_gem_ttm.c +++ b/drivers/gpu/drm/i915/gem/i915_gem_ttm.c @@ -200,7 +200,8 @@ static int i915_ttm_tt_shmem_populate(struct ttm_device *bdev, struct address_space *mapping; gfp_t mask; - filp = shmem_file_setup("i915-shmem-tt", size, VM_NORESERVE); + filp = shmem_file_setup("i915-shmem-tt", size, + mk_vma_flags(VMA_NORESERVE_BIT)); if (IS_ERR(filp)) return PTR_ERR(filp); diff --git a/drivers/gpu/drm/i915/gt/shmem_utils.c b/drivers/gpu/drm/i915/gt/shmem_utils.c index 365c4b8b04f4..5f37c699a320 100644 --- a/drivers/gpu/drm/i915/gt/shmem_utils.c +++ b/drivers/gpu/drm/i915/gt/shmem_utils.c @@ -19,7 +19,8 @@ struct file *shmem_create_from_data(const char *name, void *data, size_t len) struct file *file; int err; - file = shmem_file_setup(name, PAGE_ALIGN(len), VM_NORESERVE); + file = shmem_file_setup(name, PAGE_ALIGN(len), + mk_vma_flags(VMA_NORESERVE_BIT)); if (IS_ERR(file)) return file; diff --git a/drivers/gpu/drm/ttm/tests/ttm_tt_test.c b/drivers/gpu/drm/ttm/tests/ttm_tt_test.c index 61ec6f580b62..bd5f7d0b9b62 100644 --- a/drivers/gpu/drm/ttm/tests/ttm_tt_test.c +++ b/drivers/gpu/drm/ttm/tests/ttm_tt_test.c @@ -143,7 +143,7 @@ static void ttm_tt_fini_shmem(struct kunit *test) err = ttm_tt_init(tt, bo, 0, caching, 0); KUNIT_ASSERT_EQ(test, err, 0); - shmem = shmem_file_setup("ttm swap", BO_SIZE, 0); + shmem = shmem_file_setup("ttm swap", BO_SIZE, EMPTY_VMA_FLAGS); tt->swap_storage = shmem; ttm_tt_fini(tt); diff --git a/drivers/gpu/drm/ttm/ttm_backup.c b/drivers/gpu/drm/ttm/ttm_backup.c index 32530c75f038..6bd4c123d94c 100644 --- a/drivers/gpu/drm/ttm/ttm_backup.c +++ b/drivers/gpu/drm/ttm/ttm_backup.c @@ -178,5 +178,6 @@ EXPORT_SYMBOL_GPL(ttm_backup_bytes_avail); */ struct file *ttm_backup_shmem_create(loff_t size) { - return shmem_file_setup("ttm shmem backup", size, 0); + return shmem_file_setup("ttm shmem backup", size, + EMPTY_VMA_FLAGS); } diff --git a/drivers/gpu/drm/ttm/ttm_tt.c b/drivers/gpu/drm/ttm/ttm_tt.c index 611d20ab966d..f73a5ce87645 100644 --- a/drivers/gpu/drm/ttm/ttm_tt.c +++ b/drivers/gpu/drm/ttm/ttm_tt.c @@ -330,7 +330,7 @@ int ttm_tt_swapout(struct ttm_device *bdev, struct ttm_tt *ttm, struct page *to_page; int i, ret; - swap_storage = shmem_file_setup("ttm swap", size, 0); + swap_storage = shmem_file_setup("ttm swap", size, EMPTY_VMA_FLAGS); if (IS_ERR(swap_storage)) { pr_err("Failed allocating swap storage\n"); return PTR_ERR(swap_storage); diff --git a/fs/xfs/scrub/xfile.c b/fs/xfs/scrub/xfile.c index c753c79df203..fe0584a39f16 100644 --- a/fs/xfs/scrub/xfile.c +++ b/fs/xfs/scrub/xfile.c @@ -61,7 +61,8 @@ xfile_create( if (!xf) return -ENOMEM; - xf->file = shmem_kernel_file_setup(description, isize, VM_NORESERVE); + xf->file = shmem_kernel_file_setup(description, isize, + mk_vma_flags(VMA_NORESERVE_BIT)); if (IS_ERR(xf->file)) { error = PTR_ERR(xf->file); goto out_xfile; diff --git a/fs/xfs/xfs_buf_mem.c b/fs/xfs/xfs_buf_mem.c index dcbfa274e06d..fd6f0a5bc0ea 100644 --- a/fs/xfs/xfs_buf_mem.c +++ b/fs/xfs/xfs_buf_mem.c @@ -62,7 +62,7 @@ xmbuf_alloc( if (!btp) return -ENOMEM; - file = shmem_kernel_file_setup(descr, 0, 0); + file = shmem_kernel_file_setup(descr, 0, EMPTY_VMA_FLAGS); if (IS_ERR(file)) { error = PTR_ERR(file); goto out_free_btp; diff --git a/include/linux/shmem_fs.h b/include/linux/shmem_fs.h index e2069b3179c4..a8273b32e041 100644 --- a/include/linux/shmem_fs.h +++ b/include/linux/shmem_fs.h @@ -102,12 +102,10 @@ static inline struct shmem_inode_info *SHMEM_I(struct inode *inode) extern const struct fs_parameter_spec shmem_fs_parameters[]; extern void shmem_init(void); extern int shmem_init_fs_context(struct fs_context *fc); -extern struct file *shmem_file_setup(const char *name, - loff_t size, unsigned long flags); -extern struct file *shmem_kernel_file_setup(const char *name, loff_t size, - unsigned long flags); +struct file *shmem_file_setup(const char *name, loff_t size, vma_flags_t flags); +struct file *shmem_kernel_file_setup(const char *name, loff_t size, vma_flags_t vma_flags); extern struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt, - const char *name, loff_t size, unsigned long flags); + const char *name, loff_t size, vma_flags_t flags); int shmem_zero_setup(struct vm_area_struct *vma); int shmem_zero_setup_desc(struct vm_area_desc *desc); extern unsigned long shmem_get_unmapped_area(struct file *, unsigned long addr, diff --git a/ipc/shm.c b/ipc/shm.c index 2c7379c4c647..e8c7d1924c50 100644 --- a/ipc/shm.c +++ b/ipc/shm.c @@ -708,6 +708,7 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) struct shmid_kernel *shp; size_t numpages = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; const bool has_no_reserve = shmflg & SHM_NORESERVE; + vma_flags_t acctflag = EMPTY_VMA_FLAGS; struct file *file; char name[13]; @@ -738,7 +739,6 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) sprintf(name, "SYSV%08x", key); if (shmflg & SHM_HUGETLB) { - vma_flags_t acctflag = EMPTY_VMA_FLAGS; struct hstate *hs; size_t hugesize; @@ -755,14 +755,12 @@ static int newseg(struct ipc_namespace *ns, struct ipc_params *params) file = hugetlb_file_setup(name, hugesize, acctflag, HUGETLB_SHMFS_INODE, (shmflg >> SHM_HUGE_SHIFT) & SHM_HUGE_MASK); } else { - vm_flags_t acctflag = 0; - /* * Do not allow no accounting for OVERCOMMIT_NEVER, even * if it's asked for. */ if (has_no_reserve && sysctl_overcommit_memory != OVERCOMMIT_NEVER) - acctflag = VM_NORESERVE; + vma_flags_set(&acctflag, VMA_NORESERVE_BIT); file = shmem_kernel_file_setup(name, size, acctflag); } error = PTR_ERR(file); diff --git a/mm/memfd.c b/mm/memfd.c index 3e8f3bc4f72d..919c2a53eb96 100644 --- a/mm/memfd.c +++ b/mm/memfd.c @@ -468,7 +468,7 @@ struct file *memfd_alloc_file(const char *name, unsigned int flags) (flags >> MFD_HUGE_SHIFT) & MFD_HUGE_MASK); } else { - file = shmem_file_setup(name, 0, VM_NORESERVE); + file = shmem_file_setup(name, 0, mk_vma_flags(VMA_NORESERVE_BIT)); } if (IS_ERR(file)) return file; diff --git a/mm/shmem.c b/mm/shmem.c index b8499871e830..9a9a6e4148c9 100644 --- a/mm/shmem.c +++ b/mm/shmem.c @@ -3056,9 +3056,9 @@ static struct offset_ctx *shmem_get_offset_ctx(struct inode *inode) } static struct inode *__shmem_get_inode(struct mnt_idmap *idmap, - struct super_block *sb, - struct inode *dir, umode_t mode, - dev_t dev, unsigned long flags) + struct super_block *sb, + struct inode *dir, umode_t mode, + dev_t dev, vma_flags_t flags) { struct inode *inode; struct shmem_inode_info *info; @@ -3086,7 +3086,8 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap, spin_lock_init(&info->lock); atomic_set(&info->stop_eviction, 0); info->seals = F_SEAL_SEAL; - info->flags = (flags & VM_NORESERVE) ? SHMEM_F_NORESERVE : 0; + info->flags = vma_flags_test(&flags, VMA_NORESERVE_BIT) + ? SHMEM_F_NORESERVE : 0; info->i_crtime = inode_get_mtime(inode); info->fsflags = (dir == NULL) ? 0 : SHMEM_I(dir)->fsflags & SHMEM_FL_INHERITED; @@ -3139,7 +3140,7 @@ static struct inode *__shmem_get_inode(struct mnt_idmap *idmap, #ifdef CONFIG_TMPFS_QUOTA static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block *sb, struct inode *dir, - umode_t mode, dev_t dev, unsigned long flags) + umode_t mode, dev_t dev, vma_flags_t flags) { int err; struct inode *inode; @@ -3165,9 +3166,9 @@ errout: return ERR_PTR(err); } #else -static inline struct inode *shmem_get_inode(struct mnt_idmap *idmap, +static struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block *sb, struct inode *dir, - umode_t mode, dev_t dev, unsigned long flags) + umode_t mode, dev_t dev, vma_flags_t flags) { return __shmem_get_inode(idmap, sb, dir, mode, dev, flags); } @@ -3874,7 +3875,8 @@ shmem_mknod(struct mnt_idmap *idmap, struct inode *dir, if (!generic_ci_validate_strict_name(dir, &dentry->d_name)) return -EINVAL; - inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, dev, VM_NORESERVE); + inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, dev, + mk_vma_flags(VMA_NORESERVE_BIT)); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -3909,7 +3911,8 @@ shmem_tmpfile(struct mnt_idmap *idmap, struct inode *dir, struct inode *inode; int error; - inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, 0, VM_NORESERVE); + inode = shmem_get_inode(idmap, dir->i_sb, dir, mode, 0, + mk_vma_flags(VMA_NORESERVE_BIT)); if (IS_ERR(inode)) { error = PTR_ERR(inode); goto err_out; @@ -4106,7 +4109,7 @@ static int shmem_symlink(struct mnt_idmap *idmap, struct inode *dir, return -ENAMETOOLONG; inode = shmem_get_inode(idmap, dir->i_sb, dir, S_IFLNK | 0777, 0, - VM_NORESERVE); + mk_vma_flags(VMA_NORESERVE_BIT)); if (IS_ERR(inode)) return PTR_ERR(inode); @@ -5107,7 +5110,8 @@ static int shmem_fill_super(struct super_block *sb, struct fs_context *fc) #endif /* CONFIG_TMPFS_QUOTA */ inode = shmem_get_inode(&nop_mnt_idmap, sb, NULL, - S_IFDIR | sbinfo->mode, 0, VM_NORESERVE); + S_IFDIR | sbinfo->mode, 0, + mk_vma_flags(VMA_NORESERVE_BIT)); if (IS_ERR(inode)) { error = PTR_ERR(inode); goto failed; @@ -5807,7 +5811,7 @@ static inline void shmem_unacct_size(unsigned long flags, loff_t size) static inline struct inode *shmem_get_inode(struct mnt_idmap *idmap, struct super_block *sb, struct inode *dir, - umode_t mode, dev_t dev, unsigned long flags) + umode_t mode, dev_t dev, vma_flags_t flags) { struct inode *inode = ramfs_get_inode(sb, dir, mode, dev); return inode ? inode : ERR_PTR(-ENOSPC); @@ -5818,10 +5822,11 @@ static inline struct inode *shmem_get_inode(struct mnt_idmap *idmap, /* common code */ static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name, - loff_t size, unsigned long vm_flags, + loff_t size, vma_flags_t flags, unsigned int i_flags) { - unsigned long flags = (vm_flags & VM_NORESERVE) ? SHMEM_F_NORESERVE : 0; + const unsigned long shmem_flags = + vma_flags_test(&flags, VMA_NORESERVE_BIT) ? SHMEM_F_NORESERVE : 0; struct inode *inode; struct file *res; @@ -5834,13 +5839,13 @@ static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name, if (is_idmapped_mnt(mnt)) return ERR_PTR(-EINVAL); - if (shmem_acct_size(flags, size)) + if (shmem_acct_size(shmem_flags, size)) return ERR_PTR(-ENOMEM); inode = shmem_get_inode(&nop_mnt_idmap, mnt->mnt_sb, NULL, - S_IFREG | S_IRWXUGO, 0, vm_flags); + S_IFREG | S_IRWXUGO, 0, flags); if (IS_ERR(inode)) { - shmem_unacct_size(flags, size); + shmem_unacct_size(shmem_flags, size); return ERR_CAST(inode); } inode->i_flags |= i_flags; @@ -5863,9 +5868,10 @@ static struct file *__shmem_file_setup(struct vfsmount *mnt, const char *name, * checks are provided at the key or shm level rather than the inode. * @name: name for dentry (to be seen in /proc//maps) * @size: size to be set for the file - * @flags: VM_NORESERVE suppresses pre-accounting of the entire object size + * @flags: VMA_NORESERVE_BIT suppresses pre-accounting of the entire object size */ -struct file *shmem_kernel_file_setup(const char *name, loff_t size, unsigned long flags) +struct file *shmem_kernel_file_setup(const char *name, loff_t size, + vma_flags_t flags) { return __shmem_file_setup(shm_mnt, name, size, flags, S_PRIVATE); } @@ -5875,9 +5881,9 @@ EXPORT_SYMBOL_GPL(shmem_kernel_file_setup); * shmem_file_setup - get an unlinked file living in tmpfs * @name: name for dentry (to be seen in /proc//maps) * @size: size to be set for the file - * @flags: VM_NORESERVE suppresses pre-accounting of the entire object size + * @flags: VMA_NORESERVE_BIT suppresses pre-accounting of the entire object size */ -struct file *shmem_file_setup(const char *name, loff_t size, unsigned long flags) +struct file *shmem_file_setup(const char *name, loff_t size, vma_flags_t flags) { return __shmem_file_setup(shm_mnt, name, size, flags, 0); } @@ -5888,16 +5894,17 @@ EXPORT_SYMBOL_GPL(shmem_file_setup); * @mnt: the tmpfs mount where the file will be created * @name: name for dentry (to be seen in /proc//maps) * @size: size to be set for the file - * @flags: VM_NORESERVE suppresses pre-accounting of the entire object size + * @flags: VMA_NORESERVE_BIT suppresses pre-accounting of the entire object size */ struct file *shmem_file_setup_with_mnt(struct vfsmount *mnt, const char *name, - loff_t size, unsigned long flags) + loff_t size, vma_flags_t flags) { return __shmem_file_setup(mnt, name, size, flags, 0); } EXPORT_SYMBOL_GPL(shmem_file_setup_with_mnt); -static struct file *__shmem_zero_setup(unsigned long start, unsigned long end, vm_flags_t vm_flags) +static struct file *__shmem_zero_setup(unsigned long start, unsigned long end, + vma_flags_t flags) { loff_t size = end - start; @@ -5907,7 +5914,7 @@ static struct file *__shmem_zero_setup(unsigned long start, unsigned long end, v * accessible to the user through its mapping, use S_PRIVATE flag to * bypass file security, in the same way as shmem_kernel_file_setup(). */ - return shmem_kernel_file_setup("dev/zero", size, vm_flags); + return shmem_kernel_file_setup("dev/zero", size, flags); } /** @@ -5917,7 +5924,7 @@ static struct file *__shmem_zero_setup(unsigned long start, unsigned long end, v */ int shmem_zero_setup(struct vm_area_struct *vma) { - struct file *file = __shmem_zero_setup(vma->vm_start, vma->vm_end, vma->vm_flags); + struct file *file = __shmem_zero_setup(vma->vm_start, vma->vm_end, vma->flags); if (IS_ERR(file)) return PTR_ERR(file); @@ -5938,7 +5945,7 @@ int shmem_zero_setup(struct vm_area_struct *vma) */ int shmem_zero_setup_desc(struct vm_area_desc *desc) { - struct file *file = __shmem_zero_setup(desc->start, desc->end, desc->vm_flags); + struct file *file = __shmem_zero_setup(desc->start, desc->end, desc->vma_flags); if (IS_ERR(file)) return PTR_ERR(file); diff --git a/security/keys/big_key.c b/security/keys/big_key.c index d46862ab90d6..268f702df380 100644 --- a/security/keys/big_key.c +++ b/security/keys/big_key.c @@ -103,7 +103,7 @@ int big_key_preparse(struct key_preparsed_payload *prep) 0, enckey); /* save aligned data to file */ - file = shmem_kernel_file_setup("", enclen, 0); + file = shmem_kernel_file_setup("", enclen, EMPTY_VMA_FLAGS); if (IS_ERR(file)) { ret = PTR_ERR(file); goto err_enckey; -- cgit v1.2.3 From 5bd2c0650a9030007af5c2cf2a01dccdc67a6991 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Thu, 22 Jan 2026 16:06:18 +0000 Subject: mm: update all remaining mmap_prepare users to use vma_flags_t We will be shortly removing the vm_flags_t field from vm_area_desc so we need to update all mmap_prepare users to only use the dessc->vma_flags field. This patch achieves that and makes all ancillary changes required to make this possible. This lays the groundwork for future work to eliminate the use of vm_flags_t in vm_area_desc altogether and more broadly throughout the kernel. While we're here, we take the opportunity to replace VM_REMAP_FLAGS with VMA_REMAP_FLAGS, the vma_flags_t equivalent. No functional changes intended. Link: https://lkml.kernel.org/r/fb1f55323799f09fe6a36865b31550c9ec67c225.1769097829.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes Acked-by: Damien Le Moal [zonefs] Acked-by: "Darrick J. Wong" Acked-by: Pedro Falcato Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: Liam Howlett Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: Jarkko Sakkinen Cc: Yury Norov Cc: Chris Mason Signed-off-by: Andrew Morton --- drivers/char/mem.c | 6 +++--- drivers/dax/device.c | 10 +++++----- fs/aio.c | 2 +- fs/erofs/data.c | 5 +++-- fs/ext4/file.c | 4 ++-- fs/ntfs3/file.c | 2 +- fs/orangefs/file.c | 4 ++-- fs/ramfs/file-nommu.c | 2 +- fs/resctrl/pseudo_lock.c | 2 +- fs/romfs/mmap-nommu.c | 2 +- fs/xfs/xfs_file.c | 4 ++-- fs/zonefs/file.c | 3 ++- include/linux/dax.h | 8 ++++---- include/linux/mm.h | 24 +++++++++++++++++++----- kernel/relay.c | 2 +- mm/memory.c | 17 ++++++++--------- 16 files changed, 56 insertions(+), 41 deletions(-) (limited to 'include/linux') diff --git a/drivers/char/mem.c b/drivers/char/mem.c index 52039fae1594..cca4529431f8 100644 --- a/drivers/char/mem.c +++ b/drivers/char/mem.c @@ -306,7 +306,7 @@ static unsigned zero_mmap_capabilities(struct file *file) /* can't do an in-place private mapping if there's no MMU */ static inline int private_mapping_ok(struct vm_area_desc *desc) { - return is_nommu_shared_mapping(desc->vm_flags); + return is_nommu_shared_vma_flags(&desc->vma_flags); } #else @@ -360,7 +360,7 @@ static int mmap_mem_prepare(struct vm_area_desc *desc) desc->vm_ops = &mmap_mem_ops; - /* Remap-pfn-range will mark the range VM_IO. */ + /* Remap-pfn-range will mark the range with the I/O flag. */ mmap_action_remap_full(desc, desc->pgoff); /* We filter remap errors to -EAGAIN. */ desc->action.error_hook = mmap_filter_error; @@ -520,7 +520,7 @@ static int mmap_zero_prepare(struct vm_area_desc *desc) #ifndef CONFIG_MMU return -ENOSYS; #endif - if (desc->vm_flags & VM_SHARED) + if (vma_desc_test_flags(desc, VMA_SHARED_BIT)) return shmem_zero_setup_desc(desc); desc->action.success_hook = mmap_zero_private_success; diff --git a/drivers/dax/device.c b/drivers/dax/device.c index 22999a402e02..528e81240c4d 100644 --- a/drivers/dax/device.c +++ b/drivers/dax/device.c @@ -13,7 +13,7 @@ #include "dax-private.h" #include "bus.h" -static int __check_vma(struct dev_dax *dev_dax, vm_flags_t vm_flags, +static int __check_vma(struct dev_dax *dev_dax, vma_flags_t flags, unsigned long start, unsigned long end, struct file *file, const char *func) { @@ -24,7 +24,7 @@ static int __check_vma(struct dev_dax *dev_dax, vm_flags_t vm_flags, return -ENXIO; /* prevent private mappings from being established */ - if ((vm_flags & VM_MAYSHARE) != VM_MAYSHARE) { + if (!vma_flags_test(&flags, VMA_MAYSHARE_BIT)) { dev_info_ratelimited(dev, "%s: %s: fail, attempted private mapping\n", current->comm, func); @@ -53,7 +53,7 @@ static int __check_vma(struct dev_dax *dev_dax, vm_flags_t vm_flags, static int check_vma(struct dev_dax *dev_dax, struct vm_area_struct *vma, const char *func) { - return __check_vma(dev_dax, vma->vm_flags, vma->vm_start, vma->vm_end, + return __check_vma(dev_dax, vma->flags, vma->vm_start, vma->vm_end, vma->vm_file, func); } @@ -306,14 +306,14 @@ static int dax_mmap_prepare(struct vm_area_desc *desc) * fault time. */ id = dax_read_lock(); - rc = __check_vma(dev_dax, desc->vm_flags, desc->start, desc->end, filp, + rc = __check_vma(dev_dax, desc->vma_flags, desc->start, desc->end, filp, __func__); dax_read_unlock(id); if (rc) return rc; desc->vm_ops = &dax_vm_ops; - desc->vm_flags |= VM_HUGEPAGE; + vma_desc_set_flags(desc, VMA_HUGEPAGE_BIT); return 0; } diff --git a/fs/aio.c b/fs/aio.c index 0a23a8c0717f..59b67b8da1b2 100644 --- a/fs/aio.c +++ b/fs/aio.c @@ -394,7 +394,7 @@ static const struct vm_operations_struct aio_ring_vm_ops = { static int aio_ring_mmap_prepare(struct vm_area_desc *desc) { - desc->vm_flags |= VM_DONTEXPAND; + vma_desc_set_flags(desc, VMA_DONTEXPAND_BIT); desc->vm_ops = &aio_ring_vm_ops; return 0; } diff --git a/fs/erofs/data.c b/fs/erofs/data.c index bb13c4cb8455..e7bc29e764c6 100644 --- a/fs/erofs/data.c +++ b/fs/erofs/data.c @@ -438,11 +438,12 @@ static int erofs_file_mmap_prepare(struct vm_area_desc *desc) if (!IS_DAX(file_inode(desc->file))) return generic_file_readonly_mmap_prepare(desc); - if ((desc->vm_flags & VM_SHARED) && (desc->vm_flags & VM_MAYWRITE)) + if (vma_desc_test_flags(desc, VMA_SHARED_BIT) && + vma_desc_test_flags(desc, VMA_MAYWRITE_BIT)) return -EINVAL; desc->vm_ops = &erofs_dax_vm_ops; - desc->vm_flags |= VM_HUGEPAGE; + vma_desc_set_flags(desc, VMA_HUGEPAGE_BIT); return 0; } #else diff --git a/fs/ext4/file.c b/fs/ext4/file.c index 7a8b30932189..dfd5f4fe1647 100644 --- a/fs/ext4/file.c +++ b/fs/ext4/file.c @@ -822,13 +822,13 @@ static int ext4_file_mmap_prepare(struct vm_area_desc *desc) * We don't support synchronous mappings for non-DAX files and * for DAX files if underneath dax_device is not synchronous. */ - if (!daxdev_mapping_supported(desc->vm_flags, file_inode(file), dax_dev)) + if (!daxdev_mapping_supported(desc, file_inode(file), dax_dev)) return -EOPNOTSUPP; file_accessed(file); if (IS_DAX(file_inode(file))) { desc->vm_ops = &ext4_dax_vm_ops; - desc->vm_flags |= VM_HUGEPAGE; + vma_desc_set_flags(desc, VMA_HUGEPAGE_BIT); } else { desc->vm_ops = &ext4_file_vm_ops; } diff --git a/fs/ntfs3/file.c b/fs/ntfs3/file.c index 2e7b2e566ebe..2902fc6d9a85 100644 --- a/fs/ntfs3/file.c +++ b/fs/ntfs3/file.c @@ -347,7 +347,7 @@ static int ntfs_file_mmap_prepare(struct vm_area_desc *desc) struct inode *inode = file_inode(file); struct ntfs_inode *ni = ntfs_i(inode); u64 from = ((u64)desc->pgoff << PAGE_SHIFT); - bool rw = desc->vm_flags & VM_WRITE; + const bool rw = vma_desc_test_flags(desc, VMA_WRITE_BIT); int err; /* Avoid any operation if inode is bad. */ diff --git a/fs/orangefs/file.c b/fs/orangefs/file.c index 919f99b16834..c75aa3f419b1 100644 --- a/fs/orangefs/file.c +++ b/fs/orangefs/file.c @@ -411,8 +411,8 @@ static int orangefs_file_mmap_prepare(struct vm_area_desc *desc) "orangefs_file_mmap: called on %pD\n", file); /* set the sequential readahead hint */ - desc->vm_flags |= VM_SEQ_READ; - desc->vm_flags &= ~VM_RAND_READ; + vma_desc_set_flags(desc, VMA_SEQ_READ_BIT); + vma_desc_clear_flags(desc, VMA_RAND_READ_BIT); file_accessed(file); desc->vm_ops = &orangefs_file_vm_ops; diff --git a/fs/ramfs/file-nommu.c b/fs/ramfs/file-nommu.c index 77b8ca2757e0..0f8e838ece07 100644 --- a/fs/ramfs/file-nommu.c +++ b/fs/ramfs/file-nommu.c @@ -264,7 +264,7 @@ out: */ static int ramfs_nommu_mmap_prepare(struct vm_area_desc *desc) { - if (!is_nommu_shared_mapping(desc->vm_flags)) + if (!is_nommu_shared_vma_flags(&desc->vma_flags)) return -ENOSYS; file_accessed(desc->file); diff --git a/fs/resctrl/pseudo_lock.c b/fs/resctrl/pseudo_lock.c index 0bfc13c5b96d..e81d71abfe54 100644 --- a/fs/resctrl/pseudo_lock.c +++ b/fs/resctrl/pseudo_lock.c @@ -1044,7 +1044,7 @@ static int pseudo_lock_dev_mmap_prepare(struct vm_area_desc *desc) * Ensure changes are carried directly to the memory being mapped, * do not allow copy-on-write mapping. */ - if (!(desc->vm_flags & VM_SHARED)) { + if (!vma_desc_test_flags(desc, VMA_SHARED_BIT)) { mutex_unlock(&rdtgroup_mutex); return -EINVAL; } diff --git a/fs/romfs/mmap-nommu.c b/fs/romfs/mmap-nommu.c index 4b77c6dc4418..7c3a1a7fecee 100644 --- a/fs/romfs/mmap-nommu.c +++ b/fs/romfs/mmap-nommu.c @@ -63,7 +63,7 @@ static unsigned long romfs_get_unmapped_area(struct file *file, */ static int romfs_mmap_prepare(struct vm_area_desc *desc) { - return is_nommu_shared_mapping(desc->vm_flags) ? 0 : -ENOSYS; + return is_nommu_shared_vma_flags(&desc->vma_flags) ? 0 : -ENOSYS; } static unsigned romfs_mmap_capabilities(struct file *file) diff --git a/fs/xfs/xfs_file.c b/fs/xfs/xfs_file.c index 7874cf745af3..1238ec018bc7 100644 --- a/fs/xfs/xfs_file.c +++ b/fs/xfs/xfs_file.c @@ -1974,14 +1974,14 @@ xfs_file_mmap_prepare( * We don't support synchronous mappings for non-DAX files and * for DAX files if underneath dax_device is not synchronous. */ - if (!daxdev_mapping_supported(desc->vm_flags, file_inode(file), + if (!daxdev_mapping_supported(desc, file_inode(file), target->bt_daxdev)) return -EOPNOTSUPP; file_accessed(file); desc->vm_ops = &xfs_file_vm_ops; if (IS_DAX(inode)) - desc->vm_flags |= VM_HUGEPAGE; + vma_desc_set_flags(desc, VMA_HUGEPAGE_BIT); return 0; } diff --git a/fs/zonefs/file.c b/fs/zonefs/file.c index c1e5e30e90a0..8a7161fc49e5 100644 --- a/fs/zonefs/file.c +++ b/fs/zonefs/file.c @@ -333,7 +333,8 @@ static int zonefs_file_mmap_prepare(struct vm_area_desc *desc) * ordering between msync() and page cache writeback. */ if (zonefs_inode_is_seq(file_inode(file)) && - (desc->vm_flags & VM_SHARED) && (desc->vm_flags & VM_MAYWRITE)) + vma_desc_test_flags(desc, VMA_SHARED_BIT) && + vma_desc_test_flags(desc, VMA_MAYWRITE_BIT)) return -EINVAL; file_accessed(file); diff --git a/include/linux/dax.h b/include/linux/dax.h index 9d624f4d9df6..bf103f317cac 100644 --- a/include/linux/dax.h +++ b/include/linux/dax.h @@ -65,11 +65,11 @@ size_t dax_recovery_write(struct dax_device *dax_dev, pgoff_t pgoff, /* * Check if given mapping is supported by the file / underlying device. */ -static inline bool daxdev_mapping_supported(vm_flags_t vm_flags, +static inline bool daxdev_mapping_supported(const struct vm_area_desc *desc, const struct inode *inode, struct dax_device *dax_dev) { - if (!(vm_flags & VM_SYNC)) + if (!vma_desc_test_flags(desc, VMA_SYNC_BIT)) return true; if (!IS_DAX(inode)) return false; @@ -111,11 +111,11 @@ static inline void set_dax_nomc(struct dax_device *dax_dev) static inline void set_dax_synchronous(struct dax_device *dax_dev) { } -static inline bool daxdev_mapping_supported(vm_flags_t vm_flags, +static inline bool daxdev_mapping_supported(const struct vm_area_desc *desc, const struct inode *inode, struct dax_device *dax_dev) { - return !(vm_flags & VM_SYNC); + return !vma_desc_test_flags(desc, VMA_SYNC_BIT); } static inline size_t dax_recovery_write(struct dax_device *dax_dev, pgoff_t pgoff, void *addr, size_t bytes, struct iov_iter *i) diff --git a/include/linux/mm.h b/include/linux/mm.h index aa99b28e7a8a..05d950805701 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -550,17 +550,18 @@ enum { /* * Physically remapped pages are special. Tell the * rest of the world about it: - * VM_IO tells people not to look at these pages + * IO tells people not to look at these pages * (accesses can have side effects). - * VM_PFNMAP tells the core MM that the base pages are just + * PFNMAP tells the core MM that the base pages are just * raw PFN mappings, and do not have a "struct page" associated * with them. - * VM_DONTEXPAND + * DONTEXPAND * Disable vma merging and expanding with mremap(). - * VM_DONTDUMP + * DONTDUMP * Omit vma from core dump, even when VM_IO turned off. */ -#define VM_REMAP_FLAGS (VM_IO | VM_PFNMAP | VM_DONTEXPAND | VM_DONTDUMP) +#define VMA_REMAP_FLAGS mk_vma_flags(VMA_IO_BIT, VMA_PFNMAP_BIT, \ + VMA_DONTEXPAND_BIT, VMA_DONTDUMP_BIT) /* This mask prevents VMA from being scanned with khugepaged */ #define VM_NO_KHUGEPAGED (VM_SPECIAL | VM_HUGETLB) @@ -1925,6 +1926,14 @@ static inline bool is_cow_mapping(vm_flags_t flags) return (flags & (VM_SHARED | VM_MAYWRITE)) == VM_MAYWRITE; } +static inline bool vma_desc_is_cow_mapping(struct vm_area_desc *desc) +{ + const vma_flags_t *flags = &desc->vma_flags; + + return vma_flags_test(flags, VMA_MAYWRITE_BIT) && + !vma_flags_test(flags, VMA_SHARED_BIT); +} + #ifndef CONFIG_MMU static inline bool is_nommu_shared_mapping(vm_flags_t flags) { @@ -1938,6 +1947,11 @@ static inline bool is_nommu_shared_mapping(vm_flags_t flags) */ return flags & (VM_MAYSHARE | VM_MAYOVERLAY); } + +static inline bool is_nommu_shared_vma_flags(const vma_flags_t *flags) +{ + return vma_flags_test(flags, VMA_MAYSHARE_BIT, VMA_MAYOVERLAY_BIT); +} #endif #if defined(CONFIG_SPARSEMEM) && !defined(CONFIG_SPARSEMEM_VMEMMAP) diff --git a/kernel/relay.c b/kernel/relay.c index e36f6b926f7f..1c8e88259df0 100644 --- a/kernel/relay.c +++ b/kernel/relay.c @@ -92,7 +92,7 @@ static int relay_mmap_prepare_buf(struct rchan_buf *buf, return -EINVAL; desc->vm_ops = &relay_file_mmap_ops; - desc->vm_flags |= VM_DONTEXPAND; + vma_desc_set_flags(desc, VMA_DONTEXPAND_BIT); desc->private_data = buf; return 0; diff --git a/mm/memory.c b/mm/memory.c index 136b80ca357b..9ee60d87969b 100644 --- a/mm/memory.c +++ b/mm/memory.c @@ -2957,7 +2957,7 @@ static inline int remap_p4d_range(struct mm_struct *mm, pgd_t *pgd, return 0; } -static int get_remap_pgoff(vm_flags_t vm_flags, unsigned long addr, +static int get_remap_pgoff(bool is_cow, unsigned long addr, unsigned long end, unsigned long vm_start, unsigned long vm_end, unsigned long pfn, pgoff_t *vm_pgoff_p) { @@ -2967,7 +2967,7 @@ static int get_remap_pgoff(vm_flags_t vm_flags, unsigned long addr, * un-COW'ed pages by matching them up with "vma->vm_pgoff". * See vm_normal_page() for details. */ - if (is_cow_mapping(vm_flags)) { + if (is_cow) { if (addr != vm_start || end != vm_end) return -EINVAL; *vm_pgoff_p = pfn; @@ -2988,7 +2988,7 @@ static int remap_pfn_range_internal(struct vm_area_struct *vma, unsigned long ad if (WARN_ON_ONCE(!PAGE_ALIGNED(addr))) return -EINVAL; - VM_WARN_ON_ONCE((vma->vm_flags & VM_REMAP_FLAGS) != VM_REMAP_FLAGS); + VM_WARN_ON_ONCE(!vma_test_all_flags_mask(vma, VMA_REMAP_FLAGS)); BUG_ON(addr >= end); pfn -= addr >> PAGE_SHIFT; @@ -3112,9 +3112,9 @@ void remap_pfn_range_prepare(struct vm_area_desc *desc, unsigned long pfn) * check it again on complete and will fail there if specified addr is * invalid. */ - get_remap_pgoff(desc->vm_flags, desc->start, desc->end, + get_remap_pgoff(vma_desc_is_cow_mapping(desc), desc->start, desc->end, desc->start, desc->end, pfn, &desc->pgoff); - desc->vm_flags |= VM_REMAP_FLAGS; + vma_desc_set_flags_mask(desc, VMA_REMAP_FLAGS); } static int remap_pfn_range_prepare_vma(struct vm_area_struct *vma, unsigned long addr, @@ -3123,13 +3123,12 @@ static int remap_pfn_range_prepare_vma(struct vm_area_struct *vma, unsigned long unsigned long end = addr + PAGE_ALIGN(size); int err; - err = get_remap_pgoff(vma->vm_flags, addr, end, - vma->vm_start, vma->vm_end, - pfn, &vma->vm_pgoff); + err = get_remap_pgoff(is_cow_mapping(vma->vm_flags), addr, end, + vma->vm_start, vma->vm_end, pfn, &vma->vm_pgoff); if (err) return err; - vm_flags_set(vma, VM_REMAP_FLAGS); + vma_set_flags_mask(vma, VMA_REMAP_FLAGS); return 0; } -- cgit v1.2.3 From 53f1d936445131cb5da2212c2b60884a25cb0330 Mon Sep 17 00:00:00 2001 From: Lorenzo Stoakes Date: Thu, 22 Jan 2026 16:06:19 +0000 Subject: mm: make vm_area_desc utilise vma_flags_t only Now we have eliminated all uses of vm_area_desc->vm_flags, eliminate this field, and have mmap_prepare users utilise the vma_flags_t vm_area_desc->vma_flags field only. As part of this change we alter is_shared_maywrite() to accept a vma_flags_t parameter, and introduce is_shared_maywrite_vm_flags() for use with legacy vm_flags_t flags. We also update struct mmap_state to add a union between vma_flags and vm_flags temporarily until the mmap logic is also converted to using vma_flags_t. Also update the VMA userland tests to reflect this change. Link: https://lkml.kernel.org/r/fd2a2938b246b4505321954062b1caba7acfc77a.1769097829.git.lorenzo.stoakes@oracle.com Signed-off-by: Lorenzo Stoakes Reviewed-by: Pedro Falcato Reviewed-by: Liam R. Howlett Cc: Baolin Wang Cc: Barry Song Cc: David Hildenbrand Cc: Dev Jain Cc: Jason Gunthorpe Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Zi Yan Cc: Damien Le Moal Cc: "Darrick J. Wong" Cc: Jarkko Sakkinen Cc: Yury Norov Cc: Chris Mason Signed-off-by: Andrew Morton --- include/linux/mm.h | 9 +++++++-- include/linux/mm_types.h | 5 +---- mm/filemap.c | 2 +- mm/util.c | 2 +- mm/vma.c | 11 +++++++---- mm/vma.h | 3 +-- tools/testing/vma/vma_internal.h | 9 +++++++-- 7 files changed, 25 insertions(+), 16 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mm.h b/include/linux/mm.h index 05d950805701..f8a8fd47399c 100644 --- a/include/linux/mm.h +++ b/include/linux/mm.h @@ -1290,15 +1290,20 @@ static inline bool vma_is_accessible(const struct vm_area_struct *vma) return vma->vm_flags & VM_ACCESS_FLAGS; } -static inline bool is_shared_maywrite(vm_flags_t vm_flags) +static inline bool is_shared_maywrite_vm_flags(vm_flags_t vm_flags) { return (vm_flags & (VM_SHARED | VM_MAYWRITE)) == (VM_SHARED | VM_MAYWRITE); } +static inline bool is_shared_maywrite(const vma_flags_t *flags) +{ + return vma_flags_test_all(flags, VMA_SHARED_BIT, VMA_MAYWRITE_BIT); +} + static inline bool vma_is_shared_maywrite(const struct vm_area_struct *vma) { - return is_shared_maywrite(vma->vm_flags); + return is_shared_maywrite(&vma->flags); } static inline diff --git a/include/linux/mm_types.h b/include/linux/mm_types.h index 9b4311cfd5e8..3cc8ae722886 100644 --- a/include/linux/mm_types.h +++ b/include/linux/mm_types.h @@ -887,10 +887,7 @@ struct vm_area_desc { /* Mutable fields. Populated with initial state. */ pgoff_t pgoff; struct file *vm_file; - union { - vm_flags_t vm_flags; - vma_flags_t vma_flags; - }; + vma_flags_t vma_flags; pgprot_t page_prot; /* Write-only fields. */ diff --git a/mm/filemap.c b/mm/filemap.c index ebd75684cb0a..6cd7974d4ada 100644 --- a/mm/filemap.c +++ b/mm/filemap.c @@ -4012,7 +4012,7 @@ int generic_file_readonly_mmap(struct file *file, struct vm_area_struct *vma) int generic_file_readonly_mmap_prepare(struct vm_area_desc *desc) { - if (is_shared_maywrite(desc->vm_flags)) + if (is_shared_maywrite(&desc->vma_flags)) return -EINVAL; return generic_file_mmap_prepare(desc); } diff --git a/mm/util.c b/mm/util.c index 97cae40c0209..b05ab6f97e11 100644 --- a/mm/util.c +++ b/mm/util.c @@ -1154,7 +1154,7 @@ int __compat_vma_mmap(const struct file_operations *f_op, .pgoff = vma->vm_pgoff, .vm_file = vma->vm_file, - .vm_flags = vma->vm_flags, + .vma_flags = vma->flags, .page_prot = vma->vm_page_prot, .action.type = MMAP_NOTHING, /* Default */ diff --git a/mm/vma.c b/mm/vma.c index 39dcd9ddd4ba..be64f781a3aa 100644 --- a/mm/vma.c +++ b/mm/vma.c @@ -15,7 +15,10 @@ struct mmap_state { unsigned long end; pgoff_t pgoff; unsigned long pglen; - vm_flags_t vm_flags; + union { + vm_flags_t vm_flags; + vma_flags_t vma_flags; + }; struct file *file; pgprot_t page_prot; @@ -2369,7 +2372,7 @@ static void set_desc_from_map(struct vm_area_desc *desc, desc->pgoff = map->pgoff; desc->vm_file = map->file; - desc->vm_flags = map->vm_flags; + desc->vma_flags = map->vma_flags; desc->page_prot = map->page_prot; } @@ -2650,7 +2653,7 @@ static int call_mmap_prepare(struct mmap_state *map, map->file_doesnt_need_get = true; map->file = desc->vm_file; } - map->vm_flags = desc->vm_flags; + map->vma_flags = desc->vma_flags; map->page_prot = desc->page_prot; /* User-defined fields. */ map->vm_ops = desc->vm_ops; @@ -2823,7 +2826,7 @@ unsigned long mmap_region(struct file *file, unsigned long addr, return -EINVAL; /* Map writable and ensure this isn't a sealed memfd. */ - if (file && is_shared_maywrite(vm_flags)) { + if (file && is_shared_maywrite_vm_flags(vm_flags)) { int error = mapping_map_writable(file->f_mapping); if (error) diff --git a/mm/vma.h b/mm/vma.h index de30c69bceaf..eba388c61ef4 100644 --- a/mm/vma.h +++ b/mm/vma.h @@ -309,8 +309,7 @@ static inline void set_vma_from_desc(struct vm_area_struct *vma, vma->vm_pgoff = desc->pgoff; if (desc->vm_file != vma->vm_file) vma_set_file(vma, desc->vm_file); - if (desc->vm_flags != vma->vm_flags) - vm_flags_set(vma, desc->vm_flags); + vma->flags = desc->vma_flags; vma->vm_page_prot = desc->page_prot; /* User-defined fields. */ diff --git a/tools/testing/vma/vma_internal.h b/tools/testing/vma/vma_internal.h index 2b01794cbd61..2743f12ecf32 100644 --- a/tools/testing/vma/vma_internal.h +++ b/tools/testing/vma/vma_internal.h @@ -1009,15 +1009,20 @@ static inline void vma_desc_clear_flags_mask(struct vm_area_desc *desc, #define vma_desc_clear_flags(desc, ...) \ vma_desc_clear_flags_mask(desc, mk_vma_flags(__VA_ARGS__)) -static inline bool is_shared_maywrite(vm_flags_t vm_flags) +static inline bool is_shared_maywrite_vm_flags(vm_flags_t vm_flags) { return (vm_flags & (VM_SHARED | VM_MAYWRITE)) == (VM_SHARED | VM_MAYWRITE); } +static inline bool is_shared_maywrite(const vma_flags_t *flags) +{ + return vma_flags_test_all(flags, VMA_SHARED_BIT, VMA_MAYWRITE_BIT); +} + static inline bool vma_is_shared_maywrite(struct vm_area_struct *vma) { - return is_shared_maywrite(vma->vm_flags); + return is_shared_maywrite(&vma->flags); } static inline struct vm_area_struct *vma_next(struct vma_iterator *vmi) -- cgit v1.2.3 From 52e054f7184097bea009963e033cdd54af7bf8a2 Mon Sep 17 00:00:00 2001 From: Baolin Wang Date: Mon, 9 Feb 2026 22:07:24 +0800 Subject: mm: rmap: support batched checks of the references for large folios Patch series "support batch checking of references and unmapping for large folios", v6. Currently, folio_referenced_one() always checks the young flag for each PTE sequentially, which is inefficient for large folios. This inefficiency is especially noticeable when reclaiming clean file-backed large folios, where folio_referenced() is observed as a significant performance hotspot. Moreover, on Arm architecture, which supports contiguous PTEs, there is already an optimization to clear the young flags for PTEs within a contiguous range. However, this is not sufficient. We can extend this to perform batched operations for the entire large folio (which might exceed the contiguous range: CONT_PTE_SIZE). Similar to folio_referenced_one(), we can also apply batched unmapping for large file folios to optimize the performance of file folio reclamation. By supporting batched checking of the young flags, flushing TLB entries, and unmapping, I can observed a significant performance improvements in my performance tests for file folios reclamation. Please check the performance data in the commit message of each patch. This patch (of 5): Currently, folio_referenced_one() always checks the young flag for each PTE sequentially, which is inefficient for large folios. This inefficiency is especially noticeable when reclaiming clean file-backed large folios, where folio_referenced() is observed as a significant performance hotspot. Moreover, on Arm64 architecture, which supports contiguous PTEs, there is already an optimization to clear the young flags for PTEs within a contiguous range. However, this is not sufficient. We can extend this to perform batched operations for the entire large folio (which might exceed the contiguous range: CONT_PTE_SIZE). Introduce a new API: clear_flush_young_ptes() to facilitate batched checking of the young flags and flushing TLB entries, thereby improving performance during large folio reclamation. And it will be overridden by the architecture that implements a more efficient batch operation in the following patches. While we are at it, rename ptep_clear_flush_young_notify() to clear_flush_young_ptes_notify() to indicate that this is a batch operation. Link: https://lkml.kernel.org/r/cover.1770645603.git.baolin.wang@linux.alibaba.com Link: https://lkml.kernel.org/r/12132694536834262062d1fb304f8f8a064b6750.1770645603.git.baolin.wang@linux.alibaba.com Signed-off-by: Baolin Wang Reviewed-by: Harry Yoo Reviewed-by: Ryan Roberts Acked-by: David Hildenbrand (Arm) Cc: Catalin Marinas Cc: Jann Horn Cc: Liam Howlett Cc: Lorenzo Stoakes Cc: Matthew Wilcox (Oracle) Cc: Michal Hocko Cc: Mike Rapoport Cc: Rik van Riel Cc: Suren Baghdasaryan Cc: Vlastimil Babka Cc: Will Deacon Cc: Barry Song Signed-off-by: Andrew Morton --- include/linux/mmu_notifier.h | 9 +++++---- include/linux/pgtable.h | 35 +++++++++++++++++++++++++++++++++++ mm/rmap.c | 28 +++++++++++++++++++++++++--- 3 files changed, 65 insertions(+), 7 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mmu_notifier.h b/include/linux/mmu_notifier.h index d1094c2d5fb6..07a2bbaf86e9 100644 --- a/include/linux/mmu_notifier.h +++ b/include/linux/mmu_notifier.h @@ -515,16 +515,17 @@ static inline void mmu_notifier_range_init_owner( range->owner = owner; } -#define ptep_clear_flush_young_notify(__vma, __address, __ptep) \ +#define clear_flush_young_ptes_notify(__vma, __address, __ptep, __nr) \ ({ \ int __young; \ struct vm_area_struct *___vma = __vma; \ unsigned long ___address = __address; \ - __young = ptep_clear_flush_young(___vma, ___address, __ptep); \ + unsigned int ___nr = __nr; \ + __young = clear_flush_young_ptes(___vma, ___address, __ptep, ___nr); \ __young |= mmu_notifier_clear_flush_young(___vma->vm_mm, \ ___address, \ ___address + \ - PAGE_SIZE); \ + ___nr * PAGE_SIZE); \ __young; \ }) @@ -650,7 +651,7 @@ static inline void mmu_notifier_subscriptions_destroy(struct mm_struct *mm) #define mmu_notifier_range_update_to_read_only(r) false -#define ptep_clear_flush_young_notify ptep_clear_flush_young +#define clear_flush_young_ptes_notify clear_flush_young_ptes #define pmdp_clear_flush_young_notify pmdp_clear_flush_young #define ptep_clear_young_notify ptep_test_and_clear_young #define pmdp_clear_young_notify pmdp_test_and_clear_young diff --git a/include/linux/pgtable.h b/include/linux/pgtable.h index 21b67d937555..a50df42a893f 100644 --- a/include/linux/pgtable.h +++ b/include/linux/pgtable.h @@ -1068,6 +1068,41 @@ static inline void wrprotect_ptes(struct mm_struct *mm, unsigned long addr, } #endif +#ifndef clear_flush_young_ptes +/** + * clear_flush_young_ptes - Mark PTEs that map consecutive pages of the same + * folio as old and flush the TLB. + * @vma: The virtual memory area the pages are mapped into. + * @addr: Address the first page is mapped at. + * @ptep: Page table pointer for the first entry. + * @nr: Number of entries to clear access bit. + * + * May be overridden by the architecture; otherwise, implemented as a simple + * loop over ptep_clear_flush_young(). + * + * Note that PTE bits in the PTE range besides the PFN can differ. For example, + * some PTEs might be write-protected. + * + * Context: The caller holds the page table lock. The PTEs map consecutive + * pages that belong to the same folio. The PTEs are all in the same PMD. + */ +static inline int clear_flush_young_ptes(struct vm_area_struct *vma, + unsigned long addr, pte_t *ptep, unsigned int nr) +{ + int young = 0; + + for (;;) { + young |= ptep_clear_flush_young(vma, addr, ptep); + if (--nr == 0) + break; + ptep++; + addr += PAGE_SIZE; + } + + return young; +} +#endif + /* * On some architectures hardware does not set page access bit when accessing * memory page, it is responsibility of software setting this bit. It brings diff --git a/mm/rmap.c b/mm/rmap.c index ab099405151f..3dbc2c4e02dc 100644 --- a/mm/rmap.c +++ b/mm/rmap.c @@ -913,9 +913,11 @@ static bool folio_referenced_one(struct folio *folio, struct folio_referenced_arg *pra = arg; DEFINE_FOLIO_VMA_WALK(pvmw, folio, vma, address, 0); int ptes = 0, referenced = 0; + unsigned int nr; while (page_vma_mapped_walk(&pvmw)) { address = pvmw.address; + nr = 1; if (vma->vm_flags & VM_LOCKED) { ptes++; @@ -960,9 +962,21 @@ static bool folio_referenced_one(struct folio *folio, if (lru_gen_look_around(&pvmw)) referenced++; } else if (pvmw.pte) { - if (ptep_clear_flush_young_notify(vma, address, - pvmw.pte)) + if (folio_test_large(folio)) { + unsigned long end_addr = pmd_addr_end(address, vma->vm_end); + unsigned int max_nr = (end_addr - address) >> PAGE_SHIFT; + pte_t pteval = ptep_get(pvmw.pte); + + nr = folio_pte_batch(folio, pvmw.pte, + pteval, max_nr); + } + + ptes += nr; + if (clear_flush_young_ptes_notify(vma, address, pvmw.pte, nr)) referenced++; + /* Skip the batched PTEs */ + pvmw.pte += nr - 1; + pvmw.address += (nr - 1) * PAGE_SIZE; } else if (IS_ENABLED(CONFIG_TRANSPARENT_HUGEPAGE)) { if (pmdp_clear_flush_young_notify(vma, address, pvmw.pmd)) @@ -972,7 +986,15 @@ static bool folio_referenced_one(struct folio *folio, WARN_ON_ONCE(1); } - pra->mapcount--; + pra->mapcount -= nr; + /* + * If we are sure that we batched the entire folio, + * we can just optimize and stop right here. + */ + if (ptes == pvmw.nr_pages) { + page_vma_mapped_walk_done(&pvmw); + break; + } } if (referenced) -- cgit v1.2.3 From 68eeb0871e986ae5462439dae881e3a27bcef85f Mon Sep 17 00:00:00 2001 From: Hans de Goede Date: Sun, 21 Dec 2025 17:57:40 +0100 Subject: fbdev: Use device_create_with_groups() to fix sysfs groups registration race The fbdev sysfs attributes are registered after sending the uevent for the device creation, leaving a race window where e.g. udev rules may not be able to access the sysfs attributes because the registration is not done yet. Fix this by switching to device_create_with_groups(). This also results in a nice cleanup. After switching to device_create_with_groups() all that is left of fb_init_device() is setting the drvdata and that can be passed to device_create[_with_groups]() too. After which fb_init_device() can be completely removed. Dropping fb_init_device() + fb_cleanup_device() in turn allows removing fb_info.class_flag as they were the only user of this field. Fixes: 5fc830d6aca1 ("fbdev: Register sysfs groups through device_add_group") Cc: stable@vger.kernel.org Cc: Shixiong Ou Signed-off-by: Hans de Goede Signed-off-by: Helge Deller --- drivers/video/fbdev/core/fbsysfs.c | 36 +++--------------------------------- include/linux/fb.h | 1 - 2 files changed, 3 insertions(+), 34 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/fbdev/core/fbsysfs.c b/drivers/video/fbdev/core/fbsysfs.c index b8344c40073b..baa2bae0fb5b 100644 --- a/drivers/video/fbdev/core/fbsysfs.c +++ b/drivers/video/fbdev/core/fbsysfs.c @@ -12,8 +12,6 @@ #include "fb_internal.h" -#define FB_SYSFS_FLAG_ATTR 1 - static int activate(struct fb_info *fb_info, struct fb_var_screeninfo *var) { int err; @@ -451,33 +449,7 @@ static struct attribute *fb_device_attrs[] = { NULL, }; -static const struct attribute_group fb_device_attr_group = { - .attrs = fb_device_attrs, -}; - -static int fb_init_device(struct fb_info *fb_info) -{ - int ret; - - dev_set_drvdata(fb_info->dev, fb_info); - - fb_info->class_flag |= FB_SYSFS_FLAG_ATTR; - - ret = device_add_group(fb_info->dev, &fb_device_attr_group); - if (ret) - fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR; - - return 0; -} - -static void fb_cleanup_device(struct fb_info *fb_info) -{ - if (fb_info->class_flag & FB_SYSFS_FLAG_ATTR) { - device_remove_group(fb_info->dev, &fb_device_attr_group); - - fb_info->class_flag &= ~FB_SYSFS_FLAG_ATTR; - } -} +ATTRIBUTE_GROUPS(fb_device); int fb_device_create(struct fb_info *fb_info) { @@ -485,14 +457,13 @@ int fb_device_create(struct fb_info *fb_info) dev_t devt = MKDEV(FB_MAJOR, node); int ret; - fb_info->dev = device_create(fb_class, fb_info->device, devt, NULL, "fb%d", node); + fb_info->dev = device_create_with_groups(fb_class, fb_info->device, devt, fb_info, + fb_device_groups, "fb%d", node); if (IS_ERR(fb_info->dev)) { /* Not fatal */ ret = PTR_ERR(fb_info->dev); pr_warn("Unable to create device for framebuffer %d; error %d\n", node, ret); fb_info->dev = NULL; - } else { - fb_init_device(fb_info); } return 0; @@ -505,7 +476,6 @@ void fb_device_destroy(struct fb_info *fb_info) if (!fb_info->dev) return; - fb_cleanup_device(fb_info); device_destroy(fb_class, devt); fb_info->dev = NULL; } diff --git a/include/linux/fb.h b/include/linux/fb.h index 65fb70382675..aaa50b81f1d2 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -493,7 +493,6 @@ struct fb_info { #if defined(CONFIG_FB_DEVICE) struct device *dev; /* This is this fb device */ #endif - int class_flag; /* private sysfs flags */ #ifdef CONFIG_FB_TILEBLITTING struct fb_tile_ops *tileops; /* Tile Blitting */ #endif -- cgit v1.2.3 From 39b65316fe9d6d3f730632408ace28d49c5845a4 Mon Sep 17 00:00:00 2001 From: Chintan Patel Date: Tue, 6 Jan 2026 20:42:54 -0800 Subject: fb: Add dev_of_fbinfo() helper for optional sysfs support Add dev_of_fbinfo() to return the framebuffer struct device when CONFIG_FB_DEVICE is enabled, or NULL otherwise. This allows fbdev drivers to use sysfs interfaces via runtime checks instead of CONFIG_FB_DEVICE ifdefs, keeping the code clean while remaining fully buildable. Suggested-by: Helge Deller Reviewed-by: Helge Deller Reviewed-by: Andy Shevchenko Signed-off-by: Chintan Patel Reviewed-by: Thomas Zimmermann Signed-off-by: Helge Deller --- include/linux/fb.h | 9 +++++++++ 1 file changed, 9 insertions(+) (limited to 'include/linux') diff --git a/include/linux/fb.h b/include/linux/fb.h index aaa50b81f1d2..e70ee58d689e 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -623,6 +623,15 @@ static inline void unlock_fb_info(struct fb_info *info) mutex_unlock(&info->lock); } +static inline struct device *dev_of_fbinfo(const struct fb_info *info) +{ +#ifdef CONFIG_FB_DEVICE + return info->dev; +#else + return NULL; +#endif +} + static inline void __fb_pad_aligned_buffer(u8 *dst, u32 d_pitch, u8 *src, u32 s_pitch, u32 height) { -- cgit v1.2.3 From 3092718e1280fcbdfc4f4b4a3258be8992a0c244 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 8 Jan 2026 20:04:54 +0100 Subject: video/logo: remove logo_mac_clut224 The logo_mac_clut224 depends on the runtime value MACH_IS_MAC being true to be displayed. This makes that logo a one-of-a-kind, as it is the only one whose selection can not be decided at compile time. This dynamic logo selection logic conflicts with our upcoming plans to simplify the logo selection code. Considering that the logo_mac_clut224 is only used by the Macintosh 68k, a machine whose sales ended some thirty years ago and which thus represents a very small user base, it is preferable to resolve the conflict in favour of code simplicity. Remove the logo_mac_clut224 so that the logo selection can be statically determined at compile time. The users who wish to continue using that logo can still download it from [1] and add: CONFIG_LOGO_LINUX_CLUT224=y CONFIG_LOGO_LINUX_CLUT224_FILE="/path/to/logo_mac_clut224.ppm" to their configuration file to restore it. [1] logo_mac_clut224.ppm file Link: https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git/plain/drivers/video/logo/logo_mac_clut224.ppm?h=v6.18 Signed-off-by: Vincent Mailhol Signed-off-by: Helge Deller --- drivers/video/logo/Kconfig | 4 - drivers/video/logo/Makefile | 3 +- drivers/video/logo/logo.c | 5 - drivers/video/logo/logo_mac_clut224.ppm | 1604 ------------------------------- include/linux/linux_logo.h | 1 - 5 files changed, 1 insertion(+), 1616 deletions(-) delete mode 100644 drivers/video/logo/logo_mac_clut224.ppm (limited to 'include/linux') diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index 1d1651c067a1..413ddb4be15e 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -76,10 +76,6 @@ config LOGO_DEC_CLUT224 depends on MACH_DECSTATION || ALPHA default y -config LOGO_MAC_CLUT224 - bool "224-color Macintosh Linux logo" - depends on MAC - default y config LOGO_PARISC_CLUT224 bool "224-color PA-RISC Linux logo" diff --git a/drivers/video/logo/Makefile b/drivers/video/logo/Makefile index ac8e9da3f51a..e2b7605fa8e3 100644 --- a/drivers/video/logo/Makefile +++ b/drivers/video/logo/Makefile @@ -6,7 +6,6 @@ obj-$(CONFIG_LOGO_LINUX_MONO) += logo_linux_mono.o obj-$(CONFIG_LOGO_LINUX_VGA16) += logo_linux_vga16.o obj-$(CONFIG_LOGO_LINUX_CLUT224) += logo_linux_clut224.o obj-$(CONFIG_LOGO_DEC_CLUT224) += logo_dec_clut224.o -obj-$(CONFIG_LOGO_MAC_CLUT224) += logo_mac_clut224.o obj-$(CONFIG_LOGO_PARISC_CLUT224) += logo_parisc_clut224.o obj-$(CONFIG_LOGO_SGI_CLUT224) += logo_sgi_clut224.o obj-$(CONFIG_LOGO_SUN_CLUT224) += logo_sun_clut224.o @@ -20,7 +19,7 @@ obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o hostprogs := pnmtologo -# Create commands like "pnmtologo -t mono -n logo_mac_mono -o ..." +# Create commands like "pnmtologo -t mono -n logo_linux_mono -o ..." quiet_cmd_logo = LOGO $@ cmd_logo = $(obj)/pnmtologo -t $2 -n $(basename $(notdir $@)) -o $@ $< diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index 141f15a9a459..b4eb4f3489a0 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -79,11 +79,6 @@ const struct linux_logo * __ref fb_find_logo(int depth) /* DEC Linux logo on MIPS/MIPS64 or ALPHA */ logo = &logo_dec_clut224; #endif -#ifdef CONFIG_LOGO_MAC_CLUT224 - /* Macintosh Linux logo on m68k */ - if (MACH_IS_MAC) - logo = &logo_mac_clut224; -#endif #ifdef CONFIG_LOGO_PARISC_CLUT224 /* PA-RISC Linux logo */ logo = &logo_parisc_clut224; diff --git a/drivers/video/logo/logo_mac_clut224.ppm b/drivers/video/logo/logo_mac_clut224.ppm deleted file mode 100644 index 4dad34baea89..000000000000 --- a/drivers/video/logo/logo_mac_clut224.ppm +++ /dev/null @@ -1,1604 +0,0 @@ -P3 -# 224-color Macintosh Linux logo -80 80 -255 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 6 6 6 10 10 10 10 10 10 - 10 10 10 6 6 6 6 6 6 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 10 10 10 14 14 14 - 22 22 22 26 26 26 30 30 30 34 34 34 - 30 30 30 30 30 30 26 26 26 18 18 18 - 14 14 14 10 10 10 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 14 14 14 26 26 26 42 42 42 - 54 54 54 66 66 66 78 78 78 78 78 78 - 78 78 78 74 74 74 66 66 66 54 54 54 - 42 42 42 26 26 26 18 18 18 10 10 10 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 22 22 22 42 42 42 66 66 66 86 86 86 - 66 66 66 38 38 38 38 38 38 22 22 22 - 26 26 26 34 34 34 54 54 54 66 66 66 - 86 86 86 70 70 70 46 46 46 26 26 26 - 14 14 14 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 10 10 10 26 26 26 - 50 50 50 82 82 82 58 58 58 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 6 6 6 54 54 54 86 86 86 66 66 66 - 38 38 38 18 18 18 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 22 22 22 50 50 50 - 78 78 78 34 34 34 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 6 6 6 70 70 70 - 78 78 78 46 46 46 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 250 250 250 -250 250 250 250 250 250 250 250 250 242 242 242 -250 250 250 250 250 250 246 246 246 250 250 250 -246 246 246 242 242 242 246 246 246 231 231 231 - 46 46 46 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 14 14 14 - 46 46 46 34 34 34 6 6 6 2 2 6 - 82 82 82 242 242 242 242 242 242 246 246 246 -242 242 242 250 250 250 242 242 242 246 246 246 -242 242 242 250 250 250 242 242 242 250 250 250 -250 250 250 250 250 250 250 250 250 250 250 250 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 242 242 242 -250 250 250 250 250 250 250 250 250 250 250 250 -242 242 242 246 246 246 250 250 250 250 250 250 -250 250 250 250 250 250 242 242 242 116 116 116 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 26 26 26 - 86 86 86 101 101 101 46 46 46 10 10 10 - 2 2 6 123 123 123 242 242 242 250 250 250 -246 246 246 250 250 250 242 242 242 250 250 250 -246 246 246 250 250 250 242 242 242 250 250 250 -242 242 242 250 250 250 250 250 250 250 250 250 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 123 123 123 -234 234 234 231 231 231 234 234 234 234 234 234 -234 234 234 221 221 221 234 234 234 231 231 231 -234 234 234 234 234 234 214 214 214 10 10 10 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 30 30 30 - 94 94 94 94 94 94 58 58 58 26 26 26 - 2 2 6 10 10 10 190 190 190 242 242 242 -242 242 242 250 250 250 250 250 250 242 242 242 -246 246 246 250 250 250 250 250 250 242 242 242 -250 250 250 242 242 242 242 242 242 231 231 231 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 90 90 90 -234 234 234 226 226 226 226 226 226 218 218 218 -226 226 226 214 214 214 231 231 231 221 221 221 -231 231 231 221 221 221 116 116 116 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 26 26 26 - 54 54 54 38 38 38 18 18 18 10 10 10 - 2 2 6 2 2 6 58 58 58 242 242 242 -242 242 242 242 242 242 242 242 242 242 242 242 -242 242 242 242 242 242 242 242 242 242 242 242 -242 242 242 242 242 242 250 250 250 226 226 226 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -234 234 234 231 231 231 242 242 242 242 242 242 -234 234 234 234 234 234 238 238 238 234 234 234 -238 238 238 238 238 238 50 50 50 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 10 10 10 - 10 10 10 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 182 182 182 -242 242 242 250 250 250 250 250 250 250 250 250 -242 242 242 250 250 250 250 250 250 250 250 250 -242 242 242 242 242 242 250 250 250 206 206 206 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -250 250 250 226 226 226 234 234 234 10 10 10 - 78 78 78 66 66 66 101 98 89 90 90 90 -110 110 110 106 106 106 10 10 10 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 101 98 89 -210 210 210 238 238 238 226 226 226 238 238 238 -210 210 210 242 242 242 226 226 226 242 242 242 -242 242 242 234 234 234 250 250 250 198 198 198 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -234 234 234 234 234 234 231 231 231 2 2 6 - 0 0 0 0 0 0 0 0 0 14 14 14 - 42 42 42 82 82 82 2 2 6 2 2 6 - 2 2 6 6 6 6 10 10 10 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 6 6 6 - 14 14 14 10 10 10 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 18 18 18 - 82 82 82 34 34 34 10 10 10 0 0 0 - 6 6 6 0 0 0 0 0 0 0 0 0 -144 144 144 250 250 250 242 242 242 202 202 202 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -226 226 226 231 231 231 234 234 234 90 90 90 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 86 86 86 2 2 6 2 2 6 - 6 6 6 6 6 6 22 22 22 34 34 34 - 6 6 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 18 18 18 34 34 34 - 10 10 10 50 50 50 22 22 22 2 2 6 - 2 2 6 2 2 6 2 2 6 10 10 10 - 86 86 86 42 42 42 14 14 14 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 -158 158 158 242 242 242 234 234 234 187 187 187 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -231 231 231 226 226 226 226 226 226 178 178 178 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 86 86 86 2 2 6 2 2 6 - 38 38 38 116 116 116 94 94 94 22 22 22 - 22 22 22 2 2 6 2 2 6 2 2 6 - 14 14 14 86 86 86 138 138 138 162 162 162 -154 154 154 38 38 38 26 26 26 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 86 86 86 46 46 46 14 14 14 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 -187 187 187 234 234 234 250 250 250 190 190 190 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -226 226 226 218 218 218 234 234 234 218 218 218 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 86 86 86 2 2 6 14 14 14 -134 134 134 198 198 198 195 195 195 116 116 116 - 10 10 10 2 2 6 2 2 6 6 6 6 -101 98 89 187 187 187 210 210 210 218 218 218 -214 214 214 134 134 134 14 14 14 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 86 86 86 50 50 50 18 18 18 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -214 214 214 226 226 226 242 242 242 187 187 187 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 1 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 74 74 74 -226 226 226 214 214 214 226 226 226 190 190 190 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 86 86 86 2 2 6 54 54 54 -218 218 218 195 195 195 226 226 226 246 246 246 - 58 58 58 2 2 6 2 2 6 30 30 30 -210 210 210 253 253 253 174 174 174 123 123 123 -221 221 221 234 234 234 74 74 74 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 70 70 70 58 58 58 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -195 195 195 226 226 226 234 234 234 206 206 206 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -226 226 226 218 218 218 234 234 234 226 226 226 - 0 0 0 0 0 0 0 0 0 14 14 14 - 46 46 46 82 82 82 2 2 6 106 106 106 -170 170 170 26 26 26 86 86 86 226 226 226 -123 123 123 10 10 10 14 14 14 46 46 46 -231 231 231 190 190 190 6 6 6 70 70 70 - 90 90 90 238 238 238 158 158 158 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 70 70 70 58 58 58 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -182 182 182 234 234 234 242 242 242 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 1 0 0 0 - 0 0 1 0 0 1 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 101 98 89 -214 214 214 214 214 214 226 226 226 242 242 242 - 0 0 0 0 0 0 0 0 0 14 14 14 - 42 42 42 86 86 86 6 6 6 116 116 116 -106 106 106 6 6 6 70 70 70 149 149 149 -128 128 128 18 18 18 38 38 38 54 54 54 -221 221 221 106 106 106 2 2 6 14 14 14 - 46 46 46 190 190 190 198 198 198 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 74 74 74 62 62 62 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -190 190 190 226 226 226 226 226 226 178 178 178 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 1 0 0 0 - 0 0 1 0 0 0 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -210 210 210 214 214 214 210 210 210 250 250 250 - 0 0 0 0 0 0 0 0 0 14 14 14 - 42 42 42 94 94 94 14 14 14 101 101 101 -128 128 128 2 2 6 18 18 18 116 116 116 -118 98 46 121 92 8 121 92 8 98 78 10 -162 162 162 106 106 106 2 2 6 2 2 6 - 2 2 6 195 195 195 195 195 195 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 74 74 74 62 62 62 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -214 214 214 226 226 226 231 231 231 149 149 149 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 1 0 0 1 - 0 0 1 0 0 0 0 0 1 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -226 226 226 218 218 218 210 210 210 231 231 231 - 0 0 0 0 0 0 0 0 0 10 10 10 - 38 38 38 90 90 90 14 14 14 58 58 58 -210 210 210 26 26 26 54 38 6 154 114 10 -226 170 11 236 186 11 225 175 15 184 144 12 -215 174 15 175 146 61 37 26 9 2 2 6 - 70 70 70 246 246 246 138 138 138 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 70 70 70 66 66 66 26 26 26 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -178 178 178 218 218 218 226 226 226 162 162 162 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -214 214 214 198 198 198 210 210 210 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 38 38 38 86 86 86 14 14 14 10 10 10 -195 195 195 188 164 115 192 133 9 225 175 15 -239 182 13 234 190 10 232 195 16 232 200 30 -245 207 45 241 208 19 232 195 16 184 144 12 -218 194 134 211 206 186 42 42 42 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 50 50 50 74 74 74 30 30 30 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 -187 187 187 231 231 231 226 226 226 182 182 182 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -218 218 218 210 210 210 210 210 210 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 34 34 34 86 86 86 14 14 14 2 2 6 -121 87 25 192 133 9 219 162 10 239 182 13 -236 186 11 232 195 16 241 208 19 244 214 54 -246 218 60 246 218 38 246 215 20 241 208 19 -241 208 19 226 184 13 121 87 25 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 50 50 50 82 82 82 34 34 34 10 10 10 - 0 0 0 0 0 0 0 0 0 0 0 0 -162 162 162 226 226 226 226 226 226 162 162 162 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -206 206 206 206 206 206 202 202 202 250 250 250 - 0 0 0 0 0 0 0 0 0 10 10 10 - 34 34 34 82 82 82 30 30 30 61 42 6 -180 123 7 206 145 10 230 174 11 239 182 13 -234 190 10 238 202 15 241 208 19 246 218 74 -246 218 38 246 215 20 246 215 20 246 215 20 -226 184 13 215 174 15 184 144 12 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 26 26 26 94 94 94 42 42 42 14 14 14 - 0 0 0 0 0 0 0 0 0 0 0 0 -166 166 166 226 226 226 214 214 214 170 170 170 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -226 226 226 218 218 218 202 202 202 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 78 78 78 50 50 50 104 69 6 -192 133 9 216 158 10 236 178 12 236 186 11 -232 195 16 241 208 19 244 214 54 245 215 43 -246 215 20 246 215 20 241 208 19 198 155 10 -200 144 11 216 158 10 156 118 10 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 6 6 6 90 90 90 54 54 54 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 -178 178 178 226 226 226 226 226 226 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -214 214 214 198 198 198 202 202 202 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 78 78 78 46 46 46 22 22 22 -137 92 6 210 162 10 239 182 13 238 190 10 -238 202 15 241 208 19 246 215 20 246 215 20 -241 208 19 203 166 17 185 133 11 210 150 10 -216 158 10 210 150 10 102 78 10 2 2 6 - 6 6 6 54 54 54 14 14 14 2 2 6 - 2 2 6 62 62 62 74 74 74 30 30 30 - 10 10 10 0 0 0 0 0 0 0 0 0 -190 190 190 214 214 214 242 242 242 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 66 66 66 -210 210 210 202 202 202 202 202 202 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 34 34 34 78 78 78 50 50 50 6 6 6 - 94 70 30 139 102 15 190 146 13 226 184 13 -232 200 30 232 195 16 215 174 15 190 146 13 -168 122 10 192 133 9 210 150 10 213 154 11 -202 150 34 182 157 106 101 98 89 2 2 6 - 2 2 6 78 78 78 116 116 116 58 58 58 - 2 2 6 22 22 22 90 90 90 46 46 46 - 18 18 18 6 6 6 0 0 0 0 0 0 -195 195 195 214 214 214 226 226 226 162 162 162 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 82 82 82 -198 198 198 190 190 190 187 187 187 242 242 242 - 0 0 0 0 0 0 0 0 0 10 10 10 - 38 38 38 86 86 86 50 50 50 6 6 6 -128 128 128 174 154 114 156 107 11 168 122 10 -198 155 10 184 144 12 197 138 11 200 144 11 -206 145 10 206 145 10 197 138 11 188 164 115 -195 195 195 198 198 198 174 174 174 14 14 14 - 2 2 6 22 22 22 116 116 116 116 116 116 - 22 22 22 2 2 6 74 74 74 70 70 70 - 30 30 30 10 10 10 0 0 0 0 0 0 -178 178 178 226 226 226 226 226 226 141 141 141 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -195 195 195 195 195 195 195 195 195 250 250 250 - 0 0 0 0 0 0 6 6 6 18 18 18 - 50 50 50 101 101 101 26 26 26 10 10 10 -138 138 138 190 190 190 174 154 114 156 107 11 -197 138 11 200 144 11 197 138 11 192 133 9 -180 123 7 190 142 34 190 178 144 187 187 187 -202 202 202 221 221 221 214 214 214 66 66 66 - 2 2 6 2 2 6 50 50 50 62 62 62 - 6 6 6 2 2 6 10 10 10 90 90 90 - 50 50 50 18 18 18 6 6 6 0 0 0 -190 190 190 226 226 226 250 250 250 202 202 202 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -187 187 187 190 190 190 187 187 187 250 250 250 - 0 0 0 0 0 0 10 10 10 34 34 34 - 74 74 74 74 74 74 2 2 6 6 6 6 -144 144 144 198 198 198 190 190 190 178 166 146 -154 121 60 156 107 11 156 107 11 168 124 44 -174 154 114 187 187 187 190 190 190 210 210 210 -246 246 246 253 253 253 253 253 253 182 182 182 - 6 6 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 62 62 62 - 74 74 74 34 34 34 14 14 14 0 0 0 -174 174 174 206 206 206 242 242 242 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -190 190 190 187 187 187 190 190 190 234 234 234 - 0 0 0 10 10 10 22 22 22 54 54 54 - 94 94 94 18 18 18 2 2 6 46 46 46 -234 234 234 221 221 221 190 190 190 190 190 190 -190 190 190 187 187 187 187 187 187 190 190 190 -190 190 190 195 195 195 214 214 214 242 242 242 -253 253 253 253 253 253 253 253 253 253 253 253 - 82 82 82 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 14 14 14 - 86 86 86 54 54 54 22 22 22 6 6 6 -195 195 195 202 202 202 234 234 234 138 138 138 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -182 182 182 187 187 187 178 178 178 242 242 242 - 6 6 6 18 18 18 46 46 46 90 90 90 - 46 46 46 18 18 18 6 6 6 182 182 182 -253 253 253 246 246 246 206 206 206 190 190 190 -190 190 190 190 190 190 190 190 190 190 190 190 -206 206 206 231 231 231 250 250 250 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -202 202 202 14 14 14 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 42 42 42 86 86 86 42 42 42 18 18 18 -190 190 190 202 202 202 226 226 226 178 178 178 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 62 62 62 -195 195 195 182 182 182 187 187 187 250 250 250 - 14 14 14 38 38 38 74 74 74 66 66 66 - 2 2 6 6 6 6 90 90 90 250 250 250 -253 253 253 253 253 253 238 238 238 198 198 198 -190 190 190 190 190 190 195 195 195 221 221 221 -246 246 246 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 82 82 82 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 78 78 78 70 70 70 34 34 34 -202 202 202 182 182 182 242 242 242 158 158 158 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 26 26 26 -195 195 195 182 182 182 178 178 178 242 242 242 - 34 34 34 66 66 66 78 78 78 6 6 6 - 2 2 6 18 18 18 218 218 218 253 253 253 -253 253 253 253 253 253 253 253 253 246 246 246 -226 226 226 231 231 231 246 246 246 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 178 178 178 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 18 18 18 90 90 90 62 62 62 -218 218 218 198 198 198 250 250 250 141 141 141 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -182 182 182 178 178 178 174 174 174 250 250 250 - 58 58 58 90 90 90 18 18 18 2 2 6 - 2 2 6 110 110 110 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -250 250 250 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 231 231 231 18 18 18 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 18 18 18 94 94 94 -206 206 206 198 198 198 242 242 242 162 162 162 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 42 42 42 -166 166 166 170 170 170 187 187 187 242 242 242 - 90 90 90 26 26 26 2 2 6 2 2 6 - 14 14 14 195 195 195 250 250 250 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -250 250 250 242 242 242 54 54 54 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 38 38 38 -187 187 187 214 214 214 231 231 231 134 134 134 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -187 187 187 182 182 182 166 166 166 234 234 234 - 34 34 34 2 2 6 2 2 6 2 2 6 - 42 42 42 195 195 195 246 246 246 253 253 253 -253 253 253 253 253 253 253 253 253 250 250 250 -242 242 242 242 242 242 250 250 250 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 250 250 250 246 246 246 238 238 238 -226 226 226 231 231 231 101 101 101 6 6 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 -206 206 206 174 174 174 250 250 250 128 128 128 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 34 34 34 -178 178 178 144 144 144 170 170 170 226 226 226 - 2 2 6 2 2 6 2 2 6 6 6 6 - 70 70 70 170 170 170 206 206 206 234 234 234 -246 246 246 250 250 250 250 250 250 238 238 238 -226 226 226 231 231 231 238 238 238 250 250 250 -250 250 250 250 250 250 246 246 246 231 231 231 -214 214 214 206 206 206 202 202 202 202 202 202 -198 198 198 202 202 202 182 182 182 18 18 18 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 -202 202 202 166 166 166 214 214 214 128 128 128 - 10 10 10 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -178 178 178 158 158 158 134 134 134 242 242 242 - 2 2 6 2 2 6 2 2 6 10 10 10 - 94 94 94 182 182 182 218 218 218 242 242 242 -250 250 250 253 253 253 253 253 253 250 250 250 -234 234 234 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 246 246 246 -238 238 238 226 226 226 210 210 210 202 202 202 -195 195 195 195 195 195 210 210 210 158 158 158 - 6 6 6 14 14 14 50 50 50 14 14 14 - 2 2 6 2 2 6 2 2 6 2 2 6 -198 198 198 187 187 187 246 246 246 116 116 116 - 18 18 18 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -195 195 195 154 154 154 154 154 154 250 250 250 -250 250 250 250 250 250 242 242 242 242 242 242 -242 242 242 250 250 250 250 250 250 250 250 250 -250 250 250 250 250 250 242 242 242 250 250 250 -250 250 250 250 250 250 242 242 242 250 250 250 -250 250 250 250 250 250 250 250 250 246 246 246 -234 234 234 250 250 250 242 242 242 242 242 242 -242 242 242 250 250 250 242 242 242 242 242 242 -242 242 242 250 250 250 242 242 242 242 242 242 -250 250 250 242 242 242 242 242 242 250 250 250 -182 182 182 190 190 190 206 206 206 141 141 141 - 26 26 26 10 10 10 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -170 170 170 166 166 166 128 128 128 250 250 250 -242 242 242 250 250 250 250 250 250 242 242 242 -250 250 250 242 242 242 242 242 242 250 250 250 -242 242 242 250 250 250 250 250 250 250 250 250 -250 250 250 250 250 250 242 242 242 250 250 250 -242 242 242 250 250 250 242 242 242 250 250 250 -250 250 250 242 242 242 250 250 250 250 250 250 -242 242 242 250 250 250 250 250 250 250 250 250 -242 242 242 242 242 242 250 250 250 234 234 234 -250 250 250 242 242 242 242 242 242 250 250 250 -195 195 195 195 195 195 206 206 206 128 128 128 - 42 42 42 14 14 14 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 50 50 50 -178 178 178 174 174 174 158 158 158 170 170 170 -162 162 162 170 170 170 170 170 170 162 162 162 -166 166 166 170 170 170 154 154 154 154 154 154 -178 178 178 162 162 162 166 166 166 166 166 166 -166 166 166 158 158 158 178 178 178 162 162 162 -170 170 170 174 174 174 178 178 178 178 178 178 -170 170 170 178 178 178 170 170 170 166 166 166 -170 170 170 182 182 182 187 187 187 178 178 178 -195 195 195 195 195 195 195 195 195 195 195 195 -187 187 187 195 195 195 178 178 178 195 195 195 -206 206 206 195 195 195 210 210 210 116 116 116 - 58 58 58 22 22 22 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 50 50 50 -162 162 162 162 162 162 170 170 170 158 158 158 -162 162 162 158 158 158 162 162 162 166 166 166 -158 158 158 174 174 174 162 162 162 158 158 158 -170 170 170 166 166 166 166 166 166 174 174 174 -166 166 166 174 174 174 174 174 174 174 174 174 -174 174 174 182 182 182 166 166 166 187 187 187 -182 182 182 187 187 187 174 174 174 166 166 166 -166 166 166 187 187 187 182 182 182 158 158 158 -174 174 174 174 174 174 174 174 174 182 182 182 -182 182 182 182 182 182 178 178 178 195 195 195 -178 178 178 182 182 182 174 174 174 30 30 30 - 78 78 78 30 30 30 10 10 10 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 10 10 10 34 34 34 -154 154 154 158 158 158 154 154 154 158 158 158 -154 154 154 166 166 166 162 162 162 178 178 178 -178 178 178 166 166 166 170 170 170 158 158 158 -170 170 170 178 178 178 178 178 178 187 187 187 -195 195 195 178 178 178 178 178 178 178 178 178 -162 162 162 187 187 187 166 166 166 178 178 178 -174 174 174 178 178 178 170 170 170 170 170 170 -174 174 174 170 170 170 187 187 187 178 178 178 -178 178 178 202 202 202 170 170 170 187 187 187 -178 178 178 182 182 182 174 174 174 190 190 190 -182 182 182 166 166 166 149 149 149 6 6 6 - 86 86 86 46 46 46 14 14 14 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 14 14 14 50 50 50 -166 166 166 162 162 162 149 149 149 162 162 162 -158 158 158 170 170 170 158 158 158 158 158 158 -166 166 166 170 170 170 149 149 149 170 170 170 -158 158 158 174 174 174 166 166 166 166 166 166 -166 166 166 166 166 166 182 182 182 158 158 158 -158 158 158 174 174 174 170 170 170 158 158 158 -178 178 178 166 166 166 158 158 158 174 174 174 -170 170 170 166 166 166 174 174 174 166 166 166 -174 174 174 182 182 182 174 174 174 182 182 182 -174 174 174 178 178 178 187 187 187 206 206 206 -187 187 187 178 178 178 128 128 128 2 2 6 - 74 74 74 58 58 58 22 22 22 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 10 10 10 26 26 26 42 42 42 -158 158 158 144 144 144 149 149 149 162 162 162 -149 149 149 170 170 170 170 170 170 170 170 170 -174 174 174 170 170 170 158 158 158 162 162 162 -170 170 170 162 162 162 170 170 170 170 170 170 -162 162 162 162 162 162 170 170 170 170 170 170 -170 170 170 166 166 166 154 154 154 166 166 166 -154 154 154 162 162 162 170 170 170 149 149 149 -170 170 170 144 144 144 187 187 187 170 170 170 -170 170 170 195 195 195 187 187 187 202 202 202 -198 198 198 182 182 182 202 202 202 210 210 210 -187 187 187 178 178 178 106 106 106 2 2 6 - 42 42 42 74 74 74 30 30 30 10 10 10 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 14 14 14 42 42 42 42 42 42 -158 158 158 141 141 141 162 162 162 149 149 149 -154 154 154 158 158 158 166 166 166 174 174 174 -162 162 162 158 158 158 162 162 162 158 158 158 -158 158 158 158 158 158 166 166 166 166 166 166 -158 158 158 158 158 158 158 158 158 166 166 166 -166 166 166 170 170 170 182 182 182 187 187 187 -166 166 166 174 174 174 166 166 166 154 154 154 -174 174 174 174 174 174 166 166 166 190 190 190 - 34 34 34 2 2 6 18 18 18 2 2 6 - 34 34 34 2 2 6 18 18 18 78 78 78 -182 182 182 178 178 178 78 78 78 2 2 6 - 10 10 10 86 86 86 38 38 38 10 10 10 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 10 10 10 26 26 26 66 66 66 30 30 30 -138 138 138 144 144 144 154 154 154 149 149 149 -154 154 154 154 154 154 154 154 154 166 166 166 -162 162 162 158 158 158 162 162 162 154 154 154 -170 170 170 154 154 154 178 178 178 162 162 162 -162 162 162 170 170 170 162 162 162 154 154 154 - 2 2 6 2 2 6 34 34 34 42 42 42 - 42 42 42 34 34 34 22 18 6 34 34 34 - 42 42 42 42 42 42 66 66 66 34 34 34 -128 128 128 10 10 10 10 10 10 18 18 18 - 18 18 18 10 10 10 26 26 26 174 174 174 -187 187 187 138 138 138 34 34 34 2 2 6 - 6 6 6 86 86 86 46 46 46 14 14 14 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 18 18 18 46 46 46 86 86 86 6 6 6 -110 110 110 162 162 162 149 149 149 144 144 144 -149 149 149 166 166 166 149 149 149 162 162 162 -149 149 149 162 162 162 149 149 149 158 158 158 -166 166 166 158 158 158 158 158 158 166 166 166 -166 166 166 149 149 149 158 158 158 166 166 166 -128 128 128 18 18 18 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 22 18 6 26 26 26 - 18 18 18 6 6 6 18 18 18 166 166 166 -174 174 174 110 110 110 18 18 18 2 2 6 - 2 2 6 82 82 82 54 54 54 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 26 26 26 66 66 66 62 62 62 2 2 6 - 46 46 46 141 141 141 166 166 166 144 144 144 -154 154 154 170 170 170 158 158 158 162 162 162 -149 149 149 162 162 162 154 154 154 154 154 154 -162 162 162 144 144 144 162 162 162 154 154 154 -170 170 170 144 144 144 154 154 154 170 170 170 -116 116 116 144 144 144 110 110 110 116 116 116 -110 110 110 144 144 144 116 116 116 128 128 128 -134 134 134 116 116 116 134 134 134 149 149 149 -158 158 158 231 231 231 234 234 234 214 214 214 -202 202 202 195 195 195 166 166 166 144 144 144 -144 144 144 34 34 34 2 2 6 2 2 6 - 2 2 6 66 66 66 58 58 58 22 22 22 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 38 38 38 78 78 78 6 6 6 2 2 6 - 14 14 14 123 123 123 138 138 138 90 90 90 -110 110 110 128 128 128 154 154 154 149 149 149 -144 144 144 149 149 149 158 158 158 149 149 149 -166 166 166 158 158 158 158 158 158 166 166 166 -158 158 158 158 158 158 158 158 158 158 158 158 -144 144 144 170 170 170 162 162 162 170 170 170 -187 187 187 174 174 174 170 170 170 170 170 170 -162 162 162 170 170 170 170 170 170 178 178 178 -187 187 187 190 190 190 170 170 170 149 149 149 -149 149 149 138 138 138 170 170 170 116 116 116 - 18 18 18 2 2 6 2 2 6 2 2 6 - 2 2 6 66 66 66 62 62 62 22 22 22 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 18 18 18 - 50 50 50 74 74 74 2 2 6 2 2 6 - 14 14 14 94 94 94 134 134 134 74 74 74 - 50 50 50 158 158 158 154 154 154 166 166 166 -162 162 162 170 170 170 162 162 162 178 178 178 -170 170 170 154 154 154 162 162 162 154 154 154 -154 154 154 154 154 154 170 170 170 141 141 141 -149 149 149 166 166 166 166 166 166 166 166 166 -178 178 178 174 174 174 158 158 158 174 174 174 -174 174 174 174 174 174 174 174 174 158 158 158 -166 166 166 166 166 166 170 170 170 170 170 170 -170 170 170 162 162 162 82 82 82 10 10 10 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 66 66 66 62 62 62 22 22 22 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 18 18 18 - 54 54 54 62 62 62 2 2 6 2 2 6 - 2 2 6 34 34 34 123 123 123 18 18 18 - 22 18 6 128 128 128 149 149 149 154 154 154 -158 158 158 158 158 158 149 149 149 166 166 166 -166 166 166 158 158 158 158 158 158 182 182 182 -158 158 158 149 149 149 149 149 149 178 178 178 -162 162 162 170 170 170 170 170 170 170 170 170 -174 174 174 178 178 178 170 170 170 178 178 178 -170 170 170 178 178 178 178 178 178 162 162 162 -174 174 174 170 170 170 166 166 166 166 166 166 -141 141 141 50 50 50 30 30 30 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 66 66 66 58 58 58 22 22 22 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 22 22 22 - 58 58 58 62 62 62 2 2 6 2 2 6 - 2 2 6 2 2 6 38 38 38 144 144 144 -178 178 178 162 162 162 134 134 134 154 154 154 -154 154 154 154 154 154 154 154 154 170 170 170 -154 154 154 154 154 154 162 162 162 170 170 170 -162 162 162 154 154 154 158 158 158 174 174 174 -149 149 149 166 166 166 174 174 174 178 178 178 -174 174 174 174 174 174 166 166 166 174 174 174 -166 166 166 166 166 166 166 166 166 166 166 166 -170 170 170 170 170 170 166 166 166 138 138 138 - 42 42 42 34 34 34 18 14 6 22 22 22 - 26 26 26 18 18 18 6 6 6 2 2 6 - 2 2 6 82 82 82 54 54 54 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 26 26 26 - 62 62 62 106 106 106 74 54 14 185 133 11 -210 162 10 121 92 8 6 6 6 78 78 78 -154 154 154 149 149 149 141 141 141 149 149 149 -149 149 149 149 149 149 158 158 158 141 141 141 -149 149 149 141 141 141 158 158 158 149 149 149 -149 149 149 149 149 149 162 162 162 170 170 170 -154 154 154 170 170 170 162 162 162 166 166 166 -170 170 170 170 170 170 170 170 170 162 162 162 -162 162 162 170 170 170 170 170 170 170 170 170 -170 170 170 162 162 162 162 162 162 38 38 38 - 14 14 14 2 2 6 2 2 6 2 2 6 - 6 6 6 18 18 18 66 66 66 38 38 38 - 6 6 6 94 94 94 50 50 50 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 10 10 10 10 10 10 18 18 18 38 38 38 - 78 78 78 142 134 106 216 158 10 242 186 14 -246 190 14 246 190 14 156 118 10 10 10 10 -116 116 116 182 182 182 138 138 138 154 154 154 -154 154 154 138 138 138 162 162 162 170 170 170 -178 178 178 138 138 138 162 162 162 162 162 162 -162 162 162 158 158 158 149 149 149 174 174 174 -134 134 134 174 174 174 170 170 170 158 158 158 -158 158 158 174 174 174 141 141 141 174 174 174 -149 149 149 166 166 166 158 158 158 174 174 174 -141 141 141 178 178 178 175 146 61 37 26 9 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 38 38 38 46 46 46 - 26 26 26 106 106 106 54 54 54 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 14 14 14 22 22 22 - 30 30 30 38 38 38 50 50 50 70 70 70 -106 106 106 190 142 34 226 170 11 242 186 14 -246 190 14 246 190 14 246 190 14 154 114 10 - 6 6 6 74 74 74 226 226 226 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 231 231 231 250 250 250 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 228 184 62 -241 196 14 241 208 19 232 195 16 38 30 10 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 6 6 6 30 30 30 26 26 26 -203 166 17 154 142 90 66 66 66 26 26 26 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 18 18 18 38 38 38 58 58 58 - 78 78 78 86 86 86 101 101 101 123 123 123 -175 146 61 210 150 10 234 174 13 246 186 14 -246 190 14 246 190 14 246 190 14 238 190 10 -102 78 10 2 2 6 46 46 46 198 198 198 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 234 234 234 242 242 242 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 224 178 62 -242 186 14 241 196 14 210 166 10 22 18 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 6 6 6 121 92 8 -238 202 15 232 195 16 82 82 82 34 34 34 - 10 10 10 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 14 14 14 38 38 38 70 70 70 154 122 46 -190 142 34 200 144 11 197 138 11 197 138 11 -213 154 11 226 170 11 242 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -225 175 15 46 32 6 2 2 6 22 22 22 -158 158 158 250 250 250 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 250 250 250 242 242 242 224 178 62 -239 182 13 236 186 11 213 154 11 46 32 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 61 42 6 225 175 15 -238 190 10 236 186 11 112 100 78 42 42 42 - 14 14 14 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 22 22 22 54 54 54 154 122 46 213 154 11 -226 170 11 230 174 11 226 170 11 226 170 11 -236 178 12 242 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -241 196 14 184 144 12 10 10 10 2 2 6 - 6 6 6 116 116 116 242 242 242 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 231 231 231 198 198 198 214 170 54 -236 178 12 236 178 12 210 150 10 137 92 6 - 18 14 6 2 2 6 2 2 6 2 2 6 - 6 6 6 70 47 6 200 144 11 236 178 12 -239 182 13 239 182 13 124 112 88 58 58 58 - 22 22 22 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 70 70 70 180 133 36 226 170 11 -239 182 13 242 186 14 242 186 14 246 186 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 232 195 16 98 70 6 2 2 6 - 2 2 6 2 2 6 66 66 66 221 221 221 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 206 206 206 198 198 198 214 166 58 -230 174 11 230 174 11 216 158 10 192 133 9 -163 110 8 116 81 8 102 78 10 116 81 8 -167 114 7 197 138 11 226 170 11 239 182 13 -242 186 14 242 186 14 162 146 94 78 78 78 - 34 34 34 14 14 14 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 30 30 30 78 78 78 190 142 34 226 170 11 -239 182 13 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 241 196 14 203 166 17 22 18 6 - 2 2 6 2 2 6 2 2 6 38 38 38 -218 218 218 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -250 250 250 206 206 206 198 198 198 202 162 69 -226 170 11 236 178 12 224 166 10 210 150 10 -200 144 11 197 138 11 192 133 9 197 138 11 -210 150 10 226 170 11 242 186 14 246 190 14 -246 190 14 246 186 14 225 175 15 124 112 88 - 62 62 62 30 30 30 14 14 14 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 78 78 78 174 135 50 224 166 10 -239 182 13 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 241 196 14 139 102 15 - 2 2 6 2 2 6 2 2 6 2 2 6 - 78 78 78 250 250 250 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -250 250 250 214 214 214 198 198 198 190 150 46 -219 162 10 236 178 12 234 174 13 224 166 10 -216 158 10 213 154 11 213 154 11 216 158 10 -226 170 11 239 182 13 246 190 14 246 190 14 -246 190 14 246 190 14 242 186 14 206 162 42 -101 101 101 58 58 58 30 30 30 14 14 14 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 74 74 74 174 135 50 216 158 10 -236 178 12 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 241 196 14 226 184 13 - 61 42 6 2 2 6 2 2 6 2 2 6 - 22 22 22 238 238 238 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 226 226 226 187 187 187 180 133 36 -216 158 10 236 178 12 239 182 13 236 178 12 -230 174 11 226 170 11 226 170 11 230 174 11 -236 178 12 242 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 186 14 239 182 13 -206 162 42 106 106 106 66 66 66 34 34 34 - 14 14 14 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 26 26 26 70 70 70 163 133 67 213 154 11 -236 178 12 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 241 196 14 -190 146 13 18 14 6 2 2 6 2 2 6 - 46 46 46 246 246 246 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 221 221 221 86 86 86 156 107 11 -216 158 10 236 178 12 242 186 14 246 186 14 -242 186 14 239 182 13 239 182 13 242 186 14 -242 186 14 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -242 186 14 225 175 15 142 122 72 66 66 66 - 30 30 30 10 10 10 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 26 26 26 70 70 70 163 133 67 210 150 10 -236 178 12 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -232 195 16 121 92 8 34 34 34 106 106 106 -221 221 221 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -242 242 242 82 82 82 18 14 6 163 110 8 -216 158 10 236 178 12 242 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 242 186 14 163 133 67 - 46 46 46 18 18 18 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 10 10 10 - 30 30 30 78 78 78 163 133 67 210 150 10 -236 178 12 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -241 196 14 215 174 15 190 178 144 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 218 218 218 - 58 58 58 2 2 6 22 18 6 167 114 7 -216 158 10 236 178 12 246 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 186 14 242 186 14 190 150 46 - 54 54 54 22 22 22 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 14 14 14 - 38 38 38 86 86 86 180 133 36 213 154 11 -236 178 12 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 232 195 16 190 146 13 214 214 214 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 250 250 250 170 170 170 26 26 26 - 2 2 6 2 2 6 37 26 9 163 110 8 -219 162 10 239 182 13 246 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 186 14 236 178 12 224 166 10 142 122 72 - 46 46 46 18 18 18 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 18 18 18 - 50 50 50 109 106 95 192 133 9 224 166 10 -242 186 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -242 186 14 226 184 13 210 162 10 142 110 46 -226 226 226 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -253 253 253 253 253 253 253 253 253 253 253 253 -198 198 198 66 66 66 2 2 6 2 2 6 - 2 2 6 2 2 6 50 34 6 156 107 11 -219 162 10 239 182 13 246 186 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 242 186 14 -234 174 13 213 154 11 154 122 46 66 66 66 - 30 30 30 10 10 10 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 22 22 22 - 58 58 58 154 121 60 206 145 10 234 174 13 -242 186 14 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 186 14 236 178 12 210 162 10 163 110 8 - 61 42 6 138 138 138 218 218 218 250 250 250 -253 253 253 253 253 253 253 253 253 250 250 250 -242 242 242 210 210 210 144 144 144 66 66 66 - 6 6 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 61 42 6 163 110 8 -216 158 10 236 178 12 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 239 182 13 230 174 11 216 158 10 -190 142 34 124 112 88 70 70 70 38 38 38 - 18 18 18 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 22 22 22 - 62 62 62 168 124 44 206 145 10 224 166 10 -236 178 12 239 182 13 242 186 14 242 186 14 -246 186 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 236 178 12 216 158 10 175 118 6 - 80 54 7 2 2 6 6 6 6 30 30 30 - 54 54 54 62 62 62 50 50 50 38 38 38 - 14 14 14 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 6 6 6 80 54 7 167 114 7 -213 154 11 236 178 12 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 190 14 242 186 14 239 182 13 239 182 13 -230 174 11 210 150 10 174 135 50 124 112 88 - 82 82 82 54 54 54 34 34 34 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 18 18 18 - 50 50 50 158 118 36 192 133 9 200 144 11 -216 158 10 219 162 10 224 166 10 226 170 11 -230 174 11 236 178 12 239 182 13 239 182 13 -242 186 14 246 186 14 246 190 14 246 190 14 -246 190 14 246 190 14 246 190 14 246 190 14 -246 186 14 230 174 11 210 150 10 163 110 8 -104 69 6 10 10 10 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 6 6 6 91 60 6 167 114 7 -206 145 10 230 174 11 242 186 14 246 190 14 -246 190 14 246 190 14 246 186 14 242 186 14 -239 182 13 230 174 11 224 166 10 213 154 11 -180 133 36 124 112 88 86 86 86 58 58 58 - 38 38 38 22 22 22 10 10 10 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 14 14 14 - 34 34 34 70 70 70 138 110 50 158 118 36 -167 114 7 180 123 7 192 133 9 197 138 11 -200 144 11 206 145 10 213 154 11 219 162 10 -224 166 10 230 174 11 239 182 13 242 186 14 -246 186 14 246 186 14 246 186 14 246 186 14 -239 182 13 216 158 10 185 133 11 152 99 6 -104 69 6 18 14 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 2 2 6 2 2 6 2 2 6 - 2 2 6 6 6 6 80 54 7 152 99 6 -192 133 9 219 162 10 236 178 12 239 182 13 -246 186 14 242 186 14 239 182 13 236 178 12 -224 166 10 206 145 10 192 133 9 154 121 60 - 94 94 94 62 62 62 42 42 42 22 22 22 - 14 14 14 6 6 6 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 18 18 18 34 34 34 58 58 58 78 78 78 -101 98 89 124 112 88 142 110 46 156 107 11 -163 110 8 167 114 7 175 118 6 180 123 7 -185 133 11 197 138 11 210 150 10 219 162 10 -226 170 11 236 178 12 236 178 12 234 174 13 -219 162 10 197 138 11 163 110 8 130 83 6 - 91 60 6 10 10 10 2 2 6 2 2 6 - 18 18 18 38 38 38 38 38 38 38 38 38 - 38 38 38 38 38 38 38 38 38 38 38 38 - 38 38 38 38 38 38 26 26 26 2 2 6 - 2 2 6 6 6 6 70 47 6 137 92 6 -175 118 6 200 144 11 219 162 10 230 174 11 -234 174 13 230 174 11 219 162 10 210 150 10 -192 133 9 163 110 8 124 112 88 82 82 82 - 50 50 50 30 30 30 14 14 14 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 14 14 14 22 22 22 34 34 34 - 42 42 42 58 58 58 74 74 74 86 86 86 -101 98 89 122 102 70 130 98 46 121 87 25 -137 92 6 152 99 6 163 110 8 180 123 7 -185 133 11 197 138 11 206 145 10 200 144 11 -180 123 7 156 107 11 130 83 6 104 69 6 - 50 34 6 54 54 54 110 110 110 101 98 89 - 86 86 86 82 82 82 78 78 78 78 78 78 - 78 78 78 78 78 78 78 78 78 78 78 78 - 78 78 78 82 82 82 86 86 86 94 94 94 -106 106 106 101 101 101 86 66 34 124 80 6 -156 107 11 180 123 7 192 133 9 200 144 11 -206 145 10 200 144 11 192 133 9 175 118 6 -139 102 15 109 106 95 70 70 70 42 42 42 - 22 22 22 10 10 10 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 6 6 6 10 10 10 - 14 14 14 22 22 22 30 30 30 38 38 38 - 50 50 50 62 62 62 74 74 74 90 90 90 -101 98 89 112 100 78 121 87 25 124 80 6 -137 92 6 152 99 6 152 99 6 152 99 6 -138 86 6 124 80 6 98 70 6 86 66 30 -101 98 89 82 82 82 58 58 58 46 46 46 - 38 38 38 34 34 34 34 34 34 34 34 34 - 34 34 34 34 34 34 34 34 34 34 34 34 - 34 34 34 34 34 34 38 38 38 42 42 42 - 54 54 54 82 82 82 94 86 76 91 60 6 -134 86 6 156 107 11 167 114 7 175 118 6 -175 118 6 167 114 7 152 99 6 121 87 25 -101 98 89 62 62 62 34 34 34 18 18 18 - 6 6 6 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 6 6 6 10 10 10 - 18 18 18 22 22 22 30 30 30 42 42 42 - 50 50 50 66 66 66 86 86 86 101 98 89 -106 86 58 98 70 6 104 69 6 104 69 6 -104 69 6 91 60 6 82 62 34 90 90 90 - 62 62 62 38 38 38 22 22 22 14 14 14 - 10 10 10 10 10 10 10 10 10 10 10 10 - 10 10 10 10 10 10 6 6 6 10 10 10 - 10 10 10 10 10 10 10 10 10 14 14 14 - 22 22 22 42 42 42 70 70 70 89 81 66 - 80 54 7 104 69 6 124 80 6 137 92 6 -134 86 6 116 81 8 100 82 52 86 86 86 - 58 58 58 30 30 30 14 14 14 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 10 10 10 14 14 14 - 18 18 18 26 26 26 38 38 38 54 54 54 - 70 70 70 86 86 86 94 86 76 89 81 66 - 89 81 66 86 86 86 74 74 74 50 50 50 - 30 30 30 14 14 14 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 18 18 18 34 34 34 58 58 58 - 82 82 82 89 81 66 89 81 66 89 81 66 - 94 86 66 94 86 76 74 74 74 50 50 50 - 26 26 26 14 14 14 6 6 6 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 6 6 6 6 6 6 14 14 14 18 18 18 - 30 30 30 38 38 38 46 46 46 54 54 54 - 50 50 50 42 42 42 30 30 30 18 18 18 - 10 10 10 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 6 6 6 14 14 14 26 26 26 - 38 38 38 50 50 50 58 58 58 58 58 58 - 54 54 54 42 42 42 30 30 30 18 18 18 - 10 10 10 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 6 6 6 10 10 10 14 14 14 18 18 18 - 18 18 18 14 14 14 10 10 10 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 6 6 6 - 14 14 14 18 18 18 22 22 22 22 22 22 - 18 18 18 14 14 14 10 10 10 6 6 6 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 - 0 0 0 0 0 0 0 0 0 0 0 0 diff --git a/include/linux/linux_logo.h b/include/linux/linux_logo.h index e37699b7e839..1f04adc853a9 100644 --- a/include/linux/linux_logo.h +++ b/include/linux/linux_logo.h @@ -34,7 +34,6 @@ extern const struct linux_logo logo_linux_mono; extern const struct linux_logo logo_linux_vga16; extern const struct linux_logo logo_linux_clut224; extern const struct linux_logo logo_dec_clut224; -extern const struct linux_logo logo_mac_clut224; extern const struct linux_logo logo_parisc_clut224; extern const struct linux_logo logo_sgi_clut224; extern const struct linux_logo logo_sun_clut224; -- cgit v1.2.3 From ac6d088b3b20f9efb883e8302288276db3bdc2b8 Mon Sep 17 00:00:00 2001 From: Vincent Mailhol Date: Thu, 8 Jan 2026 20:04:55 +0100 Subject: video/logo: move logo selection logic to Kconfig Now that the path to the logo file can be directly entered in Kbuild, there is no more need to handle all the logo file selection in the Makefile and the C files. The only exception is the logo_spe_clut224 which is only used by the Cell processor (found for example in the Playstation 3) [1]. This extra logo uses its own different image which shows up on a separate line just below the normal logo. Because the extra logo uses a different image, it can not be factorized under the custom logo logic. Move all the logo file selection logic to Kbuild (except from the logo_spe_clut224.ppm), this done, clean-up the C code to only leave one entry for each logo type (monochrome, 16-colors and 224-colors). [1] Cell SPE logos Link: https://lore.kernel.org/all/20070710122702.765654000@pademelon.sonytel.be/ Signed-off-by: Vincent Mailhol Signed-off-by: Helge Deller --- drivers/video/logo/Kconfig | 43 +++++++------------------------------------ drivers/video/logo/Makefile | 13 ------------- drivers/video/logo/logo.c | 41 ++++------------------------------------- include/linux/linux_logo.h | 7 ------- 4 files changed, 11 insertions(+), 93 deletions(-) (limited to 'include/linux') diff --git a/drivers/video/logo/Kconfig b/drivers/video/logo/Kconfig index 413ddb4be15e..34ee207e5e77 100644 --- a/drivers/video/logo/Kconfig +++ b/drivers/video/logo/Kconfig @@ -25,6 +25,7 @@ config LOGO_LINUX_MONO config LOGO_LINUX_MONO_FILE string "Monochrome logo .pbm file" depends on LOGO_LINUX_MONO + default "drivers/video/logo/logo_superh_mono.pbm" if SUPERH default "drivers/video/logo/logo_linux_mono.pbm" help Takes a path to a monochromatic logo in the portable pixmap file @@ -42,6 +43,7 @@ config LOGO_LINUX_VGA16 config LOGO_LINUX_VGA16_FILE string "16-color logo .ppm file" depends on LOGO_LINUX_VGA16 + default "drivers/video/logo/logo_superh_vga16.ppm" if SUPERH default "drivers/video/logo/logo_linux_vga16.ppm" help Takes a path to a logo in the portable pixmap file format (.ppm), @@ -61,6 +63,11 @@ config LOGO_LINUX_CLUT224 config LOGO_LINUX_CLUT224_FILE string "224-color logo .ppm file" depends on LOGO_LINUX_CLUT224 + default "drivers/video/logo/logo_dec_clut224.ppm" if MACH_DECSTATION || ALPHA + default "drivers/video/logo/logo_parisc_clut224.ppm" if PARISC + default "drivers/video/logo/logo_sgi_clut224.ppm" if SGI_IP22 || SGI_IP27 || SGI_IP32 + default "drivers/video/logo/logo_sun_clut224.ppm" if SPARC + default "drivers/video/logo/logo_superh_clut224.ppm" if SUPERH default "drivers/video/logo/logo_linux_clut224.ppm" help Takes a path to a 224-color logo in the portable pixmap file @@ -71,40 +78,4 @@ config LOGO_LINUX_CLUT224_FILE magick source_image -compress none -colors 224 destination.ppm -config LOGO_DEC_CLUT224 - bool "224-color Digital Equipment Corporation Linux logo" - depends on MACH_DECSTATION || ALPHA - default y - - -config LOGO_PARISC_CLUT224 - bool "224-color PA-RISC Linux logo" - depends on PARISC - default y - -config LOGO_SGI_CLUT224 - bool "224-color SGI Linux logo" - depends on SGI_IP22 || SGI_IP27 || SGI_IP32 - default y - -config LOGO_SUN_CLUT224 - bool "224-color Sun Linux logo" - depends on SPARC - default y - -config LOGO_SUPERH_MONO - bool "Black and white SuperH Linux logo" - depends on SUPERH - default y - -config LOGO_SUPERH_VGA16 - bool "16-color SuperH Linux logo" - depends on SUPERH - default y - -config LOGO_SUPERH_CLUT224 - bool "224-color SuperH Linux logo" - depends on SUPERH - default y - endif # LOGO diff --git a/drivers/video/logo/Makefile b/drivers/video/logo/Makefile index e2b7605fa8e3..937b37d3b6c7 100644 --- a/drivers/video/logo/Makefile +++ b/drivers/video/logo/Makefile @@ -5,13 +5,6 @@ obj-$(CONFIG_LOGO) += logo.o obj-$(CONFIG_LOGO_LINUX_MONO) += logo_linux_mono.o obj-$(CONFIG_LOGO_LINUX_VGA16) += logo_linux_vga16.o obj-$(CONFIG_LOGO_LINUX_CLUT224) += logo_linux_clut224.o -obj-$(CONFIG_LOGO_DEC_CLUT224) += logo_dec_clut224.o -obj-$(CONFIG_LOGO_PARISC_CLUT224) += logo_parisc_clut224.o -obj-$(CONFIG_LOGO_SGI_CLUT224) += logo_sgi_clut224.o -obj-$(CONFIG_LOGO_SUN_CLUT224) += logo_sun_clut224.o -obj-$(CONFIG_LOGO_SUPERH_MONO) += logo_superh_mono.o -obj-$(CONFIG_LOGO_SUPERH_VGA16) += logo_superh_vga16.o -obj-$(CONFIG_LOGO_SUPERH_CLUT224) += logo_superh_clut224.o obj-$(CONFIG_SPU_BASE) += logo_spe_clut224.o @@ -32,12 +25,6 @@ $(obj)/logo_linux_vga16.c: $(CONFIG_LOGO_LINUX_VGA16_FILE) $(obj)/pnmtologo FORC $(obj)/logo_linux_clut224.c: $(CONFIG_LOGO_LINUX_CLUT224_FILE) $(obj)/pnmtologo FORCE $(call if_changed,logo,clut224) -$(obj)/%.c: $(src)/%.pbm $(obj)/pnmtologo FORCE - $(call if_changed,logo,mono) - -$(obj)/%_vga16.c: $(src)/%_vga16.ppm $(obj)/pnmtologo FORCE - $(call if_changed,logo,vga16) - $(obj)/%_clut224.c: $(src)/%_clut224.ppm $(obj)/pnmtologo FORCE $(call if_changed,logo,clut224) diff --git a/drivers/video/logo/logo.c b/drivers/video/logo/logo.c index b4eb4f3489a0..91535f8848da 100644 --- a/drivers/video/logo/logo.c +++ b/drivers/video/logo/logo.c @@ -48,54 +48,21 @@ const struct linux_logo * __ref fb_find_logo(int depth) if (nologo || logos_freed) return NULL; - if (depth >= 1) { #ifdef CONFIG_LOGO_LINUX_MONO - /* Generic Linux logo */ + if (depth >= 1) logo = &logo_linux_mono; #endif -#ifdef CONFIG_LOGO_SUPERH_MONO - /* SuperH Linux logo */ - logo = &logo_superh_mono; -#endif - } - if (depth >= 4) { #ifdef CONFIG_LOGO_LINUX_VGA16 - /* Generic Linux logo */ + if (depth >= 4) logo = &logo_linux_vga16; #endif -#ifdef CONFIG_LOGO_SUPERH_VGA16 - /* SuperH Linux logo */ - logo = &logo_superh_vga16; -#endif - } - if (depth >= 8) { #ifdef CONFIG_LOGO_LINUX_CLUT224 - /* Generic Linux logo */ + if (depth >= 8) logo = &logo_linux_clut224; #endif -#ifdef CONFIG_LOGO_DEC_CLUT224 - /* DEC Linux logo on MIPS/MIPS64 or ALPHA */ - logo = &logo_dec_clut224; -#endif -#ifdef CONFIG_LOGO_PARISC_CLUT224 - /* PA-RISC Linux logo */ - logo = &logo_parisc_clut224; -#endif -#ifdef CONFIG_LOGO_SGI_CLUT224 - /* SGI Linux logo on MIPS/MIPS64 */ - logo = &logo_sgi_clut224; -#endif -#ifdef CONFIG_LOGO_SUN_CLUT224 - /* Sun Linux logo */ - logo = &logo_sun_clut224; -#endif -#ifdef CONFIG_LOGO_SUPERH_CLUT224 - /* SuperH Linux logo */ - logo = &logo_superh_clut224; -#endif - } + return logo; } EXPORT_SYMBOL_GPL(fb_find_logo); diff --git a/include/linux/linux_logo.h b/include/linux/linux_logo.h index 1f04adc853a9..1e727a2cb4c1 100644 --- a/include/linux/linux_logo.h +++ b/include/linux/linux_logo.h @@ -33,13 +33,6 @@ struct linux_logo { extern const struct linux_logo logo_linux_mono; extern const struct linux_logo logo_linux_vga16; extern const struct linux_logo logo_linux_clut224; -extern const struct linux_logo logo_dec_clut224; -extern const struct linux_logo logo_parisc_clut224; -extern const struct linux_logo logo_sgi_clut224; -extern const struct linux_logo logo_sun_clut224; -extern const struct linux_logo logo_superh_mono; -extern const struct linux_logo logo_superh_vga16; -extern const struct linux_logo logo_superh_clut224; extern const struct linux_logo logo_spe_clut224; extern const struct linux_logo *fb_find_logo(int depth); -- cgit v1.2.3 From 8e9bf8b9e8c0a3e1ef16dd48260a113f65ed01d2 Mon Sep 17 00:00:00 2001 From: Sebastian Andrzej Siewior Date: Mon, 26 Jan 2026 19:08:36 +0100 Subject: printk, vt, fbcon: Remove console_conditional_schedule() do_con_write(), fbcon_redraw.*() invoke console_conditional_schedule() which is a conditional scheduling point based on printk's internal variables console_may_schedule. It may only be used if the console lock is acquired for instance via console_lock() or console_trylock(). Prinkt sets the internal variable to 1 (and allows to schedule) if the console lock has been acquired via console_lock(). The trylock does not allow it. The console_conditional_schedule() invocation in do_con_write() is invoked shortly before console_unlock(). The console_conditional_schedule() invocation in fbcon_redraw.*() original from fbcon_scroll() / vt's con_scroll() which originate from a line feed. In console_unlock() the variable is set to 0 (forbids to schedule) and it tries to schedule while making progress printing. This is brand new compared to when console_conditional_schedule() was added in v2.4.9.11. In v2.6.38-rc3, console_unlock() (started its existence) iterated over all consoles and flushed them with disabled interrupts. A scheduling attempt here was not possible, it relied that a long print scheduled before console_unlock(). Since commit 8d91f8b15361d ("printk: do cond_resched() between lines while outputting to consoles"), which appeared in v4.5-rc1, console_unlock() attempts to schedule if it was allowed to schedule while during console_lock(). Each record is idealy one line so after every line feed. This console_conditional_schedule() is also only relevant on PREEMPT_NONE and PREEMPT_VOLUNTARY builds. In other configurations cond_resched() becomes a nop and has no impact. I'm bringing this all up just proof that it is not required anymore. It becomes a problem on a PREEMPT_RT build with debug code enabled because that might_sleep() in cond_resched() remains and triggers a warnings. This is due to legacy_kthread_func-> console_flush_one_record -> vt_console_print-> lf -> con_scroll -> fbcon_scroll and vt_console_print() acquires a spinlock_t which does not allow a voluntary schedule. There is no need to fb_scroll() to schedule since console_flush_one_record() attempts to schedule after each line. !PREEMPT_RT is not affected because the legacy printing thread is only enabled on PREEMPT_RT builds. Therefore I suggest to remove console_conditional_schedule(). Cc: Simona Vetter Cc: Helge Deller Cc: linux-fbdev@vger.kernel.org Cc: dri-devel@lists.freedesktop.org Fixes: 5f53ca3ff83b4 ("printk: Implement legacy printer kthread for PREEMPT_RT") Signed-off-by: Sebastian Andrzej Siewior Acked-by: Greg Kroah-Hartman Acked-by: Petr Mladek # from printk() POV Signed-off-by: Helge Deller --- drivers/tty/vt/vt.c | 1 - drivers/video/fbdev/core/fbcon.c | 6 ------ include/linux/console.h | 1 - kernel/printk/printk.c | 16 ---------------- 4 files changed, 24 deletions(-) (limited to 'include/linux') diff --git a/drivers/tty/vt/vt.c b/drivers/tty/vt/vt.c index 84de274d24ca..3bdbd4c52c2f 100644 --- a/drivers/tty/vt/vt.c +++ b/drivers/tty/vt/vt.c @@ -3230,7 +3230,6 @@ rescan_last_byte: goto rescan_last_byte; } con_flush(vc, &draw); - console_conditional_schedule(); notify_update(vc); return n; diff --git a/drivers/video/fbdev/core/fbcon.c b/drivers/video/fbdev/core/fbcon.c index 36e380797a9e..98387c42e4e5 100644 --- a/drivers/video/fbdev/core/fbcon.c +++ b/drivers/video/fbdev/core/fbcon.c @@ -1608,12 +1608,10 @@ static void fbcon_redraw_move(struct vc_data *vc, struct fbcon_display *p, start = s; } } - console_conditional_schedule(); s++; } while (s < le); if (s > start) fbcon_putcs(vc, start, s - start, dy, x); - console_conditional_schedule(); dy++; } } @@ -1649,14 +1647,12 @@ static void fbcon_redraw_blit(struct vc_data *vc, struct fb_info *info, } scr_writew(c, d); - console_conditional_schedule(); s++; d++; } while (s < le); if (s > start) par->bitops->bmove(vc, info, line + ycount, x, line, x, 1, s - start); - console_conditional_schedule(); if (ycount > 0) line++; else { @@ -1704,13 +1700,11 @@ static void fbcon_redraw(struct vc_data *vc, int line, int count, int offset) } } scr_writew(c, d); - console_conditional_schedule(); s++; d++; } while (s < le); if (s > start) fbcon_putcs(vc, start, s - start, line, x); - console_conditional_schedule(); if (offset > 0) line++; else { diff --git a/include/linux/console.h b/include/linux/console.h index 1346f0b4cd8b..5520e4477ad7 100644 --- a/include/linux/console.h +++ b/include/linux/console.h @@ -697,7 +697,6 @@ extern int unregister_console(struct console *); extern void console_lock(void); extern int console_trylock(void); extern void console_unlock(void); -extern void console_conditional_schedule(void); extern void console_unblank(void); extern void console_flush_on_panic(enum con_flush_mode mode); extern struct tty_driver *console_device(int *); diff --git a/kernel/printk/printk.c b/kernel/printk/printk.c index 4ddeb55e1207..a181394604d1 100644 --- a/kernel/printk/printk.c +++ b/kernel/printk/printk.c @@ -3416,22 +3416,6 @@ void console_unlock(void) } EXPORT_SYMBOL(console_unlock); -/** - * console_conditional_schedule - yield the CPU if required - * - * If the console code is currently allowed to sleep, and - * if this CPU should yield the CPU to another task, do - * so here. - * - * Must be called within console_lock();. - */ -void __sched console_conditional_schedule(void) -{ - if (console_may_schedule) - cond_resched(); -} -EXPORT_SYMBOL(console_conditional_schedule); - void console_unblank(void) { bool found_unblank = false; -- cgit v1.2.3 From a86039b76e5d1e886d42ec28f569a9ef76409750 Mon Sep 17 00:00:00 2001 From: Thomas Zimmermann Date: Tue, 10 Feb 2026 10:43:32 +0100 Subject: fbcon: Declare struct fb_info.fbcon_par as of type struct fbcon_par The only correct type for the field fbcon_par in struct fb_info is struct fbcon_par. Declare is as such. The field is a pointer to fbcon-private data. Signed-off-by: Thomas Zimmermann Signed-off-by: Helge Deller --- include/linux/fb.h | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/fb.h b/include/linux/fb.h index e70ee58d689e..6d4a58084fd5 100644 --- a/include/linux/fb.h +++ b/include/linux/fb.h @@ -18,6 +18,7 @@ struct backlight_device; struct device; struct device_node; struct fb_info; +struct fbcon_par; struct file; struct i2c_adapter; struct inode; @@ -505,7 +506,7 @@ struct fb_info { #define FBINFO_STATE_RUNNING 0 #define FBINFO_STATE_SUSPENDED 1 u32 state; /* Hardware state i.e suspend */ - void *fbcon_par; /* fbcon use-only private area */ + struct fbcon_par *fbcon_par; /* fbcon use-only private area */ /* From here on everything is device dependent */ void *par; -- cgit v1.2.3 From 11506b3c233fcead6eba2842d17ec29c84f550d4 Mon Sep 17 00:00:00 2001 From: Andreas Hindborg Date: Sat, 14 Feb 2026 10:12:54 +0100 Subject: block: update docs for bio and bvec_iter The documentation for bio and bvec_iter refers to a vector named bvl_vec. This does not exist. Update the documentation comment with correct use. Also update documentation comments for remaining fields of `bvec_iter` to improve readability. The fields of `bvec_iter` is using a mix of tabs and spaces for indentation. While at it, change them all to tabs, which is most prevalent in this struct definition. Signed-off-by: Andreas Hindborg Signed-off-by: Jens Axboe --- include/linux/blk_types.h | 8 +++++++- include/linux/bvec.h | 29 +++++++++++++++++++++-------- 2 files changed, 28 insertions(+), 9 deletions(-) (limited to 'include/linux') diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index d59553324a84..397602606f74 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -273,7 +273,13 @@ struct bio { * Everything starting with bi_max_vecs will be preserved by bio_reset() */ - unsigned short bi_max_vecs; /* max bvl_vecs we can hold */ + /* + * Number of elements in `bi_io_vec` that were allocated for this bio. + * Only used by the bio submitter to make `bio_add_page` fail once full + * and to free the `bi_io_vec` allocation. Must not be used in drivers + * and does not hold a useful value for cloned bios. + */ + unsigned short bi_max_vecs; atomic_t __bi_cnt; /* pin count */ diff --git a/include/linux/bvec.h b/include/linux/bvec.h index 3fc0efa0825b..06fb60471aaf 100644 --- a/include/linux/bvec.h +++ b/include/linux/bvec.h @@ -75,14 +75,27 @@ static inline void bvec_set_virt(struct bio_vec *bv, void *vaddr, } struct bvec_iter { - sector_t bi_sector; /* device address in 512 byte - sectors */ - unsigned int bi_size; /* residual I/O count */ - - unsigned int bi_idx; /* current index into bvl_vec */ - - unsigned int bi_bvec_done; /* number of bytes completed in - current bvec */ + /* + * Current device address in 512 byte sectors. Only updated by the bio + * iter wrappers and not the bvec iterator helpers themselves. + */ + sector_t bi_sector; + + /* + * Remaining size in bytes. + */ + unsigned int bi_size; + + /* + * Current index into the bvec array. This indexes into `bi_io_vec` when + * iterating a bvec array that is part of a `bio`. + */ + unsigned int bi_idx; + + /* + * Current offset in the bvec entry pointed to by `bi_idx`. + */ + unsigned int bi_bvec_done; } __packed __aligned(4); struct bvec_iter_all { -- cgit v1.2.3 From 4c431a76a288ce958aaa114d8ea6fc0968942832 Mon Sep 17 00:00:00 2001 From: Randy Dunlap Date: Sun, 15 Feb 2026 21:29:41 -0800 Subject: block: fix enum descriptions kernel-doc Fix all kernel-doc warnings in blk_types.h: Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_READ' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_WRITE' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_FLUSH' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_DISCARD' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_SECURE_ERASE' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_ZONE_APPEND' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_WRITE_ZEROES' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_ZONE_OPEN' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_ZONE_CLOSE' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_ZONE_FINISH' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_ZONE_RESET' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_ZONE_RESET_ALL' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_DRV_IN' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_DRV_OUT' not described in enum 'req_op' Warning: include/linux/blk_types.h:371 Enum value 'REQ_OP_LAST' not described in enum 'req_op' Signed-off-by: Randy Dunlap Signed-off-by: Jens Axboe --- include/linux/blk_types.h | 25 +++++++++++++------------ 1 file changed, 13 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/include/linux/blk_types.h b/include/linux/blk_types.h index 397602606f74..8808ee76e73c 100644 --- a/include/linux/blk_types.h +++ b/include/linux/blk_types.h @@ -345,32 +345,33 @@ typedef __u32 __bitwise blk_mq_req_flags_t; * meaning. */ enum req_op { - /* read sectors from the device */ + /** @REQ_OP_READ: read sectors from the device */ REQ_OP_READ = (__force blk_opf_t)0, - /* write sectors to the device */ + /** @REQ_OP_WRITE: write sectors to the device */ REQ_OP_WRITE = (__force blk_opf_t)1, - /* flush the volatile write cache */ + /** @REQ_OP_FLUSH: flush the volatile write cache */ REQ_OP_FLUSH = (__force blk_opf_t)2, - /* discard sectors */ + /** @REQ_OP_DISCARD: discard sectors */ REQ_OP_DISCARD = (__force blk_opf_t)3, - /* securely erase sectors */ + /** @REQ_OP_SECURE_ERASE: securely erase sectors */ REQ_OP_SECURE_ERASE = (__force blk_opf_t)5, - /* write data at the current zone write pointer */ + /** @REQ_OP_ZONE_APPEND: write data at the current zone write pointer */ REQ_OP_ZONE_APPEND = (__force blk_opf_t)7, - /* write the zero filled sector many times */ + /** @REQ_OP_WRITE_ZEROES: write the zero filled sector many times */ REQ_OP_WRITE_ZEROES = (__force blk_opf_t)9, - /* Open a zone */ + /** @REQ_OP_ZONE_OPEN: Open a zone */ REQ_OP_ZONE_OPEN = (__force blk_opf_t)11, - /* Close a zone */ + /** @REQ_OP_ZONE_CLOSE: Close a zone */ REQ_OP_ZONE_CLOSE = (__force blk_opf_t)13, - /* Transition a zone to full */ + /** @REQ_OP_ZONE_FINISH: Transition a zone to full */ REQ_OP_ZONE_FINISH = (__force blk_opf_t)15, - /* reset a zone write pointer */ + /** @REQ_OP_ZONE_RESET: reset a zone write pointer */ REQ_OP_ZONE_RESET = (__force blk_opf_t)17, - /* reset all the zone present on the device */ + /** @REQ_OP_ZONE_RESET_ALL: reset all the zone present on the device */ REQ_OP_ZONE_RESET_ALL = (__force blk_opf_t)19, /* Driver private requests */ + /* private: */ REQ_OP_DRV_IN = (__force blk_opf_t)34, REQ_OP_DRV_OUT = (__force blk_opf_t)35, -- cgit v1.2.3 From 07919126ecfc392102555a70016db3e591abcb3d Mon Sep 17 00:00:00 2001 From: Sun Jian Date: Thu, 5 Feb 2026 20:30:17 +0800 Subject: netfilter: annotate NAT helper hook pointers with __rcu The NAT helper hook pointers are updated and dereferenced under RCU rules, but lack the proper __rcu annotation. This makes sparse report address space mismatches when the hooks are used with rcu_dereference(). Add the missing __rcu annotations to the global hook pointer declarations and definitions in Amanda, FTP, IRC, SNMP and TFTP. No functional change intended. Suggested-by: Florian Westphal Signed-off-by: Sun Jian Signed-off-by: Florian Westphal --- include/linux/netfilter/nf_conntrack_amanda.h | 2 +- include/linux/netfilter/nf_conntrack_ftp.h | 2 +- include/linux/netfilter/nf_conntrack_irc.h | 2 +- include/linux/netfilter/nf_conntrack_snmp.h | 2 +- include/linux/netfilter/nf_conntrack_tftp.h | 2 +- net/netfilter/nf_conntrack_amanda.c | 14 +++++++------- net/netfilter/nf_conntrack_ftp.c | 14 +++++++------- net/netfilter/nf_conntrack_irc.c | 13 +++++++------ net/netfilter/nf_conntrack_snmp.c | 8 ++++---- net/netfilter/nf_conntrack_tftp.c | 7 ++++--- 10 files changed, 34 insertions(+), 32 deletions(-) (limited to 'include/linux') diff --git a/include/linux/netfilter/nf_conntrack_amanda.h b/include/linux/netfilter/nf_conntrack_amanda.h index 6f0ac896fcc9..dfe89f38d1f7 100644 --- a/include/linux/netfilter/nf_conntrack_amanda.h +++ b/include/linux/netfilter/nf_conntrack_amanda.h @@ -7,7 +7,7 @@ #include #include -extern unsigned int (*nf_nat_amanda_hook)(struct sk_buff *skb, +extern unsigned int (__rcu *nf_nat_amanda_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, unsigned int protoff, unsigned int matchoff, diff --git a/include/linux/netfilter/nf_conntrack_ftp.h b/include/linux/netfilter/nf_conntrack_ftp.h index 0e38302820b9..f31292642035 100644 --- a/include/linux/netfilter/nf_conntrack_ftp.h +++ b/include/linux/netfilter/nf_conntrack_ftp.h @@ -26,7 +26,7 @@ struct nf_ct_ftp_master { /* For NAT to hook in when we find a packet which describes what other * connection we should expect. */ -extern unsigned int (*nf_nat_ftp_hook)(struct sk_buff *skb, +extern unsigned int (__rcu *nf_nat_ftp_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, enum nf_ct_ftp_type type, unsigned int protoff, diff --git a/include/linux/netfilter/nf_conntrack_irc.h b/include/linux/netfilter/nf_conntrack_irc.h index d02255f721e1..4f3ca5621998 100644 --- a/include/linux/netfilter/nf_conntrack_irc.h +++ b/include/linux/netfilter/nf_conntrack_irc.h @@ -8,7 +8,7 @@ #define IRC_PORT 6667 -extern unsigned int (*nf_nat_irc_hook)(struct sk_buff *skb, +extern unsigned int (__rcu *nf_nat_irc_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, unsigned int protoff, unsigned int matchoff, diff --git a/include/linux/netfilter/nf_conntrack_snmp.h b/include/linux/netfilter/nf_conntrack_snmp.h index 87e4f33eb55f..99107e4f5234 100644 --- a/include/linux/netfilter/nf_conntrack_snmp.h +++ b/include/linux/netfilter/nf_conntrack_snmp.h @@ -5,7 +5,7 @@ #include #include -extern int (*nf_nat_snmp_hook)(struct sk_buff *skb, +extern int (__rcu *nf_nat_snmp_hook)(struct sk_buff *skb, unsigned int protoff, struct nf_conn *ct, enum ip_conntrack_info ctinfo); diff --git a/include/linux/netfilter/nf_conntrack_tftp.h b/include/linux/netfilter/nf_conntrack_tftp.h index dc4c1b9beac0..1490b68dd7d1 100644 --- a/include/linux/netfilter/nf_conntrack_tftp.h +++ b/include/linux/netfilter/nf_conntrack_tftp.h @@ -19,7 +19,7 @@ struct tftphdr { #define TFTP_OPCODE_ACK 4 #define TFTP_OPCODE_ERROR 5 -extern unsigned int (*nf_nat_tftp_hook)(struct sk_buff *skb, +extern unsigned int (__rcu *nf_nat_tftp_hook)(struct sk_buff *skb, enum ip_conntrack_info ctinfo, struct nf_conntrack_expect *exp); diff --git a/net/netfilter/nf_conntrack_amanda.c b/net/netfilter/nf_conntrack_amanda.c index 7be4c35e4795..c0132559f6af 100644 --- a/net/netfilter/nf_conntrack_amanda.c +++ b/net/netfilter/nf_conntrack_amanda.c @@ -37,13 +37,13 @@ MODULE_PARM_DESC(master_timeout, "timeout for the master connection"); module_param(ts_algo, charp, 0400); MODULE_PARM_DESC(ts_algo, "textsearch algorithm to use (default kmp)"); -unsigned int (*nf_nat_amanda_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp) - __read_mostly; +unsigned int (__rcu *nf_nat_amanda_hook)(struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack_expect *exp) + __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_amanda_hook); enum amanda_strings { diff --git a/net/netfilter/nf_conntrack_ftp.c b/net/netfilter/nf_conntrack_ftp.c index 617f744a2e3a..5e00f9123c38 100644 --- a/net/netfilter/nf_conntrack_ftp.c +++ b/net/netfilter/nf_conntrack_ftp.c @@ -43,13 +43,13 @@ module_param_array(ports, ushort, &ports_c, 0400); static bool loose; module_param(loose, bool, 0600); -unsigned int (*nf_nat_ftp_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - enum nf_ct_ftp_type type, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp); +unsigned int (__rcu *nf_nat_ftp_hook)(struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + enum nf_ct_ftp_type type, + unsigned int protoff, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack_expect *exp); EXPORT_SYMBOL_GPL(nf_nat_ftp_hook); static int try_rfc959(const char *, size_t, struct nf_conntrack_man *, diff --git a/net/netfilter/nf_conntrack_irc.c b/net/netfilter/nf_conntrack_irc.c index 5703846bea3b..b8e6d724acd1 100644 --- a/net/netfilter/nf_conntrack_irc.c +++ b/net/netfilter/nf_conntrack_irc.c @@ -30,12 +30,13 @@ static unsigned int dcc_timeout __read_mostly = 300; static char *irc_buffer; static DEFINE_SPINLOCK(irc_buffer_lock); -unsigned int (*nf_nat_irc_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - unsigned int protoff, - unsigned int matchoff, - unsigned int matchlen, - struct nf_conntrack_expect *exp) __read_mostly; +unsigned int (__rcu *nf_nat_irc_hook)(struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + unsigned int protoff, + unsigned int matchoff, + unsigned int matchlen, + struct nf_conntrack_expect *exp) + __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_irc_hook); #define HELPER_NAME "irc" diff --git a/net/netfilter/nf_conntrack_snmp.c b/net/netfilter/nf_conntrack_snmp.c index daacf2023fa5..387dd6e58f88 100644 --- a/net/netfilter/nf_conntrack_snmp.c +++ b/net/netfilter/nf_conntrack_snmp.c @@ -25,10 +25,10 @@ static unsigned int timeout __read_mostly = 30; module_param(timeout, uint, 0400); MODULE_PARM_DESC(timeout, "timeout for master connection/replies in seconds"); -int (*nf_nat_snmp_hook)(struct sk_buff *skb, - unsigned int protoff, - struct nf_conn *ct, - enum ip_conntrack_info ctinfo); +int (__rcu *nf_nat_snmp_hook)(struct sk_buff *skb, + unsigned int protoff, + struct nf_conn *ct, + enum ip_conntrack_info ctinfo); EXPORT_SYMBOL_GPL(nf_nat_snmp_hook); static int snmp_conntrack_help(struct sk_buff *skb, unsigned int protoff, diff --git a/net/netfilter/nf_conntrack_tftp.c b/net/netfilter/nf_conntrack_tftp.c index 80ee53f29f68..89e9914e5d03 100644 --- a/net/netfilter/nf_conntrack_tftp.c +++ b/net/netfilter/nf_conntrack_tftp.c @@ -32,9 +32,10 @@ static unsigned int ports_c; module_param_array(ports, ushort, &ports_c, 0400); MODULE_PARM_DESC(ports, "Port numbers of TFTP servers"); -unsigned int (*nf_nat_tftp_hook)(struct sk_buff *skb, - enum ip_conntrack_info ctinfo, - struct nf_conntrack_expect *exp) __read_mostly; +unsigned int (__rcu *nf_nat_tftp_hook)(struct sk_buff *skb, + enum ip_conntrack_info ctinfo, + struct nf_conntrack_expect *exp) + __read_mostly; EXPORT_SYMBOL_GPL(nf_nat_tftp_hook); static int tftp_help(struct sk_buff *skb, -- cgit v1.2.3 From 47bf2e813817159f4d195be83a9b5a640ee6baec Mon Sep 17 00:00:00 2001 From: Shay Drory Date: Wed, 18 Feb 2026 09:28:59 +0200 Subject: net/mlx5: Fix multiport device check over light SFs Driver is using num_vhca_ports capability to distinguish between multiport master device and multiport slave device. num_vhca_ports is a capability the driver sets according to the MAX num_vhca_ports capability reported by FW. On the other hand, light SFs doesn't set the above capbility. This leads to wrong results whenever light SFs is checking whether he is a multiport master or slave. Therefore, use the MAX capability to distinguish between master and slave devices. Fixes: e71383fb9cd1 ("net/mlx5: Light probe local SFs") Signed-off-by: Shay Drory Reviewed-by: Moshe Shemesh Signed-off-by: Tariq Toukan Reviewed-by: Jacob Keller Link: https://patch.msgid.link/20260218072904.1764634-2-tariqt@nvidia.com Signed-off-by: Jakub Kicinski --- include/linux/mlx5/driver.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/mlx5/driver.h b/include/linux/mlx5/driver.h index e2d067b1e67b..04dcd09f7517 100644 --- a/include/linux/mlx5/driver.h +++ b/include/linux/mlx5/driver.h @@ -1282,12 +1282,12 @@ static inline bool mlx5_rl_is_supported(struct mlx5_core_dev *dev) static inline int mlx5_core_is_mp_slave(struct mlx5_core_dev *dev) { return MLX5_CAP_GEN(dev, affiliate_nic_vport_criteria) && - MLX5_CAP_GEN(dev, num_vhca_ports) <= 1; + MLX5_CAP_GEN_MAX(dev, num_vhca_ports) <= 1; } static inline int mlx5_core_is_mp_master(struct mlx5_core_dev *dev) { - return MLX5_CAP_GEN(dev, num_vhca_ports) > 1; + return MLX5_CAP_GEN_MAX(dev, num_vhca_ports) > 1; } static inline int mlx5_core_mp_enabled(struct mlx5_core_dev *dev) -- cgit v1.2.3