Files
linux/drivers/firmware/thead,th1520-aon.c
Linus Torvalds bf4afc53b7 Convert 'alloc_obj' family to use the new default GFP_KERNEL argument
This was done entirely with mindless brute force, using

    git grep -l '\<k[vmz]*alloc_objs*(.*, GFP_KERNEL)' |
        xargs sed -i 's/\(alloc_objs*(.*\), GFP_KERNEL)/\1)/'

to convert the new alloc_obj() users that had a simple GFP_KERNEL
argument to just drop that argument.

Note that due to the extreme simplicity of the scripting, any slightly
more complex cases spread over multiple lines would not be triggered:
they definitely exist, but this covers the vast bulk of the cases, and
the resulting diff is also then easier to check automatically.

For the same reason the 'flex' versions will be done as a separate
conversion.

Signed-off-by: Linus Torvalds <torvalds@linux-foundation.org>
2026-02-21 17:09:51 -08:00

251 lines
7.7 KiB
C

// SPDX-License-Identifier: GPL-2.0
/*
* Copyright (C) 2021 Alibaba Group Holding Limited.
* Copyright (c) 2024 Samsung Electronics Co., Ltd.
* Author: Michal Wilczynski <m.wilczynski@samsung.com>
*/
#include <linux/device.h>
#include <linux/firmware/thead/thead,th1520-aon.h>
#include <linux/mailbox_client.h>
#include <linux/mailbox_controller.h>
#include <linux/slab.h>
#define MAX_RX_TIMEOUT (msecs_to_jiffies(3000))
#define MAX_TX_TIMEOUT 500
struct th1520_aon_chan {
struct mbox_chan *ch;
struct th1520_aon_rpc_ack_common ack_msg;
struct mbox_client cl;
struct completion done;
/* make sure only one RPC is performed at a time */
struct mutex transaction_lock;
};
struct th1520_aon_msg_req_set_resource_power_mode {
struct th1520_aon_rpc_msg_hdr hdr;
u16 resource;
u16 mode;
u16 reserved[10];
} __packed __aligned(1);
/*
* This type is used to indicate error response for most functions.
*/
enum th1520_aon_error_codes {
LIGHT_AON_ERR_NONE = 0, /* Success */
LIGHT_AON_ERR_VERSION = 1, /* Incompatible API version */
LIGHT_AON_ERR_CONFIG = 2, /* Configuration error */
LIGHT_AON_ERR_PARM = 3, /* Bad parameter */
LIGHT_AON_ERR_NOACCESS = 4, /* Permission error (no access) */
LIGHT_AON_ERR_LOCKED = 5, /* Permission error (locked) */
LIGHT_AON_ERR_UNAVAILABLE = 6, /* Unavailable (out of resources) */
LIGHT_AON_ERR_NOTFOUND = 7, /* Not found */
LIGHT_AON_ERR_NOPOWER = 8, /* No power */
LIGHT_AON_ERR_IPC = 9, /* Generic IPC error */
LIGHT_AON_ERR_BUSY = 10, /* Resource is currently busy/active */
LIGHT_AON_ERR_FAIL = 11, /* General I/O failure */
LIGHT_AON_ERR_LAST
};
static int th1520_aon_linux_errmap[LIGHT_AON_ERR_LAST] = {
0, /* LIGHT_AON_ERR_NONE */
-EINVAL, /* LIGHT_AON_ERR_VERSION */
-EINVAL, /* LIGHT_AON_ERR_CONFIG */
-EINVAL, /* LIGHT_AON_ERR_PARM */
-EACCES, /* LIGHT_AON_ERR_NOACCESS */
-EACCES, /* LIGHT_AON_ERR_LOCKED */
-ERANGE, /* LIGHT_AON_ERR_UNAVAILABLE */
-EEXIST, /* LIGHT_AON_ERR_NOTFOUND */
-EPERM, /* LIGHT_AON_ERR_NOPOWER */
-EPIPE, /* LIGHT_AON_ERR_IPC */
-EBUSY, /* LIGHT_AON_ERR_BUSY */
-EIO, /* LIGHT_AON_ERR_FAIL */
};
static inline int th1520_aon_to_linux_errno(int errno)
{
if (errno >= LIGHT_AON_ERR_NONE && errno < LIGHT_AON_ERR_LAST)
return th1520_aon_linux_errmap[errno];
return -EIO;
}
static void th1520_aon_rx_callback(struct mbox_client *c, void *rx_msg)
{
struct th1520_aon_chan *aon_chan =
container_of(c, struct th1520_aon_chan, cl);
struct th1520_aon_rpc_msg_hdr *hdr =
(struct th1520_aon_rpc_msg_hdr *)rx_msg;
u8 recv_size = sizeof(struct th1520_aon_rpc_msg_hdr) + hdr->size;
if (recv_size != sizeof(struct th1520_aon_rpc_ack_common)) {
dev_err(c->dev, "Invalid ack size, not completing\n");
return;
}
memcpy(&aon_chan->ack_msg, rx_msg, recv_size);
complete(&aon_chan->done);
}
/**
* th1520_aon_call_rpc() - Send an RPC request to the TH1520 AON subsystem
* @aon_chan: Pointer to the AON channel structure
* @msg: Pointer to the message (RPC payload) that will be sent
*
* This function sends an RPC message to the TH1520 AON subsystem via mailbox.
* It takes the provided @msg buffer, formats it with version and service flags,
* then blocks until the RPC completes or times out. The completion is signaled
* by the `aon_chan->done` completion, which is waited upon for a duration
* defined by `MAX_RX_TIMEOUT`.
*
* Return:
* * 0 on success
* * -ETIMEDOUT if the RPC call times out
* * A negative error code if the mailbox send fails or if AON responds with
* a non-zero error code (converted via th1520_aon_to_linux_errno()).
*/
int th1520_aon_call_rpc(struct th1520_aon_chan *aon_chan, void *msg)
{
struct th1520_aon_rpc_msg_hdr *hdr = msg;
int ret;
mutex_lock(&aon_chan->transaction_lock);
reinit_completion(&aon_chan->done);
RPC_SET_VER(hdr, TH1520_AON_RPC_VERSION);
RPC_SET_SVC_ID(hdr, hdr->svc);
RPC_SET_SVC_FLAG_MSG_TYPE(hdr, RPC_SVC_MSG_TYPE_DATA);
RPC_SET_SVC_FLAG_ACK_TYPE(hdr, RPC_SVC_MSG_NEED_ACK);
ret = mbox_send_message(aon_chan->ch, msg);
if (ret < 0) {
dev_err(aon_chan->cl.dev, "RPC send msg failed: %d\n", ret);
goto out;
}
if (!wait_for_completion_timeout(&aon_chan->done, MAX_RX_TIMEOUT)) {
dev_err(aon_chan->cl.dev, "RPC send msg timeout\n");
mutex_unlock(&aon_chan->transaction_lock);
return -ETIMEDOUT;
}
ret = aon_chan->ack_msg.err_code;
out:
mutex_unlock(&aon_chan->transaction_lock);
return th1520_aon_to_linux_errno(ret);
}
EXPORT_SYMBOL_GPL(th1520_aon_call_rpc);
/**
* th1520_aon_power_update() - Change power state of a resource via TH1520 AON
* @aon_chan: Pointer to the AON channel structure
* @rsrc: Resource ID whose power state needs to be updated
* @power_on: Boolean indicating whether the resource should be powered on (true)
* or powered off (false)
*
* This function requests the TH1520 AON subsystem to set the power mode of the
* given resource (@rsrc) to either on or off. It constructs the message in
* `struct th1520_aon_msg_req_set_resource_power_mode` and then invokes
* th1520_aon_call_rpc() to make the request. If the AON call fails, an error
* message is logged along with the specific return code.
*
* Return:
* * 0 on success
* * A negative error code in case of failures (propagated from
* th1520_aon_call_rpc()).
*/
int th1520_aon_power_update(struct th1520_aon_chan *aon_chan, u16 rsrc,
bool power_on)
{
struct th1520_aon_msg_req_set_resource_power_mode msg = {};
struct th1520_aon_rpc_msg_hdr *hdr = &msg.hdr;
int ret;
hdr->svc = TH1520_AON_RPC_SVC_PM;
hdr->func = TH1520_AON_PM_FUNC_SET_RESOURCE_POWER_MODE;
hdr->size = TH1520_AON_RPC_MSG_NUM;
RPC_SET_BE16(&msg.resource, 0, rsrc);
RPC_SET_BE16(&msg.resource, 2,
(power_on ? TH1520_AON_PM_PW_MODE_ON :
TH1520_AON_PM_PW_MODE_OFF));
ret = th1520_aon_call_rpc(aon_chan, &msg);
if (ret)
dev_err(aon_chan->cl.dev, "failed to power %s resource %d ret %d\n",
power_on ? "up" : "off", rsrc, ret);
return ret;
}
EXPORT_SYMBOL_GPL(th1520_aon_power_update);
/**
* th1520_aon_init() - Initialize TH1520 AON firmware protocol interface
* @dev: Device pointer for the AON subsystem
*
* This function initializes the TH1520 AON firmware protocol interface by:
* - Allocating and initializing the AON channel structure
* - Setting up the mailbox client
* - Requesting the AON mailbox channel
* - Initializing synchronization primitives
*
* Return:
* * Valid pointer to th1520_aon_chan structure on success
* * ERR_PTR(-ENOMEM) if memory allocation fails
* * ERR_PTR() with other negative error codes from mailbox operations
*/
struct th1520_aon_chan *th1520_aon_init(struct device *dev)
{
struct th1520_aon_chan *aon_chan;
struct mbox_client *cl;
int ret;
aon_chan = kzalloc_obj(*aon_chan);
if (!aon_chan)
return ERR_PTR(-ENOMEM);
cl = &aon_chan->cl;
cl->dev = dev;
cl->tx_block = true;
cl->tx_tout = MAX_TX_TIMEOUT;
cl->rx_callback = th1520_aon_rx_callback;
aon_chan->ch = mbox_request_channel_byname(cl, "aon");
if (IS_ERR(aon_chan->ch)) {
dev_err(dev, "Failed to request aon mbox chan\n");
ret = PTR_ERR(aon_chan->ch);
kfree(aon_chan);
return ERR_PTR(ret);
}
mutex_init(&aon_chan->transaction_lock);
init_completion(&aon_chan->done);
return aon_chan;
}
EXPORT_SYMBOL_GPL(th1520_aon_init);
/**
* th1520_aon_deinit() - Clean up TH1520 AON firmware protocol interface
* @aon_chan: Pointer to the AON channel structure to clean up
*
* This function cleans up resources allocated by th1520_aon_init():
* - Frees the mailbox channel
* - Frees the AON channel
*/
void th1520_aon_deinit(struct th1520_aon_chan *aon_chan)
{
mbox_free_channel(aon_chan->ch);
kfree(aon_chan);
}
EXPORT_SYMBOL_GPL(th1520_aon_deinit);
MODULE_AUTHOR("Michal Wilczynski <m.wilczynski@samsung.com>");
MODULE_DESCRIPTION("T-HEAD TH1520 Always-On firmware protocol library");
MODULE_LICENSE("GPL");