Char/Misc driver fixes for 7.1-final

Here are some small driver fixes for 7.1-final to resolve some reported
 issues.  Included in here are:
   - slimbus qcom driver bugfixes
   - nvmem driver bugfixes
   - fastrpc driver bugfixes
   - stratix10 firmware driver bugfixes
 
 All of these have been in linux-next for over a week with no reported
 issues.
 
 Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
 -----BEGIN PGP SIGNATURE-----
 
 iG0EABECAC0WIQT0tgzFv3jCIUoxPcsxR9QN2y37KQUCaivfhg8cZ3JlZ0Brcm9h
 aC5jb20ACgkQMUfUDdst+yldawCfXjPDG6TDV7MbIQd5PyhlUZ+WEJgAoMznv7Lx
 Pi2sdZhJ4pfYBgwC/VAj
 =rn/r
 -----END PGP SIGNATURE-----

Merge tag 'char-misc-7.1-final' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc

Pull char/misc driver fixes from Greg KH:
 "Here are some small driver fixes for 7.1-final to resolve some
  reported issues. Included in here are:

   - slimbus qcom driver bugfixes

   - nvmem driver bugfixes

   - fastrpc driver bugfixes

   - stratix10 firmware driver bugfixes

  All of these have been in linux-next for over a week with no
  reported issues"

* tag 'char-misc-7.1-final' of git://git.kernel.org/pub/scm/linux/kernel/git/gregkh/char-misc:
  misc: fastrpc: fix use-after-free race in fastrpc_map_create
  misc: fastrpc: Fix NULL pointer dereference in rpmsg callback
  misc: fastrpc: fix DMA address corruption due to find_vma misuse
  misc: fastrpc: fix use-after-free of fastrpc_user in workqueue context
  slimbus: qcom-ngd-ctrl: Avoid ABBA on tx_lock/ctrl->lock
  slimbus: qcom-ngd-ctrl: Balance pm_runtime enablement for NGD
  slimbus: qcom-ngd-ctrl: Initialize controller resources in controller
  slimbus: qcom-ngd-ctrl: Register callbacks after creating the ngd
  slimbus: qcom-ngd-ctrl: Correct PDR and SSR cleanup ownership
  slimbus: qcom-ngd-ctrl: Fix probe error path ordering
  slimbus: qcom-ngd-ctrl: Fix up platform_driver registration
  slimbus: qcom-ngd-ctrl: fix OF node refcount
  nvmem: core: fix use-after-free bugs in error paths
  nvmem: layouts: onie-tlv: fix hang on unknown types
  firmware: stratix10-rsu: Fix NULL deref on rsu_send_msg() timeout in probe
  firmware: stratix10-svc: Don't fail probe when async ops unsupported
  firmware: stratix10-svc: Return -EOPNOTSUPP when ATF async unsupported
This commit is contained in:
Linus Torvalds 2026-06-12 09:00:28 -07:00
commit 9a837eff90
6 changed files with 189 additions and 121 deletions

View file

@ -723,15 +723,9 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
return -ENOMEM;
priv->client.dev = dev;
priv->client.receive_cb = NULL;
priv->client.priv = priv;
priv->status.current_image = 0;
priv->status.fail_image = 0;
priv->status.error_location = 0;
priv->status.error_details = 0;
priv->status.version = 0;
priv->status.state = 0;
priv->retry_counter = INVALID_RETRY_COUNTER;
priv->max_retry = INVALID_RETRY_COUNTER;
priv->dcmf_version.dcmf0 = INVALID_DCMF_VERSION;
priv->dcmf_version.dcmf1 = INVALID_DCMF_VERSION;
priv->dcmf_version.dcmf2 = INVALID_DCMF_VERSION;
@ -740,11 +734,11 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
priv->dcmf_status.dcmf1 = INVALID_DCMF_STATUS;
priv->dcmf_status.dcmf2 = INVALID_DCMF_STATUS;
priv->dcmf_status.dcmf3 = INVALID_DCMF_STATUS;
priv->max_retry = INVALID_RETRY_COUNTER;
priv->spt0_address = INVALID_SPT_ADDRESS;
priv->spt1_address = INVALID_SPT_ADDRESS;
/* spt0/1_address and status fields default to 0 from kzalloc */
mutex_init(&priv->lock);
init_completion(&priv->completion);
priv->chan = stratix10_svc_request_channel_byname(&priv->client,
SVC_CLIENT_RSU);
if (IS_ERR(priv->chan)) {
@ -756,11 +750,9 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
ret = stratix10_svc_add_async_client(priv->chan, false);
if (ret) {
dev_err(dev, "failed to add async client\n");
stratix10_svc_free_channel(priv->chan);
return ret;
goto free_channel;
}
init_completion(&priv->completion);
platform_set_drvdata(pdev, priv);
/* get the initial state from firmware */
@ -768,41 +760,44 @@ static int stratix10_rsu_probe(struct platform_device *pdev)
rsu_async_status_callback);
if (ret) {
dev_err(dev, "Error, getting RSU status %i\n", ret);
stratix10_svc_remove_async_client(priv->chan);
stratix10_svc_free_channel(priv->chan);
return ret;
goto remove_async_client;
}
/* get DCMF version from firmware */
ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_VERSION,
0, rsu_dcmf_version_callback);
ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_VERSION, 0,
rsu_dcmf_version_callback);
if (ret) {
dev_err(dev, "Error, getting DCMF version %i\n", ret);
stratix10_svc_free_channel(priv->chan);
goto remove_async_client;
}
ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS,
0, rsu_dcmf_status_callback);
ret = rsu_send_msg(priv, COMMAND_RSU_DCMF_STATUS, 0,
rsu_dcmf_status_callback);
if (ret) {
dev_err(dev, "Error, getting DCMF status %i\n", ret);
stratix10_svc_free_channel(priv->chan);
goto remove_async_client;
}
ret = rsu_send_msg(priv, COMMAND_RSU_MAX_RETRY, 0,
rsu_max_retry_callback);
if (ret) {
dev_err(dev, "Error, getting RSU max retry %i\n", ret);
stratix10_svc_free_channel(priv->chan);
goto remove_async_client;
}
ret = rsu_send_async_msg(dev, priv, COMMAND_RSU_GET_SPT_TABLE, 0,
rsu_async_get_spt_table_callback);
if (ret) {
dev_err(dev, "Error, getting SPT table %i\n", ret);
stratix10_svc_free_channel(priv->chan);
goto remove_async_client;
}
return 0;
remove_async_client:
stratix10_svc_remove_async_client(priv->chan);
free_channel:
stratix10_svc_free_channel(priv->chan);
return ret;
}

View file

@ -212,6 +212,7 @@ struct stratix10_async_chan {
/**
* struct stratix10_async_ctrl - Control structure for Stratix10
* asynchronous operations
* @supported: Flag indicating whether the system supports async operations
* @initialized: Flag indicating whether the control structure has
* been initialized
* @invoke_fn: Function pointer for invoking Stratix10 service calls
@ -228,6 +229,7 @@ struct stratix10_async_chan {
*/
struct stratix10_async_ctrl {
bool supported;
bool initialized;
void (*invoke_fn)(struct stratix10_async_ctrl *actrl,
const struct arm_smccc_1_2_regs *args,
@ -1103,6 +1105,7 @@ EXPORT_SYMBOL_GPL(stratix10_svc_request_channel_byname);
* Return: 0 on success, or a negative error code on failure:
* -EINVAL if the channel is NULL or the async controller is
* not initialized.
* -EOPNOTSUPP if async operations are not supported.
* -EALREADY if the async channel is already allocated.
* -ENOMEM if memory allocation fails.
* Other negative values if ID allocation fails.
@ -1121,6 +1124,9 @@ int stratix10_svc_add_async_client(struct stratix10_svc_chan *chan,
ctrl = chan->ctrl;
actrl = &ctrl->actrl;
if (!actrl->supported)
return -EOPNOTSUPP;
if (!actrl->initialized) {
dev_err(ctrl->dev, "Async controller not initialized\n");
return -EINVAL;
@ -1562,6 +1568,7 @@ static inline void stratix10_smc_1_2(struct stratix10_async_ctrl *actrl,
* initialized, -ENOMEM if memory allocation fails,
* -EADDRINUSE if the client ID is already reserved, or other
* negative error codes on failure.
* -EOPNOTSUPP if system doesn't support async operations.
*/
static int stratix10_svc_async_init(struct stratix10_svc_controller *controller)
{
@ -1585,10 +1592,12 @@ static int stratix10_svc_async_init(struct stratix10_svc_controller *controller)
!(res.a1 > ASYNC_ATF_MINIMUM_MAJOR_VERSION ||
(res.a1 == ASYNC_ATF_MINIMUM_MAJOR_VERSION &&
res.a2 >= ASYNC_ATF_MINIMUM_MINOR_VERSION))) {
dev_err(dev,
dev_info(dev,
"Intel Service Layer Driver: ATF version is not compatible for async operation\n");
return -EINVAL;
actrl->supported = false;
return -EOPNOTSUPP;
}
actrl->supported = true;
actrl->invoke_fn = stratix10_smc_1_2;
@ -1952,10 +1961,14 @@ static int stratix10_svc_drv_probe(struct platform_device *pdev)
init_completion(&controller->complete_status);
ret = stratix10_svc_async_init(controller);
if (ret) {
if (ret == -EOPNOTSUPP) {
dev_info(dev, "Intel Service Layer Driver Initialized (sync-only mode)\n");
} else if (ret) {
dev_dbg(dev, "Intel Service Layer Driver: Error on stratix10_svc_async_init %d\n",
ret);
goto err_destroy_pool;
} else {
dev_info(dev, "Intel Service Layer Driver Initialized\n");
}
fifo_size = sizeof(struct stratix10_svc_data) * SVC_NUM_DATA_IN_FIFO;

View file

@ -310,6 +310,8 @@ struct fastrpc_user {
spinlock_t lock;
/* lock for allocations */
struct mutex mutex;
/* Reference count */
struct kref refcount;
};
/* Extract SMMU PA from consolidated IOVA */
@ -386,7 +388,7 @@ static int fastrpc_map_get(struct fastrpc_map *map)
static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
struct fastrpc_map **ppmap)
struct fastrpc_map **ppmap, bool take_ref)
{
struct fastrpc_map *map = NULL;
struct dma_buf *buf;
@ -401,6 +403,12 @@ static int fastrpc_map_lookup(struct fastrpc_user *fl, int fd,
if (map->fd != fd || map->buf != buf)
continue;
if (take_ref) {
ret = fastrpc_map_get(map);
if (ret)
break;
}
*ppmap = map;
ret = 0;
break;
@ -497,15 +505,57 @@ static void fastrpc_channel_ctx_put(struct fastrpc_channel_ctx *cctx)
kref_put(&cctx->refcount, fastrpc_channel_ctx_free);
}
static void fastrpc_context_put(struct fastrpc_invoke_ctx *ctx);
static void fastrpc_user_free(struct kref *ref)
{
struct fastrpc_user *fl = container_of(ref, struct fastrpc_user, refcount);
struct fastrpc_invoke_ctx *ctx, *n;
struct fastrpc_map *map, *m;
struct fastrpc_buf *buf, *b;
if (fl->init_mem)
fastrpc_buf_free(fl->init_mem);
list_for_each_entry_safe(ctx, n, &fl->pending, node) {
list_del(&ctx->node);
fastrpc_context_put(ctx);
}
list_for_each_entry_safe(map, m, &fl->maps, node)
fastrpc_map_put(map);
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
list_del(&buf->node);
fastrpc_buf_free(buf);
}
fastrpc_channel_ctx_put(fl->cctx);
mutex_destroy(&fl->mutex);
kfree(fl);
}
static void fastrpc_user_get(struct fastrpc_user *fl)
{
kref_get(&fl->refcount);
}
static void fastrpc_user_put(struct fastrpc_user *fl)
{
kref_put(&fl->refcount, fastrpc_user_free);
}
static void fastrpc_context_free(struct kref *ref)
{
struct fastrpc_invoke_ctx *ctx;
struct fastrpc_channel_ctx *cctx;
struct fastrpc_user *fl;
unsigned long flags;
int i;
ctx = container_of(ref, struct fastrpc_invoke_ctx, refcount);
cctx = ctx->cctx;
fl = ctx->fl;
for (i = 0; i < ctx->nbufs; i++)
fastrpc_map_put(ctx->maps[i]);
@ -521,6 +571,8 @@ static void fastrpc_context_free(struct kref *ref)
kfree(ctx->olaps);
kfree(ctx);
/* Release the reference taken in fastrpc_context_alloc() */
fastrpc_user_put(fl);
fastrpc_channel_ctx_put(cctx);
}
@ -628,6 +680,8 @@ static struct fastrpc_invoke_ctx *fastrpc_context_alloc(
/* Released in fastrpc_context_put() */
fastrpc_channel_ctx_get(cctx);
/* Take a reference to user, released in fastrpc_context_free() */
fastrpc_user_get(user);
ctx->sc = sc;
ctx->retval = -1;
@ -658,6 +712,7 @@ err_idr:
spin_lock(&user->lock);
list_del(&ctx->node);
spin_unlock(&user->lock);
fastrpc_user_put(user);
fastrpc_channel_ctx_put(cctx);
kfree(ctx->maps);
kfree(ctx->olaps);
@ -871,19 +926,10 @@ get_err:
static int fastrpc_map_create(struct fastrpc_user *fl, int fd,
u64 len, u32 attr, struct fastrpc_map **ppmap)
{
struct fastrpc_session_ctx *sess = fl->sctx;
int err = 0;
if (!fastrpc_map_lookup(fl, fd, ppmap)) {
if (!fastrpc_map_get(*ppmap))
if (!fastrpc_map_lookup(fl, fd, ppmap, true))
return 0;
dev_dbg(sess->dev, "%s: Failed to get map fd=%d\n",
__func__, fd);
}
err = fastrpc_map_attach(fl, fd, len, attr, ppmap);
return err;
return fastrpc_map_attach(fl, fd, len, attr, ppmap);
}
/*
@ -1041,7 +1087,7 @@ static int fastrpc_get_args(u32 kernel, struct fastrpc_invoke_ctx *ctx)
pages[i].addr = ctx->maps[i]->dma_addr;
mmap_read_lock(current->mm);
vma = find_vma(current->mm, ctx->args[i].ptr);
vma = vma_lookup(current->mm, ctx->args[i].ptr);
if (vma)
pages[i].addr += (ctx->args[i].ptr & PAGE_MASK) -
vma->vm_start;
@ -1153,7 +1199,7 @@ cleanup_fdlist:
for (i = 0; i < FASTRPC_MAX_FDLIST; i++) {
if (!fdlist[i])
break;
if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap))
if (!fastrpc_map_lookup(fl, (int)fdlist[i], &mmap, false))
fastrpc_map_put(mmap);
}
@ -1579,9 +1625,6 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
{
struct fastrpc_user *fl = (struct fastrpc_user *)file->private_data;
struct fastrpc_channel_ctx *cctx = fl->cctx;
struct fastrpc_invoke_ctx *ctx, *n;
struct fastrpc_map *map, *m;
struct fastrpc_buf *buf, *b;
unsigned long flags;
fastrpc_release_current_dsp_process(fl);
@ -1590,28 +1633,10 @@ static int fastrpc_device_release(struct inode *inode, struct file *file)
list_del(&fl->user);
spin_unlock_irqrestore(&cctx->lock, flags);
if (fl->init_mem)
fastrpc_buf_free(fl->init_mem);
list_for_each_entry_safe(ctx, n, &fl->pending, node) {
list_del(&ctx->node);
fastrpc_context_put(ctx);
}
list_for_each_entry_safe(map, m, &fl->maps, node)
fastrpc_map_put(map);
list_for_each_entry_safe(buf, b, &fl->mmaps, node) {
list_del(&buf->node);
fastrpc_buf_free(buf);
}
fastrpc_session_free(cctx, fl->sctx);
fastrpc_channel_ctx_put(cctx);
mutex_destroy(&fl->mutex);
kfree(fl);
file->private_data = NULL;
/* Release the reference taken in fastrpc_device_open */
fastrpc_user_put(fl);
return 0;
}
@ -1655,6 +1680,7 @@ static int fastrpc_device_open(struct inode *inode, struct file *filp)
spin_lock_irqsave(&cctx->lock, flags);
list_add_tail(&fl->user, &cctx->users);
spin_unlock_irqrestore(&cctx->lock, flags);
kref_init(&fl->refcount);
return 0;
}
@ -2431,7 +2457,6 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
kref_init(&data->refcount);
dev_set_drvdata(&rpdev->dev, data);
rdev->dma_mask = &data->dma_mask;
dma_set_mask_and_coherent(rdev, DMA_BIT_MASK(32));
INIT_LIST_HEAD(&data->users);
@ -2440,6 +2465,7 @@ static int fastrpc_rpmsg_probe(struct rpmsg_device *rpdev)
idr_init(&data->ctx_idr);
data->domain_id = domain_id;
data->rpdev = rpdev;
dev_set_drvdata(&rpdev->dev, data);
err = of_platform_populate(rdev->of_node, NULL, NULL, rdev);
if (err)
@ -2513,6 +2539,9 @@ static int fastrpc_rpmsg_callback(struct rpmsg_device *rpdev, void *data,
if (len < sizeof(*rsp))
return -EINVAL;
if (!cctx)
return -ENODEV;
ctxid = ((rsp->ctx & FASTRPC_CTXID_MASK) >> 4);
spin_lock_irqsave(&cctx->lock, flags);

View file

@ -1468,18 +1468,16 @@ struct nvmem_cell *of_nvmem_cell_get(struct device_node *np, const char *id)
cell_entry = nvmem_find_cell_entry_by_node(nvmem, cell_np);
of_node_put(cell_np);
if (!cell_entry) {
__nvmem_device_put(nvmem);
nvmem_layout_module_put(nvmem);
if (nvmem->layout)
return ERR_PTR(-EPROBE_DEFER);
else
return ERR_PTR(-ENOENT);
ret = nvmem->layout ? -EPROBE_DEFER : -ENOENT;
__nvmem_device_put(nvmem);
return ERR_PTR(ret);
}
cell = nvmem_create_cell(cell_entry, id, cell_index);
if (IS_ERR(cell)) {
__nvmem_device_put(nvmem);
nvmem_layout_module_put(nvmem);
__nvmem_device_put(nvmem);
}
return cell;
@ -1593,8 +1591,8 @@ void nvmem_cell_put(struct nvmem_cell *cell)
kfree_const(cell->id);
kfree(cell);
__nvmem_device_put(nvmem);
nvmem_layout_module_put(nvmem);
__nvmem_device_put(nvmem);
}
EXPORT_SYMBOL_GPL(nvmem_cell_put);

View file

@ -119,7 +119,7 @@ static int onie_tlv_add_cells(struct device *dev, struct nvmem_device *nvmem,
cell.name = onie_tlv_cell_name(tlv.type);
if (!cell.name)
continue;
goto next;
cell.offset = hdr_len + offset + sizeof(tlv.type) + sizeof(tlv.len);
cell.bytes = tlv.len;
@ -132,6 +132,7 @@ static int onie_tlv_add_cells(struct device *dev, struct nvmem_device *nvmem,
return ret;
}
next:
offset += sizeof(tlv) + tlv.len;
}

View file

@ -1471,15 +1471,12 @@ static int qcom_slim_ngd_ssr_pdr_notify(struct qcom_slim_ngd_ctrl *ctrl,
switch (action) {
case QCOM_SSR_BEFORE_SHUTDOWN:
case SERVREG_SERVICE_STATE_DOWN:
/* Make sure the last dma xfer is finished */
mutex_lock(&ctrl->tx_lock);
if (ctrl->state != QCOM_SLIM_NGD_CTRL_DOWN) {
pm_runtime_get_noresume(ctrl->ctrl.dev);
ctrl->state = QCOM_SLIM_NGD_CTRL_DOWN;
qcom_slim_ngd_down(ctrl);
qcom_slim_ngd_exit_dma(ctrl);
}
mutex_unlock(&ctrl->tx_lock);
break;
case QCOM_SSR_AFTER_POWERUP:
case SERVREG_SERVICE_STATE_UP:
@ -1542,7 +1539,7 @@ static int of_qcom_slim_ngd_register(struct device *parent,
kfree(ngd);
return ret;
}
ngd->pdev->dev.of_node = node;
ngd->pdev->dev.of_node = of_node_get(node);
ctrl->ngd = ngd;
ret = platform_device_add(ngd->pdev);
@ -1560,6 +1557,13 @@ static int of_qcom_slim_ngd_register(struct device *parent,
return -ENODEV;
}
static void qcom_slim_ngd_unregister(struct qcom_slim_ngd_ctrl *ctrl)
{
struct qcom_slim_ngd *ngd = ctrl->ngd;
platform_device_del(ngd->pdev);
}
static int qcom_slim_ngd_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@ -1577,24 +1581,10 @@ static int qcom_slim_ngd_probe(struct platform_device *pdev)
ret = qcom_slim_ngd_qmi_svc_event_init(ctrl);
if (ret) {
dev_err(&pdev->dev, "QMI service registration failed:%d", ret);
return ret;
pm_runtime_dont_use_autosuspend(dev);
pm_runtime_disable(dev);
}
INIT_WORK(&ctrl->m_work, qcom_slim_ngd_master_worker);
INIT_WORK(&ctrl->ngd_up_work, qcom_slim_ngd_up_worker);
ctrl->mwq = create_singlethread_workqueue("ngd_master");
if (!ctrl->mwq) {
dev_err(&pdev->dev, "Failed to start master worker\n");
ret = -ENOMEM;
goto wq_err;
}
return 0;
wq_err:
qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi);
if (ctrl->mwq)
destroy_workqueue(ctrl->mwq);
return ret;
}
@ -1602,6 +1592,7 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
struct qcom_slim_ngd_ctrl *ctrl;
int irq;
int ret;
struct pdr_service *pds;
@ -1615,20 +1606,16 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
if (IS_ERR(ctrl->base))
return PTR_ERR(ctrl->base);
ret = platform_get_irq(pdev, 0);
if (ret < 0)
return ret;
irq = platform_get_irq(pdev, 0);
if (irq < 0)
return irq;
ret = devm_request_irq(dev, ret, qcom_slim_ngd_interrupt,
IRQF_TRIGGER_HIGH, "slim-ngd", ctrl);
ret = devm_request_irq(dev, irq, qcom_slim_ngd_interrupt,
IRQF_TRIGGER_HIGH | IRQF_NO_AUTOEN,
"slim-ngd", ctrl);
if (ret)
return dev_err_probe(&pdev->dev, ret, "request IRQ failed\n");
ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify;
ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb);
if (IS_ERR(ctrl->notifier))
return PTR_ERR(ctrl->notifier);
ctrl->dev = dev;
ctrl->framer.rootfreq = SLIM_ROOT_FREQ >> 3;
ctrl->framer.superfreq =
@ -1649,48 +1636,71 @@ static int qcom_slim_ngd_ctrl_probe(struct platform_device *pdev)
init_completion(&ctrl->qmi.qmi_comp);
init_completion(&ctrl->qmi_up);
INIT_WORK(&ctrl->m_work, qcom_slim_ngd_master_worker);
INIT_WORK(&ctrl->ngd_up_work, qcom_slim_ngd_up_worker);
ctrl->mwq = create_singlethread_workqueue("ngd_master");
if (!ctrl->mwq)
return dev_err_probe(dev, -ENOMEM, "Failed to start master worker\n");
ctrl->pdr = pdr_handle_alloc(slim_pd_status, ctrl);
if (IS_ERR(ctrl->pdr)) {
ret = dev_err_probe(dev, PTR_ERR(ctrl->pdr),
"Failed to init PDR handle\n");
goto err_pdr_alloc;
ret = dev_err_probe(dev, PTR_ERR(ctrl->pdr), "Failed to init PDR handle\n");
goto err_destroy_mwq;
}
ret = of_qcom_slim_ngd_register(dev, ctrl);
if (ret)
goto err_pdr_release;
pds = pdr_add_lookup(ctrl->pdr, "avs/audio", "msm/adsp/audio_pd");
if (IS_ERR(pds) && PTR_ERR(pds) != -EALREADY) {
ret = dev_err_probe(dev, PTR_ERR(pds), "pdr add lookup failed\n");
goto err_pdr_lookup;
goto err_unregister_ngd;
}
platform_driver_register(&qcom_slim_ngd_driver);
return of_qcom_slim_ngd_register(dev, ctrl);
ctrl->nb.notifier_call = qcom_slim_ngd_ssr_notify;
ctrl->notifier = qcom_register_ssr_notifier("lpass", &ctrl->nb);
if (IS_ERR(ctrl->notifier)) {
ret = PTR_ERR(ctrl->notifier);
goto err_unregister_ngd;
}
err_pdr_alloc:
qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb);
enable_irq(irq);
err_pdr_lookup:
return 0;
err_unregister_ngd:
qcom_slim_ngd_unregister(ctrl);
err_pdr_release:
pdr_handle_release(ctrl->pdr);
err_destroy_mwq:
destroy_workqueue(ctrl->mwq);
return ret;
}
static void qcom_slim_ngd_ctrl_remove(struct platform_device *pdev)
{
platform_driver_unregister(&qcom_slim_ngd_driver);
struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev);
pdr_handle_release(ctrl->pdr);
qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb);
qcom_slim_ngd_unregister(ctrl);
destroy_workqueue(ctrl->mwq);
}
static void qcom_slim_ngd_remove(struct platform_device *pdev)
{
struct qcom_slim_ngd_ctrl *ctrl = platform_get_drvdata(pdev);
pm_runtime_dont_use_autosuspend(&pdev->dev);
pm_runtime_disable(&pdev->dev);
pdr_handle_release(ctrl->pdr);
qcom_unregister_ssr_notifier(ctrl->notifier, &ctrl->nb);
qcom_slim_ngd_enable(ctrl, false);
qcom_slim_ngd_exit_dma(ctrl);
qcom_slim_ngd_qmi_svc_event_deinit(&ctrl->qmi);
if (ctrl->mwq)
destroy_workqueue(ctrl->mwq);
kfree(ctrl->ngd);
ctrl->ngd = NULL;
@ -1752,6 +1762,28 @@ static struct platform_driver qcom_slim_ngd_driver = {
},
};
module_platform_driver(qcom_slim_ngd_ctrl_driver);
static int qcom_slim_ngd_init(void)
{
int ret;
ret = platform_driver_register(&qcom_slim_ngd_driver);
if (ret)
return ret;
ret = platform_driver_register(&qcom_slim_ngd_ctrl_driver);
if (ret)
platform_driver_unregister(&qcom_slim_ngd_driver);
return ret;
}
static void qcom_slim_ngd_exit(void)
{
platform_driver_unregister(&qcom_slim_ngd_ctrl_driver);
platform_driver_unregister(&qcom_slim_ngd_driver);
}
module_init(qcom_slim_ngd_init);
module_exit(qcom_slim_ngd_exit);
MODULE_LICENSE("GPL v2");
MODULE_DESCRIPTION("Qualcomm SLIMBus NGD controller");