linux/drivers/tty/serial/serial_mctrl_gpio.c
<<
>>
Prefs
   1/*
   2 * Helpers for controlling modem lines via GPIO
   3 *
   4 * Copyright (C) 2014 Paratronic S.A.
   5 *
   6 * This program is free software; you can redistribute it and/or modify
   7 * it under the terms of the GNU General Public License as published by
   8 * the Free Software Foundation; either version 2 of the License, or
   9 * (at your option) any later version.
  10 *
  11 * This program is distributed in the hope that it will be useful,
  12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
  14 * GNU General Public License for more details.
  15 */
  16
  17#include <linux/err.h>
  18#include <linux/device.h>
  19#include <linux/irq.h>
  20#include <linux/gpio/consumer.h>
  21#include <linux/termios.h>
  22#include <linux/serial_core.h>
  23
  24#include "serial_mctrl_gpio.h"
  25
  26struct mctrl_gpios {
  27        struct uart_port *port;
  28        struct gpio_desc *gpio[UART_GPIO_MAX];
  29        int irq[UART_GPIO_MAX];
  30        unsigned int mctrl_prev;
  31        bool mctrl_on;
  32};
  33
  34static const struct {
  35        const char *name;
  36        unsigned int mctrl;
  37        bool dir_out;
  38} mctrl_gpios_desc[UART_GPIO_MAX] = {
  39        { "cts", TIOCM_CTS, false, },
  40        { "dsr", TIOCM_DSR, false, },
  41        { "dcd", TIOCM_CD, false, },
  42        { "rng", TIOCM_RNG, false, },
  43        { "rts", TIOCM_RTS, true, },
  44        { "dtr", TIOCM_DTR, true, },
  45        { "out1", TIOCM_OUT1, true, },
  46        { "out2", TIOCM_OUT2, true, },
  47};
  48
  49void mctrl_gpio_set(struct mctrl_gpios *gpios, unsigned int mctrl)
  50{
  51        enum mctrl_gpio_idx i;
  52        struct gpio_desc *desc_array[UART_GPIO_MAX];
  53        int value_array[UART_GPIO_MAX];
  54        unsigned int count = 0;
  55
  56        for (i = 0; i < UART_GPIO_MAX; i++)
  57                if (gpios->gpio[i] && mctrl_gpios_desc[i].dir_out) {
  58                        desc_array[count] = gpios->gpio[i];
  59                        value_array[count] = !!(mctrl & mctrl_gpios_desc[i].mctrl);
  60                        count++;
  61                }
  62        gpiod_set_array_value(count, desc_array, value_array);
  63}
  64EXPORT_SYMBOL_GPL(mctrl_gpio_set);
  65
  66struct gpio_desc *mctrl_gpio_to_gpiod(struct mctrl_gpios *gpios,
  67                                      enum mctrl_gpio_idx gidx)
  68{
  69        return gpios->gpio[gidx];
  70}
  71EXPORT_SYMBOL_GPL(mctrl_gpio_to_gpiod);
  72
  73unsigned int mctrl_gpio_get(struct mctrl_gpios *gpios, unsigned int *mctrl)
  74{
  75        enum mctrl_gpio_idx i;
  76
  77        for (i = 0; i < UART_GPIO_MAX; i++) {
  78                if (gpios->gpio[i] && !mctrl_gpios_desc[i].dir_out) {
  79                        if (gpiod_get_value(gpios->gpio[i]))
  80                                *mctrl |= mctrl_gpios_desc[i].mctrl;
  81                        else
  82                                *mctrl &= ~mctrl_gpios_desc[i].mctrl;
  83                }
  84        }
  85
  86        return *mctrl;
  87}
  88EXPORT_SYMBOL_GPL(mctrl_gpio_get);
  89
  90struct mctrl_gpios *mctrl_gpio_init_noauto(struct device *dev, unsigned int idx)
  91{
  92        struct mctrl_gpios *gpios;
  93        enum mctrl_gpio_idx i;
  94
  95        gpios = devm_kzalloc(dev, sizeof(*gpios), GFP_KERNEL);
  96        if (!gpios)
  97                return ERR_PTR(-ENOMEM);
  98
  99        for (i = 0; i < UART_GPIO_MAX; i++) {
 100                enum gpiod_flags flags;
 101
 102                if (mctrl_gpios_desc[i].dir_out)
 103                        flags = GPIOD_OUT_LOW;
 104                else
 105                        flags = GPIOD_IN;
 106
 107                gpios->gpio[i] =
 108                        devm_gpiod_get_index_optional(dev,
 109                                                      mctrl_gpios_desc[i].name,
 110                                                      idx, flags);
 111
 112                if (IS_ERR(gpios->gpio[i]))
 113                        return ERR_CAST(gpios->gpio[i]);
 114        }
 115
 116        return gpios;
 117}
 118EXPORT_SYMBOL_GPL(mctrl_gpio_init_noauto);
 119
 120#define MCTRL_ANY_DELTA (TIOCM_RI | TIOCM_DSR | TIOCM_CD | TIOCM_CTS)
 121static irqreturn_t mctrl_gpio_irq_handle(int irq, void *context)
 122{
 123        struct mctrl_gpios *gpios = context;
 124        struct uart_port *port = gpios->port;
 125        u32 mctrl = gpios->mctrl_prev;
 126        u32 mctrl_diff;
 127
 128        mctrl_gpio_get(gpios, &mctrl);
 129
 130        mctrl_diff = mctrl ^ gpios->mctrl_prev;
 131        gpios->mctrl_prev = mctrl;
 132
 133        if (mctrl_diff & MCTRL_ANY_DELTA && port->state != NULL) {
 134                if ((mctrl_diff & mctrl) & TIOCM_RI)
 135                        port->icount.rng++;
 136
 137                if ((mctrl_diff & mctrl) & TIOCM_DSR)
 138                        port->icount.dsr++;
 139
 140                if (mctrl_diff & TIOCM_CD)
 141                        uart_handle_dcd_change(port, mctrl & TIOCM_CD);
 142
 143                if (mctrl_diff & TIOCM_CTS)
 144                        uart_handle_cts_change(port, mctrl & TIOCM_CTS);
 145
 146                wake_up_interruptible(&port->state->port.delta_msr_wait);
 147        }
 148
 149        return IRQ_HANDLED;
 150}
 151
 152struct mctrl_gpios *mctrl_gpio_init(struct uart_port *port, unsigned int idx)
 153{
 154        struct mctrl_gpios *gpios;
 155        enum mctrl_gpio_idx i;
 156
 157        gpios = mctrl_gpio_init_noauto(port->dev, idx);
 158        if (IS_ERR(gpios))
 159                return gpios;
 160
 161        gpios->port = port;
 162
 163        for (i = 0; i < UART_GPIO_MAX; ++i) {
 164                int ret;
 165
 166                if (!gpios->gpio[i] || mctrl_gpios_desc[i].dir_out)
 167                        continue;
 168
 169                ret = gpiod_to_irq(gpios->gpio[i]);
 170                if (ret <= 0) {
 171                        dev_err(port->dev,
 172                                "failed to find corresponding irq for %s (idx=%d, err=%d)\n",
 173                                mctrl_gpios_desc[i].name, idx, ret);
 174                        return ERR_PTR(ret);
 175                }
 176                gpios->irq[i] = ret;
 177
 178                /* irqs should only be enabled in .enable_ms */
 179                irq_set_status_flags(gpios->irq[i], IRQ_NOAUTOEN);
 180
 181                ret = devm_request_irq(port->dev, gpios->irq[i],
 182                                       mctrl_gpio_irq_handle,
 183                                       IRQ_TYPE_EDGE_BOTH, dev_name(port->dev),
 184                                       gpios);
 185                if (ret) {
 186                        /* alternatively implement polling */
 187                        dev_err(port->dev,
 188                                "failed to request irq for %s (idx=%d, err=%d)\n",
 189                                mctrl_gpios_desc[i].name, idx, ret);
 190                        return ERR_PTR(ret);
 191                }
 192        }
 193
 194        return gpios;
 195}
 196EXPORT_SYMBOL_GPL(mctrl_gpio_init);
 197
 198void mctrl_gpio_free(struct device *dev, struct mctrl_gpios *gpios)
 199{
 200        enum mctrl_gpio_idx i;
 201
 202        for (i = 0; i < UART_GPIO_MAX; i++) {
 203                if (gpios->irq[i])
 204                        devm_free_irq(gpios->port->dev, gpios->irq[i], gpios);
 205
 206                if (gpios->gpio[i])
 207                        devm_gpiod_put(dev, gpios->gpio[i]);
 208        }
 209        devm_kfree(dev, gpios);
 210}
 211EXPORT_SYMBOL_GPL(mctrl_gpio_free);
 212
 213void mctrl_gpio_enable_ms(struct mctrl_gpios *gpios)
 214{
 215        enum mctrl_gpio_idx i;
 216
 217        /* .enable_ms may be called multiple times */
 218        if (gpios->mctrl_on)
 219                return;
 220
 221        gpios->mctrl_on = true;
 222
 223        /* get initial status of modem lines GPIOs */
 224        mctrl_gpio_get(gpios, &gpios->mctrl_prev);
 225
 226        for (i = 0; i < UART_GPIO_MAX; ++i) {
 227                if (!gpios->irq[i])
 228                        continue;
 229
 230                enable_irq(gpios->irq[i]);
 231        }
 232}
 233EXPORT_SYMBOL_GPL(mctrl_gpio_enable_ms);
 234
 235void mctrl_gpio_disable_ms(struct mctrl_gpios *gpios)
 236{
 237        enum mctrl_gpio_idx i;
 238
 239        if (!gpios->mctrl_on)
 240                return;
 241
 242        gpios->mctrl_on = false;
 243
 244        for (i = 0; i < UART_GPIO_MAX; ++i) {
 245                if (!gpios->irq[i])
 246                        continue;
 247
 248                disable_irq(gpios->irq[i]);
 249        }
 250}
 251EXPORT_SYMBOL_GPL(mctrl_gpio_disable_ms);
 252