mirror of
https://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux.git
synced 2026-08-31 03:35:32 -04:00
serial: max310x: register GPIO controller before adding UART ports
The MAX310x exposes four GPIOs per UART port via an in-driver
gpio_chip. devm_gpiochip_add_data() used to run after the per-port
uart_add_one_port() loop, so a device-tree consumer referencing one of
the chip's own GPIOs (for example rs485-term-gpios = <&max310x 0 ...>)
could not resolve it during port registration: the GPIO provider it
waits for is the very driver still trying to register, and the lookup
returns -EPROBE_DEFER on its own provider, deferring probe forever.
Split the per-port setup into two passes around the gpio_chip
registration:
1. Initialise per-port state - port struct fields, regmap binding,
IRQ disable, work queues. The gpio_chip callbacks dereference
s->p[i].regmap via to_max310x_port() and become callable as soon
as the chip is visible to gpiolib, so every entry must be
populated first.
2. devm_gpiochip_add_data() - register the gpio_chip.
3. Allocate a line, uart_add_one_port(), set_bit(), max310x_power().
Keeping line allocation, registration and set_bit() together
preserves the existing "bit set <=> port registered" rollback
invariant that out_uart relies on.
Signed-off-by: Tapio Reijonen <tapio.reijonen@vaisala.com>
Link: https://patch.msgid.link/20260615-b4-max310x-rs485-dt-v3-1-7e79f064bdd7@vaisala.com
Signed-off-by: Greg Kroah-Hartman <gregkh@linuxfoundation.org>
This commit is contained in:
committed by
Greg Kroah-Hartman
parent
de96e8b27b
commit
25b51d1fd3
@@ -1390,17 +1390,12 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
|
||||
|
||||
dev_dbg(dev, "Reference clock set to %i Hz\n", uartclk);
|
||||
|
||||
/*
|
||||
* Set up each port's state before registering the gpiochip,
|
||||
* since the gpiochip callbacks will read s->p[i].regmap as
|
||||
* soon as gpiolib exposes the controller.
|
||||
*/
|
||||
for (i = 0; i < devtype->nr; i++) {
|
||||
unsigned int line;
|
||||
|
||||
line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX);
|
||||
if (line == MAX310X_UART_NRMAX) {
|
||||
ret = -ERANGE;
|
||||
goto out_uart;
|
||||
}
|
||||
|
||||
/* Initialize port data */
|
||||
s->p[i].port.line = line;
|
||||
s->p[i].port.dev = dev;
|
||||
s->p[i].port.irq = irq;
|
||||
s->p[i].port.type = PORT_MAX310X;
|
||||
@@ -1430,20 +1425,16 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
|
||||
INIT_WORK(&s->p[i].md_work, max310x_md_proc);
|
||||
/* Initialize queue for changing RS485 mode */
|
||||
INIT_WORK(&s->p[i].rs_work, max310x_rs_proc);
|
||||
|
||||
/* Register port */
|
||||
ret = uart_add_one_port(&max310x_uart, &s->p[i].port);
|
||||
if (ret)
|
||||
goto out_uart;
|
||||
|
||||
set_bit(line, max310x_lines);
|
||||
|
||||
/* Go to suspend mode */
|
||||
max310x_power(&s->p[i].port, 0);
|
||||
}
|
||||
|
||||
#ifdef CONFIG_GPIOLIB
|
||||
/* Setup GPIO controller */
|
||||
/*
|
||||
* Register the GPIO controller before adding the UART ports so
|
||||
* that consumers referencing the chip's own GPIOs from device
|
||||
* tree (for example rs485-term-gpios = <&max310x ...>) can
|
||||
* resolve them at uart_add_one_port() time instead of receiving
|
||||
* -EPROBE_DEFER from their own provider.
|
||||
*/
|
||||
s->gpio.owner = THIS_MODULE;
|
||||
s->gpio.parent = dev;
|
||||
s->gpio.label = devtype->name;
|
||||
@@ -1460,6 +1451,27 @@ static int max310x_probe(struct device *dev, const struct max310x_devtype *devty
|
||||
goto out_uart;
|
||||
#endif
|
||||
|
||||
for (i = 0; i < devtype->nr; i++) {
|
||||
unsigned int line;
|
||||
|
||||
line = find_first_zero_bit(max310x_lines, MAX310X_UART_NRMAX);
|
||||
if (line == MAX310X_UART_NRMAX) {
|
||||
ret = -ERANGE;
|
||||
goto out_uart;
|
||||
}
|
||||
s->p[i].port.line = line;
|
||||
|
||||
/* Register port */
|
||||
ret = uart_add_one_port(&max310x_uart, &s->p[i].port);
|
||||
if (ret)
|
||||
goto out_uart;
|
||||
|
||||
set_bit(line, max310x_lines);
|
||||
|
||||
/* Go to suspend mode */
|
||||
max310x_power(&s->p[i].port, 0);
|
||||
}
|
||||
|
||||
/* Setup interrupt */
|
||||
ret = devm_request_threaded_irq(dev, irq, NULL, max310x_ist,
|
||||
IRQF_ONESHOT | IRQF_SHARED, dev_name(dev), s);
|
||||
|
||||
Reference in New Issue
Block a user