lists.openwall.net   lists  /  announce  owl-users  owl-dev  john-users  john-dev  passwdqc-users  yescrypt  popa3d-users  /  oss-security  kernel-hardening  musl  sabotage  tlsify  passwords  /  crypt-dev  xvendor  /  Bugtraq  Full-Disclosure  linux-kernel  linux-netdev  linux-ext4  linux-hardening  linux-cve-announce  PHC 
Open Source and information security mailing list archives
 
Hash Suite: Windows password security audit tool. GUI, reports in PDF.
[<prev] [next>] [<thread-prev] [day] [month] [year] [list]
Message-ID: <20250929033713.GC29690@nxa18884-linux.ap.freescale.net>
Date: Mon, 29 Sep 2025 11:37:13 +0800
From: Peng Fan <peng.fan@....nxp.com>
To: Shenwei Wang <shenwei.wang@....com>
Cc: Bjorn Andersson <andersson@...nel.org>,
	Mathieu Poirier <mathieu.poirier@...aro.org>,
	Rob Herring <robh@...nel.org>,
	Krzysztof Kozlowski <krzk+dt@...nel.org>,
	Conor Dooley <conor+dt@...nel.org>, Shawn Guo <shawnguo@...nel.org>,
	Sascha Hauer <s.hauer@...gutronix.de>,
	Linus Walleij <linus.walleij@...aro.org>,
	Bartosz Golaszewski <brgl@...ev.pl>,
	Pengutronix Kernel Team <kernel@...gutronix.de>,
	Fabio Estevam <festevam@...il.com>, Peng Fan <peng.fan@....com>,
	linux-remoteproc@...r.kernel.org, devicetree@...r.kernel.org,
	imx@...ts.linux.dev, linux-arm-kernel@...ts.infradead.org,
	linux-kernel@...r.kernel.org, linux-imx@....com
Subject: Re: [PATCH v2 3/4] gpio: imx-rpmsg: add imx-rpmsg GPIO driver

Hi Shenwei,

On Mon, Sep 22, 2025 at 03:04:12PM -0500, Shenwei Wang wrote:
>On i.MX SoCs, the system may include two processors:
>	- An MCU running an RTOS
>	- An MPU running Linux
>
>These processors communicate via the RPMSG protocol.
>The driver implements the standard GPIO interface, allowing
>the Linux side to control GPIO controllers which reside in
>the remote processor via RPMSG protocol.
>+ */
...
>+
>+#include <linux/err.h>
>+#include <linux/gpio/driver.h>
>+#include <linux/rpmsg/imx_rpmsg.h>
>+#include <linux/init.h>
>+#include <linux/irqdomain.h>
>+#include <linux/of.h>
>+#include <linux/platform_device.h>
>+#include <linux/rpmsg.h>

The included headers should be sorted.

>+
>+#define IMX_RPMSG_GPIO_PER_PORT	32
>+#define RPMSG_TIMEOUT	1000
>+
>+enum gpio_input_trigger_type {
>+	GPIO_RPMSG_TRI_IGNORE,
>+	GPIO_RPMSG_TRI_RISING,
>+	GPIO_RPMSG_TRI_FALLING,
>+	GPIO_RPMSG_TRI_BOTH_EDGE,
>+	GPIO_RPMSG_TRI_LOW_LEVEL,
>+	GPIO_RPMSG_TRI_HIGH_LEVEL,
>+};
>+
>+enum gpio_rpmsg_header_type {
>+	GPIO_RPMSG_SETUP,
>+	GPIO_RPMSG_REPLY,
>+	GPIO_RPMSG_NOTIFY,
>+};
>+
>+enum gpio_rpmsg_header_cmd {
>+	GPIO_RPMSG_INPUT_INIT,
>+	GPIO_RPMSG_OUTPUT_INIT,
>+	GPIO_RPMSG_INPUT_GET,
>+};
>+
>+struct gpio_rpmsg_data {
>+	struct imx_rpmsg_head header;
>+	u8 pin_idx;
>+	u8 port_idx;
>+	union {
>+		u8 event;
>+		u8 retcode;
>+		u8 value;
>+	} out;
>+	union {
>+		u8 wakeup;
>+		u8 value;
>+	} in;
>+} __packed __aligned(8);
>+
>+struct imx_rpmsg_gpio_pin {
>+	u8 irq_shutdown;
>+	u8 irq_unmask;
>+	u8 irq_mask;
>+	u32 irq_wake_enable;
>+	u32 irq_type;
>+	struct gpio_rpmsg_data msg;
>+};
>+
>+struct imx_gpio_rpmsg_info {
>+	struct rpmsg_device *rpdev;
>+	struct gpio_rpmsg_data *notify_msg;
>+	struct gpio_rpmsg_data *reply_msg;
>+	struct completion cmd_complete;
>+	struct mutex lock;
>+	void **port_store;
>+};
>+
>+struct imx_rpmsg_gpio_port {
>+	struct gpio_chip gc;
>+	struct imx_rpmsg_gpio_pin gpio_pins[IMX_RPMSG_GPIO_PER_PORT];
>+	struct imx_gpio_rpmsg_info info;
>+	int idx;
>+};
>+
>+static int gpio_send_message(struct imx_rpmsg_gpio_port *port,
>+			     struct gpio_rpmsg_data *msg,
>+			     bool sync)
>+{
>+	struct imx_gpio_rpmsg_info *info = &port->info;
>+	int err;
>+
>+	if (!info->rpdev) {
>+		dev_dbg(&info->rpdev->dev,
>+			"rpmsg channel not ready, m4 image ready?\n");

"m4 image" -> "remote core" 

>+		return -EINVAL;
>+	}
>+
>+	reinit_completion(&info->cmd_complete);
>+	err = rpmsg_send(info->rpdev->ept, (void *)msg,
>+			 sizeof(struct gpio_rpmsg_data));
>+	if (err) {
>+		dev_err(&info->rpdev->dev, "rpmsg_send failed: %d\n", err);
>+		return err;
>+	}
>+
>+	if (sync) {
>+		err = wait_for_completion_timeout(&info->cmd_complete,
>+						  msecs_to_jiffies(RPMSG_TIMEOUT));
>+		if (!err) {
>+			dev_err(&info->rpdev->dev, "rpmsg_send timeout!\n");
>+			return -ETIMEDOUT;
>+		}
>+
>+		if (info->reply_msg->out.retcode != 0) {
>+			dev_err(&info->rpdev->dev, "rpmsg not ack %d!\n",
>+				info->reply_msg->out.retcode);
>+			return -EINVAL;
>+		}
>+
>+		/* copy the reply message */
>+		memcpy(&port->gpio_pins[info->reply_msg->pin_idx].msg,
>+		       info->reply_msg, sizeof(*info->reply_msg));
>+	}
>+
>+	return 0;
>+}
>+
>+static struct gpio_rpmsg_data *gpio_get_pin_msg(struct imx_rpmsg_gpio_port *port,
>+						unsigned int offset)
>+{
>+	struct gpio_rpmsg_data *msg = &port->gpio_pins[offset].msg;
>+
>+	memset(msg, 0, sizeof(struct gpio_rpmsg_data));
>+
>+	return msg;
>+};
>+
>+static int imx_rpmsg_gpio_get(struct gpio_chip *gc, unsigned int gpio)
>+{
>+	struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc);
>+	struct gpio_rpmsg_data *msg = NULL;
>+	int ret;
>+
>+	guard(mutex)(&port->info.lock);
>+
>+	msg = gpio_get_pin_msg(port, gpio);
>+	msg->header.cate = IMX_RPMSG_GPIO;
>+	msg->header.major = IMX_RMPSG_MAJOR;
>+	msg->header.minor = IMX_RMPSG_MINOR;
>+	msg->header.type = GPIO_RPMSG_SETUP;
>+	msg->header.cmd = GPIO_RPMSG_INPUT_GET;
>+	msg->pin_idx = gpio;
>+	msg->port_idx = port->idx;
>+
>+	ret = gpio_send_message(port, msg, true);
>+	if (!ret)
>+		ret = !!port->gpio_pins[gpio].msg.in.value;
>+
>+	return ret;
>+}
>+
>+static int imx_rpmsg_gpio_direction_input(struct gpio_chip *gc,
>+					  unsigned int gpio)
>+{
>+	struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc);
>+	struct gpio_rpmsg_data *msg = NULL;
>+
>+	guard(mutex)(&port->info.lock);
>+
>+	msg = gpio_get_pin_msg(port, gpio);
>+	msg->header.cate = IMX_RPMSG_GPIO;
>+	msg->header.major = IMX_RMPSG_MAJOR;
>+	msg->header.minor = IMX_RMPSG_MINOR;
>+	msg->header.type = GPIO_RPMSG_SETUP;
>+	msg->header.cmd = GPIO_RPMSG_INPUT_INIT;
>+	msg->pin_idx = gpio;
>+	msg->port_idx = port->idx;
>+
>+	msg->out.event = GPIO_RPMSG_TRI_IGNORE;
>+	msg->in.wakeup = 0;
>+
>+	return gpio_send_message(port, msg, true);
>+}
>+
>+static inline void imx_rpmsg_gpio_direction_output_init(struct gpio_chip *gc,
>+		unsigned int gpio, int val, struct gpio_rpmsg_data *msg)
>+{
>+	struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc);
>+
>+	msg->header.cate = IMX_RPMSG_GPIO;
>+	msg->header.major = IMX_RMPSG_MAJOR;
>+	msg->header.minor = IMX_RMPSG_MINOR;
>+	msg->header.type = GPIO_RPMSG_SETUP;
>+	msg->header.cmd = GPIO_RPMSG_OUTPUT_INIT;
>+	msg->pin_idx = gpio;
>+	msg->port_idx = port->idx;
>+	msg->out.value = val;
>+}
>+
>+static int imx_rpmsg_gpio_set(struct gpio_chip *gc, unsigned int gpio, int val)
>+{
>+	struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc);
>+	struct gpio_rpmsg_data *msg = NULL;
>+
>+	guard(mutex)(&port->info.lock);
>+
>+	msg = gpio_get_pin_msg(port, gpio);
>+	imx_rpmsg_gpio_direction_output_init(gc, gpio, val, msg);
>+
>+	return gpio_send_message(port, msg, true);
>+}
>+
>+static int imx_rpmsg_gpio_direction_output(struct gpio_chip *gc,
>+					unsigned int gpio, int val)
>+{
>+	struct imx_rpmsg_gpio_port *port = gpiochip_get_data(gc);
>+	struct gpio_rpmsg_data *msg = NULL;
>+
>+	guard(mutex)(&port->info.lock);
>+
>+	msg = gpio_get_pin_msg(port, gpio);
>+	imx_rpmsg_gpio_direction_output_init(gc, gpio, val, msg);
>+
>+	return gpio_send_message(port, msg, true);
>+}
>+
>+static int imx_rpmsg_irq_set_type(struct irq_data *d, u32 type)
>+{
>+	struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
>+	u32 gpio_idx = d->hwirq;
>+	int edge = 0;
>+	int ret = 0;
>+
>+	switch (type) {
>+	case IRQ_TYPE_EDGE_RISING:
>+		edge = GPIO_RPMSG_TRI_RISING;
>+		break;
>+	case IRQ_TYPE_EDGE_FALLING:
>+		edge = GPIO_RPMSG_TRI_FALLING;
>+		break;
>+	case IRQ_TYPE_EDGE_BOTH:
>+		edge = GPIO_RPMSG_TRI_BOTH_EDGE;
>+		break;
>+	case IRQ_TYPE_LEVEL_LOW:
>+		edge = GPIO_RPMSG_TRI_LOW_LEVEL;
>+		break;
>+	case IRQ_TYPE_LEVEL_HIGH:
>+		edge = GPIO_RPMSG_TRI_HIGH_LEVEL;
>+		break;
>+	default:
>+		ret = -EINVAL;
>+		break;
>+	}
>+
>+	port->gpio_pins[gpio_idx].irq_type = edge;
>+
>+	return ret;
>+}
>+
>+static int imx_rpmsg_irq_set_wake(struct irq_data *d, u32 enable)
>+{
>+	struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
>+	u32 gpio_idx = d->hwirq;
>+
>+	port->gpio_pins[gpio_idx].irq_wake_enable = enable;
>+
>+	return 0;
>+}
>+
>+/*
>+ * This function will be called at:
>+ *  - one interrupt setup.
>+ *  - the end of one interrupt happened
>+ * The gpio over rpmsg driver will not write the real register, so save
>+ * all infos before this function and then send all infos to M core in this
>+ * step.
>+ */
>+static void imx_rpmsg_unmask_irq(struct irq_data *d)
>+{
>+	struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
>+	u32 gpio_idx = d->hwirq;
>+
>+	port->gpio_pins[gpio_idx].irq_unmask = 1;
>+}
>+
>+static void imx_rpmsg_mask_irq(struct irq_data *d)
>+{
>+	struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
>+	u32 gpio_idx = d->hwirq;
>+	/*
>+	 * No need to implement the callback at A core side.
>+	 * M core will mask interrupt after a interrupt occurred, and then
>+	 * sends a notify to A core.
>+	 * After A core dealt with the notify, A core will send a rpmsg to
>+	 * M core to unmask this interrupt again.
>+	 */
>+	port->gpio_pins[gpio_idx].irq_mask = 1;
>+}
>+
>+static void imx_rpmsg_irq_shutdown(struct irq_data *d)
>+{
>+	struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
>+	u32 gpio_idx = d->hwirq;
>+
>+	port->gpio_pins[gpio_idx].irq_shutdown = 1;
>+}
>+
>+static void imx_rpmsg_irq_bus_lock(struct irq_data *d)
>+{
>+	struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
>+
>+	mutex_lock(&port->info.lock);
>+}
>+
>+static void imx_rpmsg_irq_bus_sync_unlock(struct irq_data *d)
>+{
>+	struct imx_rpmsg_gpio_port *port = irq_data_get_irq_chip_data(d);
>+	struct gpio_rpmsg_data *msg = NULL;
>+	u32 gpio_idx = d->hwirq;
>+
>+	if (port == NULL) {
>+		mutex_unlock(&port->info.lock);
>+		return;
>+	}
>+
>+	/*
>+	 * For mask irq, do nothing here.
>+	 * M core will mask interrupt after a interrupt occurred, and then
>+	 * sends a notify to A core.
>+	 * After A core dealt with the notify, A core will send a rpmsg to
>+	 * M core to unmask this interrupt again.
>+	 */
>+
>+	if (port->gpio_pins[gpio_idx].irq_mask && !port->gpio_pins[gpio_idx].irq_unmask) {
>+		port->gpio_pins[gpio_idx].irq_mask = 0;
>+		mutex_unlock(&port->info.lock);
>+		return;
>+	}
>+
>+	msg = gpio_get_pin_msg(port, gpio_idx);
>+	msg->header.cate = IMX_RPMSG_GPIO;
>+	msg->header.major = IMX_RMPSG_MAJOR;
>+	msg->header.minor = IMX_RMPSG_MINOR;
>+	msg->header.type = GPIO_RPMSG_SETUP;
>+	msg->header.cmd = GPIO_RPMSG_INPUT_INIT;
>+	msg->pin_idx = gpio_idx;
>+	msg->port_idx = port->idx;
>+
>+	if (port->gpio_pins[gpio_idx].irq_shutdown) {
>+		msg->out.event = GPIO_RPMSG_TRI_IGNORE;
>+		msg->in.wakeup = 0;
>+		port->gpio_pins[gpio_idx].irq_shutdown = 0;
>+	} else {
>+		 /* if not set irq type, then use low level as trigger type */
>+		msg->out.event = port->gpio_pins[gpio_idx].irq_type;
>+		if (!msg->out.event)
>+			msg->out.event = GPIO_RPMSG_TRI_LOW_LEVEL;
>+		if (port->gpio_pins[gpio_idx].irq_unmask) {
>+			msg->in.wakeup = 0;
>+			port->gpio_pins[gpio_idx].irq_unmask = 0;
>+		} else /* irq set wake */
>+			msg->in.wakeup = port->gpio_pins[gpio_idx].irq_wake_enable;
>+	}
>+
>+	gpio_send_message(port, msg, false);
>+	mutex_unlock(&port->info.lock);
>+}
>+
>+static const struct irq_chip imx_rpmsg_irq_chip = {
>+	.irq_mask = imx_rpmsg_mask_irq,
>+	.irq_unmask = imx_rpmsg_unmask_irq,
>+	.irq_set_wake = imx_rpmsg_irq_set_wake,
>+	.irq_set_type = imx_rpmsg_irq_set_type,
>+	.irq_shutdown = imx_rpmsg_irq_shutdown,
>+	.irq_bus_lock = imx_rpmsg_irq_bus_lock,
>+	.irq_bus_sync_unlock = imx_rpmsg_irq_bus_sync_unlock,
>+	.flags = IRQCHIP_IMMUTABLE,
>+};
>+
>+static int imx_rpmsg_gpio_callback(struct rpmsg_device *rpdev,
>+	void *data, int len, void *priv, u32 src)
>+{
>+	struct gpio_rpmsg_data *msg = (struct gpio_rpmsg_data *)data;
>+	struct imx_rpmsg_gpio_port *port = NULL;
>+	struct imx_rpmsg_driver_data *drvdata;
>+	unsigned long flags;
>+
>+	drvdata = dev_get_drvdata(&rpdev->dev);
>+	if (msg)
>+		port = drvdata->channel_devices[msg->port_idx];
>+
>+	if (!port)
>+		return -ENODEV;
>+
>+	if (msg->header.type == GPIO_RPMSG_REPLY) {
>+		port->info.reply_msg = msg;
>+		complete(&port->info.cmd_complete);
>+	} else if (msg->header.type == GPIO_RPMSG_NOTIFY) {
>+		port->info.notify_msg = msg;
>+		local_irq_save(flags);

Would you explain a bit on why need to disable IRQ on current core.

>+		generic_handle_domain_irq(port->gc.irq.domain, msg->pin_idx);
>+		local_irq_restore(flags);
>+	} else
>+		dev_err(&rpdev->dev, "wrong command type!\n");
>+
>+	return 0;
>+}
>+
>+static void imx_rpmsg_gpio_remove_action(void *data)
>+{
>+	struct imx_rpmsg_gpio_port *port = data;
>+
>+	port->info.port_store[port->idx] = 0;
>+}
>+
>+static int imx_rpmsg_gpio_probe(struct platform_device *pdev)
>+{
>+	struct imx_rpmsg_driver_data *pltdata = pdev->dev.platform_data;
>+	struct device_node *np = pdev->dev.of_node;
>+	struct imx_rpmsg_gpio_port *port;
>+	struct gpio_irq_chip *girq;
>+	struct gpio_chip *gc;
>+	int ret;
>+
>+	if (!pltdata)
>+		return -EPROBE_DEFER;
>+
>+	port = devm_kzalloc(&pdev->dev, sizeof(*port), GFP_KERNEL);
>+	if (!port)
>+		return -ENOMEM;
>+
>+	ret = of_property_read_u32(np, "reg", &port->idx);

"device_property_read_u32" should be better.

>+	if (ret)
>+		return ret;
>+
>+	if (port->idx > MAX_DEV_PER_CHANNEL)
>+		return -EINVAL;
>+
>+	mutex_init(&port->info.lock);
>+	init_completion(&port->info.cmd_complete);
>+	port->info.rpdev = pltdata->rpdev;
>+	port->info.port_store = pltdata->channel_devices;
>+	port->info.port_store[port->idx] = port;
>+	if (!pltdata->rx_callback)
>+		pltdata->rx_callback = imx_rpmsg_gpio_callback;
>+
>+	gc = &port->gc;
>+	gc->owner = THIS_MODULE;
>+	gc->parent = &pltdata->rpdev->dev;
>+	gc->label = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-gpio%d",
>+				   pltdata->rproc_name, port->idx);
>+	gc->ngpio = IMX_RPMSG_GPIO_PER_PORT;
>+	gc->base = -1;
>+
>+	gc->direction_input = imx_rpmsg_gpio_direction_input;
>+	gc->direction_output = imx_rpmsg_gpio_direction_output;
>+	gc->get = imx_rpmsg_gpio_get;
>+	gc->set = imx_rpmsg_gpio_set;
>+
>+	platform_set_drvdata(pdev, port);
>+	girq = &gc->irq;
>+	gpio_irq_chip_set_chip(girq, &imx_rpmsg_irq_chip);
>+	girq->parent_handler = NULL;
>+	girq->num_parents = 0;
>+	girq->parents = NULL;
>+	girq->chip->name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s-gpio%d",
>+				 pltdata->rproc_name, port->idx);

Align pltdata->rproc_name with the upper line '('.

>+
>+	devm_add_action_or_reset(&pdev->dev, imx_rpmsg_gpio_remove_action, port);

return value should be checked.

>+
>+	return devm_gpiochip_add_data(&pdev->dev, gc, port);
>+}
>+

Thanks,
Peng

Powered by blists - more mailing lists

Powered by Openwall GNU/*/Linux Powered by OpenVZ