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:
commit
9a837eff90
6 changed files with 189 additions and 121 deletions
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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,
|
||||
"Intel Service Layer Driver: ATF version is not compatible for async operation\n");
|
||||
return -EINVAL;
|
||||
dev_info(dev,
|
||||
"Intel Service Layer Driver: ATF version is not compatible for async operation\n");
|
||||
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;
|
||||
|
|
|
|||
|
|
@ -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, true))
|
||||
return 0;
|
||||
|
||||
if (!fastrpc_map_lookup(fl, fd, ppmap)) {
|
||||
if (!fastrpc_map_get(*ppmap))
|
||||
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);
|
||||
|
|
|
|||
|
|
@ -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);
|
||||
|
||||
|
|
|
|||
|
|
@ -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;
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -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");
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue