X-Git-Url: https://gerrit.opnfv.org/gerrit/gitweb?a=blobdiff_plain;f=kernel%2Fdrivers%2Ftty%2Fserial%2Fserial_mctrl_gpio.c;h=3eb57eb532f18a6af5fc169b044699a453e59975;hb=e09b41010ba33a20a87472ee821fa407a5b8da36;hp=0ec756c62bcf1f859a7692c9cf2fe580cff8b56f;hpb=9ca8dbcc65cfc63d6f5ef3312a33184e1d726e00;p=kvmfornfv.git diff --git a/kernel/drivers/tty/serial/serial_mctrl_gpio.c b/kernel/drivers/tty/serial/serial_mctrl_gpio.c index 0ec756c62..3eb57eb53 100644 --- a/kernel/drivers/tty/serial/serial_mctrl_gpio.c +++ b/kernel/drivers/tty/serial/serial_mctrl_gpio.c @@ -12,18 +12,23 @@ * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. - * */ #include #include +#include #include #include +#include #include "serial_mctrl_gpio.h" struct mctrl_gpios { + struct uart_port *port; struct gpio_desc *gpio[UART_GPIO_MAX]; + int irq[UART_GPIO_MAX]; + unsigned int mctrl_prev; + bool mctrl_on; }; static const struct { @@ -49,13 +54,12 @@ void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl) unsigned int count = 0; for (i = 0; i < UART_GPIO_MAX; i++) - if (!IS_ERR_OR_NULL(gpios->gpio[i]) && - mctrl_gpios_desc[i].dir_out) { + if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) { desc_array[count] = gpios->gpio[i]; value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl); count++; } - gpiod_set_array(count, desc_array, value_array); + gpiod_set_array_value(count, desc_array, value_array); } EXPORT_SYMBOL_GPL(mctrl_gpio_set); @@ -83,7 +87,7 @@ unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl) } EXPORT_SYMBOL_GPL(mctrl_gpio_get); -struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) +struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx) { struct mctrl_gpios *gpios; enum mctrl_gpio_idx i; @@ -111,15 +115,135 @@ struct mctrl_gpios *mctrl_gpio_init(struct device *dev, unsigned int idx) return gpios; } -EXPORT_SYMBOL_GPL(mctrl_gpio_init); +EXPORT_SYMBOL_GPL(mctrl_gpio_init_noauto); + +#define MCTRL_ANY_DELTA (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS) +static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context) +{ + struct mctrl_gpios *gpios = context; + struct uart_port *port = gpios->port; + u32 mctrl = gpios->mctrl_prev; + u32 mctrl_diff; + + mctrl_gpio_get(gpios, &mctrl); + + mctrl_diff = mctrl ^ gpios->mctrl_prev; + gpios->mctrl_prev = mctrl; + + if (mctrl_diff & MCTRL_ANY_DELTA && port->state != NULL) { + if ((mctrl_diff & mctrl) & TIOCM_RI) + port->icount.rng++; + + if ((mctrl_diff & mctrl) & TIOCM_DSR) + port->icount.dsr++; + + if (mctrl_diff & TIOCM_CD) + uart_handle_dcd_change(port, mctrl & TIOCM_CD); + + if (mctrl_diff & TIOCM_CTS) + uart_handle_cts_change(port, mctrl & TIOCM_CTS); + + wake_up_interruptible(&port->state->port.delta_msr_wait); + } + + return IRQ_HANDLED; +} + +struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx) +{ + struct mctrl_gpios *gpios; + enum mctrl_gpio_idx i; + + gpios = mctrl_gpio_init_noauto(port->dev, idx); + if (IS_ERR(gpios)) + return gpios; + + gpios->port = port; + + for (i = 0; i < UART_GPIO_MAX; ++i) { + int ret; + + if (!gpios->gpio[i] || mctrl_gpios_desc[i].dir_out) + continue; + + ret = gpiod_to_irq(gpios->gpio[i]); + 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); + return ERR_PTR(ret); + } + gpios->irq[i] = ret; + + /* irqs should only be enabled in .enable_ms */ + irq_set_status_flags(gpios->irq[i], IRQ_NOAUTOEN); + + ret = devm_request_irq(port->dev, gpios->irq[i], + mctrl_gpio_irq_handle, + IRQ_TYPE_EDGE_BOTH, dev_name(port->dev), + gpios); + if (ret) { + /* alternatively implement polling */ + dev_err(port->dev, + "failed to request irq for %s (idx=%d, err=%d)\n", + mctrl_gpios_desc[i].name, idx, ret); + return ERR_PTR(ret); + } + } + + return gpios; +} void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios) { enum mctrl_gpio_idx i; - for (i = 0; i < UART_GPIO_MAX; i++) - if (!IS_ERR_OR_NULL(gpios->gpio[i])) + 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); + +void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios) +{ + enum mctrl_gpio_idx i; + + /* .enable_ms may be called multiple times */ + if (gpios->mctrl_on) + return; + + gpios->mctrl_on = true; + + /* get initial status of modem lines GPIOs */ + mctrl_gpio_get(gpios, &gpios->mctrl_prev); + + for (i = 0; i < UART_GPIO_MAX; ++i) { + if (!gpios->irq[i]) + continue; + + enable_irq(gpios->irq[i]); + } +} +EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms); + +void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios) +{ + enum mctrl_gpio_idx i; + + if (!gpios->mctrl_on) + return; + + gpios->mctrl_on = false; + + for (i = 0; i < UART_GPIO_MAX; ++i) { + if (!gpios->irq[i]) + continue; + + disable_irq(gpios->irq[i]); + } +}