From: Sasha Levin Date: Sat, 12 Jul 2025 14:54:12 +0000 (-0400) Subject: Fixes for 5.15 X-Git-Tag: v5.15.188~35 X-Git-Url: http://git.ipfire.org/cgi-bin/gitweb.cgi?a=commitdiff_plain;h=72e56eab37d5d6d3883166665a419d6f1ea73269;p=thirdparty%2Fkernel%2Fstable-queue.git Fixes for 5.15 Signed-off-by: Sasha Levin --- diff --git a/queue-5.15/asoc-codec-wcd9335-convert-to-gpio-descriptors.patch b/queue-5.15/asoc-codec-wcd9335-convert-to-gpio-descriptors.patch new file mode 100644 index 0000000000..74070aebbd --- /dev/null +++ b/queue-5.15/asoc-codec-wcd9335-convert-to-gpio-descriptors.patch @@ -0,0 +1,85 @@ +From 4017ec77dbd0717c41c779c4a5d7952ffc8b2285 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 24 Mar 2025 19:51:29 +0800 +Subject: ASoC: codec: wcd9335: Convert to GPIO descriptors + +From: Peng Fan + +[ Upstream commit d5099bc1b56417733f4cccf10c61ee74dadd5562 ] + +of_gpio.h is deprecated, update the driver to use GPIO descriptors. +- Use dev_gpiod_get to get GPIO descriptor. +- Use gpiod_set_value to configure output value. + +With legacy of_gpio API, the driver set gpio value 0 to assert reset, +and 1 to deassert reset. And the reset-gpios use GPIO_ACTIVE_LOW flag in +DTS, so set GPIOD_OUT_LOW when get GPIO descriptors, and set value 1 means +output low, set value 0 means output high with gpiod API. + +The in-tree DTS files have the right polarity set up already so we can +expect this to "just work" + +Reviewed-by: Linus Walleij +Signed-off-by: Peng Fan +Link: https://patch.msgid.link/20250324-wcd-gpiod-v2-3-773f67ce3b56@nxp.com +Reviewed-by: Bartosz Golaszewski +Signed-off-by: Mark Brown +Stable-dep-of: 9079db287fc3 ("ASoC: codecs: wcd9335: Fix missing free of regulator supplies") +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/wcd9335.c | 15 +++++++-------- + 1 file changed, 7 insertions(+), 8 deletions(-) + +diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c +index dc4ce2c3f2188..08b7ca26c3c97 100644 +--- a/sound/soc/codecs/wcd9335.c ++++ b/sound/soc/codecs/wcd9335.c +@@ -16,7 +16,7 @@ + #include + #include + #include +-#include ++#include + #include + #include + #include +@@ -338,7 +338,7 @@ struct wcd9335_codec { + int comp_enabled[COMPANDER_MAX]; + + int intr1; +- int reset_gpio; ++ struct gpio_desc *reset_gpio; + struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY]; + + unsigned int rx_port_value; +@@ -5024,12 +5024,11 @@ static const struct regmap_irq_chip wcd9335_regmap_irq1_chip = { + static int wcd9335_parse_dt(struct wcd9335_codec *wcd) + { + struct device *dev = wcd->dev; +- struct device_node *np = dev->of_node; + int ret; + +- wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); +- if (wcd->reset_gpio < 0) +- return dev_err_probe(dev, wcd->reset_gpio, "Reset GPIO missing from DT\n"); ++ wcd->reset_gpio = devm_gpiod_get(dev, "reset", GPIOD_OUT_LOW); ++ if (IS_ERR(wcd->reset_gpio)) ++ return dev_err_probe(dev, PTR_ERR(wcd->reset_gpio), "Reset GPIO missing from DT\n"); + + wcd->mclk = devm_clk_get(dev, "mclk"); + if (IS_ERR(wcd->mclk)) +@@ -5072,9 +5071,9 @@ static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) + */ + usleep_range(600, 650); + +- gpio_direction_output(wcd->reset_gpio, 0); ++ gpiod_set_value(wcd->reset_gpio, 1); + msleep(20); +- gpio_set_value(wcd->reset_gpio, 1); ++ gpiod_set_value(wcd->reset_gpio, 0); + msleep(20); + + return 0; +-- +2.39.5 + diff --git a/queue-5.15/asoc-codecs-wcd9335-fix-missing-free-of-regulator-su.patch b/queue-5.15/asoc-codecs-wcd9335-fix-missing-free-of-regulator-su.patch new file mode 100644 index 0000000000..0acae98773 --- /dev/null +++ b/queue-5.15/asoc-codecs-wcd9335-fix-missing-free-of-regulator-su.patch @@ -0,0 +1,88 @@ +From 3752e1c62b4968606391bc8586169eda1b30079f Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 26 May 2025 11:47:01 +0200 +Subject: ASoC: codecs: wcd9335: Fix missing free of regulator supplies + +From: Krzysztof Kozlowski + +[ Upstream commit 9079db287fc3e38e040b0edeb0a25770bb679c8e ] + +Driver gets and enables all regulator supplies in probe path +(wcd9335_parse_dt() and wcd9335_power_on_reset()), but does not cleanup +in final error paths and in unbind (missing remove() callback). This +leads to leaked memory and unbalanced regulator enable count during +probe errors or unbind. + +Fix this by converting entire code into devm_regulator_bulk_get_enable() +which also greatly simplifies the code. + +Fixes: 20aedafdf492 ("ASoC: wcd9335: add support to wcd9335 codec") +Cc: stable@vger.kernel.org +Signed-off-by: Krzysztof Kozlowski +Link: https://patch.msgid.link/20250526-b4-b4-asoc-wcd9395-vdd-px-fixes-v1-1-0b8a2993b7d3@linaro.org +Signed-off-by: Mark Brown +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/wcd9335.c | 25 +++++++------------------ + 1 file changed, 7 insertions(+), 18 deletions(-) + +diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c +index 08b7ca26c3c97..f20d0c9e91e49 100644 +--- a/sound/soc/codecs/wcd9335.c ++++ b/sound/soc/codecs/wcd9335.c +@@ -339,7 +339,6 @@ struct wcd9335_codec { + + int intr1; + struct gpio_desc *reset_gpio; +- struct regulator_bulk_data supplies[WCD9335_MAX_SUPPLY]; + + unsigned int rx_port_value; + unsigned int tx_port_value; +@@ -366,6 +365,10 @@ struct wcd9335_irq { + char *name; + }; + ++static const char * const wcd9335_supplies[] = { ++ "vdd-buck", "vdd-buck-sido", "vdd-tx", "vdd-rx", "vdd-io", ++}; ++ + static const struct wcd9335_slim_ch wcd9335_tx_chs[WCD9335_TX_MAX] = { + WCD9335_SLIM_TX_CH(0), + WCD9335_SLIM_TX_CH(1), +@@ -5038,30 +5041,16 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd) + if (IS_ERR(wcd->native_clk)) + return dev_err_probe(dev, PTR_ERR(wcd->native_clk), "slimbus clock not found\n"); + +- wcd->supplies[0].supply = "vdd-buck"; +- wcd->supplies[1].supply = "vdd-buck-sido"; +- wcd->supplies[2].supply = "vdd-tx"; +- wcd->supplies[3].supply = "vdd-rx"; +- wcd->supplies[4].supply = "vdd-io"; +- +- ret = regulator_bulk_get(dev, WCD9335_MAX_SUPPLY, wcd->supplies); ++ ret = devm_regulator_bulk_get_enable(dev, ARRAY_SIZE(wcd9335_supplies), ++ wcd9335_supplies); + if (ret) +- return dev_err_probe(dev, ret, "Failed to get supplies\n"); ++ return dev_err_probe(dev, ret, "Failed to get and enable supplies\n"); + + return 0; + } + + static int wcd9335_power_on_reset(struct wcd9335_codec *wcd) + { +- struct device *dev = wcd->dev; +- int ret; +- +- ret = regulator_bulk_enable(WCD9335_MAX_SUPPLY, wcd->supplies); +- if (ret) { +- dev_err(dev, "Failed to get supplies: err = %d\n", ret); +- return ret; +- } +- + /* + * For WCD9335, it takes about 600us for the Vout_A and + * Vout_D to be ready after BUCK_SIDO is powered up. +-- +2.39.5 + diff --git a/queue-5.15/asoc-codecs-wcd9335-handle-nicer-probe-deferral-and-.patch b/queue-5.15/asoc-codecs-wcd9335-handle-nicer-probe-deferral-and-.patch new file mode 100644 index 0000000000..8abbd50316 --- /dev/null +++ b/queue-5.15/asoc-codecs-wcd9335-handle-nicer-probe-deferral-and-.patch @@ -0,0 +1,85 @@ +From ab3ce384fb24a3ee3671c39955a6c53d01ce1b87 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Wed, 12 Jun 2024 18:15:17 +0200 +Subject: ASoC: codecs: wcd9335: Handle nicer probe deferral and simplify with + dev_err_probe() + +From: Krzysztof Kozlowski + +[ Upstream commit 4a03b5dbad466c902d522f3405daa4e5d80578c5 ] + +wcd9335_parse_dt() function is called only from probe(), so printing +errors on resource acquisition is discouraged, because it can pollute +dmesg. Use dev_err_probe() to fix this and also make the code a bit +simpler. + +Signed-off-by: Krzysztof Kozlowski +Link: https://msgid.link/r/20240612-asoc-wcd9xxx-wide-cleanups-v1-4-0d15885b2a06@linaro.org +Signed-off-by: Mark Brown +Stable-dep-of: 9079db287fc3 ("ASoC: codecs: wcd9335: Fix missing free of regulator supplies") +Signed-off-by: Sasha Levin +--- + sound/soc/codecs/wcd9335.c | 28 +++++++++------------------- + 1 file changed, 9 insertions(+), 19 deletions(-) + +diff --git a/sound/soc/codecs/wcd9335.c b/sound/soc/codecs/wcd9335.c +index 075ed20e9fad8..dc4ce2c3f2188 100644 +--- a/sound/soc/codecs/wcd9335.c ++++ b/sound/soc/codecs/wcd9335.c +@@ -5028,22 +5028,16 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd) + int ret; + + wcd->reset_gpio = of_get_named_gpio(np, "reset-gpios", 0); +- if (wcd->reset_gpio < 0) { +- dev_err(dev, "Reset GPIO missing from DT\n"); +- return wcd->reset_gpio; +- } ++ if (wcd->reset_gpio < 0) ++ return dev_err_probe(dev, wcd->reset_gpio, "Reset GPIO missing from DT\n"); + + wcd->mclk = devm_clk_get(dev, "mclk"); +- if (IS_ERR(wcd->mclk)) { +- dev_err(dev, "mclk not found\n"); +- return PTR_ERR(wcd->mclk); +- } ++ if (IS_ERR(wcd->mclk)) ++ return dev_err_probe(dev, PTR_ERR(wcd->mclk), "mclk not found\n"); + + wcd->native_clk = devm_clk_get(dev, "slimbus"); +- if (IS_ERR(wcd->native_clk)) { +- dev_err(dev, "slimbus clock not found\n"); +- return PTR_ERR(wcd->native_clk); +- } ++ if (IS_ERR(wcd->native_clk)) ++ return dev_err_probe(dev, PTR_ERR(wcd->native_clk), "slimbus clock not found\n"); + + wcd->supplies[0].supply = "vdd-buck"; + wcd->supplies[1].supply = "vdd-buck-sido"; +@@ -5052,10 +5046,8 @@ static int wcd9335_parse_dt(struct wcd9335_codec *wcd) + wcd->supplies[4].supply = "vdd-io"; + + ret = regulator_bulk_get(dev, WCD9335_MAX_SUPPLY, wcd->supplies); +- if (ret) { +- dev_err(dev, "Failed to get supplies: err = %d\n", ret); +- return ret; +- } ++ if (ret) ++ return dev_err_probe(dev, ret, "Failed to get supplies\n"); + + return 0; + } +@@ -5158,10 +5150,8 @@ static int wcd9335_slim_probe(struct slim_device *slim) + + wcd->dev = dev; + ret = wcd9335_parse_dt(wcd); +- if (ret) { +- dev_err(dev, "Error parsing DT: %d\n", ret); ++ if (ret) + return ret; +- } + + ret = wcd9335_power_on_reset(wcd); + if (ret) +-- +2.39.5 + diff --git a/queue-5.15/btrfs-propagate-last_unlink_trans-earlier-when-doing.patch b/queue-5.15/btrfs-propagate-last_unlink_trans-earlier-when-doing.patch new file mode 100644 index 0000000000..373b2a9a14 --- /dev/null +++ b/queue-5.15/btrfs-propagate-last_unlink_trans-earlier-when-doing.patch @@ -0,0 +1,101 @@ +From 2dad055bc504a8e69e11807d6b9bfe4d619de643 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Jun 2025 15:54:05 +0100 +Subject: btrfs: propagate last_unlink_trans earlier when doing a rmdir + +From: Filipe Manana + +[ Upstream commit c466e33e729a0ee017d10d919cba18f503853c60 ] + +In case the removed directory had a snapshot that was deleted, we are +propagating its inode's last_unlink_trans to the parent directory after +we removed the entry from the parent directory. This leaves a small race +window where someone can log the parent directory after we removed the +entry and before we updated last_unlink_trans, and as a result if we ever +try to replay such a log tree, we will fail since we will attempt to +remove a snapshot during log replay, which is currently not possible and +results in the log replay (and mount) to fail. This is the type of failure +described in commit 1ec9a1ae1e30 ("Btrfs: fix unreplayable log after +snapshot delete + parent dir fsync"). + +So fix this by propagating the last_unlink_trans to the parent directory +before we remove the entry from it. + +Fixes: 44f714dae50a ("Btrfs: improve performance on fsync against new inode after rename/unlink") +Reviewed-by: Johannes Thumshirn +Signed-off-by: Filipe Manana +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/inode.c | 36 ++++++++++++++++++------------------ + 1 file changed, 18 insertions(+), 18 deletions(-) + +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index e3e5bd4fb477a..60079099e24b9 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -4655,7 +4655,6 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) + struct inode *inode = d_inode(dentry); + int err = 0; + struct btrfs_trans_handle *trans; +- u64 last_unlink_trans; + + if (inode->i_size > BTRFS_EMPTY_DIR_SIZE) + return -ENOTEMPTY; +@@ -4666,6 +4665,23 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) + if (IS_ERR(trans)) + return PTR_ERR(trans); + ++ /* ++ * Propagate the last_unlink_trans value of the deleted dir to its ++ * parent directory. This is to prevent an unrecoverable log tree in the ++ * case we do something like this: ++ * 1) create dir foo ++ * 2) create snapshot under dir foo ++ * 3) delete the snapshot ++ * 4) rmdir foo ++ * 5) mkdir foo ++ * 6) fsync foo or some file inside foo ++ * ++ * This is because we can't unlink other roots when replaying the dir ++ * deletes for directory foo. ++ */ ++ if (BTRFS_I(inode)->last_unlink_trans >= trans->transid) ++ BTRFS_I(dir)->last_unlink_trans = BTRFS_I(inode)->last_unlink_trans; ++ + if (unlikely(btrfs_ino(BTRFS_I(inode)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) { + err = btrfs_unlink_subvol(trans, dir, dentry); + goto out; +@@ -4675,28 +4691,12 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) + if (err) + goto out; + +- last_unlink_trans = BTRFS_I(inode)->last_unlink_trans; +- + /* now the directory is empty */ + err = btrfs_unlink_inode(trans, BTRFS_I(dir), + BTRFS_I(d_inode(dentry)), dentry->d_name.name, + dentry->d_name.len); +- if (!err) { ++ if (!err) + btrfs_i_size_write(BTRFS_I(inode), 0); +- /* +- * Propagate the last_unlink_trans value of the deleted dir to +- * its parent directory. This is to prevent an unrecoverable +- * log tree in the case we do something like this: +- * 1) create dir foo +- * 2) create snapshot under dir foo +- * 3) delete the snapshot +- * 4) rmdir foo +- * 5) mkdir foo +- * 6) fsync foo or some file inside foo +- */ +- if (last_unlink_trans >= trans->transid) +- BTRFS_I(dir)->last_unlink_trans = last_unlink_trans; +- } + out: + btrfs_end_transaction(trans); + btrfs_btree_balance_dirty(BTRFS_I(dir)->root->fs_info); +-- +2.39.5 + diff --git a/queue-5.15/btrfs-use-btrfs_record_snapshot_destroy-during-rmdir.patch b/queue-5.15/btrfs-use-btrfs_record_snapshot_destroy-during-rmdir.patch new file mode 100644 index 0000000000..5bfdba11c5 --- /dev/null +++ b/queue-5.15/btrfs-use-btrfs_record_snapshot_destroy-during-rmdir.patch @@ -0,0 +1,46 @@ +From 41d54e6f52c6d59ae8fb06d274763d2d8f2ea296 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 20 Jun 2025 16:37:01 +0100 +Subject: btrfs: use btrfs_record_snapshot_destroy() during rmdir + +From: Filipe Manana + +[ Upstream commit 157501b0469969fc1ba53add5049575aadd79d80 ] + +We are setting the parent directory's last_unlink_trans directly which +may result in a concurrent task starting to log the directory not see the +update and therefore can log the directory after we removed a child +directory which had a snapshot within instead of falling back to a +transaction commit. Replaying such a log tree would result in a mount +failure since we can't currently delete snapshots (and subvolumes) during +log replay. This is the type of failure described in commit 1ec9a1ae1e30 +("Btrfs: fix unreplayable log after snapshot delete + parent dir fsync"). + +Fix this by using btrfs_record_snapshot_destroy() which updates the +last_unlink_trans field while holding the inode's log_mutex lock. + +Fixes: 44f714dae50a ("Btrfs: improve performance on fsync against new inode after rename/unlink") +Reviewed-by: Johannes Thumshirn +Signed-off-by: Filipe Manana +Signed-off-by: David Sterba +Signed-off-by: Sasha Levin +--- + fs/btrfs/inode.c | 2 +- + 1 file changed, 1 insertion(+), 1 deletion(-) + +diff --git a/fs/btrfs/inode.c b/fs/btrfs/inode.c +index 60079099e24b9..27aaa5064ff7e 100644 +--- a/fs/btrfs/inode.c ++++ b/fs/btrfs/inode.c +@@ -4680,7 +4680,7 @@ static int btrfs_rmdir(struct inode *dir, struct dentry *dentry) + * deletes for directory foo. + */ + if (BTRFS_I(inode)->last_unlink_trans >= trans->transid) +- BTRFS_I(dir)->last_unlink_trans = BTRFS_I(inode)->last_unlink_trans; ++ btrfs_record_snapshot_destroy(trans, BTRFS_I(dir)); + + if (unlikely(btrfs_ino(BTRFS_I(inode)) == BTRFS_EMPTY_SUBVOL_DIR_OBJECTID)) { + err = btrfs_unlink_subvol(trans, dir, dentry); +-- +2.39.5 + diff --git a/queue-5.15/rdma-mlx5-fix-vport-loopback-for-mpv-device.patch b/queue-5.15/rdma-mlx5-fix-vport-loopback-for-mpv-device.patch new file mode 100644 index 0000000000..c7ea038b88 --- /dev/null +++ b/queue-5.15/rdma-mlx5-fix-vport-loopback-for-mpv-device.patch @@ -0,0 +1,87 @@ +From befad83b0fa5a57dbef28b1933709addc0cf718d Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Mon, 16 Jun 2025 12:14:54 +0300 +Subject: RDMA/mlx5: Fix vport loopback for MPV device + +From: Patrisious Haddad + +[ Upstream commit a9a9e68954f29b1e197663f76289db4879fd51bb ] + +Always enable vport loopback for both MPV devices on driver start. + +Previously in some cases related to MPV RoCE, packets weren't correctly +executing loopback check at vport in FW, since it was disabled. +Due to complexity of identifying such cases for MPV always enable vport +loopback for both GVMIs when binding the slave to the master port. + +Fixes: 0042f9e458a5 ("RDMA/mlx5: Enable vport loopback when user context or QP mandate") +Signed-off-by: Patrisious Haddad +Reviewed-by: Mark Bloch +Link: https://patch.msgid.link/d4298f5ebb2197459e9e7221c51ecd6a34699847.1750064969.git.leon@kernel.org +Signed-off-by: Leon Romanovsky +Signed-off-by: Sasha Levin +--- + drivers/infiniband/hw/mlx5/main.c | 33 +++++++++++++++++++++++++++++++ + 1 file changed, 33 insertions(+) + +diff --git a/drivers/infiniband/hw/mlx5/main.c b/drivers/infiniband/hw/mlx5/main.c +index 2236c62a19807..e2f3369342c48 100644 +--- a/drivers/infiniband/hw/mlx5/main.c ++++ b/drivers/infiniband/hw/mlx5/main.c +@@ -1680,6 +1680,33 @@ static void deallocate_uars(struct mlx5_ib_dev *dev, + mlx5_cmd_free_uar(dev->mdev, bfregi->sys_pages[i]); + } + ++static int mlx5_ib_enable_lb_mp(struct mlx5_core_dev *master, ++ struct mlx5_core_dev *slave) ++{ ++ int err; ++ ++ err = mlx5_nic_vport_update_local_lb(master, true); ++ if (err) ++ return err; ++ ++ err = mlx5_nic_vport_update_local_lb(slave, true); ++ if (err) ++ goto out; ++ ++ return 0; ++ ++out: ++ mlx5_nic_vport_update_local_lb(master, false); ++ return err; ++} ++ ++static void mlx5_ib_disable_lb_mp(struct mlx5_core_dev *master, ++ struct mlx5_core_dev *slave) ++{ ++ mlx5_nic_vport_update_local_lb(slave, false); ++ mlx5_nic_vport_update_local_lb(master, false); ++} ++ + int mlx5_ib_enable_lb(struct mlx5_ib_dev *dev, bool td, bool qp) + { + int err = 0; +@@ -3147,6 +3174,8 @@ static void mlx5_ib_unbind_slave_port(struct mlx5_ib_dev *ibdev, + + lockdep_assert_held(&mlx5_ib_multiport_mutex); + ++ mlx5_ib_disable_lb_mp(ibdev->mdev, mpi->mdev); ++ + mlx5_ib_cleanup_cong_debugfs(ibdev, port_num); + + spin_lock(&port->mp.mpi_lock); +@@ -3231,6 +3260,10 @@ static bool mlx5_ib_bind_slave_port(struct mlx5_ib_dev *ibdev, + + mlx5_ib_init_cong_debugfs(ibdev, port_num); + ++ err = mlx5_ib_enable_lb_mp(ibdev->mdev, mpi->mdev); ++ if (err) ++ goto unbind; ++ + return true; + + unbind: +-- +2.39.5 + diff --git a/queue-5.15/regulator-add-devm-helpers-for-get-and-enable.patch b/queue-5.15/regulator-add-devm-helpers-for-get-and-enable.patch new file mode 100644 index 0000000000..64d4024d93 --- /dev/null +++ b/queue-5.15/regulator-add-devm-helpers-for-get-and-enable.patch @@ -0,0 +1,283 @@ +From 64ee9f643e651bf0a2afa5976e4667c6ab2d8c74 Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Fri, 12 Aug 2022 13:10:37 +0300 +Subject: regulator: Add devm helpers for get and enable + +From: Matti Vaittinen + +[ Upstream commit da279e6965b3838e99e5c0ab8f76b87bf86b31a5 ] + +A few regulator consumer drivers seem to be just getting a regulator, +enabling it and registering a devm-action to disable the regulator at +the driver detach and then forget about it. + +We can simplify this a bit by adding a devm-helper for this pattern. +Add devm_regulator_get_enable() and devm_regulator_get_enable_optional() + +Signed-off-by: Matti Vaittinen +Link: https://lore.kernel.org/r/ed7b8841193bb9749d426f3cb3b199c9460794cd.1660292316.git.mazziesaccount@gmail.com +Signed-off-by: Mark Brown +Stable-dep-of: 9079db287fc3 ("ASoC: codecs: wcd9335: Fix missing free of regulator supplies") +Signed-off-by: Sasha Levin +--- + drivers/regulator/devres.c | 164 +++++++++++++++++++++++++++++ + include/linux/regulator/consumer.h | 27 +++++ + 2 files changed, 191 insertions(+) + +diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c +index 32823a87fd409..3265e75e97ab4 100644 +--- a/drivers/regulator/devres.c ++++ b/drivers/regulator/devres.c +@@ -70,6 +70,65 @@ struct regulator *devm_regulator_get_exclusive(struct device *dev, + } + EXPORT_SYMBOL_GPL(devm_regulator_get_exclusive); + ++static void regulator_action_disable(void *d) ++{ ++ struct regulator *r = (struct regulator *)d; ++ ++ regulator_disable(r); ++} ++ ++static int _devm_regulator_get_enable(struct device *dev, const char *id, ++ int get_type) ++{ ++ struct regulator *r; ++ int ret; ++ ++ r = _devm_regulator_get(dev, id, get_type); ++ if (IS_ERR(r)) ++ return PTR_ERR(r); ++ ++ ret = regulator_enable(r); ++ if (!ret) ++ ret = devm_add_action_or_reset(dev, ®ulator_action_disable, r); ++ ++ if (ret) ++ devm_regulator_put(r); ++ ++ return ret; ++} ++ ++/** ++ * devm_regulator_get_enable_optional - Resource managed regulator get and enable ++ * @dev: device to supply ++ * @id: supply name or regulator ID. ++ * ++ * Get and enable regulator for duration of the device life-time. ++ * regulator_disable() and regulator_put() are automatically called on driver ++ * detach. See regulator_get_optional() and regulator_enable() for more ++ * information. ++ */ ++int devm_regulator_get_enable_optional(struct device *dev, const char *id) ++{ ++ return _devm_regulator_get_enable(dev, id, OPTIONAL_GET); ++} ++EXPORT_SYMBOL_GPL(devm_regulator_get_enable_optional); ++ ++/** ++ * devm_regulator_get_enable - Resource managed regulator get and enable ++ * @dev: device to supply ++ * @id: supply name or regulator ID. ++ * ++ * Get and enable regulator for duration of the device life-time. ++ * regulator_disable() and regulator_put() are automatically called on driver ++ * detach. See regulator_get() and regulator_enable() for more ++ * information. ++ */ ++int devm_regulator_get_enable(struct device *dev, const char *id) ++{ ++ return _devm_regulator_get_enable(dev, id, NORMAL_GET); ++} ++EXPORT_SYMBOL_GPL(devm_regulator_get_enable); ++ + /** + * devm_regulator_get_optional - Resource managed regulator_get_optional() + * @dev: device to supply +@@ -194,6 +253,111 @@ int devm_regulator_bulk_get_const(struct device *dev, int num_consumers, + } + EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_const); + ++static int devm_regulator_bulk_match(struct device *dev, void *res, ++ void *data) ++{ ++ struct regulator_bulk_devres *match = res; ++ struct regulator_bulk_data *target = data; ++ ++ /* ++ * We check the put uses same consumer list as the get did. ++ * We _could_ scan all entries in consumer array and check the ++ * regulators match but ATM I don't see the need. We can change this ++ * later if needed. ++ */ ++ return match->consumers == target; ++} ++ ++/** ++ * devm_regulator_bulk_put - Resource managed regulator_bulk_put() ++ * @consumers: consumers to free ++ * ++ * Deallocate regulators allocated with devm_regulator_bulk_get(). Normally ++ * this function will not need to be called and the resource management ++ * code will ensure that the resource is freed. ++ */ ++void devm_regulator_bulk_put(struct regulator_bulk_data *consumers) ++{ ++ int rc; ++ struct regulator *regulator = consumers[0].consumer; ++ ++ rc = devres_release(regulator->dev, devm_regulator_bulk_release, ++ devm_regulator_bulk_match, consumers); ++ if (rc != 0) ++ WARN_ON(rc); ++} ++EXPORT_SYMBOL_GPL(devm_regulator_bulk_put); ++ ++static void devm_regulator_bulk_disable(void *res) ++{ ++ struct regulator_bulk_devres *devres = res; ++ int i; ++ ++ for (i = 0; i < devres->num_consumers; i++) ++ regulator_disable(devres->consumers[i].consumer); ++} ++ ++/** ++ * devm_regulator_bulk_get_enable - managed get'n enable multiple regulators ++ * ++ * @dev: device to supply ++ * @num_consumers: number of consumers to register ++ * @id: list of supply names or regulator IDs ++ * ++ * @return 0 on success, an errno on failure. ++ * ++ * This helper function allows drivers to get several regulator ++ * consumers in one operation with management, the regulators will ++ * automatically be freed when the device is unbound. If any of the ++ * regulators cannot be acquired then any regulators that were ++ * allocated will be freed before returning to the caller. ++ */ ++int devm_regulator_bulk_get_enable(struct device *dev, int num_consumers, ++ const char * const *id) ++{ ++ struct regulator_bulk_devres *devres; ++ struct regulator_bulk_data *consumers; ++ int i, ret; ++ ++ devres = devm_kmalloc(dev, sizeof(*devres), GFP_KERNEL); ++ if (!devres) ++ return -ENOMEM; ++ ++ devres->consumers = devm_kcalloc(dev, num_consumers, sizeof(*consumers), ++ GFP_KERNEL); ++ consumers = devres->consumers; ++ if (!consumers) ++ return -ENOMEM; ++ ++ devres->num_consumers = num_consumers; ++ ++ for (i = 0; i < num_consumers; i++) ++ consumers[i].supply = id[i]; ++ ++ ret = devm_regulator_bulk_get(dev, num_consumers, consumers); ++ if (ret) ++ return ret; ++ ++ for (i = 0; i < num_consumers; i++) { ++ ret = regulator_enable(consumers[i].consumer); ++ if (ret) ++ goto unwind; ++ } ++ ++ ret = devm_add_action(dev, devm_regulator_bulk_disable, devres); ++ if (!ret) ++ return 0; ++ ++unwind: ++ while (--i >= 0) ++ regulator_disable(consumers[i].consumer); ++ ++ devm_regulator_bulk_put(consumers); ++ ++ return ret; ++} ++EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_enable); ++ + static void devm_rdev_release(struct device *dev, void *res) + { + regulator_unregister(*(struct regulator_dev **)res); +diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h +index 61f922e6fe353..a1fce0f27ce16 100644 +--- a/include/linux/regulator/consumer.h ++++ b/include/linux/regulator/consumer.h +@@ -203,6 +203,8 @@ struct regulator *__must_check regulator_get_optional(struct device *dev, + const char *id); + struct regulator *__must_check devm_regulator_get_optional(struct device *dev, + const char *id); ++int devm_regulator_get_enable(struct device *dev, const char *id); ++int devm_regulator_get_enable_optional(struct device *dev, const char *id); + void regulator_put(struct regulator *regulator); + void devm_regulator_put(struct regulator *regulator); + +@@ -240,12 +242,15 @@ int __must_check regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); + int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); ++void devm_regulator_bulk_put(struct regulator_bulk_data *consumers); + int __must_check devm_regulator_bulk_get_const( + struct device *dev, int num_consumers, + const struct regulator_bulk_data *in_consumers, + struct regulator_bulk_data **out_consumers); + int __must_check regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers); ++int devm_regulator_bulk_get_enable(struct device *dev, int num_consumers, ++ const char * const *id); + int regulator_bulk_disable(int num_consumers, + struct regulator_bulk_data *consumers); + int regulator_bulk_force_disable(int num_consumers, +@@ -350,6 +355,17 @@ devm_regulator_get_exclusive(struct device *dev, const char *id) + return ERR_PTR(-ENODEV); + } + ++static inline int devm_regulator_get_enable(struct device *dev, const char *id) ++{ ++ return -ENODEV; ++} ++ ++static inline int devm_regulator_get_enable_optional(struct device *dev, ++ const char *id) ++{ ++ return -ENODEV; ++} ++ + static inline struct regulator *__must_check + regulator_get_optional(struct device *dev, const char *id) + { +@@ -371,6 +387,10 @@ static inline void devm_regulator_put(struct regulator *regulator) + { + } + ++static inline void devm_regulator_bulk_put(struct regulator_bulk_data *consumers) ++{ ++} ++ + static inline int regulator_register_supply_alias(struct device *dev, + const char *id, + struct device *alias_dev, +@@ -461,6 +481,13 @@ static inline int regulator_bulk_enable(int num_consumers, + return 0; + } + ++static inline int devm_regulator_bulk_get_enable(struct device *dev, ++ int num_consumers, ++ const char * const *id) ++{ ++ return 0; ++} ++ + static inline int regulator_bulk_disable(int num_consumers, + struct regulator_bulk_data *consumers) + { +-- +2.39.5 + diff --git a/queue-5.15/regulator-core-allow-drivers-to-define-their-init-da.patch b/queue-5.15/regulator-core-allow-drivers-to-define-their-init-da.patch new file mode 100644 index 0000000000..fd8e29ea5b --- /dev/null +++ b/queue-5.15/regulator-core-allow-drivers-to-define-their-init-da.patch @@ -0,0 +1,110 @@ +From 80620341370aac64b78c5d2739d121bfa0a3c5ce Mon Sep 17 00:00:00 2001 +From: Sasha Levin +Date: Tue, 26 Jul 2022 10:38:23 -0700 +Subject: regulator: core: Allow drivers to define their init data as const + +From: Douglas Anderson + +[ Upstream commit 1de452a0edda26f1483d1d934f692eab13ba669a ] + +Drivers tend to want to define the names of their regulators somewhere +in their source file as "static const". This means, inevitable, that +every driver out there open codes something like this: + +static const char * const supply_names[] = { + "vcc", "vccl", +}; + +static int get_regulators(struct my_data *data) +{ + int i; + + data->supplies = devm_kzalloc(...) + if (!data->supplies) + return -ENOMEM; + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + data->supplies[i].supply = supply_names[i]; + + return devm_regulator_bulk_get(data->dev, + ARRAY_SIZE(supply_names), + data->supplies); +} + +Let's make this more convenient by doing providing a helper that does +the copy. + +I have chosen to have the "const" input structure here be the exact +same structure as the normal one passed to +devm_regulator_bulk_get(). This is slightly inefficent since the input +data can't possibly have anything useful for "ret" or consumer and +thus we waste 8 bytes per structure. This seems an OK tradeoff for not +introducing an extra structure. + +Signed-off-by: Douglas Anderson +Link: https://lore.kernel.org/r/20220726103631.v2.6.I38fc508a73135a5c1b873851f3553ff2a3a625f5@changeid +Signed-off-by: Mark Brown +Stable-dep-of: 9079db287fc3 ("ASoC: codecs: wcd9335: Fix missing free of regulator supplies") +Signed-off-by: Sasha Levin +--- + drivers/regulator/devres.c | 28 ++++++++++++++++++++++++++++ + include/linux/regulator/consumer.h | 4 ++++ + 2 files changed, 32 insertions(+) + +diff --git a/drivers/regulator/devres.c b/drivers/regulator/devres.c +index 9113233f41cd1..32823a87fd409 100644 +--- a/drivers/regulator/devres.c ++++ b/drivers/regulator/devres.c +@@ -166,6 +166,34 @@ int devm_regulator_bulk_get(struct device *dev, int num_consumers, + } + EXPORT_SYMBOL_GPL(devm_regulator_bulk_get); + ++/** ++ * devm_regulator_bulk_get_const - devm_regulator_bulk_get() w/ const data ++ * ++ * @dev: device to supply ++ * @num_consumers: number of consumers to register ++ * @in_consumers: const configuration of consumers ++ * @out_consumers: in_consumers is copied here and this is passed to ++ * devm_regulator_bulk_get(). ++ * ++ * This is a convenience function to allow bulk regulator configuration ++ * to be stored "static const" in files. ++ * ++ * Return: 0 on success, an errno on failure. ++ */ ++int devm_regulator_bulk_get_const(struct device *dev, int num_consumers, ++ const struct regulator_bulk_data *in_consumers, ++ struct regulator_bulk_data **out_consumers) ++{ ++ *out_consumers = devm_kmemdup(dev, in_consumers, ++ num_consumers * sizeof(*in_consumers), ++ GFP_KERNEL); ++ if (*out_consumers == NULL) ++ return -ENOMEM; ++ ++ return devm_regulator_bulk_get(dev, num_consumers, *out_consumers); ++} ++EXPORT_SYMBOL_GPL(devm_regulator_bulk_get_const); ++ + static void devm_rdev_release(struct device *dev, void *res) + { + regulator_unregister(*(struct regulator_dev **)res); +diff --git a/include/linux/regulator/consumer.h b/include/linux/regulator/consumer.h +index bbf6590a6dec2..61f922e6fe353 100644 +--- a/include/linux/regulator/consumer.h ++++ b/include/linux/regulator/consumer.h +@@ -240,6 +240,10 @@ int __must_check regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); + int __must_check devm_regulator_bulk_get(struct device *dev, int num_consumers, + struct regulator_bulk_data *consumers); ++int __must_check devm_regulator_bulk_get_const( ++ struct device *dev, int num_consumers, ++ const struct regulator_bulk_data *in_consumers, ++ struct regulator_bulk_data **out_consumers); + int __must_check regulator_bulk_enable(int num_consumers, + struct regulator_bulk_data *consumers); + int regulator_bulk_disable(int num_consumers, +-- +2.39.5 + diff --git a/queue-5.15/series b/queue-5.15/series index 383d747766..e69d5d5e4d 100644 --- a/queue-5.15/series +++ b/queue-5.15/series @@ -33,3 +33,11 @@ drm-sched-increment-job-count-before-swapping-tail-spsc-queue.patch drm-gem-fix-race-in-drm_gem_handle_create_tail.patch usb-gadget-u_serial-fix-race-condition-in-tty-wakeup.patch revert-acpi-battery-negate-current-when-discharging.patch +regulator-core-allow-drivers-to-define-their-init-da.patch +regulator-add-devm-helpers-for-get-and-enable.patch +asoc-codecs-wcd9335-handle-nicer-probe-deferral-and-.patch +asoc-codec-wcd9335-convert-to-gpio-descriptors.patch +asoc-codecs-wcd9335-fix-missing-free-of-regulator-su.patch +btrfs-propagate-last_unlink_trans-earlier-when-doing.patch +btrfs-use-btrfs_record_snapshot_destroy-during-rmdir.patch +rdma-mlx5-fix-vport-loopback-for-mpv-device.patch