Skip to content

Commit d56b794

Browse files
pelwellpopcornmix
authored andcommitted
firmware: Add an RP1 firmware interface over mbox
The RP1 firmware runs a simple communications channel over some shared memory and a mailbox. This driver provides access to that channel. Signed-off-by: Phil Elwell <phil@raspberrypi.com>
1 parent ea47185 commit d56b794

File tree

4 files changed

+377
-0
lines changed

4 files changed

+377
-0
lines changed

drivers/firmware/Kconfig

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -120,6 +120,15 @@ config RASPBERRYPI_FIRMWARE
120120
This option enables support for communicating with the firmware on the
121121
Raspberry Pi.
122122

123+
config FIRMWARE_RP1
124+
tristate "RP1 Firmware Driver"
125+
depends on MBOX_RP1
126+
help
127+
The Raspberry Pi RP1 processor presents a firmware
128+
interface using shared memory and a mailbox. To enable
129+
the driver that communicates with it, say Y. Otherwise,
130+
say N.
131+
123132
config FW_CFG_SYSFS
124133
tristate "QEMU fw_cfg device support in sysfs"
125134
depends on SYSFS && (ARM || ARM64 || PARISC || PPC_PMAC || RISCV || SPARC || X86)

drivers/firmware/Makefile

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ obj-$(CONFIG_ISCSI_IBFT) += iscsi_ibft.o
1515
obj-$(CONFIG_FIRMWARE_MEMMAP) += memmap.o
1616
obj-$(CONFIG_MTK_ADSP_IPC) += mtk-adsp-ipc.o
1717
obj-$(CONFIG_RASPBERRYPI_FIRMWARE) += raspberrypi.o
18+
obj-$(CONFIG_FIRMWARE_RP1) += rp1.o
1819
obj-$(CONFIG_FW_CFG_SYSFS) += qemu_fw_cfg.o
1920
obj-$(CONFIG_SYSFB) += sysfb.o
2021
obj-$(CONFIG_SYSFB_SIMPLEFB) += sysfb_simplefb.o

drivers/firmware/rp1.c

Lines changed: 314 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,314 @@
1+
// SPDX-License-Identifier: GPL-2.0
2+
/*
3+
* Copyright (C) 2023-24 Raspberry Pi Ltd.
4+
*
5+
* Parts of this driver are based on:
6+
* - raspberrypi.c, by Eric Anholt <eric@anholt.net>
7+
* Copyright (C) 2015 Broadcom
8+
*/
9+
10+
#include <linux/dma-mapping.h>
11+
#include <linux/kref.h>
12+
#include <linux/mailbox_client.h>
13+
#include <linux/module.h>
14+
#include <linux/of_address.h>
15+
#include <linux/of_platform.h>
16+
#include <linux/platform_device.h>
17+
#include <linux/rp1-firmware.h>
18+
19+
#define RP1_MAILBOX_FIRMWARE 0
20+
21+
enum rp1_firmware_ops {
22+
MBOX_SUCCESS = 0x0000,
23+
GET_FIRMWARE_VERSION = 0x0001, // na -> 160-bit version
24+
GET_FEATURE = 0x0002, // FOURCC -> op base (0 == unsupported), op count
25+
26+
COMMON_COUNT
27+
};
28+
29+
struct rp1_firmware {
30+
struct mbox_client cl;
31+
struct mbox_chan *chan; /* The doorbell channel */
32+
uint32_t __iomem *buf; /* The shared buffer */
33+
u32 buf_size; /* The size of the shared buffer */
34+
struct completion c;
35+
36+
struct kref consumers;
37+
};
38+
39+
struct rp1_get_feature_resp {
40+
uint32_t op_base;
41+
uint32_t op_count;
42+
};
43+
44+
static DEFINE_MUTEX(transaction_lock);
45+
46+
static const struct of_device_id rp1_firmware_of_match[] = {
47+
{ .compatible = "raspberrypi,rp1-firmware", },
48+
{},
49+
};
50+
MODULE_DEVICE_TABLE(of, rp1_firmware_of_match);
51+
52+
static void response_callback(struct mbox_client *cl, void *msg)
53+
{
54+
struct rp1_firmware *fw = container_of(cl, struct rp1_firmware, cl);
55+
56+
complete(&fw->c);
57+
}
58+
59+
/*
60+
* Sends a request to the RP1 firmware and synchronously waits for the reply.
61+
* Returns zero or a positive count of response bytes on success, negative on
62+
* error.
63+
*/
64+
65+
int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op,
66+
const void *data, unsigned int data_len,
67+
void *resp, unsigned int resp_space)
68+
{
69+
int ret;
70+
u32 rc;
71+
72+
if (data_len + 4 > fw->buf_size)
73+
return -EINVAL;
74+
75+
mutex_lock(&transaction_lock);
76+
77+
memcpy_toio(&fw->buf[1], data, data_len);
78+
writel((op << 16) | data_len, fw->buf);
79+
80+
reinit_completion(&fw->c);
81+
ret = mbox_send_message(fw->chan, NULL);
82+
if (ret >= 0) {
83+
if (wait_for_completion_timeout(&fw->c, HZ))
84+
ret = 0;
85+
else
86+
ret = -ETIMEDOUT;
87+
} else {
88+
dev_err(fw->cl.dev, "mbox_send_message returned %d\n", ret);
89+
}
90+
91+
if (ret == 0) {
92+
rc = readl(fw->buf);
93+
if (rc & 0x80000000) {
94+
ret = (int32_t)rc;
95+
} else {
96+
ret = min(rc, resp_space);
97+
memcpy_fromio(resp, &fw->buf[1], ret);
98+
}
99+
}
100+
101+
mutex_unlock(&transaction_lock);
102+
103+
return ret;
104+
}
105+
EXPORT_SYMBOL_GPL(rp1_firmware_message);
106+
107+
static void rp1_firmware_delete(struct kref *kref)
108+
{
109+
struct rp1_firmware *fw = container_of(kref, struct rp1_firmware, consumers);
110+
111+
mbox_free_channel(fw->chan);
112+
kfree(fw);
113+
}
114+
115+
void rp1_firmware_put(struct rp1_firmware *fw)
116+
{
117+
kref_put(&fw->consumers, rp1_firmware_delete);
118+
}
119+
EXPORT_SYMBOL_GPL(rp1_firmware_put);
120+
121+
int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc,
122+
uint32_t *op_base, uint32_t *op_count)
123+
{
124+
struct rp1_get_feature_resp resp;
125+
int ret;
126+
127+
memset(&resp, 0, sizeof(resp));
128+
ret = rp1_firmware_message(fw, GET_FEATURE,
129+
&fourcc, sizeof(fourcc),
130+
&resp, sizeof(resp));
131+
*op_base = resp.op_base;
132+
*op_count = resp.op_count;
133+
if (ret < 0)
134+
return ret;
135+
if (ret < sizeof(resp) || !resp.op_base)
136+
return -EOPNOTSUPP;
137+
return 0;
138+
}
139+
EXPORT_SYMBOL_GPL(rp1_firmware_get_feature);
140+
141+
static void devm_rp1_firmware_put(void *data)
142+
{
143+
struct rp1_firmware *fw = data;
144+
145+
rp1_firmware_put(fw);
146+
}
147+
148+
/**
149+
* rp1_firmware_get - Get pointer to rp1_firmware structure.
150+
*
151+
* The reference to rp1_firmware has to be released with rp1_firmware_put().
152+
*
153+
* Returns an error pointer on failure.
154+
*/
155+
struct rp1_firmware *rp1_firmware_get(struct device_node *client)
156+
{
157+
const char *match = rp1_firmware_of_match[0].compatible;
158+
struct platform_device *pdev;
159+
struct device_node *fwnode;
160+
struct rp1_firmware *fw;
161+
162+
if (client) {
163+
fwnode = of_parse_phandle(client, "firmware", 0);
164+
if (!fwnode)
165+
fwnode = of_get_parent(client);
166+
if (fwnode && !of_device_is_compatible(fwnode, match)) {
167+
of_node_put(fwnode);
168+
fwnode = NULL;
169+
}
170+
}
171+
172+
if (!fwnode)
173+
fwnode = of_find_matching_node(NULL, rp1_firmware_of_match);
174+
175+
if (!fwnode)
176+
return ERR_PTR(-ENOENT);
177+
178+
pdev = of_find_device_by_node(fwnode);
179+
of_node_put(fwnode);
180+
181+
if (!pdev)
182+
return ERR_PTR(-EPROBE_DEFER);
183+
184+
fw = platform_get_drvdata(pdev);
185+
if (!fw)
186+
goto err_defer;
187+
188+
if (!kref_get_unless_zero(&fw->consumers))
189+
goto err_defer;
190+
191+
put_device(&pdev->dev);
192+
193+
return fw;
194+
195+
err_defer:
196+
put_device(&pdev->dev);
197+
return ERR_PTR(-EPROBE_DEFER);
198+
}
199+
EXPORT_SYMBOL_GPL(rp1_firmware_get);
200+
201+
/**
202+
* devm_rp1_firmware_get - Get pointer to rp1_firmware structure.
203+
* @firmware_node: Pointer to the firmware Device Tree node.
204+
*
205+
* Returns NULL is the firmware device is not ready.
206+
*/
207+
struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *client)
208+
{
209+
struct rp1_firmware *fw;
210+
int ret;
211+
212+
fw = rp1_firmware_get(client);
213+
if (IS_ERR(fw))
214+
return fw;
215+
216+
ret = devm_add_action_or_reset(dev, devm_rp1_firmware_put, fw);
217+
if (ret)
218+
return ERR_PTR(ret);
219+
220+
return fw;
221+
}
222+
EXPORT_SYMBOL_GPL(devm_rp1_firmware_get);
223+
224+
static int rp1_firmware_probe(struct platform_device *pdev)
225+
{
226+
struct device *dev = &pdev->dev;
227+
struct device_node *shmem;
228+
struct rp1_firmware *fw;
229+
struct resource res;
230+
uint32_t version[5];
231+
int ret;
232+
233+
shmem = of_parse_phandle(dev->of_node, "shmem", 0);
234+
if (!of_device_is_compatible(shmem, "raspberrypi,rp1-shmem")) {
235+
of_node_put(shmem);
236+
return -ENXIO;
237+
}
238+
239+
ret = of_address_to_resource(shmem, 0, &res);
240+
of_node_put(shmem);
241+
if (ret) {
242+
dev_err(dev, "failed to get shared memory (%pOF) - %d\n", shmem, ret);
243+
return ret;
244+
}
245+
246+
/*
247+
* Memory will be freed by rp1_firmware_delete() once all users have
248+
* released their firmware handles. Don't use devm_kzalloc() here.
249+
*/
250+
fw = kzalloc(sizeof(*fw), GFP_KERNEL);
251+
if (!fw)
252+
return -ENOMEM;
253+
254+
fw->buf_size = resource_size(&res);
255+
fw->buf = devm_ioremap(dev, res.start, fw->buf_size);
256+
if (!fw->buf) {
257+
dev_err(dev, "failed to ioremap shared memory\n");
258+
kfree(fw);
259+
return -EADDRNOTAVAIL;
260+
}
261+
262+
fw->cl.dev = dev;
263+
fw->cl.rx_callback = response_callback;
264+
fw->cl.tx_block = false;
265+
266+
fw->chan = mbox_request_channel(&fw->cl, RP1_MAILBOX_FIRMWARE);
267+
if (IS_ERR(fw->chan)) {
268+
int ret = PTR_ERR(fw->chan);
269+
270+
if (ret != -EPROBE_DEFER)
271+
dev_err(dev, "Failed to get mbox channel: %d\n", ret);
272+
kfree(fw);
273+
return ret;
274+
}
275+
276+
init_completion(&fw->c);
277+
kref_init(&fw->consumers);
278+
279+
platform_set_drvdata(pdev, fw);
280+
281+
ret = rp1_firmware_message(fw, GET_FIRMWARE_VERSION,
282+
NULL, 0, &version, sizeof(version));
283+
if (ret == sizeof(version)) {
284+
dev_info(dev, "RP1 Firmware version %08x%08x%08x%08x%08x\n",
285+
version[0], version[1], version[2], version[3], version[4]);
286+
ret = 0;
287+
} else if (ret >= 0) {
288+
ret = -EIO;
289+
}
290+
291+
return ret;
292+
}
293+
294+
static void rp1_firmware_remove(struct platform_device *pdev)
295+
{
296+
struct rp1_firmware *fw = platform_get_drvdata(pdev);
297+
298+
rp1_firmware_put(fw);
299+
}
300+
301+
static struct platform_driver rp1_firmware_driver = {
302+
.driver = {
303+
.name = "rp1-firmware",
304+
.of_match_table = rp1_firmware_of_match,
305+
},
306+
.probe = rp1_firmware_probe,
307+
.remove = rp1_firmware_remove,
308+
};
309+
310+
module_platform_driver(rp1_firmware_driver);
311+
312+
MODULE_AUTHOR("Phil Elwell <phil@raspberrypi.com>");
313+
MODULE_DESCRIPTION("RP1 firmware driver");
314+
MODULE_LICENSE("GPL v2");

include/linux/rp1-firmware.h

Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
/* SPDX-License-Identifier: GPL-2.0 */
2+
/*
3+
* Copyright (C) 2023 2023-2024 Raspberry Pi Ltd.
4+
*/
5+
6+
#ifndef __SOC_RP1_FIRMWARE_H__
7+
#define __SOC_RP1_FIRMWARE_H__
8+
9+
#include <linux/types.h>
10+
#include <linux/of_device.h>
11+
12+
#define RP1_FOURCC(s) ((uint32_t)((s[0] << 24) | (s[1] << 16) | (s[2] << 8) | (s[3] << 0)))
13+
14+
struct rp1_firmware;
15+
16+
#if IS_ENABLED(CONFIG_FIRMWARE_RP1)
17+
int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op,
18+
const void *data, unsigned int data_len,
19+
void *resp, unsigned int resp_space);
20+
void rp1_firmware_put(struct rp1_firmware *fw);
21+
struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode);
22+
struct rp1_firmware *devm_rp1_firmware_get(struct device *dev, struct device_node *fwnode);
23+
int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc,
24+
uint32_t *op_base, uint32_t *op_count);
25+
#else
26+
static inline int rp1_firmware_message(struct rp1_firmware *fw, uint16_t op,
27+
const void *data, unsigned int data_len,
28+
void *resp, unsigned int resp_space)
29+
{
30+
return -EOPNOTSUPP;
31+
}
32+
33+
static inline void rp1_firmware_put(struct rp1_firmware *fw) { }
34+
35+
static inline struct rp1_firmware *rp1_firmware_get(struct device_node *fwnode)
36+
{
37+
return NULL;
38+
}
39+
40+
static inline struct rp1_firmware *devm_rp1_firmware_get(struct device *dev,
41+
struct device_node *fwnode)
42+
{
43+
return NULL;
44+
}
45+
46+
static inline int rp1_firmware_get_feature(struct rp1_firmware *fw, uint32_t fourcc,
47+
uint32_t *op_base, uint32_t *op_count)
48+
{
49+
return -EOPNOTSUPP;
50+
}
51+
#endif
52+
53+
#endif /* __SOC_RP1_FIRMWARE_H__ */

0 commit comments

Comments
 (0)