162306a36Sopenharmony_ci// SPDX-License-Identifier: GPL-2.0
262306a36Sopenharmony_ci/*
362306a36Sopenharmony_ci * remote processor messaging bus
462306a36Sopenharmony_ci *
562306a36Sopenharmony_ci * Copyright (C) 2011 Texas Instruments, Inc.
662306a36Sopenharmony_ci * Copyright (C) 2011 Google, Inc.
762306a36Sopenharmony_ci *
862306a36Sopenharmony_ci * Ohad Ben-Cohen <ohad@wizery.com>
962306a36Sopenharmony_ci * Brian Swetland <swetland@google.com>
1062306a36Sopenharmony_ci */
1162306a36Sopenharmony_ci
1262306a36Sopenharmony_ci#define pr_fmt(fmt) "%s: " fmt, __func__
1362306a36Sopenharmony_ci
1462306a36Sopenharmony_ci#include <linux/kernel.h>
1562306a36Sopenharmony_ci#include <linux/module.h>
1662306a36Sopenharmony_ci#include <linux/rpmsg.h>
1762306a36Sopenharmony_ci#include <linux/of_device.h>
1862306a36Sopenharmony_ci#include <linux/pm_domain.h>
1962306a36Sopenharmony_ci#include <linux/slab.h>
2062306a36Sopenharmony_ci
2162306a36Sopenharmony_ci#include "rpmsg_internal.h"
2262306a36Sopenharmony_ci
2362306a36Sopenharmony_cistruct class *rpmsg_class;
2462306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_class);
2562306a36Sopenharmony_ci
2662306a36Sopenharmony_ci/**
2762306a36Sopenharmony_ci * rpmsg_create_channel() - create a new rpmsg channel
2862306a36Sopenharmony_ci * using its name and address info.
2962306a36Sopenharmony_ci * @rpdev: rpmsg device
3062306a36Sopenharmony_ci * @chinfo: channel_info to bind
3162306a36Sopenharmony_ci *
3262306a36Sopenharmony_ci * Return: a pointer to the new rpmsg device on success, or NULL on error.
3362306a36Sopenharmony_ci */
3462306a36Sopenharmony_cistruct rpmsg_device *rpmsg_create_channel(struct rpmsg_device *rpdev,
3562306a36Sopenharmony_ci					  struct rpmsg_channel_info *chinfo)
3662306a36Sopenharmony_ci{
3762306a36Sopenharmony_ci	if (WARN_ON(!rpdev))
3862306a36Sopenharmony_ci		return NULL;
3962306a36Sopenharmony_ci	if (!rpdev->ops || !rpdev->ops->create_channel) {
4062306a36Sopenharmony_ci		dev_err(&rpdev->dev, "no create_channel ops found\n");
4162306a36Sopenharmony_ci		return NULL;
4262306a36Sopenharmony_ci	}
4362306a36Sopenharmony_ci
4462306a36Sopenharmony_ci	return rpdev->ops->create_channel(rpdev, chinfo);
4562306a36Sopenharmony_ci}
4662306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_create_channel);
4762306a36Sopenharmony_ci
4862306a36Sopenharmony_ci/**
4962306a36Sopenharmony_ci * rpmsg_release_channel() - release a rpmsg channel
5062306a36Sopenharmony_ci * using its name and address info.
5162306a36Sopenharmony_ci * @rpdev: rpmsg device
5262306a36Sopenharmony_ci * @chinfo: channel_info to bind
5362306a36Sopenharmony_ci *
5462306a36Sopenharmony_ci * Return: 0 on success or an appropriate error value.
5562306a36Sopenharmony_ci */
5662306a36Sopenharmony_ciint rpmsg_release_channel(struct rpmsg_device *rpdev,
5762306a36Sopenharmony_ci			  struct rpmsg_channel_info *chinfo)
5862306a36Sopenharmony_ci{
5962306a36Sopenharmony_ci	if (WARN_ON(!rpdev))
6062306a36Sopenharmony_ci		return -EINVAL;
6162306a36Sopenharmony_ci	if (!rpdev->ops || !rpdev->ops->release_channel) {
6262306a36Sopenharmony_ci		dev_err(&rpdev->dev, "no release_channel ops found\n");
6362306a36Sopenharmony_ci		return -ENXIO;
6462306a36Sopenharmony_ci	}
6562306a36Sopenharmony_ci
6662306a36Sopenharmony_ci	return rpdev->ops->release_channel(rpdev, chinfo);
6762306a36Sopenharmony_ci}
6862306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_release_channel);
6962306a36Sopenharmony_ci
7062306a36Sopenharmony_ci/**
7162306a36Sopenharmony_ci * rpmsg_create_ept() - create a new rpmsg_endpoint
7262306a36Sopenharmony_ci * @rpdev: rpmsg channel device
7362306a36Sopenharmony_ci * @cb: rx callback handler
7462306a36Sopenharmony_ci * @priv: private data for the driver's use
7562306a36Sopenharmony_ci * @chinfo: channel_info with the local rpmsg address to bind with @cb
7662306a36Sopenharmony_ci *
7762306a36Sopenharmony_ci * Every rpmsg address in the system is bound to an rx callback (so when
7862306a36Sopenharmony_ci * inbound messages arrive, they are dispatched by the rpmsg bus using the
7962306a36Sopenharmony_ci * appropriate callback handler) by means of an rpmsg_endpoint struct.
8062306a36Sopenharmony_ci *
8162306a36Sopenharmony_ci * This function allows drivers to create such an endpoint, and by that,
8262306a36Sopenharmony_ci * bind a callback, and possibly some private data too, to an rpmsg address
8362306a36Sopenharmony_ci * (either one that is known in advance, or one that will be dynamically
8462306a36Sopenharmony_ci * assigned for them).
8562306a36Sopenharmony_ci *
8662306a36Sopenharmony_ci * Simple rpmsg drivers need not call rpmsg_create_ept, because an endpoint
8762306a36Sopenharmony_ci * is already created for them when they are probed by the rpmsg bus
8862306a36Sopenharmony_ci * (using the rx callback provided when they registered to the rpmsg bus).
8962306a36Sopenharmony_ci *
9062306a36Sopenharmony_ci * So things should just work for simple drivers: they already have an
9162306a36Sopenharmony_ci * endpoint, their rx callback is bound to their rpmsg address, and when
9262306a36Sopenharmony_ci * relevant inbound messages arrive (i.e. messages which their dst address
9362306a36Sopenharmony_ci * equals to the src address of their rpmsg channel), the driver's handler
9462306a36Sopenharmony_ci * is invoked to process it.
9562306a36Sopenharmony_ci *
9662306a36Sopenharmony_ci * That said, more complicated drivers might need to allocate
9762306a36Sopenharmony_ci * additional rpmsg addresses, and bind them to different rx callbacks.
9862306a36Sopenharmony_ci * To accomplish that, those drivers need to call this function.
9962306a36Sopenharmony_ci *
10062306a36Sopenharmony_ci * Drivers should provide their @rpdev channel (so the new endpoint would belong
10162306a36Sopenharmony_ci * to the same remote processor their channel belongs to), an rx callback
10262306a36Sopenharmony_ci * function, an optional private data (which is provided back when the
10362306a36Sopenharmony_ci * rx callback is invoked), and an address they want to bind with the
10462306a36Sopenharmony_ci * callback. If @addr is RPMSG_ADDR_ANY, then rpmsg_create_ept will
10562306a36Sopenharmony_ci * dynamically assign them an available rpmsg address (drivers should have
10662306a36Sopenharmony_ci * a very good reason why not to always use RPMSG_ADDR_ANY here).
10762306a36Sopenharmony_ci *
10862306a36Sopenharmony_ci * Return: a pointer to the endpoint on success, or NULL on error.
10962306a36Sopenharmony_ci */
11062306a36Sopenharmony_cistruct rpmsg_endpoint *rpmsg_create_ept(struct rpmsg_device *rpdev,
11162306a36Sopenharmony_ci					rpmsg_rx_cb_t cb, void *priv,
11262306a36Sopenharmony_ci					struct rpmsg_channel_info chinfo)
11362306a36Sopenharmony_ci{
11462306a36Sopenharmony_ci	if (WARN_ON(!rpdev))
11562306a36Sopenharmony_ci		return NULL;
11662306a36Sopenharmony_ci
11762306a36Sopenharmony_ci	return rpdev->ops->create_ept(rpdev, cb, priv, chinfo);
11862306a36Sopenharmony_ci}
11962306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_create_ept);
12062306a36Sopenharmony_ci
12162306a36Sopenharmony_ci/**
12262306a36Sopenharmony_ci * rpmsg_destroy_ept() - destroy an existing rpmsg endpoint
12362306a36Sopenharmony_ci * @ept: endpoing to destroy
12462306a36Sopenharmony_ci *
12562306a36Sopenharmony_ci * Should be used by drivers to destroy an rpmsg endpoint previously
12662306a36Sopenharmony_ci * created with rpmsg_create_ept(). As with other types of "free" NULL
12762306a36Sopenharmony_ci * is a valid parameter.
12862306a36Sopenharmony_ci */
12962306a36Sopenharmony_civoid rpmsg_destroy_ept(struct rpmsg_endpoint *ept)
13062306a36Sopenharmony_ci{
13162306a36Sopenharmony_ci	if (ept && ept->ops)
13262306a36Sopenharmony_ci		ept->ops->destroy_ept(ept);
13362306a36Sopenharmony_ci}
13462306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_destroy_ept);
13562306a36Sopenharmony_ci
13662306a36Sopenharmony_ci/**
13762306a36Sopenharmony_ci * rpmsg_send() - send a message across to the remote processor
13862306a36Sopenharmony_ci * @ept: the rpmsg endpoint
13962306a36Sopenharmony_ci * @data: payload of message
14062306a36Sopenharmony_ci * @len: length of payload
14162306a36Sopenharmony_ci *
14262306a36Sopenharmony_ci * This function sends @data of length @len on the @ept endpoint.
14362306a36Sopenharmony_ci * The message will be sent to the remote processor which the @ept
14462306a36Sopenharmony_ci * endpoint belongs to, using @ept's address and its associated rpmsg
14562306a36Sopenharmony_ci * device destination addresses.
14662306a36Sopenharmony_ci * In case there are no TX buffers available, the function will block until
14762306a36Sopenharmony_ci * one becomes available, or a timeout of 15 seconds elapses. When the latter
14862306a36Sopenharmony_ci * happens, -ERESTARTSYS is returned.
14962306a36Sopenharmony_ci *
15062306a36Sopenharmony_ci * Can only be called from process context (for now).
15162306a36Sopenharmony_ci *
15262306a36Sopenharmony_ci * Return: 0 on success and an appropriate error value on failure.
15362306a36Sopenharmony_ci */
15462306a36Sopenharmony_ciint rpmsg_send(struct rpmsg_endpoint *ept, void *data, int len)
15562306a36Sopenharmony_ci{
15662306a36Sopenharmony_ci	if (WARN_ON(!ept))
15762306a36Sopenharmony_ci		return -EINVAL;
15862306a36Sopenharmony_ci	if (!ept->ops->send)
15962306a36Sopenharmony_ci		return -ENXIO;
16062306a36Sopenharmony_ci
16162306a36Sopenharmony_ci	return ept->ops->send(ept, data, len);
16262306a36Sopenharmony_ci}
16362306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_send);
16462306a36Sopenharmony_ci
16562306a36Sopenharmony_ci/**
16662306a36Sopenharmony_ci * rpmsg_sendto() - send a message across to the remote processor, specify dst
16762306a36Sopenharmony_ci * @ept: the rpmsg endpoint
16862306a36Sopenharmony_ci * @data: payload of message
16962306a36Sopenharmony_ci * @len: length of payload
17062306a36Sopenharmony_ci * @dst: destination address
17162306a36Sopenharmony_ci *
17262306a36Sopenharmony_ci * This function sends @data of length @len to the remote @dst address.
17362306a36Sopenharmony_ci * The message will be sent to the remote processor which the @ept
17462306a36Sopenharmony_ci * endpoint belongs to, using @ept's address as source.
17562306a36Sopenharmony_ci * In case there are no TX buffers available, the function will block until
17662306a36Sopenharmony_ci * one becomes available, or a timeout of 15 seconds elapses. When the latter
17762306a36Sopenharmony_ci * happens, -ERESTARTSYS is returned.
17862306a36Sopenharmony_ci *
17962306a36Sopenharmony_ci * Can only be called from process context (for now).
18062306a36Sopenharmony_ci *
18162306a36Sopenharmony_ci * Return: 0 on success and an appropriate error value on failure.
18262306a36Sopenharmony_ci */
18362306a36Sopenharmony_ciint rpmsg_sendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
18462306a36Sopenharmony_ci{
18562306a36Sopenharmony_ci	if (WARN_ON(!ept))
18662306a36Sopenharmony_ci		return -EINVAL;
18762306a36Sopenharmony_ci	if (!ept->ops->sendto)
18862306a36Sopenharmony_ci		return -ENXIO;
18962306a36Sopenharmony_ci
19062306a36Sopenharmony_ci	return ept->ops->sendto(ept, data, len, dst);
19162306a36Sopenharmony_ci}
19262306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_sendto);
19362306a36Sopenharmony_ci
19462306a36Sopenharmony_ci/**
19562306a36Sopenharmony_ci * rpmsg_send_offchannel() - send a message using explicit src/dst addresses
19662306a36Sopenharmony_ci * @ept: the rpmsg endpoint
19762306a36Sopenharmony_ci * @src: source address
19862306a36Sopenharmony_ci * @dst: destination address
19962306a36Sopenharmony_ci * @data: payload of message
20062306a36Sopenharmony_ci * @len: length of payload
20162306a36Sopenharmony_ci *
20262306a36Sopenharmony_ci * This function sends @data of length @len to the remote @dst address,
20362306a36Sopenharmony_ci * and uses @src as the source address.
20462306a36Sopenharmony_ci * The message will be sent to the remote processor which the @ept
20562306a36Sopenharmony_ci * endpoint belongs to.
20662306a36Sopenharmony_ci * In case there are no TX buffers available, the function will block until
20762306a36Sopenharmony_ci * one becomes available, or a timeout of 15 seconds elapses. When the latter
20862306a36Sopenharmony_ci * happens, -ERESTARTSYS is returned.
20962306a36Sopenharmony_ci *
21062306a36Sopenharmony_ci * Can only be called from process context (for now).
21162306a36Sopenharmony_ci *
21262306a36Sopenharmony_ci * Return: 0 on success and an appropriate error value on failure.
21362306a36Sopenharmony_ci */
21462306a36Sopenharmony_ciint rpmsg_send_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,
21562306a36Sopenharmony_ci			  void *data, int len)
21662306a36Sopenharmony_ci{
21762306a36Sopenharmony_ci	if (WARN_ON(!ept))
21862306a36Sopenharmony_ci		return -EINVAL;
21962306a36Sopenharmony_ci	if (!ept->ops->send_offchannel)
22062306a36Sopenharmony_ci		return -ENXIO;
22162306a36Sopenharmony_ci
22262306a36Sopenharmony_ci	return ept->ops->send_offchannel(ept, src, dst, data, len);
22362306a36Sopenharmony_ci}
22462306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_send_offchannel);
22562306a36Sopenharmony_ci
22662306a36Sopenharmony_ci/**
22762306a36Sopenharmony_ci * rpmsg_trysend() - send a message across to the remote processor
22862306a36Sopenharmony_ci * @ept: the rpmsg endpoint
22962306a36Sopenharmony_ci * @data: payload of message
23062306a36Sopenharmony_ci * @len: length of payload
23162306a36Sopenharmony_ci *
23262306a36Sopenharmony_ci * This function sends @data of length @len on the @ept endpoint.
23362306a36Sopenharmony_ci * The message will be sent to the remote processor which the @ept
23462306a36Sopenharmony_ci * endpoint belongs to, using @ept's address as source and its associated
23562306a36Sopenharmony_ci * rpdev's address as destination.
23662306a36Sopenharmony_ci * In case there are no TX buffers available, the function will immediately
23762306a36Sopenharmony_ci * return -ENOMEM without waiting until one becomes available.
23862306a36Sopenharmony_ci *
23962306a36Sopenharmony_ci * Can only be called from process context (for now).
24062306a36Sopenharmony_ci *
24162306a36Sopenharmony_ci * Return: 0 on success and an appropriate error value on failure.
24262306a36Sopenharmony_ci */
24362306a36Sopenharmony_ciint rpmsg_trysend(struct rpmsg_endpoint *ept, void *data, int len)
24462306a36Sopenharmony_ci{
24562306a36Sopenharmony_ci	if (WARN_ON(!ept))
24662306a36Sopenharmony_ci		return -EINVAL;
24762306a36Sopenharmony_ci	if (!ept->ops->trysend)
24862306a36Sopenharmony_ci		return -ENXIO;
24962306a36Sopenharmony_ci
25062306a36Sopenharmony_ci	return ept->ops->trysend(ept, data, len);
25162306a36Sopenharmony_ci}
25262306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_trysend);
25362306a36Sopenharmony_ci
25462306a36Sopenharmony_ci/**
25562306a36Sopenharmony_ci * rpmsg_trysendto() - send a message across to the remote processor, specify dst
25662306a36Sopenharmony_ci * @ept: the rpmsg endpoint
25762306a36Sopenharmony_ci * @data: payload of message
25862306a36Sopenharmony_ci * @len: length of payload
25962306a36Sopenharmony_ci * @dst: destination address
26062306a36Sopenharmony_ci *
26162306a36Sopenharmony_ci * This function sends @data of length @len to the remote @dst address.
26262306a36Sopenharmony_ci * The message will be sent to the remote processor which the @ept
26362306a36Sopenharmony_ci * endpoint belongs to, using @ept's address as source.
26462306a36Sopenharmony_ci * In case there are no TX buffers available, the function will immediately
26562306a36Sopenharmony_ci * return -ENOMEM without waiting until one becomes available.
26662306a36Sopenharmony_ci *
26762306a36Sopenharmony_ci * Can only be called from process context (for now).
26862306a36Sopenharmony_ci *
26962306a36Sopenharmony_ci * Return: 0 on success and an appropriate error value on failure.
27062306a36Sopenharmony_ci */
27162306a36Sopenharmony_ciint rpmsg_trysendto(struct rpmsg_endpoint *ept, void *data, int len, u32 dst)
27262306a36Sopenharmony_ci{
27362306a36Sopenharmony_ci	if (WARN_ON(!ept))
27462306a36Sopenharmony_ci		return -EINVAL;
27562306a36Sopenharmony_ci	if (!ept->ops->trysendto)
27662306a36Sopenharmony_ci		return -ENXIO;
27762306a36Sopenharmony_ci
27862306a36Sopenharmony_ci	return ept->ops->trysendto(ept, data, len, dst);
27962306a36Sopenharmony_ci}
28062306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_trysendto);
28162306a36Sopenharmony_ci
28262306a36Sopenharmony_ci/**
28362306a36Sopenharmony_ci * rpmsg_poll() - poll the endpoint's send buffers
28462306a36Sopenharmony_ci * @ept:	the rpmsg endpoint
28562306a36Sopenharmony_ci * @filp:	file for poll_wait()
28662306a36Sopenharmony_ci * @wait:	poll_table for poll_wait()
28762306a36Sopenharmony_ci *
28862306a36Sopenharmony_ci * Return: mask representing the current state of the endpoint's send buffers
28962306a36Sopenharmony_ci */
29062306a36Sopenharmony_ci__poll_t rpmsg_poll(struct rpmsg_endpoint *ept, struct file *filp,
29162306a36Sopenharmony_ci			poll_table *wait)
29262306a36Sopenharmony_ci{
29362306a36Sopenharmony_ci	if (WARN_ON(!ept))
29462306a36Sopenharmony_ci		return 0;
29562306a36Sopenharmony_ci	if (!ept->ops->poll)
29662306a36Sopenharmony_ci		return 0;
29762306a36Sopenharmony_ci
29862306a36Sopenharmony_ci	return ept->ops->poll(ept, filp, wait);
29962306a36Sopenharmony_ci}
30062306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_poll);
30162306a36Sopenharmony_ci
30262306a36Sopenharmony_ci/**
30362306a36Sopenharmony_ci * rpmsg_trysend_offchannel() - send a message using explicit src/dst addresses
30462306a36Sopenharmony_ci * @ept: the rpmsg endpoint
30562306a36Sopenharmony_ci * @src: source address
30662306a36Sopenharmony_ci * @dst: destination address
30762306a36Sopenharmony_ci * @data: payload of message
30862306a36Sopenharmony_ci * @len: length of payload
30962306a36Sopenharmony_ci *
31062306a36Sopenharmony_ci * This function sends @data of length @len to the remote @dst address,
31162306a36Sopenharmony_ci * and uses @src as the source address.
31262306a36Sopenharmony_ci * The message will be sent to the remote processor which the @ept
31362306a36Sopenharmony_ci * endpoint belongs to.
31462306a36Sopenharmony_ci * In case there are no TX buffers available, the function will immediately
31562306a36Sopenharmony_ci * return -ENOMEM without waiting until one becomes available.
31662306a36Sopenharmony_ci *
31762306a36Sopenharmony_ci * Can only be called from process context (for now).
31862306a36Sopenharmony_ci *
31962306a36Sopenharmony_ci * Return: 0 on success and an appropriate error value on failure.
32062306a36Sopenharmony_ci */
32162306a36Sopenharmony_ciint rpmsg_trysend_offchannel(struct rpmsg_endpoint *ept, u32 src, u32 dst,
32262306a36Sopenharmony_ci			     void *data, int len)
32362306a36Sopenharmony_ci{
32462306a36Sopenharmony_ci	if (WARN_ON(!ept))
32562306a36Sopenharmony_ci		return -EINVAL;
32662306a36Sopenharmony_ci	if (!ept->ops->trysend_offchannel)
32762306a36Sopenharmony_ci		return -ENXIO;
32862306a36Sopenharmony_ci
32962306a36Sopenharmony_ci	return ept->ops->trysend_offchannel(ept, src, dst, data, len);
33062306a36Sopenharmony_ci}
33162306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_trysend_offchannel);
33262306a36Sopenharmony_ci
33362306a36Sopenharmony_ci/**
33462306a36Sopenharmony_ci * rpmsg_set_flow_control() - request remote to pause/resume transmission
33562306a36Sopenharmony_ci * @ept:	the rpmsg endpoint
33662306a36Sopenharmony_ci * @pause:	pause transmission
33762306a36Sopenharmony_ci * @dst:	destination address of the endpoint
33862306a36Sopenharmony_ci *
33962306a36Sopenharmony_ci * Return: 0 on success and an appropriate error value on failure.
34062306a36Sopenharmony_ci */
34162306a36Sopenharmony_ciint rpmsg_set_flow_control(struct rpmsg_endpoint *ept, bool pause, u32 dst)
34262306a36Sopenharmony_ci{
34362306a36Sopenharmony_ci	if (WARN_ON(!ept))
34462306a36Sopenharmony_ci		return -EINVAL;
34562306a36Sopenharmony_ci	if (!ept->ops->set_flow_control)
34662306a36Sopenharmony_ci		return -EOPNOTSUPP;
34762306a36Sopenharmony_ci
34862306a36Sopenharmony_ci	return ept->ops->set_flow_control(ept, pause, dst);
34962306a36Sopenharmony_ci}
35062306a36Sopenharmony_ciEXPORT_SYMBOL_GPL(rpmsg_set_flow_control);
35162306a36Sopenharmony_ci
35262306a36Sopenharmony_ci/**
35362306a36Sopenharmony_ci * rpmsg_get_mtu() - get maximum transmission buffer size for sending message.
35462306a36Sopenharmony_ci * @ept: the rpmsg endpoint
35562306a36Sopenharmony_ci *
35662306a36Sopenharmony_ci * This function returns maximum buffer size available for a single outgoing message.
35762306a36Sopenharmony_ci *
35862306a36Sopenharmony_ci * Return: the maximum transmission size on success and an appropriate error
35962306a36Sopenharmony_ci * value on failure.
36062306a36Sopenharmony_ci */
36162306a36Sopenharmony_ci
36262306a36Sopenharmony_cissize_t rpmsg_get_mtu(struct rpmsg_endpoint *ept)
36362306a36Sopenharmony_ci{
36462306a36Sopenharmony_ci	if (WARN_ON(!ept))
36562306a36Sopenharmony_ci		return -EINVAL;
36662306a36Sopenharmony_ci	if (!ept->ops->get_mtu)
36762306a36Sopenharmony_ci		return -ENOTSUPP;
36862306a36Sopenharmony_ci
36962306a36Sopenharmony_ci	return ept->ops->get_mtu(ept);
37062306a36Sopenharmony_ci}
37162306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_get_mtu);
37262306a36Sopenharmony_ci
37362306a36Sopenharmony_ci/*
37462306a36Sopenharmony_ci * match a rpmsg channel with a channel info struct.
37562306a36Sopenharmony_ci * this is used to make sure we're not creating rpmsg devices for channels
37662306a36Sopenharmony_ci * that already exist.
37762306a36Sopenharmony_ci */
37862306a36Sopenharmony_cistatic int rpmsg_device_match(struct device *dev, void *data)
37962306a36Sopenharmony_ci{
38062306a36Sopenharmony_ci	struct rpmsg_channel_info *chinfo = data;
38162306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
38262306a36Sopenharmony_ci
38362306a36Sopenharmony_ci	if (chinfo->src != RPMSG_ADDR_ANY && chinfo->src != rpdev->src)
38462306a36Sopenharmony_ci		return 0;
38562306a36Sopenharmony_ci
38662306a36Sopenharmony_ci	if (chinfo->dst != RPMSG_ADDR_ANY && chinfo->dst != rpdev->dst)
38762306a36Sopenharmony_ci		return 0;
38862306a36Sopenharmony_ci
38962306a36Sopenharmony_ci	if (strncmp(chinfo->name, rpdev->id.name, RPMSG_NAME_SIZE))
39062306a36Sopenharmony_ci		return 0;
39162306a36Sopenharmony_ci
39262306a36Sopenharmony_ci	/* found a match ! */
39362306a36Sopenharmony_ci	return 1;
39462306a36Sopenharmony_ci}
39562306a36Sopenharmony_ci
39662306a36Sopenharmony_cistruct device *rpmsg_find_device(struct device *parent,
39762306a36Sopenharmony_ci				 struct rpmsg_channel_info *chinfo)
39862306a36Sopenharmony_ci{
39962306a36Sopenharmony_ci	return device_find_child(parent, chinfo, rpmsg_device_match);
40062306a36Sopenharmony_ci
40162306a36Sopenharmony_ci}
40262306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_find_device);
40362306a36Sopenharmony_ci
40462306a36Sopenharmony_ci/* sysfs show configuration fields */
40562306a36Sopenharmony_ci#define rpmsg_show_attr(field, path, format_string)			\
40662306a36Sopenharmony_cistatic ssize_t								\
40762306a36Sopenharmony_cifield##_show(struct device *dev,					\
40862306a36Sopenharmony_ci			struct device_attribute *attr, char *buf)	\
40962306a36Sopenharmony_ci{									\
41062306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);		\
41162306a36Sopenharmony_ci									\
41262306a36Sopenharmony_ci	return sprintf(buf, format_string, rpdev->path);		\
41362306a36Sopenharmony_ci}									\
41462306a36Sopenharmony_cistatic DEVICE_ATTR_RO(field);
41562306a36Sopenharmony_ci
41662306a36Sopenharmony_ci#define rpmsg_string_attr(field, member)				\
41762306a36Sopenharmony_cistatic ssize_t								\
41862306a36Sopenharmony_cifield##_store(struct device *dev, struct device_attribute *attr,	\
41962306a36Sopenharmony_ci	      const char *buf, size_t sz)				\
42062306a36Sopenharmony_ci{									\
42162306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);		\
42262306a36Sopenharmony_ci	const char *old;						\
42362306a36Sopenharmony_ci	char *new;							\
42462306a36Sopenharmony_ci									\
42562306a36Sopenharmony_ci	new = kstrndup(buf, sz, GFP_KERNEL);				\
42662306a36Sopenharmony_ci	if (!new)							\
42762306a36Sopenharmony_ci		return -ENOMEM;						\
42862306a36Sopenharmony_ci	new[strcspn(new, "\n")] = '\0';					\
42962306a36Sopenharmony_ci									\
43062306a36Sopenharmony_ci	device_lock(dev);						\
43162306a36Sopenharmony_ci	old = rpdev->member;						\
43262306a36Sopenharmony_ci	if (strlen(new)) {						\
43362306a36Sopenharmony_ci		rpdev->member = new;					\
43462306a36Sopenharmony_ci	} else {							\
43562306a36Sopenharmony_ci		kfree(new);						\
43662306a36Sopenharmony_ci		rpdev->member = NULL;					\
43762306a36Sopenharmony_ci	}								\
43862306a36Sopenharmony_ci	device_unlock(dev);						\
43962306a36Sopenharmony_ci									\
44062306a36Sopenharmony_ci	kfree(old);							\
44162306a36Sopenharmony_ci									\
44262306a36Sopenharmony_ci	return sz;							\
44362306a36Sopenharmony_ci}									\
44462306a36Sopenharmony_cistatic ssize_t								\
44562306a36Sopenharmony_cifield##_show(struct device *dev,					\
44662306a36Sopenharmony_ci	     struct device_attribute *attr, char *buf)			\
44762306a36Sopenharmony_ci{									\
44862306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);		\
44962306a36Sopenharmony_ci									\
45062306a36Sopenharmony_ci	return sprintf(buf, "%s\n", rpdev->member);			\
45162306a36Sopenharmony_ci}									\
45262306a36Sopenharmony_cistatic DEVICE_ATTR_RW(field)
45362306a36Sopenharmony_ci
45462306a36Sopenharmony_ci/* for more info, see Documentation/ABI/testing/sysfs-bus-rpmsg */
45562306a36Sopenharmony_cirpmsg_show_attr(name, id.name, "%s\n");
45662306a36Sopenharmony_cirpmsg_show_attr(src, src, "0x%x\n");
45762306a36Sopenharmony_cirpmsg_show_attr(dst, dst, "0x%x\n");
45862306a36Sopenharmony_cirpmsg_show_attr(announce, announce ? "true" : "false", "%s\n");
45962306a36Sopenharmony_cirpmsg_string_attr(driver_override, driver_override);
46062306a36Sopenharmony_ci
46162306a36Sopenharmony_cistatic ssize_t modalias_show(struct device *dev,
46262306a36Sopenharmony_ci			     struct device_attribute *attr, char *buf)
46362306a36Sopenharmony_ci{
46462306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
46562306a36Sopenharmony_ci	ssize_t len;
46662306a36Sopenharmony_ci
46762306a36Sopenharmony_ci	len = of_device_modalias(dev, buf, PAGE_SIZE);
46862306a36Sopenharmony_ci	if (len != -ENODEV)
46962306a36Sopenharmony_ci		return len;
47062306a36Sopenharmony_ci
47162306a36Sopenharmony_ci	return sprintf(buf, RPMSG_DEVICE_MODALIAS_FMT "\n", rpdev->id.name);
47262306a36Sopenharmony_ci}
47362306a36Sopenharmony_cistatic DEVICE_ATTR_RO(modalias);
47462306a36Sopenharmony_ci
47562306a36Sopenharmony_cistatic struct attribute *rpmsg_dev_attrs[] = {
47662306a36Sopenharmony_ci	&dev_attr_name.attr,
47762306a36Sopenharmony_ci	&dev_attr_modalias.attr,
47862306a36Sopenharmony_ci	&dev_attr_dst.attr,
47962306a36Sopenharmony_ci	&dev_attr_src.attr,
48062306a36Sopenharmony_ci	&dev_attr_announce.attr,
48162306a36Sopenharmony_ci	&dev_attr_driver_override.attr,
48262306a36Sopenharmony_ci	NULL,
48362306a36Sopenharmony_ci};
48462306a36Sopenharmony_ciATTRIBUTE_GROUPS(rpmsg_dev);
48562306a36Sopenharmony_ci
48662306a36Sopenharmony_ci/* rpmsg devices and drivers are matched using the service name */
48762306a36Sopenharmony_cistatic inline int rpmsg_id_match(const struct rpmsg_device *rpdev,
48862306a36Sopenharmony_ci				  const struct rpmsg_device_id *id)
48962306a36Sopenharmony_ci{
49062306a36Sopenharmony_ci	return strncmp(id->name, rpdev->id.name, RPMSG_NAME_SIZE) == 0;
49162306a36Sopenharmony_ci}
49262306a36Sopenharmony_ci
49362306a36Sopenharmony_ci/* match rpmsg channel and rpmsg driver */
49462306a36Sopenharmony_cistatic int rpmsg_dev_match(struct device *dev, struct device_driver *drv)
49562306a36Sopenharmony_ci{
49662306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
49762306a36Sopenharmony_ci	struct rpmsg_driver *rpdrv = to_rpmsg_driver(drv);
49862306a36Sopenharmony_ci	const struct rpmsg_device_id *ids = rpdrv->id_table;
49962306a36Sopenharmony_ci	unsigned int i;
50062306a36Sopenharmony_ci
50162306a36Sopenharmony_ci	if (rpdev->driver_override)
50262306a36Sopenharmony_ci		return !strcmp(rpdev->driver_override, drv->name);
50362306a36Sopenharmony_ci
50462306a36Sopenharmony_ci	if (ids)
50562306a36Sopenharmony_ci		for (i = 0; ids[i].name[0]; i++)
50662306a36Sopenharmony_ci			if (rpmsg_id_match(rpdev, &ids[i])) {
50762306a36Sopenharmony_ci				rpdev->id.driver_data = ids[i].driver_data;
50862306a36Sopenharmony_ci				return 1;
50962306a36Sopenharmony_ci			}
51062306a36Sopenharmony_ci
51162306a36Sopenharmony_ci	return of_driver_match_device(dev, drv);
51262306a36Sopenharmony_ci}
51362306a36Sopenharmony_ci
51462306a36Sopenharmony_cistatic int rpmsg_uevent(const struct device *dev, struct kobj_uevent_env *env)
51562306a36Sopenharmony_ci{
51662306a36Sopenharmony_ci	const struct rpmsg_device *rpdev = to_rpmsg_device(dev);
51762306a36Sopenharmony_ci	int ret;
51862306a36Sopenharmony_ci
51962306a36Sopenharmony_ci	ret = of_device_uevent_modalias(dev, env);
52062306a36Sopenharmony_ci	if (ret != -ENODEV)
52162306a36Sopenharmony_ci		return ret;
52262306a36Sopenharmony_ci
52362306a36Sopenharmony_ci	return add_uevent_var(env, "MODALIAS=" RPMSG_DEVICE_MODALIAS_FMT,
52462306a36Sopenharmony_ci					rpdev->id.name);
52562306a36Sopenharmony_ci}
52662306a36Sopenharmony_ci
52762306a36Sopenharmony_ci/*
52862306a36Sopenharmony_ci * when an rpmsg driver is probed with a channel, we seamlessly create
52962306a36Sopenharmony_ci * it an endpoint, binding its rx callback to a unique local rpmsg
53062306a36Sopenharmony_ci * address.
53162306a36Sopenharmony_ci *
53262306a36Sopenharmony_ci * if we need to, we also announce about this channel to the remote
53362306a36Sopenharmony_ci * processor (needed in case the driver is exposing an rpmsg service).
53462306a36Sopenharmony_ci */
53562306a36Sopenharmony_cistatic int rpmsg_dev_probe(struct device *dev)
53662306a36Sopenharmony_ci{
53762306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
53862306a36Sopenharmony_ci	struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
53962306a36Sopenharmony_ci	struct rpmsg_channel_info chinfo = {};
54062306a36Sopenharmony_ci	struct rpmsg_endpoint *ept = NULL;
54162306a36Sopenharmony_ci	int err;
54262306a36Sopenharmony_ci
54362306a36Sopenharmony_ci	err = dev_pm_domain_attach(dev, true);
54462306a36Sopenharmony_ci	if (err)
54562306a36Sopenharmony_ci		goto out;
54662306a36Sopenharmony_ci
54762306a36Sopenharmony_ci	if (rpdrv->callback) {
54862306a36Sopenharmony_ci		strncpy(chinfo.name, rpdev->id.name, RPMSG_NAME_SIZE);
54962306a36Sopenharmony_ci		chinfo.src = rpdev->src;
55062306a36Sopenharmony_ci		chinfo.dst = RPMSG_ADDR_ANY;
55162306a36Sopenharmony_ci
55262306a36Sopenharmony_ci		ept = rpmsg_create_ept(rpdev, rpdrv->callback, NULL, chinfo);
55362306a36Sopenharmony_ci		if (!ept) {
55462306a36Sopenharmony_ci			dev_err(dev, "failed to create endpoint\n");
55562306a36Sopenharmony_ci			err = -ENOMEM;
55662306a36Sopenharmony_ci			goto out;
55762306a36Sopenharmony_ci		}
55862306a36Sopenharmony_ci
55962306a36Sopenharmony_ci		rpdev->ept = ept;
56062306a36Sopenharmony_ci		rpdev->src = ept->addr;
56162306a36Sopenharmony_ci
56262306a36Sopenharmony_ci		ept->flow_cb = rpdrv->flowcontrol;
56362306a36Sopenharmony_ci	}
56462306a36Sopenharmony_ci
56562306a36Sopenharmony_ci	err = rpdrv->probe(rpdev);
56662306a36Sopenharmony_ci	if (err) {
56762306a36Sopenharmony_ci		dev_err(dev, "%s: failed: %d\n", __func__, err);
56862306a36Sopenharmony_ci		goto destroy_ept;
56962306a36Sopenharmony_ci	}
57062306a36Sopenharmony_ci
57162306a36Sopenharmony_ci	if (ept && rpdev->ops->announce_create) {
57262306a36Sopenharmony_ci		err = rpdev->ops->announce_create(rpdev);
57362306a36Sopenharmony_ci		if (err) {
57462306a36Sopenharmony_ci			dev_err(dev, "failed to announce creation\n");
57562306a36Sopenharmony_ci			goto remove_rpdev;
57662306a36Sopenharmony_ci		}
57762306a36Sopenharmony_ci	}
57862306a36Sopenharmony_ci
57962306a36Sopenharmony_ci	return 0;
58062306a36Sopenharmony_ci
58162306a36Sopenharmony_ciremove_rpdev:
58262306a36Sopenharmony_ci	if (rpdrv->remove)
58362306a36Sopenharmony_ci		rpdrv->remove(rpdev);
58462306a36Sopenharmony_cidestroy_ept:
58562306a36Sopenharmony_ci	if (ept)
58662306a36Sopenharmony_ci		rpmsg_destroy_ept(ept);
58762306a36Sopenharmony_ciout:
58862306a36Sopenharmony_ci	return err;
58962306a36Sopenharmony_ci}
59062306a36Sopenharmony_ci
59162306a36Sopenharmony_cistatic void rpmsg_dev_remove(struct device *dev)
59262306a36Sopenharmony_ci{
59362306a36Sopenharmony_ci	struct rpmsg_device *rpdev = to_rpmsg_device(dev);
59462306a36Sopenharmony_ci	struct rpmsg_driver *rpdrv = to_rpmsg_driver(rpdev->dev.driver);
59562306a36Sopenharmony_ci
59662306a36Sopenharmony_ci	if (rpdev->ops->announce_destroy)
59762306a36Sopenharmony_ci		rpdev->ops->announce_destroy(rpdev);
59862306a36Sopenharmony_ci
59962306a36Sopenharmony_ci	if (rpdrv->remove)
60062306a36Sopenharmony_ci		rpdrv->remove(rpdev);
60162306a36Sopenharmony_ci
60262306a36Sopenharmony_ci	dev_pm_domain_detach(dev, true);
60362306a36Sopenharmony_ci
60462306a36Sopenharmony_ci	if (rpdev->ept)
60562306a36Sopenharmony_ci		rpmsg_destroy_ept(rpdev->ept);
60662306a36Sopenharmony_ci}
60762306a36Sopenharmony_ci
60862306a36Sopenharmony_cistatic struct bus_type rpmsg_bus = {
60962306a36Sopenharmony_ci	.name		= "rpmsg",
61062306a36Sopenharmony_ci	.match		= rpmsg_dev_match,
61162306a36Sopenharmony_ci	.dev_groups	= rpmsg_dev_groups,
61262306a36Sopenharmony_ci	.uevent		= rpmsg_uevent,
61362306a36Sopenharmony_ci	.probe		= rpmsg_dev_probe,
61462306a36Sopenharmony_ci	.remove		= rpmsg_dev_remove,
61562306a36Sopenharmony_ci};
61662306a36Sopenharmony_ci
61762306a36Sopenharmony_ci/*
61862306a36Sopenharmony_ci * A helper for registering rpmsg device with driver override and name.
61962306a36Sopenharmony_ci * Drivers should not be using it, but instead rpmsg_register_device().
62062306a36Sopenharmony_ci */
62162306a36Sopenharmony_ciint rpmsg_register_device_override(struct rpmsg_device *rpdev,
62262306a36Sopenharmony_ci				   const char *driver_override)
62362306a36Sopenharmony_ci{
62462306a36Sopenharmony_ci	struct device *dev = &rpdev->dev;
62562306a36Sopenharmony_ci	int ret;
62662306a36Sopenharmony_ci
62762306a36Sopenharmony_ci	if (driver_override)
62862306a36Sopenharmony_ci		strscpy_pad(rpdev->id.name, driver_override, RPMSG_NAME_SIZE);
62962306a36Sopenharmony_ci
63062306a36Sopenharmony_ci	dev_set_name(dev, "%s.%s.%d.%d", dev_name(dev->parent),
63162306a36Sopenharmony_ci		     rpdev->id.name, rpdev->src, rpdev->dst);
63262306a36Sopenharmony_ci
63362306a36Sopenharmony_ci	dev->bus = &rpmsg_bus;
63462306a36Sopenharmony_ci
63562306a36Sopenharmony_ci	device_initialize(dev);
63662306a36Sopenharmony_ci	if (driver_override) {
63762306a36Sopenharmony_ci		ret = driver_set_override(dev, &rpdev->driver_override,
63862306a36Sopenharmony_ci					  driver_override,
63962306a36Sopenharmony_ci					  strlen(driver_override));
64062306a36Sopenharmony_ci		if (ret) {
64162306a36Sopenharmony_ci			dev_err(dev, "device_set_override failed: %d\n", ret);
64262306a36Sopenharmony_ci			put_device(dev);
64362306a36Sopenharmony_ci			return ret;
64462306a36Sopenharmony_ci		}
64562306a36Sopenharmony_ci	}
64662306a36Sopenharmony_ci
64762306a36Sopenharmony_ci	ret = device_add(dev);
64862306a36Sopenharmony_ci	if (ret) {
64962306a36Sopenharmony_ci		dev_err(dev, "device_add failed: %d\n", ret);
65062306a36Sopenharmony_ci		kfree(rpdev->driver_override);
65162306a36Sopenharmony_ci		rpdev->driver_override = NULL;
65262306a36Sopenharmony_ci		put_device(dev);
65362306a36Sopenharmony_ci	}
65462306a36Sopenharmony_ci
65562306a36Sopenharmony_ci	return ret;
65662306a36Sopenharmony_ci}
65762306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_register_device_override);
65862306a36Sopenharmony_ci
65962306a36Sopenharmony_ciint rpmsg_register_device(struct rpmsg_device *rpdev)
66062306a36Sopenharmony_ci{
66162306a36Sopenharmony_ci	return rpmsg_register_device_override(rpdev, NULL);
66262306a36Sopenharmony_ci}
66362306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_register_device);
66462306a36Sopenharmony_ci
66562306a36Sopenharmony_ci/*
66662306a36Sopenharmony_ci * find an existing channel using its name + address properties,
66762306a36Sopenharmony_ci * and destroy it
66862306a36Sopenharmony_ci */
66962306a36Sopenharmony_ciint rpmsg_unregister_device(struct device *parent,
67062306a36Sopenharmony_ci			    struct rpmsg_channel_info *chinfo)
67162306a36Sopenharmony_ci{
67262306a36Sopenharmony_ci	struct device *dev;
67362306a36Sopenharmony_ci
67462306a36Sopenharmony_ci	dev = rpmsg_find_device(parent, chinfo);
67562306a36Sopenharmony_ci	if (!dev)
67662306a36Sopenharmony_ci		return -EINVAL;
67762306a36Sopenharmony_ci
67862306a36Sopenharmony_ci	device_unregister(dev);
67962306a36Sopenharmony_ci
68062306a36Sopenharmony_ci	put_device(dev);
68162306a36Sopenharmony_ci
68262306a36Sopenharmony_ci	return 0;
68362306a36Sopenharmony_ci}
68462306a36Sopenharmony_ciEXPORT_SYMBOL(rpmsg_unregister_device);
68562306a36Sopenharmony_ci
68662306a36Sopenharmony_ci/**
68762306a36Sopenharmony_ci * __register_rpmsg_driver() - register an rpmsg driver with the rpmsg bus
68862306a36Sopenharmony_ci * @rpdrv: pointer to a struct rpmsg_driver
68962306a36Sopenharmony_ci * @owner: owning module/driver
69062306a36Sopenharmony_ci *
69162306a36Sopenharmony_ci * Return: 0 on success, and an appropriate error value on failure.
69262306a36Sopenharmony_ci */
69362306a36Sopenharmony_ciint __register_rpmsg_driver(struct rpmsg_driver *rpdrv, struct module *owner)
69462306a36Sopenharmony_ci{
69562306a36Sopenharmony_ci	rpdrv->drv.bus = &rpmsg_bus;
69662306a36Sopenharmony_ci	rpdrv->drv.owner = owner;
69762306a36Sopenharmony_ci	return driver_register(&rpdrv->drv);
69862306a36Sopenharmony_ci}
69962306a36Sopenharmony_ciEXPORT_SYMBOL(__register_rpmsg_driver);
70062306a36Sopenharmony_ci
70162306a36Sopenharmony_ci/**
70262306a36Sopenharmony_ci * unregister_rpmsg_driver() - unregister an rpmsg driver from the rpmsg bus
70362306a36Sopenharmony_ci * @rpdrv: pointer to a struct rpmsg_driver
70462306a36Sopenharmony_ci *
70562306a36Sopenharmony_ci * Return: 0 on success, and an appropriate error value on failure.
70662306a36Sopenharmony_ci */
70762306a36Sopenharmony_civoid unregister_rpmsg_driver(struct rpmsg_driver *rpdrv)
70862306a36Sopenharmony_ci{
70962306a36Sopenharmony_ci	driver_unregister(&rpdrv->drv);
71062306a36Sopenharmony_ci}
71162306a36Sopenharmony_ciEXPORT_SYMBOL(unregister_rpmsg_driver);
71262306a36Sopenharmony_ci
71362306a36Sopenharmony_ci
71462306a36Sopenharmony_cistatic int __init rpmsg_init(void)
71562306a36Sopenharmony_ci{
71662306a36Sopenharmony_ci	int ret;
71762306a36Sopenharmony_ci
71862306a36Sopenharmony_ci	rpmsg_class = class_create("rpmsg");
71962306a36Sopenharmony_ci	if (IS_ERR(rpmsg_class)) {
72062306a36Sopenharmony_ci		pr_err("failed to create rpmsg class\n");
72162306a36Sopenharmony_ci		return PTR_ERR(rpmsg_class);
72262306a36Sopenharmony_ci	}
72362306a36Sopenharmony_ci
72462306a36Sopenharmony_ci	ret = bus_register(&rpmsg_bus);
72562306a36Sopenharmony_ci	if (ret) {
72662306a36Sopenharmony_ci		pr_err("failed to register rpmsg bus: %d\n", ret);
72762306a36Sopenharmony_ci		class_destroy(rpmsg_class);
72862306a36Sopenharmony_ci	}
72962306a36Sopenharmony_ci	return ret;
73062306a36Sopenharmony_ci}
73162306a36Sopenharmony_cipostcore_initcall(rpmsg_init);
73262306a36Sopenharmony_ci
73362306a36Sopenharmony_cistatic void __exit rpmsg_fini(void)
73462306a36Sopenharmony_ci{
73562306a36Sopenharmony_ci	bus_unregister(&rpmsg_bus);
73662306a36Sopenharmony_ci	class_destroy(rpmsg_class);
73762306a36Sopenharmony_ci}
73862306a36Sopenharmony_cimodule_exit(rpmsg_fini);
73962306a36Sopenharmony_ci
74062306a36Sopenharmony_ciMODULE_DESCRIPTION("remote processor messaging bus");
74162306a36Sopenharmony_ciMODULE_LICENSE("GPL v2");
742