Message ID | 1623237532-20829-2-git-send-email-sibis@codeaurora.org (mailing list archive) |
---|---|
State | Superseded |
Headers | show |
Series | soc: qcom: aoss: Expose send for generic usecase | expand |
Quoting Sibi Sankar (2021-06-09 04:18:51) > From: Deepak Kumar Singh <deesin@codeaurora.org> > > Not all upcoming usecases will have an interface to allow the aoss > driver to hook onto. Expose the send api and create a get function to > enable drivers to send their own messages to aoss. > > Signed-off-by: Chris Lew <clew@codeaurora.org> > Signed-off-by: Deepak Kumar Singh <deesin@codeaurora.org> > Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> > Signed-off-by: Sibi Sankar <sibis@codeaurora.org> > --- > > v4: > * Fix compilation error due to missing qmp_put > * Minor typos [s/tarcks/tracks] > > drivers/soc/qcom/qcom_aoss.c | 70 ++++++++++++++++++++++++++++++++++++-- > include/linux/soc/qcom/qcom_aoss.h | 36 ++++++++++++++++++++ > 2 files changed, 104 insertions(+), 2 deletions(-) > create mode 100644 include/linux/soc/qcom/qcom_aoss.h > > diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c > index 934fcc4d2b05..e8f48760bac8 100644 > --- a/drivers/soc/qcom/qcom_aoss.c > +++ b/drivers/soc/qcom/qcom_aoss.c > @@ -522,13 +582,14 @@ static int qmp_probe(struct platform_device *pdev) > int irq; > int ret; > > - qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL); > + qmp = kzalloc(sizeof(*qmp), GFP_KERNEL); > if (!qmp) > return -ENOMEM; > > qmp->dev = &pdev->dev; > init_waitqueue_head(&qmp->event); > mutex_init(&qmp->tx_lock); > + kref_init(&qmp->refcount); > > res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > qmp->msgram = devm_ioremap_resource(&pdev->dev, res); > @@ -569,6 +630,8 @@ static int qmp_probe(struct platform_device *pdev) > > platform_set_drvdata(pdev, qmp); > > + atomic_set(&qmp->orphan, 0); > + > return 0; > > err_remove_qdss_clk: > @@ -577,6 +640,7 @@ static int qmp_probe(struct platform_device *pdev) > qmp_close(qmp); > err_free_mbox: > mbox_free_channel(qmp->mbox_chan); > + kfree(qmp); > > return ret; > } > @@ -590,7 +654,9 @@ static int qmp_remove(struct platform_device *pdev) > qmp_cooling_devices_remove(qmp); > > qmp_close(qmp); > + atomic_set(&qmp->orphan, 1); This looks odd. Why are we letting the device be removed while it is in use by other drivers? Can't we pin the device with get_device() so it can't be removed and then prevent the driver from being removed until all the consumer drivers drop the reference, i.e. suppress sysfs unbind? Otherwise it looks like a generic problem that all provider devices, clks, regulators, gpios, etc. have to deal with and thus this hand-rolled mechanism can't be right. > mbox_free_channel(qmp->mbox_chan); > + kref_put(&qmp->refcount, qmp_handle_release); > > return 0; > }
On 7/21/2021 12:07 PM, Stephen Boyd wrote: > Quoting Sibi Sankar (2021-06-09 04:18:51) >> From: Deepak Kumar Singh <deesin@codeaurora.org> >> >> Not all upcoming usecases will have an interface to allow the aoss >> driver to hook onto. Expose the send api and create a get function to >> enable drivers to send their own messages to aoss. >> >> Signed-off-by: Chris Lew <clew@codeaurora.org> >> Signed-off-by: Deepak Kumar Singh <deesin@codeaurora.org> >> Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> >> Signed-off-by: Sibi Sankar <sibis@codeaurora.org> >> --- >> >> v4: >> * Fix compilation error due to missing qmp_put >> * Minor typos [s/tarcks/tracks] >> >> drivers/soc/qcom/qcom_aoss.c | 70 ++++++++++++++++++++++++++++++++++++-- >> include/linux/soc/qcom/qcom_aoss.h | 36 ++++++++++++++++++++ >> 2 files changed, 104 insertions(+), 2 deletions(-) >> create mode 100644 include/linux/soc/qcom/qcom_aoss.h >> >> diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c >> index 934fcc4d2b05..e8f48760bac8 100644 >> --- a/drivers/soc/qcom/qcom_aoss.c >> +++ b/drivers/soc/qcom/qcom_aoss.c >> @@ -522,13 +582,14 @@ static int qmp_probe(struct platform_device *pdev) >> int irq; >> int ret; >> >> - qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL); >> + qmp = kzalloc(sizeof(*qmp), GFP_KERNEL); >> if (!qmp) >> return -ENOMEM; >> >> qmp->dev = &pdev->dev; >> init_waitqueue_head(&qmp->event); >> mutex_init(&qmp->tx_lock); >> + kref_init(&qmp->refcount); >> >> res = platform_get_resource(pdev, IORESOURCE_MEM, 0); >> qmp->msgram = devm_ioremap_resource(&pdev->dev, res); >> @@ -569,6 +630,8 @@ static int qmp_probe(struct platform_device *pdev) >> >> platform_set_drvdata(pdev, qmp); >> >> + atomic_set(&qmp->orphan, 0); >> + >> return 0; >> >> err_remove_qdss_clk: >> @@ -577,6 +640,7 @@ static int qmp_probe(struct platform_device *pdev) >> qmp_close(qmp); >> err_free_mbox: >> mbox_free_channel(qmp->mbox_chan); >> + kfree(qmp); >> >> return ret; >> } >> @@ -590,7 +654,9 @@ static int qmp_remove(struct platform_device *pdev) >> qmp_cooling_devices_remove(qmp); >> >> qmp_close(qmp); >> + atomic_set(&qmp->orphan, 1); > This looks odd. Why are we letting the device be removed while it is in > use by other drivers? Can't we pin the device with get_device() so it > can't be removed and then prevent the driver from being removed until > all the consumer drivers drop the reference, i.e. suppress sysfs unbind? > > Otherwise it looks like a generic problem that all provider devices, > clks, regulators, gpios, etc. have to deal with and thus this > hand-rolled mechanism can't be right. As per my earlier discussion with Bjorn, device could be unbound using sysfs, in which case remove() is called irrespective of whether any client driver is holding struct device reference or not. That's why i have added separate refcount for qmp handle and marking it invalid if qmp_remove() is called. >> mbox_free_channel(qmp->mbox_chan); >> + kref_put(&qmp->refcount, qmp_handle_release); >> >> return 0; >> }
Quoting Deepak Kumar Singh (2021-07-23 02:51:50) > > On 7/21/2021 12:07 PM, Stephen Boyd wrote: > > Quoting Sibi Sankar (2021-06-09 04:18:51) > >> From: Deepak Kumar Singh <deesin@codeaurora.org> > >> > >> Not all upcoming usecases will have an interface to allow the aoss > >> driver to hook onto. Expose the send api and create a get function to > >> enable drivers to send their own messages to aoss. > >> > >> Signed-off-by: Chris Lew <clew@codeaurora.org> > >> Signed-off-by: Deepak Kumar Singh <deesin@codeaurora.org> > >> Reviewed-by: Bjorn Andersson <bjorn.andersson@linaro.org> > >> Signed-off-by: Sibi Sankar <sibis@codeaurora.org> > >> --- > >> > >> v4: > >> * Fix compilation error due to missing qmp_put > >> * Minor typos [s/tarcks/tracks] > >> > >> drivers/soc/qcom/qcom_aoss.c | 70 ++++++++++++++++++++++++++++++++++++-- > >> include/linux/soc/qcom/qcom_aoss.h | 36 ++++++++++++++++++++ > >> 2 files changed, 104 insertions(+), 2 deletions(-) > >> create mode 100644 include/linux/soc/qcom/qcom_aoss.h > >> > >> diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c > >> index 934fcc4d2b05..e8f48760bac8 100644 > >> --- a/drivers/soc/qcom/qcom_aoss.c > >> +++ b/drivers/soc/qcom/qcom_aoss.c > >> @@ -522,13 +582,14 @@ static int qmp_probe(struct platform_device *pdev) > >> int irq; > >> int ret; > >> > >> - qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL); > >> + qmp = kzalloc(sizeof(*qmp), GFP_KERNEL); > >> if (!qmp) > >> return -ENOMEM; > >> > >> qmp->dev = &pdev->dev; > >> init_waitqueue_head(&qmp->event); > >> mutex_init(&qmp->tx_lock); > >> + kref_init(&qmp->refcount); > >> > >> res = platform_get_resource(pdev, IORESOURCE_MEM, 0); > >> qmp->msgram = devm_ioremap_resource(&pdev->dev, res); > >> @@ -569,6 +630,8 @@ static int qmp_probe(struct platform_device *pdev) > >> > >> platform_set_drvdata(pdev, qmp); > >> > >> + atomic_set(&qmp->orphan, 0); > >> + > >> return 0; > >> > >> err_remove_qdss_clk: > >> @@ -577,6 +640,7 @@ static int qmp_probe(struct platform_device *pdev) > >> qmp_close(qmp); > >> err_free_mbox: > >> mbox_free_channel(qmp->mbox_chan); > >> + kfree(qmp); > >> > >> return ret; > >> } > >> @@ -590,7 +654,9 @@ static int qmp_remove(struct platform_device *pdev) > >> qmp_cooling_devices_remove(qmp); > >> > >> qmp_close(qmp); > >> + atomic_set(&qmp->orphan, 1); > > This looks odd. Why are we letting the device be removed while it is in > > use by other drivers? Can't we pin the device with get_device() so it > > can't be removed and then prevent the driver from being removed until > > all the consumer drivers drop the reference, i.e. suppress sysfs unbind? > > > > Otherwise it looks like a generic problem that all provider devices, > > clks, regulators, gpios, etc. have to deal with and thus this > > hand-rolled mechanism can't be right. > > As per my earlier discussion with Bjorn, device could be unbound using > sysfs, in which case > > remove() is called irrespective of whether any client driver is holding > struct device reference > > or not. That's why i have added separate refcount for qmp handle and > marking it invalid if > > qmp_remove() is called. > We have struct device_driver::suppress_bind_attrs for that. Can you set it?
diff --git a/drivers/soc/qcom/qcom_aoss.c b/drivers/soc/qcom/qcom_aoss.c index 934fcc4d2b05..e8f48760bac8 100644 --- a/drivers/soc/qcom/qcom_aoss.c +++ b/drivers/soc/qcom/qcom_aoss.c @@ -8,10 +8,12 @@ #include <linux/io.h> #include <linux/mailbox_client.h> #include <linux/module.h> +#include <linux/of_platform.h> #include <linux/platform_device.h> #include <linux/pm_domain.h> #include <linux/thermal.h> #include <linux/slab.h> +#include <linux/soc/qcom/qcom_aoss.h> #define QMP_DESC_MAGIC 0x0 #define QMP_DESC_VERSION 0x4 @@ -61,6 +63,7 @@ struct qmp_cooling_device { * @mbox_chan: mailbox channel used to ring the doorbell on transmit * @offset: offset within @msgram where messages should be written * @size: maximum size of the messages to be transmitted + * @orphan: tracks whether qmp handle is valid * @event: wait_queue for synchronization with the IRQ * @tx_lock: provides synchronization between multiple callers of qmp_send() * @qdss_clk: QDSS clock hw struct @@ -76,6 +79,8 @@ struct qmp { size_t offset; size_t size; + atomic_t orphan; + struct kref refcount; wait_queue_head_t event; @@ -223,11 +228,17 @@ static bool qmp_message_empty(struct qmp *qmp) * * Return: 0 on success, negative errno on failure */ -static int qmp_send(struct qmp *qmp, const void *data, size_t len) +int qmp_send(struct qmp *qmp, const void *data, size_t len) { long time_left; int ret; + if (WARN_ON(IS_ERR_OR_NULL(qmp) || !data)) + return -EINVAL; + + if (atomic_read(&qmp->orphan)) + return -EINVAL; + if (WARN_ON(len + sizeof(u32) > qmp->size)) return -EINVAL; @@ -261,6 +272,7 @@ static int qmp_send(struct qmp *qmp, const void *data, size_t len) return ret; } +EXPORT_SYMBOL(qmp_send); static int qmp_qdss_clk_prepare(struct clk_hw *hw) { @@ -515,6 +527,54 @@ static void qmp_cooling_devices_remove(struct qmp *qmp) thermal_cooling_device_unregister(qmp->cooling_devs[i].cdev); } +/** + * qmp_get() - get a qmp handle from a device + * @dev: client device pointer + * + * Return: handle to qmp device on success, ERR_PTR() on failure + */ +struct qmp *qmp_get(struct device *dev) +{ + struct platform_device *pdev; + struct device_node *np; + struct qmp *qmp; + + if (!dev || !dev->of_node) + return ERR_PTR(-EINVAL); + + np = of_parse_phandle(dev->of_node, "qcom,qmp", 0); + if (!np) + return ERR_PTR(-ENODEV); + + pdev = of_find_device_by_node(np); + of_node_put(np); + if (!pdev) + return ERR_PTR(-EINVAL); + + qmp = platform_get_drvdata(pdev); + platform_device_put(pdev); + + if (qmp) + kref_get(&qmp->refcount); + + return qmp ? qmp : ERR_PTR(-EPROBE_DEFER); +} +EXPORT_SYMBOL(qmp_get); + +static void qmp_handle_release(struct kref *ref) +{ + struct qmp *qmp = container_of(ref, struct qmp, refcount); + + kfree(qmp); +} + +void qmp_put(struct qmp *qmp) +{ + if (!IS_ERR_OR_NULL(qmp)) + kref_put(&qmp->refcount, qmp_handle_release); +} +EXPORT_SYMBOL(qmp_put); + static int qmp_probe(struct platform_device *pdev) { struct resource *res; @@ -522,13 +582,14 @@ static int qmp_probe(struct platform_device *pdev) int irq; int ret; - qmp = devm_kzalloc(&pdev->dev, sizeof(*qmp), GFP_KERNEL); + qmp = kzalloc(sizeof(*qmp), GFP_KERNEL); if (!qmp) return -ENOMEM; qmp->dev = &pdev->dev; init_waitqueue_head(&qmp->event); mutex_init(&qmp->tx_lock); + kref_init(&qmp->refcount); res = platform_get_resource(pdev, IORESOURCE_MEM, 0); qmp->msgram = devm_ioremap_resource(&pdev->dev, res); @@ -569,6 +630,8 @@ static int qmp_probe(struct platform_device *pdev) platform_set_drvdata(pdev, qmp); + atomic_set(&qmp->orphan, 0); + return 0; err_remove_qdss_clk: @@ -577,6 +640,7 @@ static int qmp_probe(struct platform_device *pdev) qmp_close(qmp); err_free_mbox: mbox_free_channel(qmp->mbox_chan); + kfree(qmp); return ret; } @@ -590,7 +654,9 @@ static int qmp_remove(struct platform_device *pdev) qmp_cooling_devices_remove(qmp); qmp_close(qmp); + atomic_set(&qmp->orphan, 1); mbox_free_channel(qmp->mbox_chan); + kref_put(&qmp->refcount, qmp_handle_release); return 0; } diff --git a/include/linux/soc/qcom/qcom_aoss.h b/include/linux/soc/qcom/qcom_aoss.h new file mode 100644 index 000000000000..725c9d12fd11 --- /dev/null +++ b/include/linux/soc/qcom/qcom_aoss.h @@ -0,0 +1,36 @@ +/* SPDX-License-Identifier: GPL-2.0-only */ +/* + * Copyright (c) 2020, The Linux Foundation. All rights reserved. + */ + +#ifndef __QCOM_AOSS_H__ +#define __QCOM_AOSS_H__ + +#include <linux/err.h> +#include <linux/device.h> + +struct qmp; + +#if IS_ENABLED(CONFIG_QCOM_AOSS_QMP) + +int qmp_send(struct qmp *qmp, const void *data, size_t len); +struct qmp *qmp_get(struct device *dev); +void qmp_put(struct qmp *qmp); + +#else + +static inline int qmp_send(struct qmp *qmp, const void *data, size_t len) +{ + return -ENODEV; +} + +static inline struct qmp *qmp_get(struct device *dev) +{ + return ERR_PTR(-ENODEV); +} + +static inline void qmp_put(struct qmp *qmp) { } + +#endif + +#endif