From cb3e9a446763da8850c8280be009d95f61e11a94 Mon Sep 17 00:00:00 2001 From: David Lechner Date: Fri, 22 Nov 2024 11:42:48 -0600 Subject: iio: adc: ad_sigma_delta: add tab to align irq_line Align the irq_line field in struct ad_sigma_delta with the other fields. Signed-off-by: David Lechner Link: https://patch.msgid.link/20241122-iio-adc-ad_signal_delta-fix-align-v1-1-d0a071d2dc83@baylibre.com Signed-off-by: Jonathan Cameron --- include/linux/iio/adc/ad_sigma_delta.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index f8c1d2505940..1851f8fed3a4 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -96,7 +96,7 @@ struct ad_sigma_delta { unsigned int active_slots; unsigned int current_slot; unsigned int num_slots; - int irq_line; + int irq_line; bool status_appended; /* map slots to channels in order to know what to expect from devices */ unsigned int *slots; -- cgit v1.2.3 From 0c39208bc3af6f7afcbcdb675ad8fbe6b3022865 Mon Sep 17 00:00:00 2001 From: Robert Budai Date: Mon, 25 Nov 2024 15:35:08 +0200 Subject: iio: imu: adis: Remove documented not used elements This patch removes elements from adis.h that are documented but not used anymore. Signed-off-by: Robert Budai Link: https://patch.msgid.link/20241125133520.24328-2-robert.budai@analog.com Signed-off-by: Jonathan Cameron --- include/linux/iio/imu/adis.h | 1 - 1 file changed, 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/iio/imu/adis.h b/include/linux/iio/imu/adis.h index e6a75356567a..4bb98d9731de 100644 --- a/include/linux/iio/imu/adis.h +++ b/include/linux/iio/imu/adis.h @@ -99,7 +99,6 @@ struct adis_data { * @spi: Reference to SPI device which owns this ADIS IIO device * @trig: IIO trigger object data * @data: ADIS chip variant specific data - * @burst: ADIS burst transfer information * @burst_extra_len: Burst extra length. Should only be used by devices that can * dynamically change their burst mode length. * @state_lock: Lock used by the device to protect state -- cgit v1.2.3 From e895f2edfe4820b671c700ccc17be35b1de3d295 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Mon, 25 Nov 2024 22:16:19 +0100 Subject: iio: core: fix doc reference to iio_push_to_buffers_with_ts_unaligned Use the right name of the function, which is defined in drivers/iio/industrialio-buffer.c Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20241125-iio_memset_scan_holes-v1-11-0cb6e98d895c@gmail.com Signed-off-by: Jonathan Cameron --- include/linux/iio/iio-opaque.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/iio/iio-opaque.h b/include/linux/iio/iio-opaque.h index a89e7e43e441..4247497f3f8b 100644 --- a/include/linux/iio/iio-opaque.h +++ b/include/linux/iio/iio-opaque.h @@ -28,7 +28,7 @@ * @groupcounter: index of next attribute group * @legacy_scan_el_group: attribute group for legacy scan elements attribute group * @legacy_buffer_group: attribute group for legacy buffer attributes group - * @bounce_buffer: for devices that call iio_push_to_buffers_with_timestamp_unaligned() + * @bounce_buffer: for devices that call iio_push_to_buffers_with_ts_unaligned() * @bounce_buffer_size: size of currently allocate bounce buffer * @scan_index_timestamp: cache of the index to the timestamp * @clock_id: timestamping clock posix identifier -- cgit v1.2.3 From 5aec7c065fba0c56d6c1ea5d629395210f174be8 Mon Sep 17 00:00:00 2001 From: James Clark Date: Thu, 28 Nov 2024 12:14:14 +0000 Subject: coresight: Drop atomics in connection refcounts These belong to the device being enabled or disabled and are only ever used inside the device's spinlock. Remove the atomics to not imply that there are any other concurrent accesses. If atomics were necessary I don't think they would have been enough anyway. There would be nothing to prevent an enable or disable running concurrently if not for the spinlock. Signed-off-by: James Clark Reviewed-by: Yeoreum Yun Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20241128121414.2425119-1-james.clark@linaro.org --- drivers/hwtracing/coresight/coresight-funnel.c | 6 +++--- drivers/hwtracing/coresight/coresight-replicator.c | 6 +++--- drivers/hwtracing/coresight/coresight-tpda.c | 6 +++--- include/linux/coresight.h | 4 ++-- 4 files changed, 11 insertions(+), 11 deletions(-) (limited to 'include/linux') diff --git a/drivers/hwtracing/coresight/coresight-funnel.c b/drivers/hwtracing/coresight/coresight-funnel.c index 33efe1acbef7..8faf51469bb8 100644 --- a/drivers/hwtracing/coresight/coresight-funnel.c +++ b/drivers/hwtracing/coresight/coresight-funnel.c @@ -86,14 +86,14 @@ static int funnel_enable(struct coresight_device *csdev, bool first_enable = false; spin_lock_irqsave(&drvdata->spinlock, flags); - if (atomic_read(&in->dest_refcnt) == 0) { + if (in->dest_refcnt == 0) { if (drvdata->base) rc = dynamic_funnel_enable_hw(drvdata, in->dest_port); if (!rc) first_enable = true; } if (!rc) - atomic_inc(&in->dest_refcnt); + in->dest_refcnt++; spin_unlock_irqrestore(&drvdata->spinlock, flags); if (first_enable) @@ -130,7 +130,7 @@ static void funnel_disable(struct coresight_device *csdev, bool last_disable = false; spin_lock_irqsave(&drvdata->spinlock, flags); - if (atomic_dec_return(&in->dest_refcnt) == 0) { + if (--in->dest_refcnt == 0) { if (drvdata->base) dynamic_funnel_disable_hw(drvdata, in->dest_port); last_disable = true; diff --git a/drivers/hwtracing/coresight/coresight-replicator.c b/drivers/hwtracing/coresight/coresight-replicator.c index 0fba87de6d1a..a1181c9048c0 100644 --- a/drivers/hwtracing/coresight/coresight-replicator.c +++ b/drivers/hwtracing/coresight/coresight-replicator.c @@ -126,7 +126,7 @@ static int replicator_enable(struct coresight_device *csdev, bool first_enable = false; spin_lock_irqsave(&drvdata->spinlock, flags); - if (atomic_read(&out->src_refcnt) == 0) { + if (out->src_refcnt == 0) { if (drvdata->base) rc = dynamic_replicator_enable(drvdata, in->dest_port, out->src_port); @@ -134,7 +134,7 @@ static int replicator_enable(struct coresight_device *csdev, first_enable = true; } if (!rc) - atomic_inc(&out->src_refcnt); + out->src_refcnt++; spin_unlock_irqrestore(&drvdata->spinlock, flags); if (first_enable) @@ -180,7 +180,7 @@ static void replicator_disable(struct coresight_device *csdev, bool last_disable = false; spin_lock_irqsave(&drvdata->spinlock, flags); - if (atomic_dec_return(&out->src_refcnt) == 0) { + if (--out->src_refcnt == 0) { if (drvdata->base) dynamic_replicator_disable(drvdata, in->dest_port, out->src_port); diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index bfca103f9f84..4ec676bea1ce 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -190,10 +190,10 @@ static int tpda_enable(struct coresight_device *csdev, int ret = 0; spin_lock(&drvdata->spinlock); - if (atomic_read(&in->dest_refcnt) == 0) { + if (in->dest_refcnt == 0) { ret = __tpda_enable(drvdata, in->dest_port); if (!ret) { - atomic_inc(&in->dest_refcnt); + in->dest_refcnt++; csdev->refcnt++; dev_dbg(drvdata->dev, "TPDA inport %d enabled.\n", in->dest_port); } @@ -223,7 +223,7 @@ static void tpda_disable(struct coresight_device *csdev, struct tpda_drvdata *drvdata = dev_get_drvdata(csdev->dev.parent); spin_lock(&drvdata->spinlock); - if (atomic_dec_return(&in->dest_refcnt) == 0) { + if (--in->dest_refcnt == 0) { __tpda_disable(drvdata, in->dest_port); csdev->refcnt--; } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index c13342594278..834029cb9ba2 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -200,8 +200,8 @@ struct coresight_connection { struct coresight_device *dest_dev; struct coresight_sysfs_link *link; struct coresight_device *src_dev; - atomic_t src_refcnt; - atomic_t dest_refcnt; + int src_refcnt; + int dest_refcnt; }; /** -- cgit v1.2.3 From fd9b7e8e9fbc23d69fa4accc881dea2cf13a2e2e Mon Sep 17 00:00:00 2001 From: Mao Jinlong Date: Thu, 21 Nov 2024 14:28:28 +0800 Subject: coresight: Add support to get static id for system trace sources Dynamic trace id was introduced in coresight subsystem, so trace id is allocated dynamically. However, some hardware ATB source has static trace id and it cannot be changed via software programming. For such source, it can call coresight_get_static_trace_id to get the fixed trace id from device node and pass id to coresight_trace_id_get_static_system_id to reserve the id. Signed-off-by: Mao Jinlong Reviewed-by: Mike Leach Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20241121062829.11571-3-quic_jinlmao@quicinc.com --- drivers/hwtracing/coresight/coresight-platform.c | 6 ++++ drivers/hwtracing/coresight/coresight-trace-id.c | 43 +++++++++++++++++------- drivers/hwtracing/coresight/coresight-trace-id.h | 9 +++++ include/linux/coresight.h | 1 + 4 files changed, 47 insertions(+), 12 deletions(-) (limited to 'include/linux') diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index 64e171eaad82..633d96b9577a 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -796,6 +796,12 @@ int coresight_get_cpu(struct device *dev) } EXPORT_SYMBOL_GPL(coresight_get_cpu); +int coresight_get_static_trace_id(struct device *dev, u32 *id) +{ + return fwnode_property_read_u32(dev_fwnode(dev), "arm,static-trace-id", id); +} +EXPORT_SYMBOL_GPL(coresight_get_static_trace_id); + struct coresight_platform_data * coresight_get_platform_data(struct device *dev) { diff --git a/drivers/hwtracing/coresight/coresight-trace-id.c b/drivers/hwtracing/coresight/coresight-trace-id.c index d98e12cb30ec..378af743be45 100644 --- a/drivers/hwtracing/coresight/coresight-trace-id.c +++ b/drivers/hwtracing/coresight/coresight-trace-id.c @@ -12,6 +12,12 @@ #include "coresight-trace-id.h" +enum trace_id_flags { + TRACE_ID_ANY = 0x0, + TRACE_ID_PREFER_ODD = 0x1, + TRACE_ID_REQ_STATIC = 0x2, +}; + /* Default trace ID map. Used in sysfs mode and for system sources */ static DEFINE_PER_CPU(atomic_t, id_map_default_cpu_ids) = ATOMIC_INIT(0); static struct coresight_trace_id_map id_map_default = { @@ -74,21 +80,25 @@ static int coresight_trace_id_find_odd_id(struct coresight_trace_id_map *id_map) * Otherwise allocate next available ID. */ static int coresight_trace_id_alloc_new_id(struct coresight_trace_id_map *id_map, - int preferred_id, bool prefer_odd_id) + int preferred_id, unsigned int flags) { int id = 0; /* for backwards compatibility, cpu IDs may use preferred value */ - if (IS_VALID_CS_TRACE_ID(preferred_id) && - !test_bit(preferred_id, id_map->used_ids)) { - id = preferred_id; - goto trace_id_allocated; - } else if (prefer_odd_id) { + if (IS_VALID_CS_TRACE_ID(preferred_id)) { + if (!test_bit(preferred_id, id_map->used_ids)) { + id = preferred_id; + goto trace_id_allocated; + } else if (flags & TRACE_ID_REQ_STATIC) + return -EBUSY; + } else if (flags & TRACE_ID_PREFER_ODD) { /* may use odd ids to avoid preferred legacy cpu IDs */ id = coresight_trace_id_find_odd_id(id_map); if (id) goto trace_id_allocated; - } + } else if (!IS_VALID_CS_TRACE_ID(preferred_id) && + (flags & TRACE_ID_REQ_STATIC)) + return -EINVAL; /* * skip reserved bit 0, look at bitmap length of @@ -153,7 +163,7 @@ static int _coresight_trace_id_get_cpu_id(int cpu, struct coresight_trace_id_map */ id = coresight_trace_id_alloc_new_id(id_map, CORESIGHT_LEGACY_CPU_TRACE_ID(cpu), - false); + TRACE_ID_ANY); if (!IS_VALID_CS_TRACE_ID(id)) goto get_cpu_id_out_unlock; @@ -188,14 +198,14 @@ static void _coresight_trace_id_put_cpu_id(int cpu, struct coresight_trace_id_ma DUMP_ID_MAP(id_map); } -static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *id_map) +static int coresight_trace_id_map_get_system_id(struct coresight_trace_id_map *id_map, + int preferred_id, unsigned int traceid_flags) { unsigned long flags; int id; spin_lock_irqsave(&id_map->lock, flags); - /* prefer odd IDs for system components to avoid legacy CPU IDS */ - id = coresight_trace_id_alloc_new_id(id_map, 0, true); + id = coresight_trace_id_alloc_new_id(id_map, preferred_id, traceid_flags); spin_unlock_irqrestore(&id_map->lock, flags); DUMP_ID(id); @@ -255,10 +265,19 @@ EXPORT_SYMBOL_GPL(coresight_trace_id_read_cpu_id_map); int coresight_trace_id_get_system_id(void) { - return coresight_trace_id_map_get_system_id(&id_map_default); + /* prefer odd IDs for system components to avoid legacy CPU IDS */ + return coresight_trace_id_map_get_system_id(&id_map_default, 0, + TRACE_ID_PREFER_ODD); } EXPORT_SYMBOL_GPL(coresight_trace_id_get_system_id); +int coresight_trace_id_get_static_system_id(int trace_id) +{ + return coresight_trace_id_map_get_system_id(&id_map_default, + trace_id, TRACE_ID_REQ_STATIC); +} +EXPORT_SYMBOL_GPL(coresight_trace_id_get_static_system_id); + void coresight_trace_id_put_system_id(int id) { coresight_trace_id_map_put_system_id(&id_map_default, id); diff --git a/drivers/hwtracing/coresight/coresight-trace-id.h b/drivers/hwtracing/coresight/coresight-trace-id.h index 9aae50a553ca..db68e1ec56b6 100644 --- a/drivers/hwtracing/coresight/coresight-trace-id.h +++ b/drivers/hwtracing/coresight/coresight-trace-id.h @@ -116,6 +116,15 @@ int coresight_trace_id_read_cpu_id_map(int cpu, struct coresight_trace_id_map *i */ int coresight_trace_id_get_system_id(void); +/** + * Allocate a CoreSight static trace ID for a system component. + * + * Used to allocate static IDs for system trace sources such as dummy source. + * + * return: Trace ID or -EINVAL if allocation is impossible. + */ +int coresight_trace_id_get_static_system_id(int id); + /** * Release an allocated system trace ID. * diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 834029cb9ba2..055ce5cd5c44 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -662,6 +662,7 @@ void coresight_relaxed_write64(struct coresight_device *csdev, void coresight_write64(struct coresight_device *csdev, u64 val, u32 offset); extern int coresight_get_cpu(struct device *dev); +extern int coresight_get_static_trace_id(struct device *dev, u32 *id); struct coresight_platform_data *coresight_get_platform_data(struct device *dev); struct coresight_connection * -- cgit v1.2.3 From a87ef09b1fdf75fdc2d6b386ff23a35589173055 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 6 Dec 2024 18:28:36 +0100 Subject: iio: adc: ad_sigma_delta: Add support for reading irq status using a GPIO MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Some of the ADCs by Analog signal their irq condition on the MISO line. So typically that line is connected to an SPI controller and a GPIO. The GPIO is used as input and the respective interrupt is enabled when the last SPI transfer is completed. Depending on the GPIO controller the toggling MISO line might make the interrupt pending even while it's masked. In that case the irq handler is called immediately after irq_enable() and so before the device actually pulls that line low which results in non-sense values being reported to the upper layers. The only way to find out if the line was actually pulled low is to read the GPIO. (There is a flag in AD7124's status register that also signals if an interrupt was asserted, but reading that register toggles the MISO line and so might trigger another spurious interrupt.) Add the possibility to specify an interrupt GPIO in the machine description in addition to the plain interrupt. This GPIO is used then to check if the irq line is actually active in the irq handler. Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/5be9a4cc4dc600ec384c88db01dd661a21506b9c.1733504533.git.u.kleine-koenig@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 38 +++++++++++++++++++++++++++++----- include/linux/iio/adc/ad_sigma_delta.h | 2 ++ 2 files changed, 35 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 3fd200b34161..8fe2ed8b30f9 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -539,12 +539,29 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) { struct ad_sigma_delta *sigma_delta = private; - complete(&sigma_delta->completion); - disable_irq_nosync(irq); - sigma_delta->irq_dis = true; - iio_trigger_poll(sigma_delta->trig); + /* + * AD7124 and a few others use the same physical line for interrupt + * reporting (R̅D̅Y̅) and MISO. + * As MISO toggles when reading a register, this likely results in a + * pending interrupt. This has two consequences: a) The irq might + * trigger immediately after it's enabled even though the conversion + * isn't done yet; and b) checking the STATUS register's R̅D̅Y̅ flag is + * off-limits as reading that would trigger another irq event. + * + * So read the MOSI line as GPIO (if available) and only trigger the irq + * if the line is active. Without such a GPIO assume this is a valid + * interrupt. + */ + if (!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) { + complete(&sigma_delta->completion); + disable_irq_nosync(irq); + sigma_delta->irq_dis = true; + iio_trigger_poll(sigma_delta->trig); - return IRQ_HANDLED; + return IRQ_HANDLED; + } + + return IRQ_NONE; } /** @@ -679,6 +696,17 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, else sigma_delta->irq_line = spi->irq; + sigma_delta->rdy_gpiod = devm_gpiod_get_optional(&spi->dev, "rdy", GPIOD_IN); + if (IS_ERR(sigma_delta->rdy_gpiod)) + return dev_err_probe(&spi->dev, PTR_ERR(sigma_delta->rdy_gpiod), + "Failed to find rdy gpio\n"); + + if (sigma_delta->rdy_gpiod && !sigma_delta->irq_line) { + sigma_delta->irq_line = gpiod_to_irq(sigma_delta->rdy_gpiod); + if (sigma_delta->irq_line < 0) + return sigma_delta->irq_line; + } + iio_device_set_drvdata(indio_dev, sigma_delta); return 0; diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 1851f8fed3a4..895b7ebf4be5 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -29,6 +29,7 @@ struct ad_sd_calib_data { struct ad_sigma_delta; struct device; +struct gpio_desc; struct iio_dev; /** @@ -96,6 +97,7 @@ struct ad_sigma_delta { unsigned int active_slots; unsigned int current_slot; unsigned int num_slots; + struct gpio_desc *rdy_gpiod; int irq_line; bool status_appended; /* map slots to channels in order to know what to expect from devices */ -- cgit v1.2.3 From f522589c139debb8af56dbead0c6e9dfca2d5ce4 Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 6 Dec 2024 18:28:38 +0100 Subject: iio: adc: ad_sigma_delta: Fix a race condition MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The ad_sigma_delta driver helper uses irq_disable_nosync(). With that one it is possible that the irq handler still runs after the irq_disable_nosync() function call returns. Also to properly synchronize irq disabling in the different threads proper locking is needed and because it's unclear if the irq handler's irq_disable_nosync() call comes first or the one in the enabler's error path, all code locations that disable the irq must check for .irq_dis first to ensure there is exactly one disable call per enable call. So add a spinlock to the struct ad_sigma_delta and use it to synchronize irq enabling and disabling. Also only act in the irq handler if the irq is still enabled. Fixes: af3008485ea0 ("iio:adc: Add common code for ADI Sigma Delta devices") Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/9e6def47e2e773e0e15b7a2c29d22629b53d91b1.1733504533.git.u.kleine-koenig@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad_sigma_delta.c | 56 +++++++++++++++++++++------------- include/linux/iio/adc/ad_sigma_delta.h | 1 + 2 files changed, 36 insertions(+), 21 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 65608dc2bfec..950baf4160da 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -202,6 +202,27 @@ int ad_sd_reset(struct ad_sigma_delta *sigma_delta, } EXPORT_SYMBOL_NS_GPL(ad_sd_reset, "IIO_AD_SIGMA_DELTA"); +static bool ad_sd_disable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + /* It's already off, return false to indicate nothing was changed */ + if (sigma_delta->irq_dis) + return false; + + sigma_delta->irq_dis = true; + disable_irq_nosync(sigma_delta->irq_line); + return true; +} + +static void ad_sd_enable_irq(struct ad_sigma_delta *sigma_delta) +{ + guard(spinlock_irqsave)(&sigma_delta->irq_lock); + + sigma_delta->irq_dis = false; + enable_irq(sigma_delta->irq_line); +} + int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, unsigned int mode, unsigned int channel) { @@ -221,12 +242,10 @@ int ad_sd_calibrate(struct ad_sigma_delta *sigma_delta, if (ret < 0) goto out; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); time_left = wait_for_completion_timeout(&sigma_delta->completion, 2 * HZ); if (time_left == 0) { - sigma_delta->irq_dis = true; - disable_irq_nosync(sigma_delta->irq_line); + ad_sd_disable_irq(sigma_delta); ret = -EIO; } else { ret = 0; @@ -294,8 +313,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_SINGLE); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); ret = wait_for_completion_interruptible_timeout( &sigma_delta->completion, HZ); @@ -314,10 +332,7 @@ int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, &raw_sample); out: - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->irq_line); - sigma_delta->irq_dis = true; - } + ad_sd_disable_irq(sigma_delta); sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); @@ -396,8 +411,7 @@ static int ad_sd_buffer_postenable(struct iio_dev *indio_dev) if (ret) goto err_unlock; - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); return 0; @@ -414,10 +428,7 @@ static int ad_sd_buffer_postdisable(struct iio_dev *indio_dev) reinit_completion(&sigma_delta->completion); wait_for_completion_timeout(&sigma_delta->completion, HZ); - if (!sigma_delta->irq_dis) { - disable_irq_nosync(sigma_delta->irq_line); - sigma_delta->irq_dis = true; - } + ad_sd_disable_irq(sigma_delta); sigma_delta->keep_cs_asserted = false; ad_sigma_delta_set_mode(sigma_delta, AD_SD_MODE_IDLE); @@ -516,8 +527,7 @@ static irqreturn_t ad_sd_trigger_handler(int irq, void *p) irq_handled: iio_trigger_notify_done(indio_dev->trig); - sigma_delta->irq_dis = false; - enable_irq(sigma_delta->irq_line); + ad_sd_enable_irq(sigma_delta); return IRQ_HANDLED; } @@ -551,11 +561,13 @@ static irqreturn_t ad_sd_data_rdy_trig_poll(int irq, void *private) * So read the MOSI line as GPIO (if available) and only trigger the irq * if the line is active. Without such a GPIO assume this is a valid * interrupt. + * + * Also as disable_irq_nosync() is used to disable the irq, only act if + * the irq wasn't disabled before. */ - if (!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) { + if ((!sigma_delta->rdy_gpiod || gpiod_get_value(sigma_delta->rdy_gpiod)) && + ad_sd_disable_irq(sigma_delta)) { complete(&sigma_delta->completion); - disable_irq_nosync(irq); - sigma_delta->irq_dis = true; iio_trigger_poll(sigma_delta->trig); return IRQ_HANDLED; @@ -691,6 +703,8 @@ int ad_sd_init(struct ad_sigma_delta *sigma_delta, struct iio_dev *indio_dev, } } + spin_lock_init(&sigma_delta->irq_lock); + if (info->irq_line) sigma_delta->irq_line = info->irq_line; else diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 895b7ebf4be5..200130e4244d 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -86,6 +86,7 @@ struct ad_sigma_delta { /* private: */ struct completion completion; + spinlock_t irq_lock; /* protects .irq_dis and irq en/disable state */ bool irq_dis; bool bus_locked; -- cgit v1.2.3 From 07a28874bb49700036a3ab435dd95ae31afd21ae Mon Sep 17 00:00:00 2001 From: Uwe Kleine-König Date: Fri, 6 Dec 2024 18:28:39 +0100 Subject: iio: adc: ad_sigma_delta: Store information about reset sequence length MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit The various chips can be reset using a sequence of SPI transfers with MOSI = 1. The length of such a sequence varies from chip to chip. Store that length in struct ad_sigma_delta_info and replace the respective parameter to ad_sd_reset() with it. Note the ad7192 used to pass 48 as length but the documentation specifies 40 as the required length. Assuming the latter is right. (Using a too long sequence doesn't hurt apart from using a longer spi transfer than necessary, so this is no relevant fix.) The motivation for storing this information is that this is useful to clear a pending R̅D̅Y̅ signal in the next change. Signed-off-by: Uwe Kleine-König Link: https://patch.msgid.link/9750db62fce638bf140ff48172c23bff7f785e5b.1733504533.git.u.kleine-koenig@baylibre.com Signed-off-by: Jonathan Cameron --- drivers/iio/adc/ad7124.c | 3 ++- drivers/iio/adc/ad7173.c | 1 + drivers/iio/adc/ad7192.c | 4 +++- drivers/iio/adc/ad7791.c | 1 + drivers/iio/adc/ad7793.c | 3 ++- drivers/iio/adc/ad_sigma_delta.c | 5 ++--- include/linux/iio/adc/ad_sigma_delta.h | 5 +++-- 7 files changed, 14 insertions(+), 8 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/adc/ad7124.c b/drivers/iio/adc/ad7124.c index b7491198e33c..af74bea18a69 100644 --- a/drivers/iio/adc/ad7124.c +++ b/drivers/iio/adc/ad7124.c @@ -571,6 +571,7 @@ static const struct ad_sigma_delta_info ad7124_sigma_delta_info = { .data_reg = AD7124_DATA, .num_slots = 8, .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 64, }; static int ad7124_read_raw(struct iio_dev *indio_dev, @@ -756,7 +757,7 @@ static int ad7124_soft_reset(struct ad7124_state *st) unsigned int readval, timeout; int ret; - ret = ad_sd_reset(&st->sd, 64); + ret = ad_sd_reset(&st->sd); if (ret < 0) return ret; diff --git a/drivers/iio/adc/ad7173.c b/drivers/iio/adc/ad7173.c index c5ac4b7e7c2c..d48b5d98207e 100644 --- a/drivers/iio/adc/ad7173.c +++ b/drivers/iio/adc/ad7173.c @@ -763,6 +763,7 @@ static struct ad_sigma_delta_info ad7173_sigma_delta_info = { .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), .data_reg = AD7173_REG_DATA, + .num_resetclks = 64, }; static int ad7173_setup(struct iio_dev *indio_dev) diff --git a/drivers/iio/adc/ad7192.c b/drivers/iio/adc/ad7192.c index 1c87db0e0460..e96a5ae92375 100644 --- a/drivers/iio/adc/ad7192.c +++ b/drivers/iio/adc/ad7192.c @@ -361,6 +361,7 @@ static const struct ad_sigma_delta_info ad7192_sigma_delta_info = { .status_ch_mask = GENMASK(3, 0), .num_slots = 4, .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 40, }; static const struct ad_sigma_delta_info ad7194_sigma_delta_info = { @@ -373,6 +374,7 @@ static const struct ad_sigma_delta_info ad7194_sigma_delta_info = { .read_mask = BIT(6), .status_ch_mask = GENMASK(3, 0), .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 40, }; static const struct ad_sd_calib_data ad7192_calib_arr[8] = { @@ -565,7 +567,7 @@ static int ad7192_setup(struct iio_dev *indio_dev, struct device *dev) int i, ret, id; /* reset the serial interface */ - ret = ad_sd_reset(&st->sd, 48); + ret = ad_sd_reset(&st->sd); if (ret < 0) return ret; usleep_range(500, 1000); /* Wait for at least 500us */ diff --git a/drivers/iio/adc/ad7791.c b/drivers/iio/adc/ad7791.c index e1bf13fe2cd7..76118fe22db8 100644 --- a/drivers/iio/adc/ad7791.c +++ b/drivers/iio/adc/ad7791.c @@ -254,6 +254,7 @@ static const struct ad_sigma_delta_info ad7791_sigma_delta_info = { .addr_shift = 4, .read_mask = BIT(3), .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 32, }; static int ad7791_read_raw(struct iio_dev *indio_dev, diff --git a/drivers/iio/adc/ad7793.c b/drivers/iio/adc/ad7793.c index d55c71566707..1b50d9643a63 100644 --- a/drivers/iio/adc/ad7793.c +++ b/drivers/iio/adc/ad7793.c @@ -206,6 +206,7 @@ static const struct ad_sigma_delta_info ad7793_sigma_delta_info = { .addr_shift = 3, .read_mask = BIT(6), .irq_flags = IRQF_TRIGGER_FALLING, + .num_resetclks = 32, }; static const struct ad_sd_calib_data ad7793_calib_arr[6] = { @@ -265,7 +266,7 @@ static int ad7793_setup(struct iio_dev *indio_dev, return ret; /* reset the serial interface */ - ret = ad_sd_reset(&st->sd, 32); + ret = ad_sd_reset(&st->sd); if (ret < 0) goto out; usleep_range(500, 2000); /* Wait for at least 500us */ diff --git a/drivers/iio/adc/ad_sigma_delta.c b/drivers/iio/adc/ad_sigma_delta.c index 950baf4160da..c290d07ab1c5 100644 --- a/drivers/iio/adc/ad_sigma_delta.c +++ b/drivers/iio/adc/ad_sigma_delta.c @@ -178,13 +178,12 @@ EXPORT_SYMBOL_NS_GPL(ad_sd_read_reg, "IIO_AD_SIGMA_DELTA"); * ad_sd_reset() - Reset the serial interface * * @sigma_delta: The sigma delta device - * @reset_length: Number of SCLKs with DIN = 1 * * Returns 0 on success, an error code otherwise. **/ -int ad_sd_reset(struct ad_sigma_delta *sigma_delta, - unsigned int reset_length) +int ad_sd_reset(struct ad_sigma_delta *sigma_delta) { + unsigned int reset_length = sigma_delta->info->num_resetclks; uint8_t *buf; unsigned int size; int ret; diff --git a/include/linux/iio/adc/ad_sigma_delta.h b/include/linux/iio/adc/ad_sigma_delta.h index 200130e4244d..417073c52380 100644 --- a/include/linux/iio/adc/ad_sigma_delta.h +++ b/include/linux/iio/adc/ad_sigma_delta.h @@ -54,6 +54,7 @@ struct iio_dev; * @irq_flags: flags for the interrupt used by the triggered buffer * @num_slots: Number of sequencer slots * @irq_line: IRQ for reading conversions. If 0, spi->irq will be used + * @num_resetclks: Number of SPI clk cycles with MOSI=1 to reset the chip. */ struct ad_sigma_delta_info { int (*set_channel)(struct ad_sigma_delta *, unsigned int channel); @@ -70,6 +71,7 @@ struct ad_sigma_delta_info { unsigned long irq_flags; unsigned int num_slots; int irq_line; + unsigned int num_resetclks; }; /** @@ -181,8 +183,7 @@ int ad_sd_write_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, int ad_sd_read_reg(struct ad_sigma_delta *sigma_delta, unsigned int reg, unsigned int size, unsigned int *val); -int ad_sd_reset(struct ad_sigma_delta *sigma_delta, - unsigned int reset_length); +int ad_sd_reset(struct ad_sigma_delta *sigma_delta); int ad_sigma_delta_single_conversion(struct iio_dev *indio_dev, const struct iio_chan_spec *chan, int *val); -- cgit v1.2.3 From 22ccb0a1c57c436de899ccd3170d6d2ce7238836 Mon Sep 17 00:00:00 2001 From: Matteo Martelli Date: Mon, 2 Dec 2024 16:11:07 +0100 Subject: iio: consumers: ensure read buffers for labels and ext_info are page aligned Attributes of iio providers are exposed via sysfs. Typically, providers pass attribute values to the iio core, which handles formatting and printing to sysfs. However, some attributes, such as labels or extended info, are directly formatted and printed to sysfs by provider drivers using sysfs_emit() and sysfs_emit_at(). These helpers assume the read buffer, allocated by sysfs fop, is page-aligned. When these attributes are accessed by consumer drivers, the read buffer is allocated by the consumer and may not be page-aligned, leading to failures in the provider's callback that utilizes sysfs_emit*. Add a check to ensure that read buffers for labels and external info attributes are page-aligned. Update the prototype documentation as well. Signed-off-by: Matteo Martelli Link: https://patch.msgid.link/20241202-iio-kmalloc-align-v1-1-aa9568c03937@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/inkern.c | 11 +++++++++++ include/linux/iio/consumer.h | 4 ++-- 2 files changed, 13 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/inkern.c b/drivers/iio/inkern.c index 136b225b6bc8..520743fde103 100644 --- a/drivers/iio/inkern.c +++ b/drivers/iio/inkern.c @@ -7,6 +7,7 @@ #include #include #include +#include #include #include #include @@ -989,6 +990,11 @@ ssize_t iio_read_channel_ext_info(struct iio_channel *chan, { const struct iio_chan_spec_ext_info *ext_info; + if (!buf || offset_in_page(buf)) { + pr_err("iio: invalid ext_info read buffer\n"); + return -EINVAL; + } + ext_info = iio_lookup_ext_info(chan, attr); if (!ext_info) return -EINVAL; @@ -1014,6 +1020,11 @@ EXPORT_SYMBOL_GPL(iio_write_channel_ext_info); ssize_t iio_read_channel_label(struct iio_channel *chan, char *buf) { + if (!buf || offset_in_page(buf)) { + pr_err("iio: invalid label read buffer\n"); + return -EINVAL; + } + return do_iio_read_channel_label(chan->indio_dev, chan->channel, buf); } EXPORT_SYMBOL_GPL(iio_read_channel_label); diff --git a/include/linux/iio/consumer.h b/include/linux/iio/consumer.h index 333d1d8ccb37..6a4479616479 100644 --- a/include/linux/iio/consumer.h +++ b/include/linux/iio/consumer.h @@ -418,7 +418,7 @@ unsigned int iio_get_channel_ext_info_count(struct iio_channel *chan); * @chan: The channel being queried. * @attr: The ext_info attribute to read. * @buf: Where to store the attribute value. Assumed to hold - * at least PAGE_SIZE bytes. + * at least PAGE_SIZE bytes and to be aligned at PAGE_SIZE. * * Returns the number of bytes written to buf (perhaps w/o zero termination; * it need not even be a string), or an error code. @@ -445,7 +445,7 @@ ssize_t iio_write_channel_ext_info(struct iio_channel *chan, const char *attr, * iio_read_channel_label() - read label for a given channel * @chan: The channel being queried. * @buf: Where to store the attribute value. Assumed to hold - * at least PAGE_SIZE bytes. + * at least PAGE_SIZE bytes and to be aligned at PAGE_SIZE. * * Returns the number of bytes written to buf, or an error code. */ -- cgit v1.2.3 From 62374ce1876be26b3f33575680e67ca69a59db54 Mon Sep 17 00:00:00 2001 From: Tao Zhang Date: Fri, 13 Dec 2024 18:07:29 +0800 Subject: coresight: Add a helper to check if a device is source Since there are a lot of places in the code to check whether the device is source, add a helper to check it. Signed-off-by: Tao Zhang Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20241213100731.25914-3-quic_taozha@quicinc.com --- drivers/hwtracing/coresight/coresight-tpda.c | 2 +- include/linux/coresight.h | 7 ++++++- 2 files changed, 7 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/hwtracing/coresight/coresight-tpda.c b/drivers/hwtracing/coresight/coresight-tpda.c index 4ec676bea1ce..ab94067c0ed3 100644 --- a/drivers/hwtracing/coresight/coresight-tpda.c +++ b/drivers/hwtracing/coresight/coresight-tpda.c @@ -24,7 +24,7 @@ DEFINE_CORESIGHT_DEVLIST(tpda_devs, "tpda"); static bool coresight_device_is_tpdm(struct coresight_device *csdev) { - return (csdev->type == CORESIGHT_DEV_TYPE_SOURCE) && + return (coresight_is_device_source(csdev)) && (csdev->subtype.source_subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_TPDM); } diff --git a/include/linux/coresight.h b/include/linux/coresight.h index 055ce5cd5c44..c50d128e8d93 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -588,9 +588,14 @@ static inline void csdev_access_write64(struct csdev_access *csa, u64 val, u32 o } #endif /* CONFIG_64BIT */ +static inline bool coresight_is_device_source(struct coresight_device *csdev) +{ + return csdev && (csdev->type == CORESIGHT_DEV_TYPE_SOURCE); +} + static inline bool coresight_is_percpu_source(struct coresight_device *csdev) { - return csdev && (csdev->type == CORESIGHT_DEV_TYPE_SOURCE) && + return csdev && coresight_is_device_source(csdev) && (csdev->subtype.source_subtype == CORESIGHT_DEV_SUBTYPE_SOURCE_PROC); } -- cgit v1.2.3 From ec9903d6cc34e61b77e609a0425e7a0a804fb95a Mon Sep 17 00:00:00 2001 From: Tao Zhang Date: Fri, 13 Dec 2024 18:07:30 +0800 Subject: coresight: Add support for trace filtering by source Some replicators have hard coded filtering of "trace" data, based on the source device. This is different from the trace filtering based on TraceID, available in the standard programmable replicators. e.g., Qualcomm replicators have filtering based on custom trace protocol format and is not programmable. The source device could be connected to the replicator via intermediate components (e.g., a funnel). Thus we need platform information from the firmware tables to decide the source device corresponding to a given output port from the replicator. Given this affects "trace path building" and traversing the path back from the sink to source, add the concept of "filtering by source" to the generic coresight connection. The specified source will be marked like below in the Devicetree. test-replicator { ... ... ... ... out-ports { ... ... ... ... port@0 { reg = <0>; xyz: endpoint { remote-endpoint = <&zyx>; filter-source = <&source_1>; <-- To specify the source to }; be filtered out here. }; port@1 { reg = <1>; abc: endpoint { remote-endpoint = <&cba>; filter-source = <&source_2>; <-- To specify the source to }; be filtered out here. }; }; }; Signed-off-by: Tao Zhang Signed-off-by: Suzuki K Poulose Link: https://lore.kernel.org/r/20241213100731.25914-4-quic_taozha@quicinc.com --- drivers/hwtracing/coresight/coresight-core.c | 113 +++++++++++++++++++---- drivers/hwtracing/coresight/coresight-platform.c | 21 +++++ include/linux/coresight.h | 5 + 3 files changed, 120 insertions(+), 19 deletions(-) (limited to 'include/linux') diff --git a/drivers/hwtracing/coresight/coresight-core.c b/drivers/hwtracing/coresight/coresight-core.c index ea38ecf26fcb..0a9380350fb5 100644 --- a/drivers/hwtracing/coresight/coresight-core.c +++ b/drivers/hwtracing/coresight/coresight-core.c @@ -75,22 +75,54 @@ struct coresight_device *coresight_get_percpu_sink(int cpu) } EXPORT_SYMBOL_GPL(coresight_get_percpu_sink); +static struct coresight_device *coresight_get_source(struct list_head *path) +{ + struct coresight_device *csdev; + + if (!path) + return NULL; + + csdev = list_first_entry(path, struct coresight_node, link)->csdev; + if (!coresight_is_device_source(csdev)) + return NULL; + + return csdev; +} + +/** + * coresight_blocks_source - checks whether the connection matches the source + * of path if connection is bound to specific source. + * @src: The source device of the trace path + * @conn: The connection of one outport + * + * Return false if the connection doesn't have a source binded or source of the + * path matches the source binds to connection. + */ +static bool coresight_blocks_source(struct coresight_device *src, + struct coresight_connection *conn) +{ + return conn->filter_src_fwnode && (conn->filter_src_dev != src); +} + static struct coresight_connection * -coresight_find_out_connection(struct coresight_device *src_dev, - struct coresight_device *dest_dev) +coresight_find_out_connection(struct coresight_device *csdev, + struct coresight_device *out_dev, + struct coresight_device *trace_src) { int i; struct coresight_connection *conn; - for (i = 0; i < src_dev->pdata->nr_outconns; i++) { - conn = src_dev->pdata->out_conns[i]; - if (conn->dest_dev == dest_dev) + for (i = 0; i < csdev->pdata->nr_outconns; i++) { + conn = csdev->pdata->out_conns[i]; + if (coresight_blocks_source(trace_src, conn)) + continue; + if (conn->dest_dev == out_dev) return conn; } - dev_err(&src_dev->dev, - "couldn't find output connection, src_dev: %s, dest_dev: %s\n", - dev_name(&src_dev->dev), dev_name(&dest_dev->dev)); + dev_err(&csdev->dev, + "couldn't find output connection, csdev: %s, out_dev: %s\n", + dev_name(&csdev->dev), dev_name(&out_dev->dev)); return ERR_PTR(-ENODEV); } @@ -251,7 +283,8 @@ static void coresight_disable_sink(struct coresight_device *csdev) static int coresight_enable_link(struct coresight_device *csdev, struct coresight_device *parent, - struct coresight_device *child) + struct coresight_device *child, + struct coresight_device *source) { int link_subtype; struct coresight_connection *inconn, *outconn; @@ -259,8 +292,8 @@ static int coresight_enable_link(struct coresight_device *csdev, if (!parent || !child) return -EINVAL; - inconn = coresight_find_out_connection(parent, csdev); - outconn = coresight_find_out_connection(csdev, child); + inconn = coresight_find_out_connection(parent, csdev, source); + outconn = coresight_find_out_connection(csdev, child, source); link_subtype = csdev->subtype.link_subtype; if (link_subtype == CORESIGHT_DEV_SUBTYPE_LINK_MERG && IS_ERR(inconn)) @@ -273,15 +306,16 @@ static int coresight_enable_link(struct coresight_device *csdev, static void coresight_disable_link(struct coresight_device *csdev, struct coresight_device *parent, - struct coresight_device *child) + struct coresight_device *child, + struct coresight_device *source) { struct coresight_connection *inconn, *outconn; if (!parent || !child) return; - inconn = coresight_find_out_connection(parent, csdev); - outconn = coresight_find_out_connection(csdev, child); + inconn = coresight_find_out_connection(parent, csdev, source); + outconn = coresight_find_out_connection(csdev, child, source); link_ops(csdev)->disable(csdev, inconn, outconn); } @@ -375,7 +409,8 @@ static void coresight_disable_path_from(struct list_head *path, case CORESIGHT_DEV_TYPE_LINK: parent = list_prev_entry(nd, link)->csdev; child = list_next_entry(nd, link)->csdev; - coresight_disable_link(csdev, parent, child); + coresight_disable_link(csdev, parent, child, + coresight_get_source(path)); break; default: break; @@ -418,7 +453,9 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode, u32 type; struct coresight_node *nd; struct coresight_device *csdev, *parent, *child; + struct coresight_device *source; + source = coresight_get_source(path); list_for_each_entry_reverse(nd, path, link) { csdev = nd->csdev; type = csdev->type; @@ -456,7 +493,7 @@ int coresight_enable_path(struct list_head *path, enum cs_mode mode, case CORESIGHT_DEV_TYPE_LINK: parent = list_prev_entry(nd, link)->csdev; child = list_next_entry(nd, link)->csdev; - ret = coresight_enable_link(csdev, parent, child); + ret = coresight_enable_link(csdev, parent, child, source); if (ret) goto err; break; @@ -619,6 +656,7 @@ static void coresight_drop_device(struct coresight_device *csdev) /** * _coresight_build_path - recursively build a path from a @csdev to a sink. * @csdev: The device to start from. + * @source: The trace source device of the path. * @sink: The final sink we want in this path. * @path: The list to add devices to. * @@ -628,6 +666,7 @@ static void coresight_drop_device(struct coresight_device *csdev) * the source is the first device and the sink the last one. */ static int _coresight_build_path(struct coresight_device *csdev, + struct coresight_device *source, struct coresight_device *sink, struct list_head *path) { @@ -641,7 +680,7 @@ static int _coresight_build_path(struct coresight_device *csdev, if (coresight_is_percpu_source(csdev) && coresight_is_percpu_sink(sink) && sink == per_cpu(csdev_sink, source_ops(csdev)->cpu_id(csdev))) { - if (_coresight_build_path(sink, sink, path) == 0) { + if (_coresight_build_path(sink, source, sink, path) == 0) { found = true; goto out; } @@ -652,8 +691,12 @@ static int _coresight_build_path(struct coresight_device *csdev, struct coresight_device *child_dev; child_dev = csdev->pdata->out_conns[i]->dest_dev; + + if (coresight_blocks_source(source, csdev->pdata->out_conns[i])) + continue; + if (child_dev && - _coresight_build_path(child_dev, sink, path) == 0) { + _coresight_build_path(child_dev, source, sink, path) == 0) { found = true; break; } @@ -698,7 +741,7 @@ struct list_head *coresight_build_path(struct coresight_device *source, INIT_LIST_HEAD(path); - rc = _coresight_build_path(source, sink, path); + rc = _coresight_build_path(source, source, sink, path); if (rc) { kfree(path); return ERR_PTR(rc); @@ -927,6 +970,16 @@ static int coresight_orphan_match(struct device *dev, void *data) for (i = 0; i < src_csdev->pdata->nr_outconns; i++) { conn = src_csdev->pdata->out_conns[i]; + /* Fix filter source device before skip the port */ + if (conn->filter_src_fwnode && !conn->filter_src_dev) { + if (dst_csdev && + (conn->filter_src_fwnode == dst_csdev->dev.fwnode) && + !WARN_ON_ONCE(!coresight_is_device_source(dst_csdev))) + conn->filter_src_dev = dst_csdev; + else + still_orphan = true; + } + /* Skip the port if it's already connected. */ if (conn->dest_dev) continue; @@ -977,18 +1030,40 @@ static int coresight_fixup_orphan_conns(struct coresight_device *csdev) csdev, coresight_orphan_match); } +static int coresight_clear_filter_source(struct device *dev, void *data) +{ + int i; + struct coresight_device *source = data; + struct coresight_device *csdev = to_coresight_device(dev); + + for (i = 0; i < csdev->pdata->nr_outconns; ++i) { + if (csdev->pdata->out_conns[i]->filter_src_dev == source) + csdev->pdata->out_conns[i]->filter_src_dev = NULL; + } + return 0; +} + /* coresight_remove_conns - Remove other device's references to this device */ static void coresight_remove_conns(struct coresight_device *csdev) { int i, j; struct coresight_connection *conn; + if (coresight_is_device_source(csdev)) + bus_for_each_dev(&coresight_bustype, NULL, csdev, + coresight_clear_filter_source); + /* * Remove the input connection references from the destination device * for each output connection. */ for (i = 0; i < csdev->pdata->nr_outconns; i++) { conn = csdev->pdata->out_conns[i]; + if (conn->filter_src_fwnode) { + conn->filter_src_dev = NULL; + fwnode_handle_put(conn->filter_src_fwnode); + } + if (!conn->dest_dev) continue; diff --git a/drivers/hwtracing/coresight/coresight-platform.c b/drivers/hwtracing/coresight/coresight-platform.c index 633d96b9577a..8192ba3279f0 100644 --- a/drivers/hwtracing/coresight/coresight-platform.c +++ b/drivers/hwtracing/coresight/coresight-platform.c @@ -243,6 +243,27 @@ static int of_coresight_parse_endpoint(struct device *dev, conn.dest_fwnode = fwnode_handle_get(rdev_fwnode); conn.dest_port = rendpoint.port; + /* + * Get the firmware node of the filter source through the + * reference. This could be used to filter the source in + * building path. + */ + conn.filter_src_fwnode = + fwnode_find_reference(&ep->fwnode, "filter-source", 0); + if (IS_ERR(conn.filter_src_fwnode)) { + conn.filter_src_fwnode = NULL; + } else { + conn.filter_src_dev = + coresight_find_csdev_by_fwnode(conn.filter_src_fwnode); + if (conn.filter_src_dev && + !coresight_is_device_source(conn.filter_src_dev)) { + dev_warn(dev, "port %d: Filter handle is not a trace source : %s\n", + conn.src_port, dev_name(&conn.filter_src_dev->dev)); + conn.filter_src_dev = NULL; + conn.filter_src_fwnode = NULL; + } + } + new_conn = coresight_add_out_conn(dev, pdata, &conn); if (IS_ERR_VALUE(new_conn)) { fwnode_handle_put(conn.dest_fwnode); diff --git a/include/linux/coresight.h b/include/linux/coresight.h index c50d128e8d93..17276965ff1d 100644 --- a/include/linux/coresight.h +++ b/include/linux/coresight.h @@ -172,6 +172,9 @@ struct coresight_desc { * @dest_dev: a @coresight_device representation of the component connected to @src_port. NULL until the device is created * @link: Representation of the connection as a sysfs link. + * @filter_src_fwnode: filter source component's fwnode handle. + * @filter_src_dev: a @coresight_device representation of the component that + needs to be filtered. * * The full connection structure looks like this, where in_conns store * references to same connection as the source device's out_conns. @@ -200,6 +203,8 @@ struct coresight_connection { struct coresight_device *dest_dev; struct coresight_sysfs_link *link; struct coresight_device *src_dev; + struct fwnode_handle *filter_src_fwnode; + struct coresight_device *filter_src_dev; int src_refcnt; int dest_refcnt; }; -- cgit v1.2.3 From 94ddd8bf98d76f03297a2b33a951711b31f7bc38 Mon Sep 17 00:00:00 2001 From: Lee Jones Date: Mon, 23 Dec 2024 15:18:37 +0000 Subject: misc: trivial: Remove undesired double space from struct definition When one is too lazy to use an LSP to conduct look-ups on struct definitions, one might use the ever useful `struct {` search string. However this doesn't work with `struct miscdevice {` because of a stray double space. Assuming that this wasn't intentional, let's simply remove it. Signed-off-by: Lee Jones Link: https://lore.kernel.org/r/20241223151843.472645-1-lee@kernel.org Signed-off-by: Greg Kroah-Hartman --- include/linux/miscdevice.h | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) (limited to 'include/linux') diff --git a/include/linux/miscdevice.h b/include/linux/miscdevice.h index c0fea6ca5076..69e110c2b86a 100644 --- a/include/linux/miscdevice.h +++ b/include/linux/miscdevice.h @@ -76,7 +76,7 @@ struct device; struct attribute_group; -struct miscdevice { +struct miscdevice { int minor; const char *name; const struct file_operations *fops; -- cgit v1.2.3 From 9351bbb1b022227644022850bf2160b04e970195 Mon Sep 17 00:00:00 2001 From: Vasileios Amoiridis Date: Sat, 14 Dec 2024 20:14:21 +0100 Subject: iio: core: mark scan_timestamp as __private Since there are no more direct accesses to the indio_dev->scan_timestamp value, it can be marked as __private and use the macro ACCESS_PRIVATE() in order to access it. Like this, static checkers will be able to inform in case someone tries to either write to the value, or read its value directly. Signed-off-by: Vasileios Amoiridis Link: https://patch.msgid.link/20241214191421.94172-5-vassilisamir@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-buffer.c | 2 +- include/linux/iio/buffer.h | 2 +- include/linux/iio/iio.h | 2 +- 3 files changed, 3 insertions(+), 3 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/industrialio-buffer.c b/drivers/iio/industrialio-buffer.c index 2708f87df719..a80f7cc25a27 100644 --- a/drivers/iio/industrialio-buffer.c +++ b/drivers/iio/industrialio-buffer.c @@ -1137,7 +1137,7 @@ static int iio_enable_buffers(struct iio_dev *indio_dev, int ret; indio_dev->active_scan_mask = config->scan_mask; - indio_dev->scan_timestamp = config->scan_timestamp; + ACCESS_PRIVATE(indio_dev, scan_timestamp) = config->scan_timestamp; indio_dev->scan_bytes = config->scan_bytes; iio_dev_opaque->currentmode = config->mode; diff --git a/include/linux/iio/buffer.h b/include/linux/iio/buffer.h index 418b1307d3f2..3b8d618bb3df 100644 --- a/include/linux/iio/buffer.h +++ b/include/linux/iio/buffer.h @@ -37,7 +37,7 @@ int iio_pop_from_buffer(struct iio_buffer *buffer, void *data); static inline int iio_push_to_buffers_with_timestamp(struct iio_dev *indio_dev, void *data, int64_t timestamp) { - if (indio_dev->scan_timestamp) { + if (ACCESS_PRIVATE(indio_dev, scan_timestamp)) { size_t ts_offset = indio_dev->scan_bytes / sizeof(int64_t) - 1; ((int64_t *)data)[ts_offset] = timestamp; } diff --git a/include/linux/iio/iio.h b/include/linux/iio/iio.h index ae65890d4567..56161e02f002 100644 --- a/include/linux/iio/iio.h +++ b/include/linux/iio/iio.h @@ -611,7 +611,7 @@ struct iio_dev { const unsigned long *available_scan_masks; unsigned int __private masklength; const unsigned long *active_scan_mask; - bool scan_timestamp; + bool __private scan_timestamp; struct iio_trigger *trig; struct iio_poll_func *pollfunc; struct iio_poll_func *pollfunc_event; -- cgit v1.2.3 From e2f9d754fc5b5dcb53a0df627f386b63f8ba2d68 Mon Sep 17 00:00:00 2001 From: Fabrice Gasnier Date: Fri, 20 Dec 2024 10:59:21 +0100 Subject: iio: trigger: stm32-timer: add support for stm32mp25 Add support for STM32MP25 SoC. Use newly introduced compatible to handle this new HW variant. Add TIM20 trigger definitions that can be used by the stm32 analog-to-digital converter. Use compatible data to identify it. As the counter framework is now superseding the deprecated IIO counter interface (IIO_COUNT), don't support it. Only register IIO trigger devices for ADC usage. So, make the valids_table a cfg option. Signed-off-by: Fabrice Gasnier Link: https://patch.msgid.link/20241220095927.1122782-4-fabrice.gasnier@foss.st.com Signed-off-by: Jonathan Cameron --- drivers/iio/trigger/stm32-timer-trigger.c | 21 +++++++++++++++++++-- include/linux/iio/timer/stm32-timer-trigger.h | 6 ++++++ 2 files changed, 25 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/drivers/iio/trigger/stm32-timer-trigger.c b/drivers/iio/trigger/stm32-timer-trigger.c index bd58e60f586c..e41cb741253b 100644 --- a/drivers/iio/trigger/stm32-timer-trigger.c +++ b/drivers/iio/trigger/stm32-timer-trigger.c @@ -38,6 +38,9 @@ static const void *triggers_table[][MAX_TRIGGERS] = { { TIM15_TRGO,}, { TIM16_OC1,}, { TIM17_OC1,}, + { }, /* timer 18 */ + { }, /* timer 19 */ + { TIM20_TRGO, TIM20_TRGO2, TIM20_OC1, TIM20_OC2, TIM20_OC3, }, }; /* List the triggers accepted by each timer */ @@ -791,7 +794,7 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) return -EINVAL; /* Create an IIO device only if we have triggers to be validated */ - if (*cfg->valids_table[index]) + if (cfg->valids_table && *cfg->valids_table[index]) priv = stm32_setup_counter_device(dev); else priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -804,7 +807,8 @@ static int stm32_timer_trigger_probe(struct platform_device *pdev) priv->clk = ddata->clk; priv->max_arr = ddata->max_arr; priv->triggers = triggers_table[index]; - priv->valids = cfg->valids_table[index]; + if (cfg->valids_table && *cfg->valids_table[index]) + priv->valids = cfg->valids_table[index]; stm32_timer_detect_trgo2(priv); mutex_init(&priv->lock); @@ -896,6 +900,16 @@ static const struct stm32_timer_trigger_cfg stm32h7_timer_trg_cfg = { .num_valids_table = ARRAY_SIZE(stm32h7_valids_table), }; +static const struct stm32_timer_trigger_cfg stm32mp25_timer_trg_cfg = { + /* + * valids_table not used: counter framework is now superseding the deprecated IIO + * counter interface (IIO_COUNT), so don't support it. num_valids_table is only + * kept here to register the IIO HW triggers. valids_table should be moved at some + * point to the stm32-timer-cnt driver instead. + */ + .num_valids_table = ARRAY_SIZE(triggers_table), +}; + static const struct of_device_id stm32_trig_of_match[] = { { .compatible = "st,stm32-timer-trigger", @@ -903,6 +917,9 @@ static const struct of_device_id stm32_trig_of_match[] = { }, { .compatible = "st,stm32h7-timer-trigger", .data = (void *)&stm32h7_timer_trg_cfg, + }, { + .compatible = "st,stm32mp25-timer-trigger", + .data = (void *)&stm32mp25_timer_trg_cfg, }, { /* end node */ }, }; diff --git a/include/linux/iio/timer/stm32-timer-trigger.h b/include/linux/iio/timer/stm32-timer-trigger.h index 37572e4dc73a..1ee237b56183 100644 --- a/include/linux/iio/timer/stm32-timer-trigger.h +++ b/include/linux/iio/timer/stm32-timer-trigger.h @@ -72,6 +72,12 @@ #define TIM17_OC1 "tim17_oc1" +#define TIM20_OC1 "tim20_oc1" +#define TIM20_OC2 "tim20_oc2" +#define TIM20_OC3 "tim20_oc3" +#define TIM20_TRGO "tim20_trgo" +#define TIM20_TRGO2 "tim20_trgo2" + #if IS_REACHABLE(CONFIG_IIO_STM32_TIMER_TRIGGER) bool is_stm32_timer_trigger(struct iio_trigger *trig); #else -- cgit v1.2.3 From 6fdbc7b9aa20b1db47d13a5f2a4d31fb2f8f3822 Mon Sep 17 00:00:00 2001 From: Théo Lebrun Date: Mon, 30 Dec 2024 14:30:27 +0000 Subject: nvmem: specify ->reg_read/reg_write() expected return values MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Both ->reg_read() and ->reg_write() return values are not easy to deduce. Explicit that they should return zero on success (and negative values otherwise). Such callbacks, in some alternative world, could return the number of bytes in the success case. That would be translated to errors in the nvmem core because of checks like: ret = nvmem->reg_write(nvmem->priv, offset, val, bytes); if (ret) { // error case } This mistake is not just theoretical, see commit 28b008751aa2 ("nvmem: rmem: Fix return value of rmem_read()"). Signed-off-by: Théo Lebrun Signed-off-by: Srinivas Kandagatla Link: https://lore.kernel.org/r/20241230143035.265518-4-srinivas.kandagatla@linaro.org Signed-off-by: Greg Kroah-Hartman --- include/linux/nvmem-provider.h | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'include/linux') diff --git a/include/linux/nvmem-provider.h b/include/linux/nvmem-provider.h index 3ebeaa0ded00..515676ebe598 100644 --- a/include/linux/nvmem-provider.h +++ b/include/linux/nvmem-provider.h @@ -92,8 +92,8 @@ struct nvmem_cell_info { * @read_only: Device is read-only. * @root_only: Device is accessibly to root only. * @of_node: If given, this will be used instead of the parent's of_node. - * @reg_read: Callback to read data. - * @reg_write: Callback to write data. + * @reg_read: Callback to read data; return zero if successful. + * @reg_write: Callback to write data; return zero if successful. * @size: Device size. * @word_size: Minimum read/write access granularity. * @stride: Minimum read/write access stride. -- cgit v1.2.3 From 7716d085531bf797c882ed67eda184ac58a387a8 Mon Sep 17 00:00:00 2001 From: Javier Carrasco Date: Mon, 30 Dec 2024 16:13:52 +0100 Subject: iio: gts-helper: add helpers to ease searches of gain_sel and new_gain This helper functions reduce the burden in the drivers that want to fetch a gain and time selector for a given scale or a new optimal gain. The former is currently achieved by calling iio_gts_find_gain_sel_for_scale_using_time() for the current time selector, and then iterating over the rest of time selectors if the gain selector was not found. The latter requires a combination of multiple iio-gts helpers to find the new gain, look for an optimal gain if there was no exact match, and set a minimum gain if the optimal gain is not in the range of available gains. Provide simpler workflows by means of functions that address common patterns in the users of the iio-gts helpers. Acked-by: Matti Vaittinen Signed-off-by: Javier Carrasco Link: https://patch.msgid.link/20241230-veml3235_scale-v3-1-48a5795e2f64@gmail.com Signed-off-by: Jonathan Cameron --- drivers/iio/industrialio-gts-helper.c | 77 +++++++++++++++++++++++++++++++++++ include/linux/iio/iio-gts-helper.h | 6 +++ 2 files changed, 83 insertions(+) (limited to 'include/linux') diff --git a/drivers/iio/industrialio-gts-helper.c b/drivers/iio/industrialio-gts-helper.c index 3b5a99815062..d70ebe3bf774 100644 --- a/drivers/iio/industrialio-gts-helper.c +++ b/drivers/iio/industrialio-gts-helper.c @@ -915,6 +915,41 @@ int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel } EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_sel_for_scale_using_time, "IIO_GTS_HELPER"); +/** + * iio_gts_find_gain_time_sel_for_scale - Fetch gain and time selectors for scale + * @gts: Gain time scale descriptor + * @scale_int: Integral part of the scale (typically val1) + * @scale_nano: Fractional part of the scale (nano or ppb) + * @gain_sel: Pointer to value where gain selector is stored. + * @time_sel: Pointer to value where time selector is stored. + * + * Wrapper around iio_gts_find_gain_for_scale_using_time() to fetch the + * gain and time selectors for a given scale. + * + * Return: 0 on success and -EINVAL on error. + */ +int iio_gts_find_gain_time_sel_for_scale(struct iio_gts *gts, int scale_int, + int scale_nano, int *gain_sel, + int *time_sel) +{ + int i, ret; + + for (i = 0; i < gts->num_itime; i++) { + *time_sel = gts->itime_table[i].sel; + ret = iio_gts_find_gain_sel_for_scale_using_time(gts, *time_sel, + scale_int, + scale_nano, + gain_sel); + if (ret) + continue; + + return 0; + } + + return -EINVAL; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_gain_time_sel_for_scale, "IIO_GTS_HELPER"); + static int iio_gts_get_total_gain(struct iio_gts *gts, int gain, int time) { const struct iio_itime_sel_mul *itime; @@ -1086,6 +1121,48 @@ int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain, } EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_old_gain_time, "IIO_GTS_HELPER"); +/** + * iio_gts_find_new_gain_by_gain_time_min - compensate for time change + * @gts: Gain time scale descriptor + * @old_gain: Previously set gain + * @old_time: Selector corresponding previously set time + * @new_time: Selector corresponding new time to be set + * @new_gain: Pointer to value where new gain is to be written + * @in_range: Indicate if the @new_gain was in the range of + * supported gains. + * + * Wrapper around iio_gts_find_new_gain_by_old_gain_time() that tries to + * set an optimal value if no exact match was found, defaulting to the + * minimum gain to avoid saturations if the optimal value is not in the + * range of supported gains. + * + * Return: 0 on success and a negative value if no gain was found. + */ +int iio_gts_find_new_gain_by_gain_time_min(struct iio_gts *gts, int old_gain, + int old_time, int new_time, + int *new_gain, bool *in_range) +{ + int ret; + + *in_range = true; + ret = iio_gts_find_new_gain_by_old_gain_time(gts, old_gain, old_time, + new_time, new_gain); + if (*new_gain < 0) + return -EINVAL; + + if (ret) { + *new_gain = iio_find_closest_gain_low(gts, *new_gain, in_range); + if (*new_gain < 0) { + *new_gain = iio_gts_get_min_gain(gts); + if (*new_gain < 0) + return -EINVAL; + } + } + + return 0; +} +EXPORT_SYMBOL_NS_GPL(iio_gts_find_new_gain_by_gain_time_min, "IIO_GTS_HELPER"); + MODULE_LICENSE("GPL"); MODULE_AUTHOR("Matti Vaittinen "); MODULE_DESCRIPTION("IIO light sensor gain-time-scale helpers"); diff --git a/include/linux/iio/iio-gts-helper.h b/include/linux/iio/iio-gts-helper.h index 9cb6c80dea71..e5de7a124bad 100644 --- a/include/linux/iio/iio-gts-helper.h +++ b/include/linux/iio/iio-gts-helper.h @@ -188,6 +188,9 @@ int iio_gts_total_gain_to_scale(struct iio_gts *gts, int total_gain, int iio_gts_find_gain_sel_for_scale_using_time(struct iio_gts *gts, int time_sel, int scale_int, int scale_nano, int *gain_sel); +int iio_gts_find_gain_time_sel_for_scale(struct iio_gts *gts, int scale_int, + int scale_nano, int *gain_sel, + int *time_sel); int iio_gts_get_scale(struct iio_gts *gts, int gain, int time, int *scale_int, int *scale_nano); int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts, @@ -196,6 +199,9 @@ int iio_gts_find_new_gain_sel_by_old_gain_time(struct iio_gts *gts, int iio_gts_find_new_gain_by_old_gain_time(struct iio_gts *gts, int old_gain, int old_time, int new_time, int *new_gain); +int iio_gts_find_new_gain_by_gain_time_min(struct iio_gts *gts, int old_gain, + int old_time, int new_time, + int *new_gain, bool *in_range); int iio_gts_avail_times(struct iio_gts *gts, const int **vals, int *type, int *length); int iio_gts_all_avail_scales(struct iio_gts *gts, const int **vals, int *type, -- cgit v1.2.3 From c79a39dc8d060b9e64e8b0fa9d245d44befeefbe Mon Sep 17 00:00:00 2001 From: Calvin Owens Date: Mon, 11 Nov 2024 20:13:29 -0800 Subject: pps: Fix a use-after-free On a board running ntpd and gpsd, I'm seeing a consistent use-after-free in sys_exit() from gpsd when rebooting: pps pps1: removed ------------[ cut here ]------------ kobject: '(null)' (00000000db4bec24): is not initialized, yet kobject_put() is being called. WARNING: CPU: 2 PID: 440 at lib/kobject.c:734 kobject_put+0x120/0x150 CPU: 2 UID: 299 PID: 440 Comm: gpsd Not tainted 6.11.0-rc6-00308-gb31c44928842 #1 Hardware name: Raspberry Pi 4 Model B Rev 1.1 (DT) pstate: 60000005 (nZCv daif -PAN -UAO -TCO -DIT -SSBS BTYPE=--) pc : kobject_put+0x120/0x150 lr : kobject_put+0x120/0x150 sp : ffffffc0803d3ae0 x29: ffffffc0803d3ae0 x28: ffffff8042dc9738 x27: 0000000000000001 x26: 0000000000000000 x25: ffffff8042dc9040 x24: ffffff8042dc9440 x23: ffffff80402a4620 x22: ffffff8042ef4bd0 x21: ffffff80405cb600 x20: 000000000008001b x19: ffffff8040b3b6e0 x18: 0000000000000000 x17: 0000000000000000 x16: 0000000000000000 x15: 696e6920746f6e20 x14: 7369203a29343263 x13: 205d303434542020 x12: 0000000000000000 x11: 0000000000000000 x10: 0000000000000000 x9 : 0000000000000000 x8 : 0000000000000000 x7 : 0000000000000000 x6 : 0000000000000000 x5 : 0000000000000000 x4 : 0000000000000000 x3 : 0000000000000000 x2 : 0000000000000000 x1 : 0000000000000000 x0 : 0000000000000000 Call trace: kobject_put+0x120/0x150 cdev_put+0x20/0x3c __fput+0x2c4/0x2d8 ____fput+0x1c/0x38 task_work_run+0x70/0xfc do_exit+0x2a0/0x924 do_group_exit+0x34/0x90 get_signal+0x7fc/0x8c0 do_signal+0x128/0x13b4 do_notify_resume+0xdc/0x160 el0_svc+0xd4/0xf8 el0t_64_sync_handler+0x140/0x14c el0t_64_sync+0x190/0x194 ---[ end trace 0000000000000000 ]--- ...followed by more symptoms of corruption, with similar stacks: refcount_t: underflow; use-after-free. kernel BUG at lib/list_debug.c:62! Kernel panic - not syncing: Oops - BUG: Fatal exception This happens because pps_device_destruct() frees the pps_device with the embedded cdev immediately after calling cdev_del(), but, as the comment above cdev_del() notes, fops for previously opened cdevs are still callable even after cdev_del() returns. I think this bug has always been there: I can't explain why it suddenly started happening every time I reboot this particular board. In commit d953e0e837e6 ("pps: Fix a use-after free bug when unregistering a source."), George Spelvin suggested removing the embedded cdev. That seems like the simplest way to fix this, so I've implemented his suggestion, using __register_chrdev() with pps_idr becoming the source of truth for which minor corresponds to which device. But now that pps_idr defines userspace visibility instead of cdev_add(), we need to be sure the pps->dev refcount can't reach zero while userspace can still find it again. So, the idr_remove() call moves to pps_unregister_cdev(), and pps_idr now holds a reference to pps->dev. pps_core: source serial1 got cdev (251:1) <...> pps pps1: removed pps_core: unregistering pps1 pps_core: deallocating pps1 Fixes: d953e0e837e6 ("pps: Fix a use-after free bug when unregistering a source.") Cc: stable@vger.kernel.org Signed-off-by: Calvin Owens Reviewed-by: Michal Schmidt Link: https://lore.kernel.org/r/a17975fd5ae99385791929e563f72564edbcf28f.1731383727.git.calvin@wbinvd.org Signed-off-by: Greg Kroah-Hartman --- drivers/pps/clients/pps-gpio.c | 4 +- drivers/pps/clients/pps-ktimer.c | 4 +- drivers/pps/clients/pps-ldisc.c | 6 +- drivers/pps/clients/pps_parport.c | 4 +- drivers/pps/kapi.c | 10 +-- drivers/pps/kc.c | 10 +-- drivers/pps/pps.c | 127 ++++++++++++++++++++------------------ drivers/ptp/ptp_ocp.c | 2 +- include/linux/pps_kernel.h | 3 +- 9 files changed, 87 insertions(+), 83 deletions(-) (limited to 'include/linux') diff --git a/drivers/pps/clients/pps-gpio.c b/drivers/pps/clients/pps-gpio.c index 634c3b2f8c26..f77b19884f05 100644 --- a/drivers/pps/clients/pps-gpio.c +++ b/drivers/pps/clients/pps-gpio.c @@ -214,8 +214,8 @@ static int pps_gpio_probe(struct platform_device *pdev) return -EINVAL; } - dev_info(data->pps->dev, "Registered IRQ %d as PPS source\n", - data->irq); + dev_dbg(&data->pps->dev, "Registered IRQ %d as PPS source\n", + data->irq); return 0; } diff --git a/drivers/pps/clients/pps-ktimer.c b/drivers/pps/clients/pps-ktimer.c index d33106bd7a29..2f465549b843 100644 --- a/drivers/pps/clients/pps-ktimer.c +++ b/drivers/pps/clients/pps-ktimer.c @@ -56,7 +56,7 @@ static struct pps_source_info pps_ktimer_info = { static void __exit pps_ktimer_exit(void) { - dev_info(pps->dev, "ktimer PPS source unregistered\n"); + dev_dbg(&pps->dev, "ktimer PPS source unregistered\n"); del_timer_sync(&ktimer); pps_unregister_source(pps); @@ -74,7 +74,7 @@ static int __init pps_ktimer_init(void) timer_setup(&ktimer, pps_ktimer_event, 0); mod_timer(&ktimer, jiffies + HZ); - dev_info(pps->dev, "ktimer PPS source registered\n"); + dev_dbg(&pps->dev, "ktimer PPS source registered\n"); return 0; } diff --git a/drivers/pps/clients/pps-ldisc.c b/drivers/pps/clients/pps-ldisc.c index 443d6bae19d1..fa5660f3c4b7 100644 --- a/drivers/pps/clients/pps-ldisc.c +++ b/drivers/pps/clients/pps-ldisc.c @@ -32,7 +32,7 @@ static void pps_tty_dcd_change(struct tty_struct *tty, bool active) pps_event(pps, &ts, active ? PPS_CAPTUREASSERT : PPS_CAPTURECLEAR, NULL); - dev_dbg(pps->dev, "PPS %s at %lu\n", + dev_dbg(&pps->dev, "PPS %s at %lu\n", active ? "assert" : "clear", jiffies); } @@ -69,7 +69,7 @@ static int pps_tty_open(struct tty_struct *tty) goto err_unregister; } - dev_info(pps->dev, "source \"%s\" added\n", info.path); + dev_dbg(&pps->dev, "source \"%s\" added\n", info.path); return 0; @@ -89,7 +89,7 @@ static void pps_tty_close(struct tty_struct *tty) if (WARN_ON(!pps)) return; - dev_info(pps->dev, "removed\n"); + dev_info(&pps->dev, "removed\n"); pps_unregister_source(pps); } diff --git a/drivers/pps/clients/pps_parport.c b/drivers/pps/clients/pps_parport.c index abaffb4e1c1c..24db06750297 100644 --- a/drivers/pps/clients/pps_parport.c +++ b/drivers/pps/clients/pps_parport.c @@ -81,7 +81,7 @@ static void parport_irq(void *handle) /* check the signal (no signal means the pulse is lost this time) */ if (!signal_is_set(port)) { local_irq_restore(flags); - dev_err(dev->pps->dev, "lost the signal\n"); + dev_err(&dev->pps->dev, "lost the signal\n"); goto out_assert; } @@ -98,7 +98,7 @@ static void parport_irq(void *handle) /* timeout */ dev->cw_err++; if (dev->cw_err >= CLEAR_WAIT_MAX_ERRORS) { - dev_err(dev->pps->dev, "disabled clear edge capture after %d" + dev_err(&dev->pps->dev, "disabled clear edge capture after %d" " timeouts\n", dev->cw_err); dev->cw = 0; dev->cw_err = 0; diff --git a/drivers/pps/kapi.c b/drivers/pps/kapi.c index d9d566f70ed1..92d1b62ea239 100644 --- a/drivers/pps/kapi.c +++ b/drivers/pps/kapi.c @@ -41,7 +41,7 @@ static void pps_add_offset(struct pps_ktime *ts, struct pps_ktime *offset) static void pps_echo_client_default(struct pps_device *pps, int event, void *data) { - dev_info(pps->dev, "echo %s %s\n", + dev_info(&pps->dev, "echo %s %s\n", event & PPS_CAPTUREASSERT ? "assert" : "", event & PPS_CAPTURECLEAR ? "clear" : ""); } @@ -112,7 +112,7 @@ struct pps_device *pps_register_source(struct pps_source_info *info, goto kfree_pps; } - dev_info(pps->dev, "new PPS source %s\n", info->name); + dev_dbg(&pps->dev, "new PPS source %s\n", info->name); return pps; @@ -166,7 +166,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, /* check event type */ BUG_ON((event & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR)) == 0); - dev_dbg(pps->dev, "PPS event at %lld.%09ld\n", + dev_dbg(&pps->dev, "PPS event at %lld.%09ld\n", (s64)ts->ts_real.tv_sec, ts->ts_real.tv_nsec); timespec_to_pps_ktime(&ts_real, ts->ts_real); @@ -188,7 +188,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, /* Save the time stamp */ pps->assert_tu = ts_real; pps->assert_sequence++; - dev_dbg(pps->dev, "capture assert seq #%u\n", + dev_dbg(&pps->dev, "capture assert seq #%u\n", pps->assert_sequence); captured = ~0; @@ -202,7 +202,7 @@ void pps_event(struct pps_device *pps, struct pps_event_time *ts, int event, /* Save the time stamp */ pps->clear_tu = ts_real; pps->clear_sequence++; - dev_dbg(pps->dev, "capture clear seq #%u\n", + dev_dbg(&pps->dev, "capture clear seq #%u\n", pps->clear_sequence); captured = ~0; diff --git a/drivers/pps/kc.c b/drivers/pps/kc.c index 50dc59af45be..fbd23295afd7 100644 --- a/drivers/pps/kc.c +++ b/drivers/pps/kc.c @@ -43,11 +43,11 @@ int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args) pps_kc_hardpps_mode = 0; pps_kc_hardpps_dev = NULL; spin_unlock_irq(&pps_kc_hardpps_lock); - dev_info(pps->dev, "unbound kernel" + dev_info(&pps->dev, "unbound kernel" " consumer\n"); } else { spin_unlock_irq(&pps_kc_hardpps_lock); - dev_err(pps->dev, "selected kernel consumer" + dev_err(&pps->dev, "selected kernel consumer" " is not bound\n"); return -EINVAL; } @@ -57,11 +57,11 @@ int pps_kc_bind(struct pps_device *pps, struct pps_bind_args *bind_args) pps_kc_hardpps_mode = bind_args->edge; pps_kc_hardpps_dev = pps; spin_unlock_irq(&pps_kc_hardpps_lock); - dev_info(pps->dev, "bound kernel consumer: " + dev_info(&pps->dev, "bound kernel consumer: " "edge=0x%x\n", bind_args->edge); } else { spin_unlock_irq(&pps_kc_hardpps_lock); - dev_err(pps->dev, "another kernel consumer" + dev_err(&pps->dev, "another kernel consumer" " is already bound\n"); return -EINVAL; } @@ -83,7 +83,7 @@ void pps_kc_remove(struct pps_device *pps) pps_kc_hardpps_mode = 0; pps_kc_hardpps_dev = NULL; spin_unlock_irq(&pps_kc_hardpps_lock); - dev_info(pps->dev, "unbound kernel consumer" + dev_info(&pps->dev, "unbound kernel consumer" " on device removal\n"); } else spin_unlock_irq(&pps_kc_hardpps_lock); diff --git a/drivers/pps/pps.c b/drivers/pps/pps.c index 25d47907db17..6a02245ea35f 100644 --- a/drivers/pps/pps.c +++ b/drivers/pps/pps.c @@ -25,7 +25,7 @@ * Local variables */ -static dev_t pps_devt; +static int pps_major; static struct class *pps_class; static DEFINE_MUTEX(pps_idr_lock); @@ -62,7 +62,7 @@ static int pps_cdev_pps_fetch(struct pps_device *pps, struct pps_fdata *fdata) else { unsigned long ticks; - dev_dbg(pps->dev, "timeout %lld.%09d\n", + dev_dbg(&pps->dev, "timeout %lld.%09d\n", (long long) fdata->timeout.sec, fdata->timeout.nsec); ticks = fdata->timeout.sec * HZ; @@ -80,7 +80,7 @@ static int pps_cdev_pps_fetch(struct pps_device *pps, struct pps_fdata *fdata) /* Check for pending signals */ if (err == -ERESTARTSYS) { - dev_dbg(pps->dev, "pending signal caught\n"); + dev_dbg(&pps->dev, "pending signal caught\n"); return -EINTR; } @@ -98,7 +98,7 @@ static long pps_cdev_ioctl(struct file *file, switch (cmd) { case PPS_GETPARAMS: - dev_dbg(pps->dev, "PPS_GETPARAMS\n"); + dev_dbg(&pps->dev, "PPS_GETPARAMS\n"); spin_lock_irq(&pps->lock); @@ -114,7 +114,7 @@ static long pps_cdev_ioctl(struct file *file, break; case PPS_SETPARAMS: - dev_dbg(pps->dev, "PPS_SETPARAMS\n"); + dev_dbg(&pps->dev, "PPS_SETPARAMS\n"); /* Check the capabilities */ if (!capable(CAP_SYS_TIME)) @@ -124,14 +124,14 @@ static long pps_cdev_ioctl(struct file *file, if (err) return -EFAULT; if (!(params.mode & (PPS_CAPTUREASSERT | PPS_CAPTURECLEAR))) { - dev_dbg(pps->dev, "capture mode unspecified (%x)\n", + dev_dbg(&pps->dev, "capture mode unspecified (%x)\n", params.mode); return -EINVAL; } /* Check for supported capabilities */ if ((params.mode & ~pps->info.mode) != 0) { - dev_dbg(pps->dev, "unsupported capabilities (%x)\n", + dev_dbg(&pps->dev, "unsupported capabilities (%x)\n", params.mode); return -EINVAL; } @@ -144,7 +144,7 @@ static long pps_cdev_ioctl(struct file *file, /* Restore the read only parameters */ if ((params.mode & (PPS_TSFMT_TSPEC | PPS_TSFMT_NTPFP)) == 0) { /* section 3.3 of RFC 2783 interpreted */ - dev_dbg(pps->dev, "time format unspecified (%x)\n", + dev_dbg(&pps->dev, "time format unspecified (%x)\n", params.mode); pps->params.mode |= PPS_TSFMT_TSPEC; } @@ -165,7 +165,7 @@ static long pps_cdev_ioctl(struct file *file, break; case PPS_GETCAP: - dev_dbg(pps->dev, "PPS_GETCAP\n"); + dev_dbg(&pps->dev, "PPS_GETCAP\n"); err = put_user(pps->info.mode, iuarg); if (err) @@ -176,7 +176,7 @@ static long pps_cdev_ioctl(struct file *file, case PPS_FETCH: { struct pps_fdata fdata; - dev_dbg(pps->dev, "PPS_FETCH\n"); + dev_dbg(&pps->dev, "PPS_FETCH\n"); err = copy_from_user(&fdata, uarg, sizeof(struct pps_fdata)); if (err) @@ -206,7 +206,7 @@ static long pps_cdev_ioctl(struct file *file, case PPS_KC_BIND: { struct pps_bind_args bind_args; - dev_dbg(pps->dev, "PPS_KC_BIND\n"); + dev_dbg(&pps->dev, "PPS_KC_BIND\n"); /* Check the capabilities */ if (!capable(CAP_SYS_TIME)) @@ -218,7 +218,7 @@ static long pps_cdev_ioctl(struct file *file, /* Check for supported capabilities */ if ((bind_args.edge & ~pps->info.mode) != 0) { - dev_err(pps->dev, "unsupported capabilities (%x)\n", + dev_err(&pps->dev, "unsupported capabilities (%x)\n", bind_args.edge); return -EINVAL; } @@ -227,7 +227,7 @@ static long pps_cdev_ioctl(struct file *file, if (bind_args.tsformat != PPS_TSFMT_TSPEC || (bind_args.edge & ~PPS_CAPTUREBOTH) != 0 || bind_args.consumer != PPS_KC_HARDPPS) { - dev_err(pps->dev, "invalid kernel consumer bind" + dev_err(&pps->dev, "invalid kernel consumer bind" " parameters (%x)\n", bind_args.edge); return -EINVAL; } @@ -259,7 +259,7 @@ static long pps_cdev_compat_ioctl(struct file *file, struct pps_fdata fdata; int err; - dev_dbg(pps->dev, "PPS_FETCH\n"); + dev_dbg(&pps->dev, "PPS_FETCH\n"); err = copy_from_user(&compat, uarg, sizeof(struct pps_fdata_compat)); if (err) @@ -296,20 +296,36 @@ static long pps_cdev_compat_ioctl(struct file *file, #define pps_cdev_compat_ioctl NULL #endif +static struct pps_device *pps_idr_get(unsigned long id) +{ + struct pps_device *pps; + + mutex_lock(&pps_idr_lock); + pps = idr_find(&pps_idr, id); + if (pps) + get_device(&pps->dev); + + mutex_unlock(&pps_idr_lock); + return pps; +} + static int pps_cdev_open(struct inode *inode, struct file *file) { - struct pps_device *pps = container_of(inode->i_cdev, - struct pps_device, cdev); + struct pps_device *pps = pps_idr_get(iminor(inode)); + + if (!pps) + return -ENODEV; + file->private_data = pps; - kobject_get(&pps->dev->kobj); return 0; } static int pps_cdev_release(struct inode *inode, struct file *file) { - struct pps_device *pps = container_of(inode->i_cdev, - struct pps_device, cdev); - kobject_put(&pps->dev->kobj); + struct pps_device *pps = file->private_data; + + WARN_ON(pps->id != iminor(inode)); + put_device(&pps->dev); return 0; } @@ -331,22 +347,13 @@ static void pps_device_destruct(struct device *dev) { struct pps_device *pps = dev_get_drvdata(dev); - cdev_del(&pps->cdev); - - /* Now we can release the ID for re-use */ pr_debug("deallocating pps%d\n", pps->id); - mutex_lock(&pps_idr_lock); - idr_remove(&pps_idr, pps->id); - mutex_unlock(&pps_idr_lock); - - kfree(dev); kfree(pps); } int pps_register_cdev(struct pps_device *pps) { int err; - dev_t devt; mutex_lock(&pps_idr_lock); /* @@ -363,40 +370,29 @@ int pps_register_cdev(struct pps_device *pps) goto out_unlock; } pps->id = err; - mutex_unlock(&pps_idr_lock); - - devt = MKDEV(MAJOR(pps_devt), pps->id); - - cdev_init(&pps->cdev, &pps_cdev_fops); - pps->cdev.owner = pps->info.owner; - err = cdev_add(&pps->cdev, devt, 1); - if (err) { - pr_err("%s: failed to add char device %d:%d\n", - pps->info.name, MAJOR(pps_devt), pps->id); + pps->dev.class = pps_class; + pps->dev.parent = pps->info.dev; + pps->dev.devt = MKDEV(pps_major, pps->id); + dev_set_drvdata(&pps->dev, pps); + dev_set_name(&pps->dev, "pps%d", pps->id); + err = device_register(&pps->dev); + if (err) goto free_idr; - } - pps->dev = device_create(pps_class, pps->info.dev, devt, pps, - "pps%d", pps->id); - if (IS_ERR(pps->dev)) { - err = PTR_ERR(pps->dev); - goto del_cdev; - } /* Override the release function with our own */ - pps->dev->release = pps_device_destruct; + pps->dev.release = pps_device_destruct; - pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, - MAJOR(pps_devt), pps->id); + pr_debug("source %s got cdev (%d:%d)\n", pps->info.name, pps_major, + pps->id); + get_device(&pps->dev); + mutex_unlock(&pps_idr_lock); return 0; -del_cdev: - cdev_del(&pps->cdev); - free_idr: - mutex_lock(&pps_idr_lock); idr_remove(&pps_idr, pps->id); + put_device(&pps->dev); out_unlock: mutex_unlock(&pps_idr_lock); return err; @@ -406,7 +402,13 @@ void pps_unregister_cdev(struct pps_device *pps) { pr_debug("unregistering pps%d\n", pps->id); pps->lookup_cookie = NULL; - device_destroy(pps_class, pps->dev->devt); + device_destroy(pps_class, pps->dev.devt); + + /* Now we can release the ID for re-use */ + mutex_lock(&pps_idr_lock); + idr_remove(&pps_idr, pps->id); + put_device(&pps->dev); + mutex_unlock(&pps_idr_lock); } /* @@ -426,6 +428,11 @@ void pps_unregister_cdev(struct pps_device *pps) * so that it will not be used again, even if the pps device cannot * be removed from the idr due to pending references holding the minor * number in use. + * + * Since pps_idr holds a reference to the device, the returned + * pps_device is guaranteed to be valid until pps_unregister_cdev() is + * called on it. But after calling pps_unregister_cdev(), it may be + * freed at any time. */ struct pps_device *pps_lookup_dev(void const *cookie) { @@ -448,13 +455,11 @@ EXPORT_SYMBOL(pps_lookup_dev); static void __exit pps_exit(void) { class_destroy(pps_class); - unregister_chrdev_region(pps_devt, PPS_MAX_SOURCES); + __unregister_chrdev(pps_major, 0, PPS_MAX_SOURCES, "pps"); } static int __init pps_init(void) { - int err; - pps_class = class_create("pps"); if (IS_ERR(pps_class)) { pr_err("failed to allocate class\n"); @@ -462,8 +467,9 @@ static int __init pps_init(void) } pps_class->dev_groups = pps_groups; - err = alloc_chrdev_region(&pps_devt, 0, PPS_MAX_SOURCES, "pps"); - if (err < 0) { + pps_major = __register_chrdev(0, 0, PPS_MAX_SOURCES, "pps", + &pps_cdev_fops); + if (pps_major < 0) { pr_err("failed to allocate char device region\n"); goto remove_class; } @@ -476,8 +482,7 @@ static int __init pps_init(void) remove_class: class_destroy(pps_class); - - return err; + return pps_major; } subsys_initcall(pps_init); diff --git a/drivers/ptp/ptp_ocp.c b/drivers/ptp/ptp_ocp.c index 5feecaadde8e..120db96d9e95 100644 --- a/drivers/ptp/ptp_ocp.c +++ b/drivers/ptp/ptp_ocp.c @@ -4420,7 +4420,7 @@ ptp_ocp_complete(struct ptp_ocp *bp) pps = pps_lookup_dev(bp->ptp); if (pps) - ptp_ocp_symlink(bp, pps->dev, "pps"); + ptp_ocp_symlink(bp, &pps->dev, "pps"); ptp_ocp_debugfs_add_device(bp); diff --git a/include/linux/pps_kernel.h b/include/linux/pps_kernel.h index 78c8ac4951b5..c7abce28ed29 100644 --- a/include/linux/pps_kernel.h +++ b/include/linux/pps_kernel.h @@ -56,8 +56,7 @@ struct pps_device { unsigned int id; /* PPS source unique ID */ void const *lookup_cookie; /* For pps_lookup_dev() only */ - struct cdev cdev; - struct device *dev; + struct device dev; struct fasync_struct *async_queue; /* fasync method */ spinlock_t lock; }; -- cgit v1.2.3 From 86b525bed2758878e788c9fb6b8fb281fd61bdb0 Mon Sep 17 00:00:00 2001 From: Rodolfo Giometti Date: Fri, 8 Nov 2024 08:31:12 +0100 Subject: drivers pps: add PPS generators support Sometimes one needs to be able not only to catch PPS signals but to produce them also. For example, running a distributed simulation, which requires computers' clock to be synchronized very tightly. This patch adds PPS generators class in order to have a well-defined interface for these devices. Signed-off-by: Rodolfo Giometti Link: https://lore.kernel.org/r/20241108073115.759039-2-giometti@enneenne.com Signed-off-by: Greg Kroah-Hartman --- Documentation/userspace-api/ioctl/ioctl-number.rst | 1 + MAINTAINERS | 1 + drivers/pps/Makefile | 3 +- drivers/pps/generators/Kconfig | 13 +- drivers/pps/generators/Makefile | 3 + drivers/pps/generators/pps_gen.c | 344 +++++++++++++++++++++ drivers/pps/generators/sysfs.c | 75 +++++ include/linux/pps_gen_kernel.h | 78 +++++ include/uapi/linux/pps_gen.h | 37 +++ 9 files changed, 553 insertions(+), 2 deletions(-) create mode 100644 drivers/pps/generators/pps_gen.c create mode 100644 drivers/pps/generators/sysfs.c create mode 100644 include/linux/pps_gen_kernel.h create mode 100644 include/uapi/linux/pps_gen.h (limited to 'include/linux') diff --git a/Documentation/userspace-api/ioctl/ioctl-number.rst b/Documentation/userspace-api/ioctl/ioctl-number.rst index 243f1f1b554a..f7cb871cb714 100644 --- a/Documentation/userspace-api/ioctl/ioctl-number.rst +++ b/Documentation/userspace-api/ioctl/ioctl-number.rst @@ -283,6 +283,7 @@ Code Seq# Include File Comments 'p' 80-9F linux/ppdev.h user-space parport 'p' A1-A5 linux/pps.h LinuxPPS +'p' B1-B3 linux/pps-gen.h LinuxPPS 'q' 00-1F linux/serio.h 'q' 80-FF linux/telephony.h Internet PhoneJACK, Internet LineJACK diff --git a/MAINTAINERS b/MAINTAINERS index 124b8574aa76..530eca844bd4 100644 --- a/MAINTAINERS +++ b/MAINTAINERS @@ -18719,6 +18719,7 @@ F: Documentation/devicetree/bindings/pps/pps-gpio.yaml F: Documentation/driver-api/pps.rst F: drivers/pps/ F: include/linux/pps*.h +F: include/uapi/linux/pps-gen.h F: include/uapi/linux/pps.h PRESSURE STALL INFORMATION (PSI) diff --git a/drivers/pps/Makefile b/drivers/pps/Makefile index ceaf65cc1f1d..0aea394d4e4d 100644 --- a/drivers/pps/Makefile +++ b/drivers/pps/Makefile @@ -6,6 +6,7 @@ pps_core-y := pps.o kapi.o sysfs.o pps_core-$(CONFIG_NTP_PPS) += kc.o obj-$(CONFIG_PPS) := pps_core.o -obj-y += clients/ generators/ +obj-y += clients/ +obj-$(CONFIG_PPS_GENERATOR) += generators/ ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG diff --git a/drivers/pps/generators/Kconfig b/drivers/pps/generators/Kconfig index d615e640fcad..b34e483eff21 100644 --- a/drivers/pps/generators/Kconfig +++ b/drivers/pps/generators/Kconfig @@ -3,7 +3,16 @@ # PPS generators configuration # -comment "PPS generators support" +menuconfig PPS_GENERATOR + tristate "PPS generators support" + help + PPS generators are special hardware which are able to produce PPS + (Pulse Per Second) signals. + + To compile this driver as a module, choose M here: the module + will be called pps_gen_core. + +if PPS_GENERATOR config PPS_GENERATOR_PARPORT tristate "Parallel port PPS signal generator" @@ -12,3 +21,5 @@ config PPS_GENERATOR_PARPORT If you say yes here you get support for a PPS signal generator which utilizes STROBE pin of a parallel port to send PPS signals. It uses parport abstraction layer and hrtimers to precisely control the signal. + +endif # PPS_GENERATOR diff --git a/drivers/pps/generators/Makefile b/drivers/pps/generators/Makefile index 2589fd0f2481..034a78edfa26 100644 --- a/drivers/pps/generators/Makefile +++ b/drivers/pps/generators/Makefile @@ -3,6 +3,9 @@ # Makefile for PPS generators. # +pps_gen_core-y := pps_gen.o sysfs.o +obj-$(CONFIG_PPS_GENERATOR) := pps_gen_core.o + obj-$(CONFIG_PPS_GENERATOR_PARPORT) += pps_gen_parport.o ccflags-$(CONFIG_PPS_DEBUG) := -DDEBUG diff --git a/drivers/pps/generators/pps_gen.c b/drivers/pps/generators/pps_gen.c new file mode 100644 index 000000000000..ca592f1736f4 --- /dev/null +++ b/drivers/pps/generators/pps_gen.c @@ -0,0 +1,344 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PPS generators core file + * + * Copyright (C) 2024 Rodolfo Giometti + */ + +#define pr_fmt(fmt) KBUILD_MODNAME ": " fmt + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +/* + * Local variables + */ + +static dev_t pps_gen_devt; +static struct class *pps_gen_class; + +static DEFINE_IDA(pps_gen_ida); + +/* + * Char device methods + */ + +static __poll_t pps_gen_cdev_poll(struct file *file, poll_table *wait) +{ + struct pps_gen_device *pps_gen = file->private_data; + + poll_wait(file, &pps_gen->queue, wait); + return EPOLLIN | EPOLLRDNORM; +} + +static int pps_gen_cdev_fasync(int fd, struct file *file, int on) +{ + struct pps_gen_device *pps_gen = file->private_data; + + return fasync_helper(fd, file, on, &pps_gen->async_queue); +} + +static long pps_gen_cdev_ioctl(struct file *file, + unsigned int cmd, unsigned long arg) +{ + struct pps_gen_device *pps_gen = file->private_data; + void __user *uarg = (void __user *) arg; + unsigned int __user *uiuarg = (unsigned int __user *) arg; + unsigned int status; + int ret; + + switch (cmd) { + case PPS_GEN_SETENABLE: + dev_dbg(pps_gen->dev, "PPS_GEN_SETENABLE\n"); + + ret = get_user(status, uiuarg); + if (ret) + return -EFAULT; + + ret = pps_gen->info.enable(pps_gen, status); + if (ret) + return ret; + pps_gen->enabled = status; + + break; + + case PPS_GEN_USESYSTEMCLOCK: + dev_dbg(pps_gen->dev, "PPS_GEN_USESYSTEMCLOCK\n"); + + ret = put_user(pps_gen->info.use_system_clock, uiuarg); + if (ret) + return -EFAULT; + + break; + + case PPS_GEN_FETCHEVENT: { + struct pps_gen_event info; + unsigned int ev = pps_gen->last_ev; + + dev_dbg(pps_gen->dev, "PPS_GEN_FETCHEVENT\n"); + + ret = wait_event_interruptible(pps_gen->queue, + ev != pps_gen->last_ev); + if (ret == -ERESTARTSYS) { + dev_dbg(pps_gen->dev, "pending signal caught\n"); + return -EINTR; + } + + spin_lock_irq(&pps_gen->lock); + info.sequence = pps_gen->sequence; + info.event = pps_gen->event; + spin_unlock_irq(&pps_gen->lock); + + ret = copy_to_user(uarg, &info, sizeof(struct pps_gen_event)); + if (ret) + return -EFAULT; + + break; + } + default: + return -ENOTTY; + } + + return 0; +} + +static int pps_gen_cdev_open(struct inode *inode, struct file *file) +{ + struct pps_gen_device *pps_gen = container_of(inode->i_cdev, + struct pps_gen_device, cdev); + + get_device(pps_gen->dev); + file->private_data = pps_gen; + return 0; +} + +static int pps_gen_cdev_release(struct inode *inode, struct file *file) +{ + struct pps_gen_device *pps_gen = file->private_data; + + put_device(pps_gen->dev); + return 0; +} + +/* + * Char device stuff + */ + +static const struct file_operations pps_gen_cdev_fops = { + .owner = THIS_MODULE, + .poll = pps_gen_cdev_poll, + .fasync = pps_gen_cdev_fasync, + .unlocked_ioctl = pps_gen_cdev_ioctl, + .open = pps_gen_cdev_open, + .release = pps_gen_cdev_release, +}; + +static void pps_gen_device_destruct(struct device *dev) +{ + struct pps_gen_device *pps_gen = dev_get_drvdata(dev); + + cdev_del(&pps_gen->cdev); + + pr_debug("deallocating pps-gen%d\n", pps_gen->id); + ida_free(&pps_gen_ida, pps_gen->id); + + kfree(dev); + kfree(pps_gen); +} + +static int pps_gen_register_cdev(struct pps_gen_device *pps_gen) +{ + int err; + dev_t devt; + + err = ida_alloc_max(&pps_gen_ida, PPS_GEN_MAX_SOURCES - 1, GFP_KERNEL); + if (err < 0) { + if (err == -ENOSPC) { + pr_err("too many PPS sources in the system\n"); + err = -EBUSY; + } + return err; + } + pps_gen->id = err; + + devt = MKDEV(MAJOR(pps_gen_devt), pps_gen->id); + + cdev_init(&pps_gen->cdev, &pps_gen_cdev_fops); + pps_gen->cdev.owner = pps_gen->info.owner; + + err = cdev_add(&pps_gen->cdev, devt, 1); + if (err) { + pr_err("failed to add char device %d:%d\n", + MAJOR(pps_gen_devt), pps_gen->id); + goto free_ida; + } + pps_gen->dev = device_create(pps_gen_class, pps_gen->info.parent, devt, + pps_gen, "pps-gen%d", pps_gen->id); + if (IS_ERR(pps_gen->dev)) { + err = PTR_ERR(pps_gen->dev); + goto del_cdev; + } + pps_gen->dev->release = pps_gen_device_destruct; + dev_set_drvdata(pps_gen->dev, pps_gen); + + pr_debug("generator got cdev (%d:%d)\n", + MAJOR(pps_gen_devt), pps_gen->id); + + return 0; + +del_cdev: + cdev_del(&pps_gen->cdev); +free_ida: + ida_free(&pps_gen_ida, pps_gen->id); + return err; +} + +static void pps_gen_unregister_cdev(struct pps_gen_device *pps_gen) +{ + pr_debug("unregistering pps-gen%d\n", pps_gen->id); + device_destroy(pps_gen_class, pps_gen->dev->devt); +} + +/* + * Exported functions + */ + +/** + * pps_gen_register_source() - add a PPS generator in the system + * @info: the PPS generator info struct + * + * This function is used to register a new PPS generator in the system. + * When it returns successfully the new generator is up and running, and + * it can be managed by the userspace. + * + * Return: the PPS generator device in case of success, and ERR_PTR(errno) + * otherwise. + */ +struct pps_gen_device *pps_gen_register_source(struct pps_gen_source_info *info) +{ + struct pps_gen_device *pps_gen; + int err; + + pps_gen = kzalloc(sizeof(struct pps_gen_device), GFP_KERNEL); + if (pps_gen == NULL) { + err = -ENOMEM; + goto pps_gen_register_source_exit; + } + pps_gen->info = *info; + pps_gen->enabled = false; + + init_waitqueue_head(&pps_gen->queue); + spin_lock_init(&pps_gen->lock); + + /* Create the char device */ + err = pps_gen_register_cdev(pps_gen); + if (err < 0) { + pr_err(" unable to create char device\n"); + goto kfree_pps_gen; + } + + return pps_gen; + +kfree_pps_gen: + kfree(pps_gen); + +pps_gen_register_source_exit: + pr_err("unable to register generator\n"); + + return ERR_PTR(err); +} +EXPORT_SYMBOL(pps_gen_register_source); + +/** + * pps_gen_unregister_source() - remove a PPS generator from the system + * @pps_gen: the PPS generator device to be removed + * + * This function is used to deregister a PPS generator from the system. When + * called, it disables the generator so no pulses are generated anymore. + */ +void pps_gen_unregister_source(struct pps_gen_device *pps_gen) +{ + pps_gen_unregister_cdev(pps_gen); +} +EXPORT_SYMBOL(pps_gen_unregister_source); + +/* pps_gen_event - register a PPS generator event into the system + * @pps: the PPS generator device + * @event: the event type + * @data: userdef pointer + * + * This function is used by each PPS generator in order to register a new + * PPS event into the system (it's usually called inside an IRQ handler). + */ +void pps_gen_event(struct pps_gen_device *pps_gen, + unsigned int event, void *data) +{ + unsigned long flags; + + dev_dbg(pps_gen->dev, "PPS generator event %u\n", event); + + spin_lock_irqsave(&pps_gen->lock, flags); + + pps_gen->event = event; + pps_gen->sequence++; + + pps_gen->last_ev++; + wake_up_interruptible_all(&pps_gen->queue); + kill_fasync(&pps_gen->async_queue, SIGIO, POLL_IN); + + spin_unlock_irqrestore(&pps_gen->lock, flags); +} +EXPORT_SYMBOL(pps_gen_event); + +/* + * Module stuff + */ + +static void __exit pps_gen_exit(void) +{ + class_destroy(pps_gen_class); + unregister_chrdev_region(pps_gen_devt, PPS_GEN_MAX_SOURCES); +} + +static int __init pps_gen_init(void) +{ + int err; + + pps_gen_class = class_create("pps-gen"); + if (IS_ERR(pps_gen_class)) { + pr_err("failed to allocate class\n"); + return PTR_ERR(pps_gen_class); + } + pps_gen_class->dev_groups = pps_gen_groups; + + err = alloc_chrdev_region(&pps_gen_devt, 0, + PPS_GEN_MAX_SOURCES, "pps-gen"); + if (err < 0) { + pr_err("failed to allocate char device region\n"); + goto remove_class; + } + + return 0; + +remove_class: + class_destroy(pps_gen_class); + return err; +} + +subsys_initcall(pps_gen_init); +module_exit(pps_gen_exit); + +MODULE_AUTHOR("Rodolfo Giometti "); +MODULE_DESCRIPTION("LinuxPPS generators support"); +MODULE_LICENSE("GPL"); diff --git a/drivers/pps/generators/sysfs.c b/drivers/pps/generators/sysfs.c new file mode 100644 index 000000000000..faf8b1c6d202 --- /dev/null +++ b/drivers/pps/generators/sysfs.c @@ -0,0 +1,75 @@ +// SPDX-License-Identifier: GPL-2.0-or-later +/* + * PPS generators sysfs support + * + * Copyright (C) 2024 Rodolfo Giometti + */ + +#include +#include +#include +#include + +/* + * Attribute functions + */ + +static ssize_t system_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pps_gen_device *pps_gen = dev_get_drvdata(dev); + + return sysfs_emit(buf, "%d\n", pps_gen->info.use_system_clock); +} +static DEVICE_ATTR_RO(system); + +static ssize_t time_show(struct device *dev, struct device_attribute *attr, + char *buf) +{ + struct pps_gen_device *pps_gen = dev_get_drvdata(dev); + struct timespec64 time; + int ret; + + ret = pps_gen->info.get_time(pps_gen, &time); + if (ret) + return ret; + + return sysfs_emit(buf, "%llu %09lu\n", time.tv_sec, time.tv_nsec); +} +static DEVICE_ATTR_RO(time); + +static ssize_t enable_store(struct device *dev, struct device_attribute *attr, + const char *buf, size_t count) +{ + struct pps_gen_device *pps_gen = dev_get_drvdata(dev); + bool status; + int ret; + + ret = kstrtobool(buf, &status); + if (ret) + return ret; + + ret = pps_gen->info.enable(pps_gen, status); + if (ret) + return ret; + pps_gen->enabled = status; + + return count; +} +static DEVICE_ATTR_WO(enable); + +static struct attribute *pps_gen_attrs[] = { + &dev_attr_enable.attr, + &dev_attr_time.attr, + &dev_attr_system.attr, + NULL, +}; + +static const struct attribute_group pps_gen_group = { + .attrs = pps_gen_attrs, +}; + +const struct attribute_group *pps_gen_groups[] = { + &pps_gen_group, + NULL, +}; diff --git a/include/linux/pps_gen_kernel.h b/include/linux/pps_gen_kernel.h new file mode 100644 index 000000000000..022ea0ac4440 --- /dev/null +++ b/include/linux/pps_gen_kernel.h @@ -0,0 +1,78 @@ +/* SPDX-License-Identifier: GPL-2.0-or-later */ +/* + * PPS generator API kernel header + * + * Copyright (C) 2024 Rodolfo Giometti + */ + +#ifndef LINUX_PPS_GEN_KERNEL_H +#define LINUX_PPS_GEN_KERNEL_H + +#include +#include +#include + +/* + * Global defines + */ + +#define PPS_GEN_MAX_SOURCES 16 /* should be enough... */ + +struct pps_gen_device; + +/** + * struct pps_gen_source_info - the specific PPS generator info + * @use_system_clock: true, if the system clock is used to generate pulses + * @get_time: query the time stored into the generator clock + * @enable: enable/disable the PPS pulses generation + * + * This is the main generator struct where all needed information must be + * placed before calling the pps_gen_register_source(). + */ +struct pps_gen_source_info { + bool use_system_clock; + + int (*get_time)(struct pps_gen_device *pps_gen, + struct timespec64 *time); + int (*enable)(struct pps_gen_device *pps_gen, bool enable); + +/* private: internal use only */ + struct module *owner; + struct device *parent; /* for device_create */ +}; + +/* The main struct */ +struct pps_gen_device { + struct pps_gen_source_info info; /* PSS generator info */ + bool enabled; /* PSS generator status */ + + unsigned int event; + unsigned int sequence; + + unsigned int last_ev; /* last PPS event id */ + wait_queue_head_t queue; /* PPS event queue */ + + unsigned int id; /* PPS generator unique ID */ + struct cdev cdev; + struct device *dev; + struct fasync_struct *async_queue; /* fasync method */ + spinlock_t lock; +}; + +/* + * Global variables + */ + +extern const struct attribute_group *pps_gen_groups[]; + +/* + * Exported functions + */ + +extern struct pps_gen_device *pps_gen_register_source( + struct pps_gen_source_info *info); +extern void pps_gen_unregister_source(struct pps_gen_device *pps_gen); +extern void pps_gen_event(struct pps_gen_device *pps_gen, + unsigned int event, void *data); + +#endif /* LINUX_PPS_GEN_KERNEL_H */ diff --git a/include/uapi/linux/pps_gen.h b/include/uapi/linux/pps_gen.h new file mode 100644 index 000000000000..60a5d0fcfa68 --- /dev/null +++ b/include/uapi/linux/pps_gen.h @@ -0,0 +1,37 @@ +/* SPDX-License-Identifier: GPL-2.0 WITH Linux-syscall-note */ +/* + * PPS generator API header + * + * Copyright (C) 2024 Rodolfo Giometti + */ + +#ifndef _PPS_GEN_H_ +#define _PPS_GEN_H_ + +#include +#include + +/** + * struct pps_gen_event - the PPS generator events + * @event: the event type + * @sequence: the event sequence number + * + * Userspace can get the last PPS generator event by using the + * ioctl(pps_gen, PPS_GEN_FETCHEVENT, ...) syscall. + * The sequence field can be used to save the last event ID, while in the + * event field is stored the last event type. Currently known event is: + * + * PPS_GEN_EVENT_MISSEDPULSE : last pulse was not generated + */ +struct pps_gen_event { + unsigned int event; + unsigned int sequence; +}; + +#define PPS_GEN_EVENT_MISSEDPULSE 1 + +#define PPS_GEN_SETENABLE _IOW('p', 0xb1, unsigned int *) +#define PPS_GEN_USESYSTEMCLOCK _IOR('p', 0xb2, unsigned int *) +#define PPS_GEN_FETCHEVENT _IOR('p', 0xb3, struct pps_gen_event *) + +#endif /* _PPS_GEN_H_ */ -- cgit v1.2.3 From 567a311d0a1a433f1e5bff508f3eb7ebfa189aa3 Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Fri, 20 Dec 2024 00:29:57 +0100 Subject: VMCI: remove unused ioctl definitions IOCTL_VMCI_SOCKETS_VERSION and IOCTL_VMCI_SOCKETS_GET_AF_VALUE were never implemented, because VSOCK ended up being implemented as a generic mechanism with a static AF value. Likewise, IOCTL_VMCI_SOCKETS_GET_LOCAL_CID ended up being implemented as IOCTL_VM_SOCKETS_GET_LOCAL_CID. This isn't a UAPI header, so it should be fine to remove the unused values. I've left a comment noting IOCTL_VM_SOCKETS_GET_LOCAL_CID is in the VMCI range to avoid unintentional reuse. Signed-off-by: Alyssa Ross Acked-by: Vishnu Dasa Link: https://lore.kernel.org/r/fzdcrz4yfedokmbm22h2iwsluix4jwejwaltuwcdr6kz3yu6eu@nue5xc6ayevo Signed-off-by: Greg Kroah-Hartman --- include/linux/vmw_vmci_defs.h | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) (limited to 'include/linux') diff --git a/include/linux/vmw_vmci_defs.h b/include/linux/vmw_vmci_defs.h index 6fb663b36f72..c2df94696593 100644 --- a/include/linux/vmw_vmci_defs.h +++ b/include/linux/vmw_vmci_defs.h @@ -453,9 +453,7 @@ enum { #define IOCTL_VMCI_CTX_GET_CPT_STATE _IO(7, 0xb1) #define IOCTL_VMCI_CTX_SET_CPT_STATE _IO(7, 0xb2) #define IOCTL_VMCI_GET_CONTEXT_ID _IO(7, 0xb3) -#define IOCTL_VMCI_SOCKETS_VERSION _IO(7, 0xb4) -#define IOCTL_VMCI_SOCKETS_GET_AF_VALUE _IO(7, 0xb8) -#define IOCTL_VMCI_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9) +/*IOCTL_VM_SOCKETS_GET_LOCAL_CID _IO(7, 0xb9)*/ #define IOCTL_VMCI_SET_NOTIFY _IO(7, 0xcb) /* 1995 */ /*IOCTL_VMMON_START _IO(7, 0xd1)*/ /* 2001 */ -- cgit v1.2.3 From e364374369b365351ad8ad69a10b5f7861f24bcd Mon Sep 17 00:00:00 2001 From: Alyssa Ross Date: Thu, 9 Jan 2025 20:38:07 +0100 Subject: VMCI: fix reference to ioctl-number.rst MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit There has never been an ioctl-number.h — this must have been a typo for ioctl-number.txt (which later become ioctl-number.rst). At the time this comment was written, the note didn't actually end up appearing anywhere, but I fixed the omission from ioctl-number.rst in 0a8e4dc1d353 ("Documentation: ioctl: document 0x07 ioctl code"). Fixes: 20259849bb1a ("VMCI: Some header and config files.") Signed-off-by: Alyssa Ross Link: https://lore.kernel.org/r/re3xng4uwull2cu53xnu5dtv3wlstfiv3v7rmbwtw2qbvj5mo3@q45iujse5ovc Signed-off-by: Greg Kroah-Hartman --- include/linux/vmw_vmci_defs.h | 10 +++++----- 1 file changed, 5 insertions(+), 5 deletions(-) (limited to 'include/linux') diff --git a/include/linux/vmw_vmci_defs.h b/include/linux/vmw_vmci_defs.h index c2df94696593..60c9eacd2cf3 100644 --- a/include/linux/vmw_vmci_defs.h +++ b/include/linux/vmw_vmci_defs.h @@ -431,11 +431,11 @@ enum { ((((_p)[0] & 0xFF) << 24) | (((_p)[1] & 0xFF) << 16) | ((_p)[2])) /* - * The VMCI IOCTLs. We use identity code 7, as noted in ioctl-number.h, and - * we start at sequence 9f. This gives us the same values that our shipping - * products use, starting at 1951, provided we leave out the direction and - * structure size. Note that VMMon occupies the block following us, starting - * at 2001. + * The VMCI IOCTLs. We use identity code 7, as noted in ioctl-number.rst, + * and we start at sequence 9f. This gives us the same values that our + * shipping products use, starting at 1951, provided we leave out the + * direction and structure size. Note that VMMon occupies the block + * following us, starting at 2001. */ #define IOCTL_VMCI_VERSION _IO(7, 0x9f) /* 1951 */ #define IOCTL_VMCI_INIT_CONTEXT _IO(7, 0xa0) -- cgit v1.2.3