Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
torvalds
GitHub Repository: torvalds/linux
Path: blob/master/drivers/firmware/thead,th1520-aon.c
26378 views
1
// SPDX-License-Identifier: GPL-2.0
2
/*
3
* Copyright (C) 2021 Alibaba Group Holding Limited.
4
* Copyright (c) 2024 Samsung Electronics Co., Ltd.
5
* Author: Michal Wilczynski <[email protected]>
6
*/
7
8
#include <linux/device.h>
9
#include <linux/firmware/thead/thead,th1520-aon.h>
10
#include <linux/mailbox_client.h>
11
#include <linux/mailbox_controller.h>
12
#include <linux/slab.h>
13
14
#define MAX_RX_TIMEOUT (msecs_to_jiffies(3000))
15
#define MAX_TX_TIMEOUT 500
16
17
struct th1520_aon_chan {
18
struct mbox_chan *ch;
19
struct th1520_aon_rpc_ack_common ack_msg;
20
struct mbox_client cl;
21
struct completion done;
22
23
/* make sure only one RPC is performed at a time */
24
struct mutex transaction_lock;
25
};
26
27
struct th1520_aon_msg_req_set_resource_power_mode {
28
struct th1520_aon_rpc_msg_hdr hdr;
29
u16 resource;
30
u16 mode;
31
u16 reserved[10];
32
} __packed __aligned(1);
33
34
/*
35
* This type is used to indicate error response for most functions.
36
*/
37
enum th1520_aon_error_codes {
38
LIGHT_AON_ERR_NONE = 0, /* Success */
39
LIGHT_AON_ERR_VERSION = 1, /* Incompatible API version */
40
LIGHT_AON_ERR_CONFIG = 2, /* Configuration error */
41
LIGHT_AON_ERR_PARM = 3, /* Bad parameter */
42
LIGHT_AON_ERR_NOACCESS = 4, /* Permission error (no access) */
43
LIGHT_AON_ERR_LOCKED = 5, /* Permission error (locked) */
44
LIGHT_AON_ERR_UNAVAILABLE = 6, /* Unavailable (out of resources) */
45
LIGHT_AON_ERR_NOTFOUND = 7, /* Not found */
46
LIGHT_AON_ERR_NOPOWER = 8, /* No power */
47
LIGHT_AON_ERR_IPC = 9, /* Generic IPC error */
48
LIGHT_AON_ERR_BUSY = 10, /* Resource is currently busy/active */
49
LIGHT_AON_ERR_FAIL = 11, /* General I/O failure */
50
LIGHT_AON_ERR_LAST
51
};
52
53
static int th1520_aon_linux_errmap[LIGHT_AON_ERR_LAST] = {
54
0, /* LIGHT_AON_ERR_NONE */
55
-EINVAL, /* LIGHT_AON_ERR_VERSION */
56
-EINVAL, /* LIGHT_AON_ERR_CONFIG */
57
-EINVAL, /* LIGHT_AON_ERR_PARM */
58
-EACCES, /* LIGHT_AON_ERR_NOACCESS */
59
-EACCES, /* LIGHT_AON_ERR_LOCKED */
60
-ERANGE, /* LIGHT_AON_ERR_UNAVAILABLE */
61
-EEXIST, /* LIGHT_AON_ERR_NOTFOUND */
62
-EPERM, /* LIGHT_AON_ERR_NOPOWER */
63
-EPIPE, /* LIGHT_AON_ERR_IPC */
64
-EBUSY, /* LIGHT_AON_ERR_BUSY */
65
-EIO, /* LIGHT_AON_ERR_FAIL */
66
};
67
68
static inline int th1520_aon_to_linux_errno(int errno)
69
{
70
if (errno >= LIGHT_AON_ERR_NONE && errno < LIGHT_AON_ERR_LAST)
71
return th1520_aon_linux_errmap[errno];
72
73
return -EIO;
74
}
75
76
static void th1520_aon_rx_callback(struct mbox_client *c, void *rx_msg)
77
{
78
struct th1520_aon_chan *aon_chan =
79
container_of(c, struct th1520_aon_chan, cl);
80
struct th1520_aon_rpc_msg_hdr *hdr =
81
(struct th1520_aon_rpc_msg_hdr *)rx_msg;
82
u8 recv_size = sizeof(struct th1520_aon_rpc_msg_hdr) + hdr->size;
83
84
if (recv_size != sizeof(struct th1520_aon_rpc_ack_common)) {
85
dev_err(c->dev, "Invalid ack size, not completing\n");
86
return;
87
}
88
89
memcpy(&aon_chan->ack_msg, rx_msg, recv_size);
90
complete(&aon_chan->done);
91
}
92
93
/**
94
* th1520_aon_call_rpc() - Send an RPC request to the TH1520 AON subsystem
95
* @aon_chan: Pointer to the AON channel structure
96
* @msg: Pointer to the message (RPC payload) that will be sent
97
*
98
* This function sends an RPC message to the TH1520 AON subsystem via mailbox.
99
* It takes the provided @msg buffer, formats it with version and service flags,
100
* then blocks until the RPC completes or times out. The completion is signaled
101
* by the `aon_chan->done` completion, which is waited upon for a duration
102
* defined by `MAX_RX_TIMEOUT`.
103
*
104
* Return:
105
* * 0 on success
106
* * -ETIMEDOUT if the RPC call times out
107
* * A negative error code if the mailbox send fails or if AON responds with
108
* a non-zero error code (converted via th1520_aon_to_linux_errno()).
109
*/
110
int th1520_aon_call_rpc(struct th1520_aon_chan *aon_chan, void *msg)
111
{
112
struct th1520_aon_rpc_msg_hdr *hdr = msg;
113
int ret;
114
115
mutex_lock(&aon_chan->transaction_lock);
116
reinit_completion(&aon_chan->done);
117
118
RPC_SET_VER(hdr, TH1520_AON_RPC_VERSION);
119
RPC_SET_SVC_ID(hdr, hdr->svc);
120
RPC_SET_SVC_FLAG_MSG_TYPE(hdr, RPC_SVC_MSG_TYPE_DATA);
121
RPC_SET_SVC_FLAG_ACK_TYPE(hdr, RPC_SVC_MSG_NEED_ACK);
122
123
ret = mbox_send_message(aon_chan->ch, msg);
124
if (ret < 0) {
125
dev_err(aon_chan->cl.dev, "RPC send msg failed: %d\n", ret);
126
goto out;
127
}
128
129
if (!wait_for_completion_timeout(&aon_chan->done, MAX_RX_TIMEOUT)) {
130
dev_err(aon_chan->cl.dev, "RPC send msg timeout\n");
131
mutex_unlock(&aon_chan->transaction_lock);
132
return -ETIMEDOUT;
133
}
134
135
ret = aon_chan->ack_msg.err_code;
136
137
out:
138
mutex_unlock(&aon_chan->transaction_lock);
139
140
return th1520_aon_to_linux_errno(ret);
141
}
142
EXPORT_SYMBOL_GPL(th1520_aon_call_rpc);
143
144
/**
145
* th1520_aon_power_update() - Change power state of a resource via TH1520 AON
146
* @aon_chan: Pointer to the AON channel structure
147
* @rsrc: Resource ID whose power state needs to be updated
148
* @power_on: Boolean indicating whether the resource should be powered on (true)
149
* or powered off (false)
150
*
151
* This function requests the TH1520 AON subsystem to set the power mode of the
152
* given resource (@rsrc) to either on or off. It constructs the message in
153
* `struct th1520_aon_msg_req_set_resource_power_mode` and then invokes
154
* th1520_aon_call_rpc() to make the request. If the AON call fails, an error
155
* message is logged along with the specific return code.
156
*
157
* Return:
158
* * 0 on success
159
* * A negative error code in case of failures (propagated from
160
* th1520_aon_call_rpc()).
161
*/
162
int th1520_aon_power_update(struct th1520_aon_chan *aon_chan, u16 rsrc,
163
bool power_on)
164
{
165
struct th1520_aon_msg_req_set_resource_power_mode msg = {};
166
struct th1520_aon_rpc_msg_hdr *hdr = &msg.hdr;
167
int ret;
168
169
hdr->svc = TH1520_AON_RPC_SVC_PM;
170
hdr->func = TH1520_AON_PM_FUNC_SET_RESOURCE_POWER_MODE;
171
hdr->size = TH1520_AON_RPC_MSG_NUM;
172
173
RPC_SET_BE16(&msg.resource, 0, rsrc);
174
RPC_SET_BE16(&msg.resource, 2,
175
(power_on ? TH1520_AON_PM_PW_MODE_ON :
176
TH1520_AON_PM_PW_MODE_OFF));
177
178
ret = th1520_aon_call_rpc(aon_chan, &msg);
179
if (ret)
180
dev_err(aon_chan->cl.dev, "failed to power %s resource %d ret %d\n",
181
power_on ? "up" : "off", rsrc, ret);
182
183
return ret;
184
}
185
EXPORT_SYMBOL_GPL(th1520_aon_power_update);
186
187
/**
188
* th1520_aon_init() - Initialize TH1520 AON firmware protocol interface
189
* @dev: Device pointer for the AON subsystem
190
*
191
* This function initializes the TH1520 AON firmware protocol interface by:
192
* - Allocating and initializing the AON channel structure
193
* - Setting up the mailbox client
194
* - Requesting the AON mailbox channel
195
* - Initializing synchronization primitives
196
*
197
* Return:
198
* * Valid pointer to th1520_aon_chan structure on success
199
* * ERR_PTR(-ENOMEM) if memory allocation fails
200
* * ERR_PTR() with other negative error codes from mailbox operations
201
*/
202
struct th1520_aon_chan *th1520_aon_init(struct device *dev)
203
{
204
struct th1520_aon_chan *aon_chan;
205
struct mbox_client *cl;
206
int ret;
207
208
aon_chan = kzalloc(sizeof(*aon_chan), GFP_KERNEL);
209
if (!aon_chan)
210
return ERR_PTR(-ENOMEM);
211
212
cl = &aon_chan->cl;
213
cl->dev = dev;
214
cl->tx_block = true;
215
cl->tx_tout = MAX_TX_TIMEOUT;
216
cl->rx_callback = th1520_aon_rx_callback;
217
218
aon_chan->ch = mbox_request_channel_byname(cl, "aon");
219
if (IS_ERR(aon_chan->ch)) {
220
dev_err(dev, "Failed to request aon mbox chan\n");
221
ret = PTR_ERR(aon_chan->ch);
222
kfree(aon_chan);
223
return ERR_PTR(ret);
224
}
225
226
mutex_init(&aon_chan->transaction_lock);
227
init_completion(&aon_chan->done);
228
229
return aon_chan;
230
}
231
EXPORT_SYMBOL_GPL(th1520_aon_init);
232
233
/**
234
* th1520_aon_deinit() - Clean up TH1520 AON firmware protocol interface
235
* @aon_chan: Pointer to the AON channel structure to clean up
236
*
237
* This function cleans up resources allocated by th1520_aon_init():
238
* - Frees the mailbox channel
239
* - Frees the AON channel
240
*/
241
void th1520_aon_deinit(struct th1520_aon_chan *aon_chan)
242
{
243
mbox_free_channel(aon_chan->ch);
244
kfree(aon_chan);
245
}
246
EXPORT_SYMBOL_GPL(th1520_aon_deinit);
247
248
MODULE_AUTHOR("Michal Wilczynski <[email protected]>");
249
MODULE_DESCRIPTION("T-HEAD TH1520 Always-On firmware protocol library");
250
MODULE_LICENSE("GPL");
251
252