A number of CPC features, like retransmission or remote's receive window, are cport-based. In order to prepare for these features, introduce a CPC CPort context structure.
The CPC Host Device module now implements cport_allocate and cport_release callbacks in order to allocate and release these structures when requested by Greybus module.
Signed-off-by: Damien Riégel damien.riegel@silabs.com --- drivers/greybus/cpc/Makefile | 2 +- drivers/greybus/cpc/cpc.h | 29 ++++++++++ drivers/greybus/cpc/cport.c | 37 ++++++++++++ drivers/greybus/cpc/host.c | 109 ++++++++++++++++++++++++++++++++++- drivers/greybus/cpc/host.h | 12 ++++ 5 files changed, 187 insertions(+), 2 deletions(-) create mode 100644 drivers/greybus/cpc/cpc.h create mode 100644 drivers/greybus/cpc/cport.c
diff --git a/drivers/greybus/cpc/Makefile b/drivers/greybus/cpc/Makefile index 490982a0ff5..3d50f8c5473 100644 --- a/drivers/greybus/cpc/Makefile +++ b/drivers/greybus/cpc/Makefile @@ -1,6 +1,6 @@ # SPDX-License-Identifier: GPL-2.0
-gb-cpc-y := host.o +gb-cpc-y := cport.o host.o
# CPC core obj-$(CONFIG_GREYBUS_CPC) += gb-cpc.o diff --git a/drivers/greybus/cpc/cpc.h b/drivers/greybus/cpc/cpc.h new file mode 100644 index 00000000000..85d02954307 --- /dev/null +++ b/drivers/greybus/cpc/cpc.h @@ -0,0 +1,29 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright (c) 2025, Silicon Laboratories, Inc. + */ + +#ifndef __CPC_H +#define __CPC_H + +#include <linux/device.h> +#include <linux/greybus.h> +#include <linux/types.h> + +/** + * struct cpc_cport - CPC cport + * @id: cport ID + * @cpc_hd: pointer to the CPC host device this cport belongs to + */ +struct cpc_cport { + u16 id; + + struct cpc_host_device *cpc_hd; +}; + +struct cpc_cport *cpc_cport_alloc(u16 cport_id, gfp_t gfp_mask); +void cpc_cport_release(struct cpc_cport *cport); + +int cpc_cport_message_send(struct cpc_cport *cport, struct gb_message *message, gfp_t gfp_mask); + +#endif diff --git a/drivers/greybus/cpc/cport.c b/drivers/greybus/cpc/cport.c new file mode 100644 index 00000000000..88bdb2f8182 --- /dev/null +++ b/drivers/greybus/cpc/cport.c @@ -0,0 +1,37 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Copyright (c) 2025, Silicon Laboratories, Inc. + */ + +#include "cpc.h" +#include "host.h" + +/** + * cpc_cport_alloc() - Allocate and initialize CPC cport. + * @cport_id: cport ID. + * @gfp_mask: GFP mask for allocation. + * + * Return: Pointer to allocated and initialized cpc_cport, or NULL on failure. + */ +struct cpc_cport *cpc_cport_alloc(u16 cport_id, gfp_t gfp_mask) +{ + struct cpc_cport *cport; + + cport = kzalloc(sizeof(*cport), gfp_mask); + if (!cport) + return NULL; + + cport->id = cport_id; + + return cport; +} + +void cpc_cport_release(struct cpc_cport *cport) +{ + kfree(cport); +} + +int cpc_cport_message_send(struct cpc_cport *cport, struct gb_message *message, gfp_t gfp_mask) +{ + return cport->cpc_hd->driver->message_send(cport->cpc_hd, cport->id, message, gfp_mask); +} diff --git a/drivers/greybus/cpc/host.c b/drivers/greybus/cpc/host.c index 1eb6c87e25f..033ff7f0184 100644 --- a/drivers/greybus/cpc/host.c +++ b/drivers/greybus/cpc/host.c @@ -7,6 +7,7 @@ #include <linux/greybus.h> #include <linux/module.h>
+#include "cpc.h" #include "host.h"
@@ -15,12 +16,95 @@ static struct cpc_host_device *gb_hd_to_cpc_hd(struct gb_host_device *hd) return (struct cpc_host_device *)&hd->hd_priv; }
+static struct cpc_cport *cpc_hd_get_cport(struct cpc_host_device *cpc_hd, u16 cport_id) +{ + struct cpc_cport *cport; + + mutex_lock(&cpc_hd->lock); + for (int i = 0; i < ARRAY_SIZE(cpc_hd->cports); i++) { + cport = cpc_hd->cports[i]; + if (cport && cport->id == cport_id) + goto unlock; + } + + cport = NULL; + +unlock: + mutex_unlock(&cpc_hd->lock); + + return cport; +} + +static int cpc_hd_message_send(struct cpc_host_device *cpc_hd, u16 cport_id, + struct gb_message *message, gfp_t gfp_mask) +{ + struct cpc_cport *cport; + + cport = cpc_hd_get_cport(cpc_hd, cport_id); + if (!cport) { + dev_err(cpc_hd_dev(cpc_hd), "message_send: cport %u not found\n", cport_id); + return -EINVAL; + } + + return cpc_cport_message_send(cport, message, gfp_mask); +} + +static int cpc_hd_cport_allocate(struct cpc_host_device *cpc_hd, int cport_id, unsigned long flags) +{ + struct cpc_cport *cport; + int ret; + + mutex_lock(&cpc_hd->lock); + for (int i = 0; i < ARRAY_SIZE(cpc_hd->cports); i++) { + if (cpc_hd->cports[i] != NULL) + continue; + + if (cport_id < 0) + cport_id = i; + + cport = cpc_cport_alloc(cport_id, GFP_KERNEL); + if (!cport) { + ret = -ENOMEM; + goto unlock; + } + + cport->cpc_hd = cpc_hd; + + cpc_hd->cports[i] = cport; + ret = cport_id; + goto unlock; + } + + ret = -ENOSPC; +unlock: + mutex_unlock(&cpc_hd->lock); + + return ret; +} + +static void cpc_hd_cport_release(struct cpc_host_device *cpc_hd, u16 cport_id) +{ + struct cpc_cport *cport; + + mutex_lock(&cpc_hd->lock); + for (int i = 0; i < ARRAY_SIZE(cpc_hd->cports); i++) { + cport = cpc_hd->cports[i]; + + if (cport && cport->id == cport_id) { + cpc_cport_release(cport); + cpc_hd->cports[i] = NULL; + break; + } + } + mutex_unlock(&cpc_hd->lock); +} + static int cpc_gb_message_send(struct gb_host_device *gb_hd, u16 cport_id, struct gb_message *message, gfp_t gfp_mask) { struct cpc_host_device *cpc_hd = gb_hd_to_cpc_hd(gb_hd);
- return cpc_hd->driver->message_send(cpc_hd, cport_id, message, gfp_mask); + return cpc_hd_message_send(cpc_hd, cport_id, message, gfp_mask); }
static void cpc_gb_message_cancel(struct gb_message *message) @@ -28,12 +112,33 @@ static void cpc_gb_message_cancel(struct gb_message *message) /* Not implemented */ }
+static int cpc_gb_cport_allocate(struct gb_host_device *gb_hd, int cport_id, unsigned long flags) +{ + struct cpc_host_device *cpc_hd = gb_hd_to_cpc_hd(gb_hd); + + return cpc_hd_cport_allocate(cpc_hd, cport_id, flags); +} + +static void cpc_gb_cport_release(struct gb_host_device *gb_hd, u16 cport_id) +{ + struct cpc_host_device *cpc_hd = gb_hd_to_cpc_hd(gb_hd); + + return cpc_hd_cport_release(cpc_hd, cport_id); +} + static struct gb_hd_driver cpc_gb_driver = { .hd_priv_size = sizeof(struct cpc_host_device), .message_send = cpc_gb_message_send, .message_cancel = cpc_gb_message_cancel, + .cport_allocate = cpc_gb_cport_allocate, + .cport_release = cpc_gb_cport_release, };
+static void cpc_hd_init(struct cpc_host_device *cpc_hd) +{ + mutex_init(&cpc_hd->lock); +} + struct cpc_host_device *cpc_hd_create(struct cpc_hd_driver *driver, struct device *parent) { struct cpc_host_device *cpc_hd; @@ -52,6 +157,8 @@ struct cpc_host_device *cpc_hd_create(struct cpc_hd_driver *driver, struct devic cpc_hd->gb_hd = hd; cpc_hd->driver = driver;
+ cpc_hd_init(cpc_hd); + return cpc_hd; } EXPORT_SYMBOL_GPL(cpc_hd_create); diff --git a/drivers/greybus/cpc/host.h b/drivers/greybus/cpc/host.h index fe07826b765..1c168cdd2bf 100644 --- a/drivers/greybus/cpc/host.h +++ b/drivers/greybus/cpc/host.h @@ -8,11 +8,13 @@
#include <linux/device.h> #include <linux/greybus.h> +#include <linux/mutex.h> #include <linux/types.h>
#define GB_CPC_MSG_SIZE_MAX 2048 #define GB_CPC_NUM_CPORTS 8
+struct cpc_cport; struct cpc_host_device;
struct cpc_hd_driver { @@ -25,12 +27,22 @@ struct cpc_hd_driver { * struct cpc_host_device - CPC host device. * @gb_hd: pointer to Greybus Host Device this device belongs to. * @driver: driver operations. + * @lock: mutex to synchronize access to cport array. + * @cports: array of cport pointers allocated by Greybus core. */ struct cpc_host_device { struct gb_host_device *gb_hd; const struct cpc_hd_driver *driver; + + struct mutex lock; /* Synchronize access to cports */ + struct cpc_cport *cports[GB_CPC_NUM_CPORTS]; };
+static inline struct device *cpc_hd_dev(struct cpc_host_device *cpc_hd) +{ + return &cpc_hd->gb_hd->dev; +} + struct cpc_host_device *cpc_hd_create(struct cpc_hd_driver *driver, struct device *parent); int cpc_hd_add(struct cpc_host_device *cpc_hd); void cpc_hd_put(struct cpc_host_device *cpc_hd);