Re: [PATCH] ring-buffer: Fix wake ups when buffer_percent is set to 100

2023-12-28 Thread Steven Rostedt
On Wed, 27 Dec 2023 07:57:08 +0900
Masami Hiramatsu (Google)  wrote:

> On Tue, 26 Dec 2023 12:59:02 -0500
> Steven Rostedt  wrote:
> 
> > From: "Steven Rostedt (Google)" 
> > 
> > The tracefs file "buffer_percent" is to allow user space to set a
> > water-mark on how much of the tracing ring buffer needs to be filled in
> > order to wake up a blocked reader.
> > 
> >  0 - is to wait until any data is in the buffer
> >  1 - is to wait for 1% of the sub buffers to be filled
> >  50 - would be half of the sub buffers are filled with data
> >  100 - is not to wake the waiter until the ring buffer is completely full
> > 
> > Unfortunately the test for being full was:
> > 
> > dirty = ring_buffer_nr_dirty_pages(buffer, cpu);
> > return (dirty * 100) > (full * nr_pages);
> > 
> > Where "full" is the value for "buffer_percent".
> > 
> > There is two issues with the above when full == 100.
> > 
> > 1. dirty * 100 > 100 * nr_pages will never be true
> >That is, the above is basically saying that if the user sets
> >buffer_percent to 100, more pages need to be dirty than exist in the
> >ring buffer!
> > 
> > 2. The page that the writer is on is never considered dirty, as dirty
> >pages are only those that are full. When the writer goes to a new
> >sub-buffer, it clears the contents of that sub-buffer.
> > 
> > That is, even if the check was ">=" it would still not be equal as the
> > most pages that can be considered "dirty" is nr_pages - 1.
> > 
> > To fix this, add one to dirty and use ">=" in the compare.
> > 
> > Cc: sta...@vger.kernel.org
> > Fixes: 03329f9939781 ("tracing: Add tracefs file buffer_percentage")
> > Signed-off-by: Steven Rostedt (Google) 
> > ---
> >  kernel/trace/ring_buffer.c | 9 +++--
> >  1 file changed, 7 insertions(+), 2 deletions(-)
> > 
> > diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
> > index 83eab547f1d1..32c0dd2fd1c3 100644
> > --- a/kernel/trace/ring_buffer.c
> > +++ b/kernel/trace/ring_buffer.c
> > @@ -881,9 +881,14 @@ static __always_inline bool full_hit(struct 
> > trace_buffer *buffer, int cpu, int f
> > if (!nr_pages || !full)
> > return true;
> >  
> > -   dirty = ring_buffer_nr_dirty_pages(buffer, cpu);
> > +   /*
> > +* Add one as dirty will never equal nr_pages, as the sub-buffer
> > +* that the writer is on is not counted as dirty.
> > +* This is needed if "buffer_percent" is set to 100.
> > +*/
> > +   dirty = ring_buffer_nr_dirty_pages(buffer, cpu) + 1;  
> 
> Is this "+ 1" required? If we have 200 pages and 1 buffer is dirty,
> it is 0.5% dirty. Consider @full = 1%.

Yes it is required, as the comment above it states. dirty will never
equal nr_pages. Without it, buffer_percent == 100 will never wake up.

The +1 is to add the page the writer is on, which is never considered
"dirty".

> 
> @dirty = 1 + 1 = 2 and @dirty * 100 == 200. but 
> @full * @nr_pages = 1 * 200 = 200.
> Thus it hits (200 >= 200 is true) even if dirty pages are 0.5%.

Do we care?

What's the difference if it wakes up on 2 dirty pages or 1? It would be
very hard to measure the difference.

But if you say 100, which means "I want to wake up when full" it will
never wake up. Because it will always be nr_pages - 1.

We could also say the +1 is the reader page too, because that's not
counted as well.

In other words, we can bike shed this to make 1% accurate (which
honestly, I have no idea what the use case for that would be) or we can
fix the bug that has 100% which just means, wake me up if the buffer is
full, and when the writer is on the last page, it is considered full.

-- Steve



[PATCH] tracing: Fix blocked reader of snapshot buffer

2023-12-28 Thread Steven Rostedt
From: "Steven Rostedt (Google)" 

If an application blocks on the snapshot or snapshot_raw files, expecting
to be woken up when a snapshot occurs, it will not happen. Or it may
happen with an unexpected result.

That result is that the application will be reading the main buffer
instead of the snapshot buffer. That is because when the snapshot occurs,
the main and snapshot buffers are swapped. But the reader has a descriptor
still pointing to the buffer that it originally connected to.

This is fine for the main buffer readers, as they may be blocked waiting
for a watermark to be hit, and when a snapshot occurs, the data that the
main readers want is now on the snapshot buffer.

But for waiters of the snapshot buffer, they are waiting for an event to
occur that will trigger the snapshot and they can then consume it quickly
to save the snapshot before the next snapshot occurs. But to do this, they
need to read the new snapshot buffer, not the old one that is now
receiving new data.

Also, it does not make sense to have a watermark "buffer_percent" on the
snapshot buffer, as the snapshot buffer is static and does not receive new
data except all at once.

Cc: sta...@vger.kernel.org
Fixes: debdd57f5145f ("tracing: Make a snapshot feature available from 
userspace")
Signed-off-by: Steven Rostedt (Google) 
---
 kernel/trace/ring_buffer.c |  3 ++-
 kernel/trace/trace.c   | 20 +---
 2 files changed, 19 insertions(+), 4 deletions(-)

diff --git a/kernel/trace/ring_buffer.c b/kernel/trace/ring_buffer.c
index e476d42c84c5..07dae67424a9 100644
--- a/kernel/trace/ring_buffer.c
+++ b/kernel/trace/ring_buffer.c
@@ -838,7 +838,8 @@ void ring_buffer_wake_waiters(struct trace_buffer *buffer, 
int cpu)
/* make sure the waiters see the new index */
smp_wmb();
 
-   rb_wake_up_waiters(&rbwork->work);
+   /* This can be called in any context */
+   irq_work_queue(&rbwork->work);
 }
 
 /**
diff --git a/kernel/trace/trace.c b/kernel/trace/trace.c
index cfeaf2cd204e..606787edae8c 100644
--- a/kernel/trace/trace.c
+++ b/kernel/trace/trace.c
@@ -1902,6 +1902,9 @@ update_max_tr(struct trace_array *tr, struct task_struct 
*tsk, int cpu,
__update_max_tr(tr, tsk, cpu);
 
arch_spin_unlock(&tr->max_lock);
+
+   /* Any waiters on the old snapshot buffer need to wake up */
+   ring_buffer_wake_waiters(tr->array_buffer.buffer, RING_BUFFER_ALL_CPUS);
 }
 
 /**
@@ -1953,12 +1956,23 @@ update_max_tr_single(struct trace_array *tr, struct 
task_struct *tsk, int cpu)
 
 static int wait_on_pipe(struct trace_iterator *iter, int full)
 {
+   int ret;
+
/* Iterators are static, they should be filled or empty */
if (trace_buffer_iter(iter, iter->cpu_file))
return 0;
 
-   return ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file,
-   full);
+   ret = ring_buffer_wait(iter->array_buffer->buffer, iter->cpu_file, 
full);
+
+#ifdef CONFIG_TRACER_MAX_TRACE
+   /*
+* Make sure this is still the snapshot buffer, as if a snapshot were
+* to happen, this would now be the main buffer.
+*/
+   if (iter->snapshot)
+   iter->array_buffer = &iter->tr->max_buffer;
+#endif
+   return ret;
 }
 
 #ifdef CONFIG_FTRACE_STARTUP_TEST
@@ -8560,7 +8574,7 @@ tracing_buffers_splice_read(struct file *file, loff_t 
*ppos,
 
wait_index = READ_ONCE(iter->wait_index);
 
-   ret = wait_on_pipe(iter, iter->tr->buffer_percent);
+   ret = wait_on_pipe(iter, iter->snapshot ? 0 : 
iter->tr->buffer_percent);
if (ret)
goto out;
 
-- 
2.42.0




[PATCH 5/5] media: venus: Convert to dev_pm_domain_attach|detach_list() for vcodec

2023-12-28 Thread Ulf Hansson
Let's avoid some of the boilerplate code to manage the vcodec PM domains,
by converting into using dev_pm_domain_attach|detach_list().

Cc: Mauro Carvalho Chehab 
Cc: Stanimir Varbanov 
Cc: Vikash Garodia 
Cc: "Bryan O'Donoghue" 
Cc: Bjorn Andersson 
Cc: Konrad Dybcio 
Cc: 
Signed-off-by: Ulf Hansson 
---
 drivers/media/platform/qcom/venus/core.c  | 12 +++--
 drivers/media/platform/qcom/venus/core.h  |  7 ++-
 .../media/platform/qcom/venus/pm_helpers.c| 48 +++
 3 files changed, 26 insertions(+), 41 deletions(-)

diff --git a/drivers/media/platform/qcom/venus/core.c 
b/drivers/media/platform/qcom/venus/core.c
index 9cffe975581b..bd9b474280e4 100644
--- a/drivers/media/platform/qcom/venus/core.c
+++ b/drivers/media/platform/qcom/venus/core.c
@@ -16,6 +16,7 @@
 #include 
 #include 
 #include 
+#include 
 #include 
 #include 
 #include 
@@ -114,7 +115,8 @@ static void venus_sys_error_handler(struct work_struct 
*work)
pm_runtime_put_sync(core->dev);
 
for (i = 0; i < max_attempts; i++) {
-   if (!core->pmdomains[0] || 
!pm_runtime_active(core->pmdomains[0]))
+   if (!core->pmdomains ||
+   !pm_runtime_active(core->pmdomains->pd_devs[0]))
break;
usleep_range(1000, 1500);
}
@@ -705,7 +707,7 @@ static const struct venus_resources sdm845_res_v2 = {
.vcodec0_clks = { "vcodec0_core", "vcodec0_bus" },
.vcodec1_clks = { "vcodec1_core", "vcodec1_bus" },
.vcodec_clks_num = 2,
-   .vcodec_pmdomains = { "venus", "vcodec0", "vcodec1" },
+   .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0", "vcodec1" },
.vcodec_pmdomains_num = 3,
.opp_pmdomain = (const char *[]) { "cx", NULL },
.vcodec_num = 2,
@@ -754,7 +756,7 @@ static const struct venus_resources sc7180_res = {
.clks_num = 3,
.vcodec0_clks = { "vcodec0_core", "vcodec0_bus" },
.vcodec_clks_num = 2,
-   .vcodec_pmdomains = { "venus", "vcodec0" },
+   .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
.opp_pmdomain = (const char *[]) { "cx", NULL },
.vcodec_num = 1,
@@ -811,7 +813,7 @@ static const struct venus_resources sm8250_res = {
.resets_num = 2,
.vcodec0_clks = { "vcodec0_core" },
.vcodec_clks_num = 1,
-   .vcodec_pmdomains = { "venus", "vcodec0" },
+   .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
.opp_pmdomain = (const char *[]) { "mx", NULL },
.vcodec_num = 1,
@@ -870,7 +872,7 @@ static const struct venus_resources sc7280_res = {
.clks_num = 3,
.vcodec0_clks = {"vcodec_core", "vcodec_bus"},
.vcodec_clks_num = 2,
-   .vcodec_pmdomains = { "venus", "vcodec0" },
+   .vcodec_pmdomains = (const char *[]) { "venus", "vcodec0" },
.vcodec_pmdomains_num = 2,
.opp_pmdomain = (const char *[]) { "cx", NULL },
.vcodec_num = 1,
diff --git a/drivers/media/platform/qcom/venus/core.h 
b/drivers/media/platform/qcom/venus/core.h
index 4a633261ece4..7ef341bf21cc 100644
--- a/drivers/media/platform/qcom/venus/core.h
+++ b/drivers/media/platform/qcom/venus/core.h
@@ -25,7 +25,6 @@
 
 #define VIDC_CLKS_NUM_MAX  4
 #define VIDC_VCODEC_CLKS_NUM_MAX   2
-#define VIDC_PMDOMAINS_NUM_MAX 3
 #define VIDC_RESETS_NUM_MAX2
 
 extern int venus_fw_debug;
@@ -72,7 +71,7 @@ struct venus_resources {
const char * const vcodec0_clks[VIDC_VCODEC_CLKS_NUM_MAX];
const char * const vcodec1_clks[VIDC_VCODEC_CLKS_NUM_MAX];
unsigned int vcodec_clks_num;
-   const char * const vcodec_pmdomains[VIDC_PMDOMAINS_NUM_MAX];
+   const char **vcodec_pmdomains;
unsigned int vcodec_pmdomains_num;
const char **opp_pmdomain;
unsigned int vcodec_num;
@@ -134,7 +133,7 @@ struct venus_format {
  * @video_path: an interconnect handle to video to/from memory path
  * @cpucfg_path: an interconnect handle to cpu configuration path
  * @has_opp_table: does OPP table exist
- * @pmdomains: an array of pmdomains struct device pointers
+ * @pmdomains: a pointer to a list of pmdomains
  * @opp_dl_venus: an device-link for device OPP
  * @opp_pmdomain: an OPP power-domain
  * @resets: an array of reset signals
@@ -187,7 +186,7 @@ struct venus_core {
struct icc_path *video_path;
struct icc_path *cpucfg_path;
bool has_opp_table;
-   struct device *pmdomains[VIDC_PMDOMAINS_NUM_MAX];
+   struct dev_pm_domain_list *pmdomains;
struct device_link *opp_dl_venus;
struct device *opp_pmdomain;
struct reset_control *resets[VIDC_RESETS_NUM_MAX];
diff --git a/drivers/media/platform/qcom/venus/pm_helpers.c 
b/drivers/media/platform/qcom/venus/pm_helpers.c
index a1b127caa90a..502822059498 100644
--- a/drivers/media/platform/qcom/venus/pm_helpers.c
+++ b/drivers/media/plat

[PATCH 4/5] remoteproc: qcom_q6v5_adsp: Convert to dev_pm_domain_attach|detach_list()

2023-12-28 Thread Ulf Hansson
Let's avoid some of the boilerplate code to manage the various PM domain
cases, by converting into using dev_pm_domain_attach|detach_list().

As a part of the conversion, we are moving over to use device_links, which
simplifies the runtime PM support too. Moreover, while attaching let's
trust that an already attached single PM domain is the correct one.

Cc: Mathieu Poirier 
Cc: Bjorn Andersson 
Cc: Konrad Dybcio 
Cc: 
Signed-off-by: Ulf Hansson 
---
 drivers/remoteproc/qcom_q6v5_adsp.c | 160 +---
 1 file changed, 73 insertions(+), 87 deletions(-)

diff --git a/drivers/remoteproc/qcom_q6v5_adsp.c 
b/drivers/remoteproc/qcom_q6v5_adsp.c
index 6c67514cc493..93f9a1537ec6 100644
--- a/drivers/remoteproc/qcom_q6v5_adsp.c
+++ b/drivers/remoteproc/qcom_q6v5_adsp.c
@@ -55,8 +55,6 @@
 #define QDSP6SS_CORE_CBCR  0x20
 #define QDSP6SS_SLEEP_CBCR 0x3c
 
-#define QCOM_Q6V5_RPROC_PROXY_PD_MAX   3
-
 #define LPASS_BOOT_CORE_START  BIT(0)
 #define LPASS_BOOT_CMD_START   BIT(0)
 #define LPASS_EFUSE_Q6SS_EVB_SEL 0x0
@@ -74,7 +72,8 @@ struct adsp_pil_data {
 
const char **clk_ids;
int num_clks;
-   const char **proxy_pd_names;
+   const char **pd_names;
+   unsigned int num_pds;
const char *load_state;
 };
 
@@ -110,8 +109,7 @@ struct qcom_adsp {
size_t mem_size;
bool has_iommu;
 
-   struct device *proxy_pds[QCOM_Q6V5_RPROC_PROXY_PD_MAX];
-   size_t proxy_pd_count;
+   struct dev_pm_domain_list *pd_list;
 
struct qcom_rproc_glink glink_subdev;
struct qcom_rproc_ssr ssr_subdev;
@@ -120,98 +118,92 @@ struct qcom_adsp {
int (*shutdown)(struct qcom_adsp *adsp);
 };
 
-static int qcom_rproc_pds_attach(struct device *dev, struct qcom_adsp *adsp,
-const char **pd_names)
+static int qcom_rproc_pds_attach(struct qcom_adsp *adsp, const char **pd_names,
+unsigned int num_pds)
 {
-   struct device **devs = adsp->proxy_pds;
-   size_t num_pds = 0;
+   struct device *dev = adsp->dev;
+   struct dev_pm_domain_attach_data pd_data = {
+   .pd_names = pd_names,
+   .num_pd_names = num_pds,
+   };
int ret;
-   int i;
-
-   if (!pd_names)
-   return 0;
 
/* Handle single power domain */
-   if (dev->pm_domain) {
-   devs[0] = dev;
-   pm_runtime_enable(dev);
-   return 1;
-   }
+   if (dev->pm_domain)
+   goto out;
 
-   while (pd_names[num_pds])
-   num_pds++;
+   if (!pd_names)
+   return 0;
 
-   if (num_pds > ARRAY_SIZE(adsp->proxy_pds))
-   return -E2BIG;
+   ret = dev_pm_domain_attach_list(dev, &pd_data, &adsp->pd_list);
+   if (ret < 0)
+   return ret;
 
-   for (i = 0; i < num_pds; i++) {
-   devs[i] = dev_pm_domain_attach_by_name(dev, pd_names[i]);
-   if (IS_ERR_OR_NULL(devs[i])) {
-   ret = PTR_ERR(devs[i]) ? : -ENODATA;
-   goto unroll_attach;
-   }
-   }
+out:
+   pm_runtime_enable(dev);
+   return 0;
+}
 
-   return num_pds;
+static void qcom_rproc_pds_detach(struct qcom_adsp *adsp)
+{
+   struct device *dev = adsp->dev;
+   struct dev_pm_domain_list *pds = adsp->pd_list;
 
-unroll_attach:
-   for (i--; i >= 0; i--)
-   dev_pm_domain_detach(devs[i], false);
+   dev_pm_domain_detach_list(pds);
 
-   return ret;
+   if (dev->pm_domain || pds)
+   pm_runtime_disable(adsp->dev);
 }
 
-static void qcom_rproc_pds_detach(struct qcom_adsp *adsp, struct device **pds,
- size_t pd_count)
+static int qcom_rproc_pds_enable(struct qcom_adsp *adsp)
 {
struct device *dev = adsp->dev;
-   int i;
+   struct dev_pm_domain_list *pds = adsp->pd_list;
+   int ret, i = 0;
 
-   /* Handle single power domain */
-   if (dev->pm_domain && pd_count) {
-   pm_runtime_disable(dev);
-   return;
-   }
+   if (!dev->pm_domain && !pds)
+   return 0;
 
-   for (i = 0; i < pd_count; i++)
-   dev_pm_domain_detach(pds[i], false);
-}
+   if (dev->pm_domain)
+   dev_pm_genpd_set_performance_state(dev, INT_MAX);
 
-static int qcom_rproc_pds_enable(struct qcom_adsp *adsp, struct device **pds,
-size_t pd_count)
-{
-   int ret;
-   int i;
-
-   for (i = 0; i < pd_count; i++) {
-   dev_pm_genpd_set_performance_state(pds[i], INT_MAX);
-   ret = pm_runtime_resume_and_get(pds[i]);
-   if (ret < 0) {
-   dev_pm_genpd_set_performance_state(pds[i], 0);
-   goto unroll_pd_votes;
-   }
+   while (pds && i < pds->num_pds) {
+   dev_pm_genpd_set_performance_state(pds->pd_devs[i],

[PATCH 3/5] remoteproc: imx_rproc: Convert to dev_pm_domain_attach|detach_list()

2023-12-28 Thread Ulf Hansson
Let's avoid the boilerplate code to manage the multiple PM domain case, by
converting into using dev_pm_domain_attach|detach_list().

Cc: Mathieu Poirier 
Cc: Bjorn Andersson 
Cc: Shawn Guo 
Cc: Sascha Hauer 
Cc: 
Signed-off-by: Ulf Hansson 
---
 drivers/remoteproc/imx_rproc.c | 73 +-
 1 file changed, 9 insertions(+), 64 deletions(-)

diff --git a/drivers/remoteproc/imx_rproc.c b/drivers/remoteproc/imx_rproc.c
index 8bb293b9f327..3161f14442bc 100644
--- a/drivers/remoteproc/imx_rproc.c
+++ b/drivers/remoteproc/imx_rproc.c
@@ -92,7 +92,6 @@ struct imx_rproc_mem {
 
 static int imx_rproc_xtr_mbox_init(struct rproc *rproc);
 static void imx_rproc_free_mbox(struct rproc *rproc);
-static int imx_rproc_detach_pd(struct rproc *rproc);
 
 struct imx_rproc {
struct device   *dev;
@@ -113,10 +112,8 @@ struct imx_rproc {
u32 rproc_pt;   /* partition id */
u32 rsrc_id;/* resource id */
u32 entry;  /* cpu start address */
-   int num_pd;
u32 core_index;
-   struct device   **pd_dev;
-   struct device_link  **pd_dev_link;
+   struct dev_pm_domain_list   *pd_list;
 };
 
 static const struct imx_rproc_att imx_rproc_att_imx93[] = {
@@ -853,7 +850,7 @@ static void imx_rproc_put_scu(struct rproc *rproc)
return;
 
if (imx_sc_rm_is_resource_owned(priv->ipc_handle, priv->rsrc_id)) {
-   imx_rproc_detach_pd(rproc);
+   dev_pm_domain_detach_list(priv->pd_list);
return;
}
 
@@ -880,72 +877,20 @@ static int imx_rproc_partition_notify(struct 
notifier_block *nb,
 static int imx_rproc_attach_pd(struct imx_rproc *priv)
 {
struct device *dev = priv->dev;
-   int ret, i;
-
-   /*
-* If there is only one power-domain entry, the platform driver 
framework
-* will handle it, no need handle it in this driver.
-*/
-   priv->num_pd = of_count_phandle_with_args(dev->of_node, "power-domains",
- "#power-domain-cells");
-   if (priv->num_pd <= 1)
-   return 0;
-
-   priv->pd_dev = devm_kmalloc_array(dev, priv->num_pd, 
sizeof(*priv->pd_dev), GFP_KERNEL);
-   if (!priv->pd_dev)
-   return -ENOMEM;
-
-   priv->pd_dev_link = devm_kmalloc_array(dev, priv->num_pd, 
sizeof(*priv->pd_dev_link),
-  GFP_KERNEL);
-
-   if (!priv->pd_dev_link)
-   return -ENOMEM;
-
-   for (i = 0; i < priv->num_pd; i++) {
-   priv->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i);
-   if (IS_ERR(priv->pd_dev[i])) {
-   ret = PTR_ERR(priv->pd_dev[i]);
-   goto detach_pd;
-   }
-
-   priv->pd_dev_link[i] = device_link_add(dev, priv->pd_dev[i], 
DL_FLAG_STATELESS |
-  DL_FLAG_PM_RUNTIME | 
DL_FLAG_RPM_ACTIVE);
-   if (!priv->pd_dev_link[i]) {
-   dev_pm_domain_detach(priv->pd_dev[i], false);
-   ret = -EINVAL;
-   goto detach_pd;
-   }
-   }
-
-   return 0;
-
-detach_pd:
-   while (--i >= 0) {
-   device_link_del(priv->pd_dev_link[i]);
-   dev_pm_domain_detach(priv->pd_dev[i], false);
-   }
-
-   return ret;
-}
-
-static int imx_rproc_detach_pd(struct rproc *rproc)
-{
-   struct imx_rproc *priv = rproc->priv;
-   int i;
+   int ret;
+   struct dev_pm_domain_attach_data pd_data = {
+   .pd_flags = PD_FLAG_DEV_LINK_ON,
+   };
 
/*
 * If there is only one power-domain entry, the platform driver 
framework
 * will handle it, no need handle it in this driver.
 */
-   if (priv->num_pd <= 1)
+   if (dev->pm_domain)
return 0;
 
-   for (i = 0; i < priv->num_pd; i++) {
-   device_link_del(priv->pd_dev_link[i]);
-   dev_pm_domain_detach(priv->pd_dev[i], false);
-   }
-
-   return 0;
+   ret = dev_pm_domain_attach_list(dev, &pd_data, &priv->pd_list);
+   return ret < 0 ? ret : 0;
 }
 
 static int imx_rproc_detect_mode(struct imx_rproc *priv)
-- 
2.34.1




[PATCH 2/5] remoteproc: imx_dsp_rproc: Convert to dev_pm_domain_attach|detach_list()

2023-12-28 Thread Ulf Hansson
Let's avoid the boilerplate code to manage the multiple PM domain case, by
converting into using dev_pm_domain_attach|detach_list().

Cc: Mathieu Poirier 
Cc: Bjorn Andersson 
Cc: Shawn Guo 
Cc: Sascha Hauer 
Cc: 
Signed-off-by: Ulf Hansson 
---
 drivers/remoteproc/imx_dsp_rproc.c | 82 --
 1 file changed, 9 insertions(+), 73 deletions(-)

diff --git a/drivers/remoteproc/imx_dsp_rproc.c 
b/drivers/remoteproc/imx_dsp_rproc.c
index 8fcda9b74545..0409b7c47d5c 100644
--- a/drivers/remoteproc/imx_dsp_rproc.c
+++ b/drivers/remoteproc/imx_dsp_rproc.c
@@ -103,12 +103,10 @@ enum imx_dsp_rp_mbox_messages {
  * @tx_ch: mailbox tx channel handle
  * @rx_ch: mailbox rx channel handle
  * @rxdb_ch: mailbox rx doorbell channel handle
- * @pd_dev: power domain device
- * @pd_dev_link: power domain device link
+ * @pd_list: power domain list
  * @ipc_handle: System Control Unit ipc handle
  * @rproc_work: work for processing virtio interrupts
  * @pm_comp: completion primitive to sync for suspend response
- * @num_domains: power domain number
  * @flags: control flags
  */
 struct imx_dsp_rproc {
@@ -121,12 +119,10 @@ struct imx_dsp_rproc {
struct mbox_chan*tx_ch;
struct mbox_chan*rx_ch;
struct mbox_chan*rxdb_ch;
-   struct device   **pd_dev;
-   struct device_link  **pd_dev_link;
+   struct dev_pm_domain_list   *pd_list;
struct imx_sc_ipc   *ipc_handle;
struct work_struct  rproc_work;
struct completion   pm_comp;
-   int num_domains;
u32 flags;
 };
 
@@ -954,74 +950,14 @@ static const struct rproc_ops imx_dsp_rproc_ops = {
 static int imx_dsp_attach_pm_domains(struct imx_dsp_rproc *priv)
 {
struct device *dev = priv->rproc->dev.parent;
-   int ret, i;
-
-   priv->num_domains = of_count_phandle_with_args(dev->of_node,
-  "power-domains",
-  "#power-domain-cells");
-
-   /* If only one domain, then no need to link the device */
-   if (priv->num_domains <= 1)
-   return 0;
-
-   priv->pd_dev = devm_kmalloc_array(dev, priv->num_domains,
- sizeof(*priv->pd_dev),
- GFP_KERNEL);
-   if (!priv->pd_dev)
-   return -ENOMEM;
-
-   priv->pd_dev_link = devm_kmalloc_array(dev, priv->num_domains,
-  sizeof(*priv->pd_dev_link),
-  GFP_KERNEL);
-   if (!priv->pd_dev_link)
-   return -ENOMEM;
-
-   for (i = 0; i < priv->num_domains; i++) {
-   priv->pd_dev[i] = dev_pm_domain_attach_by_id(dev, i);
-   if (IS_ERR(priv->pd_dev[i])) {
-   ret = PTR_ERR(priv->pd_dev[i]);
-   goto detach_pm;
-   }
-
-   /*
-* device_link_add will check priv->pd_dev[i], if it is
-* NULL, then will break.
-*/
-   priv->pd_dev_link[i] = device_link_add(dev,
-  priv->pd_dev[i],
-  DL_FLAG_STATELESS |
-  DL_FLAG_PM_RUNTIME);
-   if (!priv->pd_dev_link[i]) {
-   dev_pm_domain_detach(priv->pd_dev[i], false);
-   ret = -EINVAL;
-   goto detach_pm;
-   }
-   }
-
-   return 0;
-
-detach_pm:
-   while (--i >= 0) {
-   device_link_del(priv->pd_dev_link[i]);
-   dev_pm_domain_detach(priv->pd_dev[i], false);
-   }
-
-   return ret;
-}
-
-static int imx_dsp_detach_pm_domains(struct imx_dsp_rproc *priv)
-{
-   int i;
+   int ret;
 
-   if (priv->num_domains <= 1)
+   /* A single PM domain is already attached. */
+   if (dev->pm_domain)
return 0;
 
-   for (i = 0; i < priv->num_domains; i++) {
-   device_link_del(priv->pd_dev_link[i]);
-   dev_pm_domain_detach(priv->pd_dev[i], false);
-   }
-
-   return 0;
+   ret = dev_pm_domain_attach_list(dev, NULL, &priv->pd_list);
+   return ret < 0 ? ret : 0;
 }
 
 /**
@@ -1153,7 +1089,7 @@ static int imx_dsp_rproc_probe(struct platform_device 
*pdev)
return 0;
 
 err_detach_domains:
-   imx_dsp_detach_pm_domains(priv);
+   dev_pm_domain_detach_list(priv->pd_list);
 err_put_rproc:
rproc_free(rproc);
 
@@ -1167,7 +1103,7 @@ static void imx_dsp_rproc_remove(struct platform_device 
*pdev)
 
pm_runtime_disable(&

[PATCH 1/5] PM: domains: Add helper functions to attach/detach multiple PM domains

2023-12-28 Thread Ulf Hansson
Attaching/detaching of a device to multiple PM domains has started to
become a common operation for many drivers, typically during ->probe() and
->remove(). In most cases, this has lead to lots of boilerplate code in the
drivers.

To fixup up the situation, let's introduce a pair of helper functions,
dev_pm_domain_attach|detach_list(), that driver can use instead of the
open-coding. Note that, it seems reasonable to limit the support for these
helpers to DT based platforms, at it's the only valid use case for now.

Signed-off-by: Ulf Hansson 
---
 drivers/base/power/common.c | 133 
 include/linux/pm_domain.h   |  38 +++
 2 files changed, 171 insertions(+)

diff --git a/drivers/base/power/common.c b/drivers/base/power/common.c
index 44ec20918a4d..1ef51889fc6f 100644
--- a/drivers/base/power/common.c
+++ b/drivers/base/power/common.c
@@ -167,6 +167,114 @@ struct device *dev_pm_domain_attach_by_name(struct device 
*dev,
 }
 EXPORT_SYMBOL_GPL(dev_pm_domain_attach_by_name);
 
+/**
+ * dev_pm_domain_attach_list - Associate a device with its PM domains.
+ * @dev: The device used to lookup the PM domains for.
+ * @data: The data used for attaching to the PM domains.
+ * @list: An out-parameter with an allocated list of attached PM domains.
+ *
+ * This function helps to attach a device to its multiple PM domains. The
+ * caller, which is typically a driver's probe function, may provide a list of
+ * names for the PM domains that we should try to attach the device to, but it
+ * may also provide an empty list, in case the attach should be done for all of
+ * the available PM domains.
+ *
+ * Callers must ensure proper synchronization of this function with power
+ * management callbacks.
+ *
+ * Returns the number of attached PM domains or a negative error code in case 
of
+ * a failure. Note that, to detach the list of PM domains, the driver shall 
call
+ * dev_pm_domain_detach_list(), typically during the remove phase.
+ */
+int dev_pm_domain_attach_list(struct device *dev,
+ const struct dev_pm_domain_attach_data *data,
+ struct dev_pm_domain_list **list)
+{
+   struct device_node *np = dev->of_node;
+   struct dev_pm_domain_list *pds;
+   struct device *pd_dev = NULL;
+   int ret, i, num_pds = 0;
+   bool by_id = true;
+   u32 link_flags = data && data->pd_flags & PD_FLAG_NO_DEV_LINK ? 0 :
+   DL_FLAG_STATELESS | DL_FLAG_PM_RUNTIME;
+
+   if (dev->pm_domain)
+   return -EEXIST;
+
+   /* For now this is limited to OF based platforms. */
+   if (!np)
+   return 0;
+
+   if (data && data->pd_names) {
+   num_pds = data->num_pd_names;
+   by_id = false;
+   } else {
+   num_pds = of_count_phandle_with_args(np, "power-domains",
+"#power-domain-cells");
+   }
+
+   if (num_pds <= 0)
+   return 0;
+
+   pds = devm_kzalloc(dev, sizeof(*pds), GFP_KERNEL);
+   if (!pds)
+   return -ENOMEM;
+
+   pds->pd_devs = devm_kcalloc(dev, num_pds, sizeof(*pds->pd_devs),
+   GFP_KERNEL);
+   if (!pds->pd_devs)
+   return -ENOMEM;
+
+   pds->pd_links = devm_kcalloc(dev, num_pds, sizeof(*pds->pd_links),
+GFP_KERNEL);
+   if (!pds->pd_links)
+   return -ENOMEM;
+
+   if (link_flags && data->pd_flags & PD_FLAG_DEV_LINK_ON)
+   link_flags |= DL_FLAG_RPM_ACTIVE;
+
+   for (i = 0; i < num_pds; i++) {
+   if (by_id)
+   pd_dev = dev_pm_domain_attach_by_id(dev, i);
+   else
+   pd_dev = dev_pm_domain_attach_by_name(dev,
+   data->pd_names[i]);
+   if (IS_ERR_OR_NULL(pd_dev)) {
+   ret = pd_dev ? PTR_ERR(pd_dev) : -ENODEV;
+   goto err_attach;
+   }
+
+   if (link_flags) {
+   struct device_link *link;
+
+   link = device_link_add(dev, pd_dev, link_flags);
+   if (!link) {
+   ret = -ENODEV;
+   goto err_link;
+   }
+
+   pds->pd_links[i] = link;
+   }
+
+   pds->pd_devs[i] = pd_dev;
+   }
+
+   pds->num_pds = num_pds;
+   *list = pds;
+   return num_pds;
+
+err_link:
+   dev_pm_domain_detach(pd_dev, true);
+err_attach:
+   while (--i >= 0) {
+   if (pds->pd_links[i])
+   device_link_del(pds->pd_links[i]);
+   dev_pm_domain_detach(pds->pd_devs[i], true);
+   }
+   return ret;
+}
+EXPORT_SYMBOL_GPL(dev_pm_domain_attach_list);
+
 /**
  * dev_pm_domain_detach - Detach a d

[PATCH 0/5] PM: domains: Add helpers for multi PM domains to avoid open-coding

2023-12-28 Thread Ulf Hansson
Attaching/detaching of a device to multiple PM domains has started to become a
common operation for many drivers, typically during ->probe() and ->remove().
In most cases, this has lead to lots of boilerplate code in the drivers.

This series adds a pair of helper functions to manage the attach/detach of a
device to its multiple PM domains. Moreover, a couple of drivers have been
converted to use the new helpers as a proof of concept.

Note 1)
The changes in the drivers have only been compile tested, while the helpers
have been tested along with a couple of local dummy drivers that I have hacked
up to model both genpd providers and genpd consumers.

Note 2)
I was struggling to make up mind if we should have a separate helper to attach
all available power-domains described in DT, rather than providing "NULL" to the
dev_pm_domain_attach_list(). I decided not to, but please let me know if you
prefer the other option.

Note 3)
For OPP integration, as a follow up I am striving to make the
dev_pm_opp_attach_genpd() redundant. Instead I think we should move towards
using dev_pm_opp_set_config()->_opp_set_required_devs(), which would allow us to
use the helpers that $subject series is adding.

Kind regards
Ulf Hansson

Ulf Hansson (5):
  PM: domains: Add helper functions to attach/detach multiple PM domains
  remoteproc: imx_dsp_rproc: Convert to
dev_pm_domain_attach|detach_list()
  remoteproc: imx_rproc: Convert to dev_pm_domain_attach|detach_list()
  remoteproc: qcom_q6v5_adsp: Convert to
dev_pm_domain_attach|detach_list()
  media: venus: Convert to dev_pm_domain_attach|detach_list() for vcodec

 drivers/base/power/common.c   | 133 +++
 drivers/media/platform/qcom/venus/core.c  |  12 +-
 drivers/media/platform/qcom/venus/core.h  |   7 +-
 .../media/platform/qcom/venus/pm_helpers.c|  48 ++
 drivers/remoteproc/imx_dsp_rproc.c|  82 +
 drivers/remoteproc/imx_rproc.c|  73 +---
 drivers/remoteproc/qcom_q6v5_adsp.c   | 160 --
 include/linux/pm_domain.h |  38 +
 8 files changed, 288 insertions(+), 265 deletions(-)

-- 
2.34.1




[RFC PATCH 5/5] MAINTAINERS: add entries for the 88pm88x regulators driver

2023-12-28 Thread Karel Balej
From: Karel Balej 

List the related files under the Marvell 88PM88X PMICs entry.

Signed-off-by: Karel Balej 
---
 MAINTAINERS | 2 ++
 1 file changed, 2 insertions(+)

diff --git a/MAINTAINERS b/MAINTAINERS
index f35ec0f186a9..f9676aec7397 100644
--- a/MAINTAINERS
+++ b/MAINTAINERS
@@ -12743,8 +12743,10 @@ M: Karel Balej 
 S: Maintained
 F: Documentation/devicetree/bindings/input/marvell,88pm88x-onkey.yaml
 F: Documentation/devicetree/bindings/mfd/marvell,88pm88x.yaml
+F: 
Documentation/devicetree/bindings/regulator/marvell,88pm88x-regulator.yaml
 F: drivers/input/misc/88pm88x-onkey.c
 F: drivers/mfd/88pm88x.c
+F: drivers/regulators/88pm88x-regulator.c
 F: include/linux/mfd/88pm88x.h
 
 MARVELL ARMADA 3700 PHY DRIVERS
-- 
2.43.0




[RFC PATCH 4/5] regulator: add 88pm88x regulators driver

2023-12-28 Thread Karel Balej
From: Karel Balej 

Support the LDO and buck regulators of the Marvell 88PM886 PMIC. Support
for 88PM880 is not included but should be easy to implement being just a
matter of defining the additional LDOs and all bucks and modifying the
88PM88X MFD driver appropriately.

Signed-off-by: Karel Balej 
---
 drivers/mfd/88pm88x.c |  15 ++
 drivers/regulator/88pm88x-regulator.c | 214 ++
 drivers/regulator/Kconfig |   6 +
 drivers/regulator/Makefile|   1 +
 include/linux/mfd/88pm88x.h   |  11 ++
 5 files changed, 247 insertions(+)
 create mode 100644 drivers/regulator/88pm88x-regulator.c

diff --git a/drivers/mfd/88pm88x.c b/drivers/mfd/88pm88x.c
index 69a8e39d43b3..999d0539b720 100644
--- a/drivers/mfd/88pm88x.c
+++ b/drivers/mfd/88pm88x.c
@@ -68,6 +68,21 @@ static struct mfd_cell pm886_devs[] = {
.num_resources = ARRAY_SIZE(pm88x_onkey_resources),
.resources = pm88x_onkey_resources,
},
+   {
+   .name = "88pm88x-regulator",
+   .id = PM88X_REGULATOR_ID_LDO2,
+   .of_compatible = "marvell,88pm88x-regulator",
+   },
+   {
+   .name = "88pm88x-regulator",
+   .id = PM88X_REGULATOR_ID_LDO15,
+   .of_compatible = "marvell,88pm88x-regulator",
+   },
+   {
+   .name = "88pm88x-regulator",
+   .id = PM886_REGULATOR_ID_BUCK2,
+   .of_compatible = "marvell,88pm88x-regulator",
+   },
 };
 
 static struct pm88x_data pm886_a1_data = {
diff --git a/drivers/regulator/88pm88x-regulator.c 
b/drivers/regulator/88pm88x-regulator.c
new file mode 100644
index ..8b55e1365387
--- /dev/null
+++ b/drivers/regulator/88pm88x-regulator.c
@@ -0,0 +1,214 @@
+// SPDX-License-Identifier: GPL-2.0-only
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+#include 
+
+#include 
+
+#define PM88X_REG_LDO_EN1  0x09
+#define PM88X_REG_LDO_EN2  0x0a
+
+#define PM88X_REG_BUCK_EN  0x08
+
+#define PM88X_REG_LDO1_VOUT0x20
+#define PM88X_REG_LDO2_VOUT0x26
+#define PM88X_REG_LDO3_VOUT0x2c
+#define PM88X_REG_LDO4_VOUT0x32
+#define PM88X_REG_LDO5_VOUT0x38
+#define PM88X_REG_LDO6_VOUT0x3e
+#define PM88X_REG_LDO7_VOUT0x44
+#define PM88X_REG_LDO8_VOUT0x4a
+#define PM88X_REG_LDO9_VOUT0x50
+#define PM88X_REG_LDO10_VOUT   0x56
+#define PM88X_REG_LDO11_VOUT   0x5c
+#define PM88X_REG_LDO12_VOUT   0x62
+#define PM88X_REG_LDO13_VOUT   0x68
+#define PM88X_REG_LDO14_VOUT   0x6e
+#define PM88X_REG_LDO15_VOUT   0x74
+#define PM88X_REG_LDO16_VOUT   0x7a
+
+#define PM886_REG_BUCK1_VOUT   0xa5
+#define PM886_REG_BUCK2_VOUT   0xb3
+#define PM886_REG_BUCK3_VOUT   0xc1
+#define PM886_REG_BUCK4_VOUT   0xcf
+#define PM886_REG_BUCK5_VOUT   0xdd
+
+#define PM88X_LDO_VSEL_MASK0x0f
+#define PM88X_BUCK_VSEL_MASK   0x7f
+
+struct pm88x_regulator {
+   struct regulator_desc desc;
+   int max_uA;
+};
+
+static int pm88x_regulator_get_ilim(struct regulator_dev *rdev)
+{
+   struct pm88x_regulator *data = rdev_get_drvdata(rdev);
+
+   if (!data) {
+   dev_err(&rdev->dev, "Failed to get regulator data\n");
+   return -EINVAL;
+   }
+   return data->max_uA;
+}
+
+static const struct regulator_ops pm88x_ldo_ops = {
+   .list_voltage = regulator_list_voltage_table,
+   .map_voltage = regulator_map_voltage_iterate,
+   .set_voltage_sel = regulator_set_voltage_sel_regmap,
+   .get_voltage_sel = regulator_get_voltage_sel_regmap,
+   .enable = regulator_enable_regmap,
+   .disable = regulator_disable_regmap,
+   .is_enabled = regulator_is_enabled_regmap,
+   .get_current_limit = pm88x_regulator_get_ilim,
+};
+
+static const struct regulator_ops pm88x_buck_ops = {
+   .list_voltage = regulator_list_voltage_linear_range,
+   .map_voltage = regulator_map_voltage_linear_range,
+   .set_voltage_sel = regulator_set_voltage_sel_regmap,
+   .get_voltage_sel = regulator_get_voltage_sel_regmap,
+   .enable = regulator_enable_regmap,
+   .disable = regulator_disable_regmap,
+   .is_enabled = regulator_is_enabled_regmap,
+   .get_current_limit = pm88x_regulator_get_ilim,
+};
+
+static const unsigned int pm88x_ldo_volt_table1[] = {
+   170, 180, 190, 250, 280, 290, 310, 330,
+};
+
+static const unsigned int pm88x_ldo_volt_table2[] = {
+   120, 125, 170, 180, 185, 190, 250, 260,
+   270, 275, 280, 285, 290, 300, 310, 330,
+};
+
+static const unsigned int pm88x_ldo_volt_table3[] = {
+   170, 180, 190, 200, 210, 250, 270, 280,
+};

[RFC PATCH 3/5] dt-bindings: regulator: add documentation entry for 88pm88x-regulator

2023-12-28 Thread Karel Balej
From: Karel Balej 

The Marvell 88PM88X PMICs provide regulators among other things.
Document how to use them.

Signed-off-by: Karel Balej 
---
 .../bindings/mfd/marvell,88pm88x.yaml | 17 +++
 .../regulator/marvell,88pm88x-regulator.yaml  | 28 +++
 2 files changed, 45 insertions(+)
 create mode 100644 
Documentation/devicetree/bindings/regulator/marvell,88pm88x-regulator.yaml

diff --git a/Documentation/devicetree/bindings/mfd/marvell,88pm88x.yaml 
b/Documentation/devicetree/bindings/mfd/marvell,88pm88x.yaml
index 115b41c9f22c..e6944369fc5c 100644
--- a/Documentation/devicetree/bindings/mfd/marvell,88pm88x.yaml
+++ b/Documentation/devicetree/bindings/mfd/marvell,88pm88x.yaml
@@ -54,6 +54,23 @@ examples:
 onkey {
   compatible = "marvell,88pm88x-onkey";
 };
+
+regulators {
+  ldo2: ldo2 {
+regulator-min-microvolt = <310>;
+regulator-max-microvolt = <330>;
+};
+
+  ldo15: ldo15 {
+regulator-min-microvolt = <330>;
+regulator-max-microvolt = <330>;
+};
+
+  buck2: buck2 {
+regulator-min-microvolt = <180>;
+regulator-max-microvolt = <180>;
+};
+};
   };
 };
 ...
diff --git 
a/Documentation/devicetree/bindings/regulator/marvell,88pm88x-regulator.yaml 
b/Documentation/devicetree/bindings/regulator/marvell,88pm88x-regulator.yaml
new file mode 100644
index ..c6ac17b113e7
--- /dev/null
+++ b/Documentation/devicetree/bindings/regulator/marvell,88pm88x-regulator.yaml
@@ -0,0 +1,28 @@
+# SPDX-License-Identifier: GPL-2.0-only OR BSD-2-Clause
+%YAML 1.2
+---
+$id: http://devicetree.org/schemas/regulator/marvell,88pm88x-regulator.yaml#
+$schema: http://devicetree.org/meta-schemas/core.yaml#
+
+title: Marvell 88PM88X PMICs regulators
+
+maintainers:
+  - Karel Balej 
+
+description: |
+  This module is part of the Marvell 88PM88X MFD device. For more details
+  see Documentation/devicetree/bindings/mfd/marvell,88pm88x.yaml.
+
+  The regulator controller is represented as a sub-node of the PMIC node
+  in the device tree.
+
+  The valid names for 88PM886 regulator nodes are ldo[1-9], ldo1[0-6], 
buck[1-5].
+
+patternProperties:
+  "^(ldo|buck)[0-9]+$":
+type: object
+description:
+  Properties for single regulator.
+$ref: regulator.yaml#
+
+additionalProperties: false
-- 
2.43.0




[RFC PATCH 2/5] mfd: 88pm88x: initialize the regulators regmaps

2023-12-28 Thread Karel Balej
From: Karel Balej 

The regulators registers are accessed via a different I2C address than
the already implemented functionality. Initialize the new regmap for the
regulator driver to use. For 88PM886 the buck regmap is the same as LDO
regmap, however this is not the case for 88PM880.

Signed-off-by: Karel Balej 
---
 drivers/mfd/88pm88x.c   | 33 +
 include/linux/mfd/88pm88x.h |  4 
 2 files changed, 37 insertions(+)

diff --git a/drivers/mfd/88pm88x.c b/drivers/mfd/88pm88x.c
index 3424d88a58f6..69a8e39d43b3 100644
--- a/drivers/mfd/88pm88x.c
+++ b/drivers/mfd/88pm88x.c
@@ -98,6 +98,35 @@ static int pm88x_power_off_handler(struct sys_off_data *data)
return NOTIFY_DONE;
 }
 
+static int pm88x_initialize_subregmaps(struct pm88x_chip *chip)
+{
+   struct i2c_client *page;
+   struct regmap *regmap;
+   int ret;
+
+   /* LDO page */
+   page = devm_i2c_new_dummy_device(&chip->client->dev, 
chip->client->adapter,
+   chip->client->addr + 
PM88X_PAGE_OFFSET_LDO);
+   if (IS_ERR(page)) {
+   ret = PTR_ERR(page);
+   dev_err(&chip->client->dev, "Failed to initialize LDO client: 
%d\n",
+   ret);
+   return ret;
+   }
+   regmap = devm_regmap_init_i2c(page, &pm88x_i2c_regmap);
+   if (IS_ERR(regmap)) {
+   ret = PTR_ERR(regmap);
+   dev_err(&chip->client->dev, "Failed to initialize LDO regmap: 
%d\n",
+   ret);
+   return ret;
+   }
+   chip->regmaps[PM88X_REGMAP_LDO] = regmap;
+   /* buck regmap is the same as LDO */
+   chip->regmaps[PM88X_REGMAP_BUCK] = regmap;
+
+   return 0;
+}
+
 static int pm88x_setup_irq(struct pm88x_chip *chip)
 {
int ret;
@@ -155,6 +184,10 @@ static int pm88x_probe(struct i2c_client *client)
return -EINVAL;
}
 
+   ret = pm88x_initialize_subregmaps(chip);
+   if (ret)
+   return ret;
+
ret = pm88x_setup_irq(chip);
if (ret)
return ret;
diff --git a/include/linux/mfd/88pm88x.h b/include/linux/mfd/88pm88x.h
index 9a335f6b9c07..703e6104c1d8 100644
--- a/include/linux/mfd/88pm88x.h
+++ b/include/linux/mfd/88pm88x.h
@@ -39,8 +39,12 @@
 
 #define PM88X_REG_AON_CTRL20xe2
 
+#define PM88X_PAGE_OFFSET_LDO  1
+
 enum pm88x_regmap_index {
PM88X_REGMAP_BASE,
+   PM88X_REGMAP_LDO,
+   PM88X_REGMAP_BUCK,
 
PM88X_REGMAP_NR
 };
-- 
2.43.0




[RFC PATCH 1/5] mfd: 88pm88x: differences with respect to the PMIC RFC series

2023-12-28 Thread Karel Balej
From: Karel Balej 

Signed-off-by: Karel Balej 
---
 drivers/mfd/88pm88x.c   | 14 --
 include/linux/mfd/88pm88x.h |  2 ++
 2 files changed, 10 insertions(+), 6 deletions(-)

diff --git a/drivers/mfd/88pm88x.c b/drivers/mfd/88pm88x.c
index 5db6c65b667d..3424d88a58f6 100644
--- a/drivers/mfd/88pm88x.c
+++ b/drivers/mfd/88pm88x.c
@@ -57,16 +57,16 @@ static struct reg_sequence pm886_presets[] = {
REG_SEQ0(PM88X_REG_BK_OSC_CTRL3, 0xc0),
 };
 
-static struct resource onkey_resources[] = {
+static struct resource pm88x_onkey_resources[] = {
DEFINE_RES_IRQ_NAMED(PM88X_IRQ_ONKEY, "88pm88x-onkey"),
 };
 
-static struct mfd_cell pm88x_devs[] = {
+static struct mfd_cell pm886_devs[] = {
{
.name = "88pm88x-onkey",
-   .num_resources = ARRAY_SIZE(onkey_resources),
-   .resources = onkey_resources,
-   .id = -1,
+   .of_compatible = "marvell,88pm88x-onkey",
+   .num_resources = ARRAY_SIZE(pm88x_onkey_resources),
+   .resources = pm88x_onkey_resources,
},
 };
 
@@ -74,6 +74,8 @@ static struct pm88x_data pm886_a1_data = {
.whoami = PM886_A1_WHOAMI,
.presets = pm886_presets,
.num_presets = ARRAY_SIZE(pm886_presets),
+   .devs = pm886_devs,
+   .num_devs = ARRAY_SIZE(pm886_devs),
 };
 
 static const struct regmap_config pm88x_i2c_regmap = {
@@ -157,7 +159,7 @@ static int pm88x_probe(struct i2c_client *client)
if (ret)
return ret;
 
-   ret = devm_mfd_add_devices(&client->dev, 0, pm88x_devs, 
ARRAY_SIZE(pm88x_devs),
+   ret = devm_mfd_add_devices(&client->dev, 0, chip->data->devs, 
chip->data->num_devs,
NULL, 0, regmap_irq_get_domain(chip->irq_data));
if (ret) {
dev_err(&client->dev, "Failed to add devices: %d\n", ret);
diff --git a/include/linux/mfd/88pm88x.h b/include/linux/mfd/88pm88x.h
index a34c57447827..9a335f6b9c07 100644
--- a/include/linux/mfd/88pm88x.h
+++ b/include/linux/mfd/88pm88x.h
@@ -49,6 +49,8 @@ struct pm88x_data {
unsigned int whoami;
struct reg_sequence *presets;
unsigned int num_presets;
+   struct mfd_cell *devs;
+   unsigned int num_devs;
 };
 
 struct pm88x_chip {
-- 
2.43.0




[RFC PATCH 0/5] regulator: support for Marvell 88PM886 LDOs and bucks

2023-12-28 Thread Karel Balej
From: Karel Balej 

Hello,

the following adds the regulators driver for Marvell 88PM88X PMICs
implementing only the 88PM886 specific parts - however extension for
88PM880 should be trivial. The series adding MFD driver for these PMICs
is available here [1]. Please note that this series depends on that
one.

The motivation and testing platform for this is the
samsung,coreprimevelte smartphone for which the initial support efforts
are ongoing here [2]. This PMIC is also found in at least two other
devices with the PXA1908 SoC, such as samsung,xcover3lte and
samsung,grandprimevelte.

As the only reference for this driver served the smartphone's downstream
kernel tree which is available here [3].

Please note that the first patch of this series is just a joining step
with respect to series [1] and will be amalgated with future versions of
it and dropped here. Also please note that that this series has the same
defects as the MFD one and thus please only review the new parts.
Lastly, as I would like to get some feedback on whether the approach I
have taken here is OK, I have only defined descriptions for three
regulators so far, the remaining eighteen will be defined in the same
style and will of course be added when this series leaves the RFC state
at the latest.

[1] 
https://lore.kernel.org/all/20231217131838.7569-1-kar...@gimli.ms.mff.cuni.cz/
[2] 
https://lore.kernel.org/all/20231102-pxa1908-lkml-v7-0-cabb1a0cb...@skole.hr/
[3] https://github.com/CoderCharmander/g361f-kernel

Thank you,
K. B.

Karel Balej (5):
  mfd: 88pm88x: differences with respect to the PMIC RFC series
  mfd: 88pm88x: initialize the regulators regmaps
  dt-bindings: regulator: add documentation entry for 88pm88x-regulator
  regulator: add 88pm88x regulators driver
  MAINTAINERS: add entries for the 88pm88x regulators driver

 .../bindings/mfd/marvell,88pm88x.yaml |  17 ++
 .../regulator/marvell,88pm88x-regulator.yaml  |  28 +++
 MAINTAINERS   |   2 +
 drivers/mfd/88pm88x.c |  62 -
 drivers/regulator/88pm88x-regulator.c | 214 ++
 drivers/regulator/Kconfig |   6 +
 drivers/regulator/Makefile|   1 +
 include/linux/mfd/88pm88x.h   |  17 ++
 8 files changed, 341 insertions(+), 6 deletions(-)
 create mode 100644 
Documentation/devicetree/bindings/regulator/marvell,88pm88x-regulator.yaml
 create mode 100644 drivers/regulator/88pm88x-regulator.c

-- 
2.43.0