Commit 4e489a6e authored by Bartosz Golaszewski's avatar Bartosz Golaszewski Committed by Greg Kroah-Hartman

serial: st-asc: don't get/put GPIOs in atomic context

Since commit 1f2bcb8c ("gpio: protect the descriptor label with
SRCU") gpiod_set_consumer_name() calls synchronize_srcu() which led to
a "sleeping in atomic context" smatch warning.

This function (along with gpiod_get/put() and all other GPIO APIs apart
from gpiod_get/set_value() and gpiod_direction_input/output()) should
have never been called with a spinlock taken. We're only fixing this now
as GPIOLIB has been rebuilt to use SRCU for access serialization which
uncovered this problem.

Move the calls to gpiod_get/put() outside the spinlock critical section.
Reported-by: default avatarDan Carpenter <dan.carpenter@linaro.org>
Closes: https://lore.kernel.org/linux-gpio/deee1438-efc1-47c4-8d80-0ab2cf01d60a@moroto.mountain/Signed-off-by: default avatarBartosz Golaszewski <bartosz.golaszewski@linaro.org>
Reviewed-by: default avatarPatrice Chotard <patrice.chotard@foss.st.com>
Link: https://lore.kernel.org/r/20240220113410.16613-1-brgl@bgdev.plSigned-off-by: default avatarGreg Kroah-Hartman <gregkh@linuxfoundation.org>
parent b8a4ed34
......@@ -465,6 +465,7 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios,
const struct ktermios *old)
{
struct asc_port *ascport = to_asc_port(port);
bool manual_rts, toggle_rts = false;
struct gpio_desc *gpiod;
unsigned int baud;
u32 ctrl_val;
......@@ -518,25 +519,13 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios,
/* If flow-control selected, stop handling RTS manually */
if (ascport->rts) {
devm_gpiod_put(port->dev, ascport->rts);
ascport->rts = NULL;
pinctrl_select_state(ascport->pinctrl,
ascport->states[DEFAULT]);
toggle_rts = true;
manual_rts = false;
}
} else {
/* If flow-control disabled, it's safe to handle RTS manually */
if (!ascport->rts && ascport->states[NO_HW_FLOWCTRL]) {
pinctrl_select_state(ascport->pinctrl,
ascport->states[NO_HW_FLOWCTRL]);
gpiod = devm_gpiod_get(port->dev, "rts", GPIOD_OUT_LOW);
if (!IS_ERR(gpiod)) {
gpiod_set_consumer_name(gpiod,
port->dev->of_node->name);
ascport->rts = gpiod;
}
}
if (!ascport->rts && ascport->states[NO_HW_FLOWCTRL])
manual_rts = toggle_rts = true;
}
if ((baud < 19200) && !ascport->force_m1) {
......@@ -595,6 +584,25 @@ static void asc_set_termios(struct uart_port *port, struct ktermios *termios,
asc_out(port, ASC_CTL, (ctrl_val | ASC_CTL_RUN));
uart_port_unlock_irqrestore(port, flags);
if (toggle_rts) {
if (manual_rts) {
pinctrl_select_state(ascport->pinctrl,
ascport->states[NO_HW_FLOWCTRL]);
gpiod = devm_gpiod_get(port->dev, "rts", GPIOD_OUT_LOW);
if (!IS_ERR(gpiod)) {
gpiod_set_consumer_name(gpiod,
port->dev->of_node->name);
ascport->rts = gpiod;
}
} else {
devm_gpiod_put(port->dev, ascport->rts);
ascport->rts = NULL;
pinctrl_select_state(ascport->pinctrl,
ascport->states[DEFAULT]);
}
}
}
static const char *asc_type(struct uart_port *port)
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment