summaryrefslogtreecommitdiff
path: root/drivers/tty/serial/serial_mctrl_gpio.c
diff options
context:
space:
mode:
Diffstat (limited to 'drivers/tty/serial/serial_mctrl_gpio.c')
-rw-r--r--drivers/tty/serial/serial_mctrl_gpio.c180
1 files changed, 139 insertions, 41 deletions
diff --git a/drivers/tty/serial/serial_mctrl_gpio.c b/drivers/tty/serial/serial_mctrl_gpio.c
index 39ed56214cd3..7b02c5ca4afd 100644
--- a/drivers/tty/serial/serial_mctrl_gpio.c
+++ b/drivers/tty/serial/serial_mctrl_gpio.c
@@ -12,6 +12,7 @@
#include <linux/termios.h>
#include <linux/serial_core.h>
#include <linux/module.h>
+#include <linux/property.h>
#include "serial_mctrl_gpio.h"
@@ -26,16 +27,28 @@ struct mctrl_gpios {
static const struct {
const char *name;
unsigned int mctrl;
- bool dir_out;
+ enum gpiod_flags flags;
} mctrl_gpios_desc[UART_GPIO_MAX] = {
- { "cts", TIOCM_CTS, false, },
- { "dsr", TIOCM_DSR, false, },
- { "dcd", TIOCM_CD, false, },
- { "rng", TIOCM_RNG, false, },
- { "rts", TIOCM_RTS, true, },
- { "dtr", TIOCM_DTR, true, },
+ { "cts", TIOCM_CTS, GPIOD_IN, },
+ { "dsr", TIOCM_DSR, GPIOD_IN, },
+ { "dcd", TIOCM_CD, GPIOD_IN, },
+ { "rng", TIOCM_RNG, GPIOD_IN, },
+ { "rts", TIOCM_RTS, GPIOD_OUT_LOW, },
+ { "dtr", TIOCM_DTR, GPIOD_OUT_LOW, },
};
+static bool mctrl_gpio_flags_is_dir_out(unsigned int idx)
+{
+ return mctrl_gpios_desc[idx].flags & GPIOD_FLAGS_BIT_DIR_OUT;
+}
+
+/**
+ * mctrl_gpio_set - set gpios according to mctrl state
+ * @gpios: gpios to set
+ * @mctrl: state to set
+ *
+ * Set the gpios according to the mctrl state.
+ */
void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
{
enum mctrl_gpio_idx i;
@@ -47,7 +60,7 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
return;
for (i = 0; i < UART_GPIO_MAX; i++)
- if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
+ if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
desc_array[count] = gpios->gpio[i];
__assign_bit(count, values,
mctrl & mctrl_gpios_desc[i].mctrl);
@@ -57,13 +70,30 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
}
EXPORT_SYMBOL_GPL(mctrl_gpio_set);
+/**
+ * mctrl_gpio_to_gpiod - obtain gpio_desc of modem line index
+ * @gpios: gpios to look into
+ * @gidx: index of the modem line
+ * Returns: the gpio_desc structure associated to the modem line index
+ */
struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
enum mctrl_gpio_idx gidx)
{
+ if (gpios == NULL)
+ return NULL;
+
return gpios->gpio[gidx];
}
EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
+/**
+ * mctrl_gpio_get - update mctrl with the gpios values.
+ * @gpios: gpios to get the info from
+ * @mctrl: mctrl to set
+ * Returns: modified mctrl (the same value as in @mctrl)
+ *
+ * Update mctrl with the gpios values.
+ */
unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
{
enum mctrl_gpio_idx i;
@@ -72,7 +102,7 @@ unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
return *mctrl;
for (i = 0; i < UART_GPIO_MAX; i++) {
- if (gpios->gpio[i] && !mctrl_gpios_desc[i].dir_out) {
+ if (gpios->gpio[i] && !mctrl_gpio_flags_is_dir_out(i)) {
if (gpiod_get_value(gpios->gpio[i]))
*mctrl |= mctrl_gpios_desc[i].mctrl;
else
@@ -93,7 +123,7 @@ mctrl_gpio_get_outputs(struct mctrl_gpios *gpios, unsigned int *mctrl)
return *mctrl;
for (i = 0; i < UART_GPIO_MAX; i++) {
- if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
+ if (gpios->gpio[i] && mctrl_gpio_flags_is_dir_out(i)) {
if (gpiod_get_value(gpios->gpio[i]))
*mctrl |= mctrl_gpios_desc[i].mctrl;
else
@@ -115,17 +145,25 @@ struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
return ERR_PTR(-ENOMEM);
for (i = 0; i < UART_GPIO_MAX; i++) {
- enum gpiod_flags flags;
+ char *gpio_str;
+ bool present;
- if (mctrl_gpios_desc[i].dir_out)
- flags = GPIOD_OUT_LOW;
- else
- flags = GPIOD_IN;
+ /* Check if GPIO property exists and continue if not */
+ gpio_str = kasprintf(GFP_KERNEL, "%s-gpios",
+ mctrl_gpios_desc[i].name);
+ if (!gpio_str)
+ continue;
+
+ present = device_property_present(dev, gpio_str);
+ kfree(gpio_str);
+ if (!present)
+ continue;
gpios->gpio[i] =
devm_gpiod_get_index_optional(dev,
mctrl_gpios_desc[i].name,
- idx, flags);
+ idx,
+ mctrl_gpios_desc[i].flags);
if (IS_ERR(gpios->gpio[i]))
return ERR_CAST(gpios->gpio[i]);
@@ -146,7 +184,7 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context)
mctrl_gpio_get(gpios, &mctrl);
- spin_lock_irqsave(&port->lock, flags);
+ uart_port_lock_irqsave(port, &flags);
mctrl_diff = mctrl ^ gpios->mctrl_prev;
gpios->mctrl_prev = mctrl;
@@ -167,11 +205,22 @@ static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context)
wake_up_interruptible(&port->state->port.delta_msr_wait);
}
- spin_unlock_irqrestore(&port->lock, flags);
+ uart_port_unlock_irqrestore(port, flags);
return IRQ_HANDLED;
}
+/**
+ * mctrl_gpio_init - initialize uart gpios
+ * @port: port to initialize gpios for
+ * @idx: index of the gpio in the @port's device
+ *
+ * This will get the {cts,rts,...}-gpios from device tree if they are present
+ * and request them, set direction etc, and return an allocated structure.
+ * `devm_*` functions are used, so there's no need to explicitly free.
+ * As this sets up the irq handling, make sure to not handle changes to the
+ * gpio input lines in your driver, too.
+ */
struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
{
struct mctrl_gpios *gpios;
@@ -186,11 +235,11 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
for (i = 0; i < UART_GPIO_MAX; ++i) {
int ret;
- if (!gpios->gpio[i] || mctrl_gpios_desc[i].dir_out)
+ if (!gpios->gpio[i] || mctrl_gpio_flags_is_dir_out(i))
continue;
ret = gpiod_to_irq(gpios->gpio[i]);
- if (ret <= 0) {
+ if (ret < 0) {
dev_err(port->dev,
"failed to find corresponding irq for %s (idx=%d, err=%d)\n",
mctrl_gpios_desc[i].name, idx, ret);
@@ -218,24 +267,10 @@ struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
}
EXPORT_SYMBOL_GPL(mctrl_gpio_init);
-void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
-{
- enum mctrl_gpio_idx i;
-
- if (gpios == NULL)
- return;
-
- for (i = 0; i < UART_GPIO_MAX; i++) {
- if (gpios->irq[i])
- devm_free_irq(gpios->port->dev, gpios->irq[i], gpios);
-
- if (gpios->gpio[i])
- devm_gpiod_put(dev, gpios->gpio[i]);
- }
- devm_kfree(dev, gpios);
-}
-EXPORT_SYMBOL_GPL(mctrl_gpio_free);
-
+/**
+ * mctrl_gpio_enable_ms - enable irqs and handling of changes to the ms lines
+ * @gpios: gpios to enable
+ */
void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios)
{
enum mctrl_gpio_idx i;
@@ -261,7 +296,7 @@ void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios)
}
EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms);
-void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
+static void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios, bool sync)
{
enum mctrl_gpio_idx i;
@@ -277,9 +312,72 @@ void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
if (!gpios->irq[i])
continue;
- disable_irq(gpios->irq[i]);
+ if (sync)
+ disable_irq(gpios->irq[i]);
+ else
+ disable_irq_nosync(gpios->irq[i]);
+ }
+}
+
+/**
+ * mctrl_gpio_disable_ms_sync - disable irqs and handling of changes to the ms
+ * lines, and wait for any pending IRQ to be processed
+ * @gpios: gpios to disable
+ */
+void mctrl_gpio_disable_ms_sync(struct mctrl_gpios *gpios)
+{
+ mctrl_gpio_disable_ms(gpios, true);
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms_sync);
+
+/**
+ * mctrl_gpio_disable_ms_no_sync - disable irqs and handling of changes to the
+ * ms lines, and return immediately
+ * @gpios: gpios to disable
+ */
+void mctrl_gpio_disable_ms_no_sync(struct mctrl_gpios *gpios)
+{
+ mctrl_gpio_disable_ms(gpios, false);
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms_no_sync);
+
+void mctrl_gpio_enable_irq_wake(struct mctrl_gpios *gpios)
+{
+ enum mctrl_gpio_idx i;
+
+ if (!gpios)
+ return;
+
+ if (!gpios->mctrl_on)
+ return;
+
+ for (i = 0; i < UART_GPIO_MAX; ++i) {
+ if (!gpios->irq[i])
+ continue;
+
+ enable_irq_wake(gpios->irq[i]);
+ }
+}
+EXPORT_SYMBOL_GPL(mctrl_gpio_enable_irq_wake);
+
+void mctrl_gpio_disable_irq_wake(struct mctrl_gpios *gpios)
+{
+ enum mctrl_gpio_idx i;
+
+ if (!gpios)
+ return;
+
+ if (!gpios->mctrl_on)
+ return;
+
+ for (i = 0; i < UART_GPIO_MAX; ++i) {
+ if (!gpios->irq[i])
+ continue;
+
+ disable_irq_wake(gpios->irq[i]);
}
}
-EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms);
+EXPORT_SYMBOL_GPL(mctrl_gpio_disable_irq_wake);
+MODULE_DESCRIPTION("Helpers for controlling modem lines via GPIO");
MODULE_LICENSE("GPL");